Skip to content

Commit

Permalink
iOS Document Scanner fix (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
mazenchami authored Apr 4, 2024
1 parent 92b8150 commit da4cbdb
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 137 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-pugs-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@infinitered/infinite-red-ai": patch
---

isolating document scanner to only android (fixes iOS crash)
171 changes: 171 additions & 0 deletions apps/InfiniteRedAI/app/screens/DocumentScannerScreen.android.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { FC } from "react"
import { observer } from "mobx-react-lite"
import { ViewStyle, View, ImageStyle, TextStyle } from "react-native"
import { NativeStackScreenProps } from "@react-navigation/native-stack"
import { AppStackScreenProps } from "../navigators"
import { Screen, Text, Icon, Button, ListItem, TextField, Toggle } from "../components"
import { useTypedNavigation } from "../navigators/useTypedNavigation"
import {
ResultFormatOptions,
ScannerModeOptions,
launchDocumentScannerAsync,
} from "@infinitered/react-native-mlkit-document-scanner"
import { spacing } from "app/theme"

interface DocumentScannerScreenProps
extends NativeStackScreenProps<AppStackScreenProps<"DocumentScanner">> {}

export const DocumentScannerScreen: FC<DocumentScannerScreenProps> = observer(
function DocumentScannerScreen() {
const navigation = useTypedNavigation<"DocumentScanner">()
const [result, setResult] = React.useState<string>("")
const [allowGallery, setAllowGallery] = React.useState<boolean>(false)
const [mode, setMode] = React.useState<ScannerModeOptions>(ScannerModeOptions.FULL)
const [resultFormat, setResultFormat] = React.useState<ResultFormatOptions>(
ResultFormatOptions.ALL,
)
const [pageLimit, setPageLimit] = React.useState<number>(1)

return (
<Screen
style={$root}
preset="scroll"
safeAreaEdges={["top", "bottom"]}
contentContainerStyle={{ paddingBottom: spacing.lg }}
>
<View>
<Icon icon={"back"} onPress={() => navigation.navigate("Home")} style={$backIcon} />
<Text preset={"heading"} text="Document Scanner" />
<Text style={$description}>
Configure scanner options and tap `Scan Document` to launch the modal
</Text>

<View style={{ gap: spacing.lg }}>
<ListItem
text="Page Limit"
textStyle={$optionText}
RightComponent={
<TextField
value={pageLimit.toString()}
onChangeText={(text) => {
// parse string to int and make sure if not or less than 1, set to 1
const num = parseInt(text)
setPageLimit(isNaN(num) || num < 1 ? 1 : num)
}}
containerStyle={$pageLimit}
textAlign="right"
/>
}
/>
<ListItem
text="Allow Gallery Import"
textStyle={$optionText}
RightComponent={
<Toggle
variant="switch"
value={allowGallery}
onPress={() => setAllowGallery(!allowGallery)}
containerStyle={$centerSwitch}
/>
}
/>
<ListItem
text="Mode"
textStyle={$optionText}
RightComponent={
<View style={$radioCol}>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.BASE}
label="Base"
onPress={() => setMode(ScannerModeOptions.BASE)}
/>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.BASE_WITH_FILTER}
label="Base w/ Filter"
onPress={() => setMode(ScannerModeOptions.BASE_WITH_FILTER)}
/>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.FULL}
label="Full"
onPress={() => setMode(ScannerModeOptions.FULL)}
/>
</View>
}
/>
<ListItem
text="Result Formats"
textStyle={$optionText}
RightComponent={
<View style={$radioCol}>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.ALL}
label="All"
onPress={() => setResultFormat(ResultFormatOptions.ALL)}
/>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.PDF}
label="PDF"
onPress={() => setResultFormat(ResultFormatOptions.PDF)}
/>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.JPEG}
label="JPEG"
onPress={() => setResultFormat(ResultFormatOptions.JPEG)}
/>
</View>
}
/>

<Button
onPress={async () => {
const result = await launchDocumentScannerAsync({
pageLimit,
galleryImportAllowed: allowGallery,
resultFormats: resultFormat,
scannerMode: mode,
})
setResult(JSON.stringify(result))
}}
text="Scan Document"
/>
</View>
</View>

<Text style={$description}>Result: {result}</Text>
</Screen>
)
},
)

const $root: ViewStyle = {
flex: 1,
padding: 16,
display: "flex",
flexDirection: "column",
}
const $backIcon: ImageStyle = { marginVertical: 8 }

const $description: TextStyle = {
marginVertical: 8,
color: "rgba(0,0,0,0.6)",
}

const $optionText: TextStyle = {
alignSelf: "flex-start",
fontWeight: "bold",
}

const $radioCol: ViewStyle = {
width: "50%",
gap: spacing.md,
}

const $centerSwitch: ViewStyle = { alignSelf: "center" }

const $pageLimit: ViewStyle = { flex: 1 }
141 changes: 5 additions & 136 deletions apps/InfiniteRedAI/app/screens/DocumentScannerScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
import React, { FC } from "react"
import { observer } from "mobx-react-lite"
import { ViewStyle, View, ImageStyle, TextStyle } from "react-native"
import { ViewStyle, View, ImageStyle } from "react-native"
import { NativeStackScreenProps } from "@react-navigation/native-stack"
import { AppStackScreenProps } from "../navigators"
import { Screen, Text, Icon, Button, ListItem, TextField, Toggle } from "../components"
import { Screen, Text, Icon } from "../components"
import { useTypedNavigation } from "../navigators/useTypedNavigation"
import {
ResultFormatOptions,
ScannerModeOptions,
launchDocumentScannerAsync,
} from "@infinitered/react-native-mlkit-document-scanner"
import { spacing } from "app/theme"

interface DocumentScannerScreenProps
extends NativeStackScreenProps<AppStackScreenProps<"DocumentScanner">> {}

export const DocumentScannerScreen: FC<DocumentScannerScreenProps> = observer(
function DocumentScannerScreen() {
const navigation = useTypedNavigation<"ImageLabeling">()
const [result, setResult] = React.useState<string>("")
const [allowGallery, setAllowGallery] = React.useState<boolean>(false)
const [mode, setMode] = React.useState<ScannerModeOptions>(ScannerModeOptions.FULL)
const [resultFormat, setResultFormat] = React.useState<ResultFormatOptions>(
ResultFormatOptions.ALL,
)
const [pageLimit, setPageLimit] = React.useState<number>(1)
const navigation = useTypedNavigation<"DocumentScanner">()

return (
<Screen
Expand All @@ -35,109 +23,8 @@ export const DocumentScannerScreen: FC<DocumentScannerScreenProps> = observer(
>
<View>
<Icon icon={"back"} onPress={() => navigation.navigate("Home")} style={$backIcon} />
<Text preset={"heading"} text="Document Scanner" />
<Text style={$description}>
Configure scanner options and tap `Scan Document` to launch the modal
</Text>

<View style={{ gap: spacing.lg }}>
<ListItem
text="Page Limit"
textStyle={$optionText}
RightComponent={
<TextField
value={pageLimit.toString()}
onChangeText={(text) => {
// parse string to int and make sure if not or less than 1, set to 1
const num = parseInt(text)
setPageLimit(isNaN(num) || num < 1 ? 1 : num)
}}
containerStyle={$pageLimit}
textAlign="right"
/>
}
/>
<ListItem
text="Allow Gallery Import"
textStyle={$optionText}
RightComponent={
<Toggle
variant="switch"
value={allowGallery}
onPress={() => setAllowGallery(!allowGallery)}
containerStyle={$centerSwitch}
/>
}
/>
<ListItem
text="Mode"
textStyle={$optionText}
RightComponent={
<View style={$radioCol}>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.BASE}
label="Base"
onPress={() => setMode(ScannerModeOptions.BASE)}
/>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.BASE_WITH_FILTER}
label="Base w/ Filter"
onPress={() => setMode(ScannerModeOptions.BASE_WITH_FILTER)}
/>
<Toggle
variant="radio"
value={mode === ScannerModeOptions.FULL}
label="Full"
onPress={() => setMode(ScannerModeOptions.FULL)}
/>
</View>
}
/>
<ListItem
text="Result Formats"
textStyle={$optionText}
RightComponent={
<View style={$radioCol}>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.ALL}
label="All"
onPress={() => setResultFormat(ResultFormatOptions.ALL)}
/>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.PDF}
label="PDF"
onPress={() => setResultFormat(ResultFormatOptions.PDF)}
/>
<Toggle
variant="radio"
value={resultFormat === ResultFormatOptions.JPEG}
label="JPEG"
onPress={() => setResultFormat(ResultFormatOptions.JPEG)}
/>
</View>
}
/>

<Button
onPress={async () => {
const result = await launchDocumentScannerAsync({
pageLimit,
galleryImportAllowed: allowGallery,
resultFormats: resultFormat,
scannerMode: mode,
})
setResult(JSON.stringify(result))
}}
text="Scan Document"
/>
</View>
<Text preset={"heading"} text="Not available on iOS yet!" />
</View>

<Text style={$description}>Result: {result}</Text>
</Screen>
)
},
Expand All @@ -149,23 +36,5 @@ const $root: ViewStyle = {
display: "flex",
flexDirection: "column",
}
const $backIcon: ImageStyle = { marginVertical: 8 }

const $description: TextStyle = {
marginVertical: 8,
color: "rgba(0,0,0,0.6)",
}

const $optionText: TextStyle = {
alignSelf: "flex-start",
fontWeight: "bold",
}

const $radioCol: ViewStyle = {
width: "50%",
gap: spacing.md,
}

const $centerSwitch: ViewStyle = { alignSelf: "center" }

const $pageLimit: ViewStyle = { flex: 1 }
const $backIcon: ImageStyle = { marginVertical: 8 }
2 changes: 1 addition & 1 deletion apps/InfiniteRedAI/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"bundle:android": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res",
"release:ios": "echo 'Not implemented yet: release:ios. Use Xcode. More info: https://reactnative.dev/docs/next/publishing-to-app-store'",
"release:android": "cd android && rm -rf app/src/main/res/drawable-* && ./gradlew assembleRelease && cd - && echo 'APK generated in ./android/app/build/outputs/apk/release/app-release.apk'",
"clean": "rm -rf node_modulesios /build ios android && watchman watch-del-all",
"clean": "rm -rf node_modules /build ios android && watchman watch-del-all",
"clean-all": "npx react-native clean-project-auto",
"expo:start": "expo start",
"prebuild": "expo prebuild"
Expand Down

0 comments on commit da4cbdb

Please sign in to comment.