append preview (#9)
* append preview * remove alert * revert page change * revert allert * backpress logic for all screens * shift scanning state * remove alert * save on device * revert save on device * on press back and outside fix for rename dialog
This commit is contained in:
28
App.tsx
28
App.tsx
@@ -1,5 +1,5 @@
|
||||
import React, {useState, useCallback, useEffect} from 'react';
|
||||
import {BackHandler} from 'react-native';
|
||||
import {Alert, BackHandler} from 'react-native';
|
||||
import HomeScreen from './src/screens/HomeScreen';
|
||||
import ScanScreen from './src/screens/ScanScreen';
|
||||
import ReviewScreen from './src/screens/ReviewScreen';
|
||||
@@ -38,6 +38,13 @@ export default function App() {
|
||||
[createDocument],
|
||||
);
|
||||
|
||||
const confirmDiscardReview = useCallback(() => {
|
||||
Alert.alert('Discard scan?', 'Going back will discard all scanned pages.', [
|
||||
{text: 'Stay', style: 'cancel'},
|
||||
{text: 'Discard', style: 'destructive', onPress: goHome},
|
||||
]);
|
||||
}, [goHome]);
|
||||
|
||||
const handleView = useCallback((doc: ScannedDocument) => {
|
||||
setViewingDoc(doc);
|
||||
setScreen('viewer');
|
||||
@@ -46,11 +53,22 @@ export default function App() {
|
||||
useEffect(() => {
|
||||
if (screen === 'home') return;
|
||||
const sub = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||
goHome();
|
||||
return true;
|
||||
if (screen === 'scan') {
|
||||
goHome();
|
||||
return true;
|
||||
}
|
||||
if (screen === 'review') {
|
||||
confirmDiscardReview();
|
||||
return true;
|
||||
}
|
||||
if (screen === 'viewer') {
|
||||
goHome();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return () => sub.remove();
|
||||
}, [screen, goHome]);
|
||||
}, [screen, goHome, confirmDiscardReview]);
|
||||
|
||||
const handleRenameFromViewer = useCallback(
|
||||
(name: string) => {
|
||||
@@ -74,7 +92,7 @@ export default function App() {
|
||||
pages={pendingPages}
|
||||
onSave={handleSaveDocument}
|
||||
onAddMore={() => setScreen('scan')}
|
||||
onCancel={goHome}
|
||||
onCancel={confirmDiscardReview}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
StyleSheet,
|
||||
Dimensions,
|
||||
StatusBar,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import {ScannedPage, FilterMode} from '../types';
|
||||
import FilterPicker from '../components/FilterPicker';
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, {useState, useRef, useCallback, useEffect} from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
StyleSheet,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
@@ -34,7 +33,6 @@ async function copyToPermanent(tempUri: string): Promise<string> {
|
||||
export default function ScanScreen({onComplete, onCancel}: Props) {
|
||||
const t = useTheme();
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [pageCount, setPageCount] = useState(0);
|
||||
const pagesRef = useRef<ScannedPage[]>([]);
|
||||
|
||||
const scan = useCallback(async () => {
|
||||
@@ -69,17 +67,8 @@ export default function ScanScreen({onComplete, onCancel}: Props) {
|
||||
}));
|
||||
|
||||
pagesRef.current = [...pagesRef.current, ...newPages];
|
||||
setPageCount(pagesRef.current.length);
|
||||
setScanning(false);
|
||||
|
||||
Alert.alert(
|
||||
`${pagesRef.current.length} page${pagesRef.current.length !== 1 ? 's' : ''} scanned`,
|
||||
'Add more pages or finish?',
|
||||
[
|
||||
{text: 'Add More', onPress: scan},
|
||||
{text: 'Finish', style: 'default', onPress: () => onComplete(pagesRef.current)},
|
||||
],
|
||||
);
|
||||
onComplete(pagesRef.current);
|
||||
} catch (err: any) {
|
||||
setScanning(false);
|
||||
Alert.alert('Scan Error', err?.message ?? 'Could not scan document.');
|
||||
@@ -97,21 +86,7 @@ export default function ScanScreen({onComplete, onCancel}: Props) {
|
||||
{scanning && (
|
||||
<View style={styles.center}>
|
||||
<ActivityIndicator size="large" color={t.accent} />
|
||||
<Text style={[styles.hint, {color: t.textSecondary}]}>Opening camera…</Text>
|
||||
</View>
|
||||
)}
|
||||
{!scanning && pageCount > 0 && (
|
||||
<View style={styles.center}>
|
||||
<Text style={[styles.count, {color: t.text}]}>{pageCount}</Text>
|
||||
<Text style={[styles.pagesLabel, {color: t.textSecondary}]}>pages ready</Text>
|
||||
<View style={styles.actions}>
|
||||
<TouchableOpacity style={[styles.btnSecondary, {borderColor: t.accent}]} onPress={scan}>
|
||||
<Text style={[styles.btnSecondaryText, {color: t.accent}]}>Add More</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.btnPrimary} onPress={() => onComplete(pagesRef.current)}>
|
||||
<Text style={styles.btnPrimaryText}>Finish</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={[styles.hint, {color: t.textSecondary}]}>Processing pages…</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -122,21 +97,4 @@ const styles = StyleSheet.create({
|
||||
container: {flex: 1},
|
||||
center: {flex: 1, alignItems: 'center', justifyContent: 'center', gap: 12},
|
||||
hint: {fontSize: 15, marginTop: 12},
|
||||
count: {fontSize: 72, fontWeight: '700'},
|
||||
pagesLabel: {fontSize: 18},
|
||||
actions: {flexDirection: 'row', gap: 16, marginTop: 24},
|
||||
btnPrimary: {
|
||||
backgroundColor: '#007AFF',
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 28,
|
||||
},
|
||||
btnPrimaryText: {color: '#fff', fontSize: 16, fontWeight: '600'},
|
||||
btnSecondary: {
|
||||
borderWidth: 1,
|
||||
paddingHorizontal: 32,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 28,
|
||||
},
|
||||
btnSecondaryText: {fontSize: 16, fontWeight: '600'},
|
||||
});
|
||||
|
||||
@@ -140,40 +140,49 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{generating && (
|
||||
{generating && (
|
||||
<View style={styles.loadingOverlay}>
|
||||
<ActivityIndicator size="large" color="#fff" />
|
||||
<Text style={styles.loadingText}>Generating PDF…</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Modal visible={renaming} transparent animationType="fade">
|
||||
<View style={styles.modalBg}>
|
||||
<View style={[styles.modalBox, {backgroundColor: t.surface}]}>
|
||||
<Text style={[styles.modalTitle, {color: t.text}]}>Rename Document</Text>
|
||||
<TextInput
|
||||
style={[styles.modalInput, {backgroundColor: t.bg, color: t.text, borderColor: t.border}]}
|
||||
value={renameText}
|
||||
onChangeText={setRenameText}
|
||||
autoFocus
|
||||
selectTextOnFocus
|
||||
placeholderTextColor={t.textSecondary}
|
||||
/>
|
||||
<View style={styles.modalActions}>
|
||||
<TouchableOpacity onPress={() => setRenaming(false)} style={styles.modalBtn}>
|
||||
<Text style={[styles.modalCancel, {color: t.textSecondary}]}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (renameText.trim()) onRename(renameText.trim());
|
||||
setRenaming(false);
|
||||
}}
|
||||
style={styles.modalBtn}>
|
||||
<Text style={[styles.modalConfirm, {color: t.accent}]}>Rename</Text>
|
||||
</TouchableOpacity>
|
||||
<Modal
|
||||
visible={renaming}
|
||||
transparent
|
||||
animationType="fade"
|
||||
onRequestClose={() => setRenaming(false)}>
|
||||
<TouchableOpacity
|
||||
style={styles.modalBg}
|
||||
activeOpacity={1}
|
||||
onPress={() => setRenaming(false)}>
|
||||
<TouchableOpacity activeOpacity={1} onPress={() => {}}>
|
||||
<View style={[styles.modalBox, {backgroundColor: t.surface}]}>
|
||||
<Text style={[styles.modalTitle, {color: t.text}]}>Rename Document</Text>
|
||||
<TextInput
|
||||
style={[styles.modalInput, {backgroundColor: t.bg, color: t.text, borderColor: t.border}]}
|
||||
value={renameText}
|
||||
onChangeText={setRenameText}
|
||||
autoFocus
|
||||
selectTextOnFocus
|
||||
placeholderTextColor={t.textSecondary}
|
||||
/>
|
||||
<View style={styles.modalActions}>
|
||||
<TouchableOpacity onPress={() => setRenaming(false)} style={styles.modalBtn}>
|
||||
<Text style={[styles.modalCancel, {color: t.danger}]}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
if (renameText.trim()) onRename(renameText.trim());
|
||||
setRenaming(false);
|
||||
}}
|
||||
style={styles.modalBtn}>
|
||||
<Text style={[styles.modalConfirm, {color: t.accent}]}>Rename</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</TouchableOpacity>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user