-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(mobile): [Scanner] add scanner view and integrate with AI api
- Loading branch information
Quốc Khánh
committed
Jul 15, 2024
1 parent
3f97df8
commit e9bb6a8
Showing
9 changed files
with
308 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,197 @@ | ||
import { Toolbar } from '@/components/common/toolbar' | ||
import { Text, View } from 'react-native' | ||
import { Button } from '@/components/ui/button' | ||
import { Text } from '@/components/ui/text' | ||
import { getAITransactionData } from '@/mutations/transaction' | ||
import { t } from '@lingui/macro' | ||
import { useLingui } from '@lingui/react' | ||
import { useMutation } from '@tanstack/react-query' | ||
import { type CameraType, CameraView, useCameraPermissions } from 'expo-camera' | ||
import * as Haptics from 'expo-haptics' | ||
import { SaveFormat, manipulateAsync } from 'expo-image-manipulator' | ||
import * as ImagePicker from 'expo-image-picker' | ||
import { useRouter } from 'expo-router' | ||
import { | ||
CameraIcon, | ||
ImagesIcon, | ||
LoaderIcon, | ||
SwitchCameraIcon, | ||
} from 'lucide-react-native' | ||
import { cssInterop } from 'nativewind' | ||
import { useRef, useState } from 'react' | ||
import { Alert } from 'react-native' | ||
import { ImageBackground, View } from 'react-native' | ||
|
||
cssInterop(CameraView, { | ||
className: { | ||
target: 'style', | ||
}, | ||
}) | ||
|
||
export default function ScannerScreen() { | ||
const camera = useRef<CameraView>(null) | ||
const router = useRouter() | ||
const [facing, setFacing] = useState<CameraType>('back') | ||
const [permission, requestPermission] = useCameraPermissions() | ||
const [imageUri, setImageUri] = useState<string | null>(null) | ||
const { i18n } = useLingui() | ||
|
||
const { mutateAsync } = useMutation({ | ||
mutationFn: getAITransactionData, | ||
onError(error) { | ||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error) | ||
Alert.alert(error.message) | ||
setImageUri(null) | ||
}, | ||
onSuccess(result) { | ||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success) | ||
// router.push() | ||
if (result.amount) { | ||
router.push({ | ||
pathname: '/transaction/new-record', | ||
// biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||
params: result as any, | ||
}) | ||
} | ||
setImageUri(null) | ||
}, | ||
}) | ||
|
||
function toggleFacing() { | ||
Haptics.selectionAsync() | ||
setFacing(facing === 'back' ? 'front' : 'back') | ||
} | ||
|
||
async function takePicture() { | ||
Haptics.selectionAsync() | ||
const result = await camera.current?.takePictureAsync({ | ||
scale: 0.5, | ||
quality: 0.5, | ||
}) | ||
if (result?.uri) { | ||
const manipResult = await manipulateAsync( | ||
result.uri, | ||
[ | ||
{ | ||
resize: { width: 1024 }, | ||
}, | ||
], | ||
{ | ||
compress: 0.5, | ||
format: SaveFormat.WEBP, | ||
}, | ||
) | ||
setImageUri(manipResult.uri) | ||
await mutateAsync(manipResult.uri) | ||
} | ||
} | ||
|
||
async function pickImage() { | ||
Haptics.selectionAsync() | ||
const result = await ImagePicker.launchImageLibraryAsync({ | ||
allowsMultipleSelection: false, | ||
mediaTypes: ImagePicker.MediaTypeOptions.Images, | ||
allowsEditing: false, | ||
quality: 0.5, | ||
}) | ||
if (result.canceled) { | ||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error) | ||
return | ||
} | ||
const manipResult = await manipulateAsync( | ||
result.assets[0].uri, | ||
[ | ||
{ | ||
resize: { width: 1024 }, | ||
}, | ||
], | ||
{ | ||
compress: 0.5, | ||
format: SaveFormat.WEBP, | ||
}, | ||
) | ||
setImageUri(manipResult.uri) | ||
await mutateAsync(manipResult.uri) | ||
} | ||
|
||
if (!permission) { | ||
// Camera permissions are still loading. | ||
return ( | ||
<View className="flex-1 items-center bg-muted justify-center"> | ||
<LoaderIcon className="size-7 animate-spin text-primary" /> | ||
</View> | ||
) | ||
} | ||
|
||
if (!permission.granted) { | ||
// Camera permissions are not granted. | ||
return ( | ||
<View className="flex-1 items-center bg-muted gap-4 justify-center"> | ||
<CameraIcon className="size-16 text-muted-foreground" /> | ||
<Text>{t(i18n)`Camera permissions are not granted`}</Text> | ||
<Button variant="outline" onPress={requestPermission}> | ||
<Text>{t(i18n)`Grant camera permissions`}</Text> | ||
</Button> | ||
</View> | ||
) | ||
} | ||
|
||
if (imageUri) { | ||
return ( | ||
<View className="flex-1 bg-card"> | ||
<ImageBackground | ||
source={{ uri: imageUri }} | ||
className="flex-1 items-center" | ||
> | ||
<View className="top-6 bg-background/50 p-2 px-4 rounded-md"> | ||
<Text>{t(i18n)`Processing transaction...`}</Text> | ||
</View> | ||
<Button | ||
variant="secondary" | ||
size="icon" | ||
className="w-auto h-auto p-1 absolute bottom-6 rounded-full bg-primary-foreground" | ||
disabled | ||
> | ||
<View className="w-16 h-16 bg-primary-foreground border-2 border-primary justify-center items-center rounded-full"> | ||
<LoaderIcon className="size-7 animate-spin text-primary" /> | ||
</View> | ||
</Button> | ||
</ImageBackground> | ||
</View> | ||
) | ||
} | ||
|
||
return ( | ||
<View className="flex-1 bg-card"> | ||
<Text className="font-sans">Scanner Screen</Text> | ||
<Toolbar /> | ||
<CameraView ref={camera} className="flex-1 items-center" facing={facing}> | ||
<View className="top-6 bg-background/50 p-2 px-4 rounded-md"> | ||
<Text>{t(i18n)`Take a picture of your transaction`}</Text> | ||
</View> | ||
<View className="absolute bottom-6 left-6 right-6 flex-row items-center justify-between gap-4"> | ||
<Button | ||
variant="secondary" | ||
size="icon" | ||
className="rounded-full w-12 h-12" | ||
onPress={pickImage} | ||
> | ||
<ImagesIcon className="size-6 text-primary" /> | ||
</Button> | ||
<Button | ||
variant="secondary" | ||
size="icon" | ||
className="w-auto h-auto p-1 rounded-full bg-primary-foreground" | ||
onPress={takePicture} | ||
> | ||
<View className="w-16 h-16 bg-primary-foreground border-2 border-primary rounded-full" /> | ||
</Button> | ||
<Button | ||
variant="secondary" | ||
size="icon" | ||
className="rounded-full w-12 h-12" | ||
onPress={toggleFacing} | ||
> | ||
<SwitchCameraIcon className="size-6 text-primary" /> | ||
</Button> | ||
</View> | ||
</CameraView> | ||
</View> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.