[Odometer] Image capture for native#82432
Conversation
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
603fe12 to
b58813d
Compare
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
|
🚧 @Julesssss has triggered a test Expensify/App build. You can view the workflow run here. |
This comment has been minimized.
This comment has been minimized.
…patible with react-compiler
|
Failing TypeScript check is caused only by the lack of translations - besides that all is well ✅ |
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index 9c495ed5..25e4c4c2 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -7551,6 +7551,9 @@ Fordern Sie Spesendetails wie Belege und Beschreibungen an, legen Sie Limits und
endTitle: 'Kilometerzähler-Endfoto',
deleteOdometerPhoto: 'Kilometerzähler-Foto löschen',
deleteOdometerPhotoConfirmation: 'Möchtest du dieses Kilometerzählerfoto wirklich löschen?',
+ cameraAccessRequired: 'Für das Aufnehmen von Bildern ist der Kamerazugriff erforderlich.',
+ snapPhotoStart: '<muted-text-label>Machen Sie zu <strong>Beginn</strong> Ihrer Fahrt ein Foto von Ihrem Kilometerzähler.</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>Machen Sie ein Foto von Ihrem Kilometerzähler am <strong>Ende</strong> Ihrer Fahrt.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 1f1c9f1f..b17c07e0 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -7573,6 +7573,9 @@ Rendez obligatoires des informations de dépense comme les reçus et les descrip
endTitle: 'Photo du compteur (arrivée)',
deleteOdometerPhoto: 'Supprimer la photo du compteur kilométrique',
deleteOdometerPhotoConfirmation: 'Voulez-vous vraiment supprimer cette photo de l’odomètre ?',
+ cameraAccessRequired: "L'accès à l'appareil photo est requis pour prendre des photos.",
+ snapPhotoStart: '<muted-text-label>Prenez une photo de votre compteur kilométrique au <strong>début</strong> de votre trajet.</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>Prenez une photo de votre compteur kilométrique à la <strong>fin</strong> de votre trajet.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 20f9ef14..00d02351 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -7537,6 +7537,9 @@ Richiedi dettagli sulle spese come ricevute e descrizioni, imposta limiti e valo
endTitle: 'Foto contachilometri finale',
deleteOdometerPhoto: 'Elimina foto del contachilometri',
deleteOdometerPhotoConfirmation: 'Sei sicuro di voler eliminare questa foto del contachilometri?',
+ cameraAccessRequired: 'Per scattare foto è necessario l’accesso alla fotocamera.',
+ snapPhotoStart: "<muted-text-label>Scatta una foto del contachilometri all'<strong>inizio</strong> del viaggio.</muted-text-label>",
+ snapPhotoEnd: '<muted-text-label>Scatta una foto del contachilometri alla <strong>fine</strong> del viaggio.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index e4aaa48a..0233d855 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -7462,6 +7462,9 @@ ${reportName}
endTitle: '走行距離計(終了時)の写真',
deleteOdometerPhoto: '走行距離計の写真を削除',
deleteOdometerPhotoConfirmation: 'この走行距離計の写真を削除してもよろしいですか?',
+ cameraAccessRequired: '写真を撮影するにはカメラへのアクセス権限が必要です。',
+ snapPhotoStart: '<muted-text-label>移動を<strong>開始</strong>するときに、走行距離計の写真を撮影してください。</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>走行の<strong>終了時</strong>に、オドメーターの写真を撮影してください。</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 140fb8b0..59900a88 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -7516,6 +7516,9 @@ Vereis onkostendetails zoals bonnen en beschrijvingen, stel limieten en standaar
endTitle: 'Eindfoto kilometerteller',
deleteOdometerPhoto: 'Kilometerstandfoto verwijderen',
deleteOdometerPhotoConfirmation: 'Weet je zeker dat je deze kilometertellerfoto wilt verwijderen?',
+ cameraAccessRequired: 'Cameratoegang is nodig om foto’s te maken.',
+ snapPhotoStart: '<muted-text-label>Maak een foto van je kilometerteller aan het <strong>begin</strong> van je rit.</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>Maak een foto van je kilometerteller aan het <strong>einde</strong> van je rit.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index e0dbafea..43da6c83 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -7503,6 +7503,9 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
endTitle: 'Końcowe zdjęcie licznika przebiegu',
deleteOdometerPhoto: 'Usuń zdjęcie licznika przebiegu',
deleteOdometerPhotoConfirmation: 'Na pewno chcesz usunąć to zdjęcie licznika?',
+ cameraAccessRequired: 'Aby robić zdjęcia, wymagany jest dostęp do aparatu.',
+ snapPhotoStart: '<muted-text-label>Zrób zdjęcie licznika kilometrów na <strong>początku</strong> podróży.</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>Zrób zdjęcie licznika kilometrów na <strong>koniec</strong> swojej podróży.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index 4026f03c..4bd4d888 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -7508,6 +7508,9 @@ Exija dados de despesas como recibos e descrições, defina limites e padrões e
endTitle: 'Foto final do hodômetro',
deleteOdometerPhoto: 'Excluir foto do odômetro',
deleteOdometerPhotoConfirmation: 'Tem certeza de que deseja excluir esta foto do odômetro?',
+ cameraAccessRequired: 'É necessário acessar a câmera para tirar fotos.',
+ snapPhotoStart: '<muted-text-label>Tire uma foto do odômetro no <strong>início</strong> da sua viagem.</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>Tire uma foto do odômetro no <strong>final</strong> da sua viagem.</muted-text-label>',
},
},
gps: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 98e3dea5..cd91887d 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -7344,6 +7344,9 @@ ${reportName}
endTitle: '结束里程表照片',
deleteOdometerPhoto: '删除里程表照片',
deleteOdometerPhotoConfirmation: '确定要删除这个里程表照片吗?',
+ cameraAccessRequired: '拍照需要启用相机访问权限。',
+ snapPhotoStart: '<muted-text-label>在行程<strong>开始</strong>时拍一张里程表照片。</muted-text-label>',
+ snapPhotoEnd: '<muted-text-label>在行程<strong>结束</strong>时拍一张里程表的照片。</muted-text-label>',
},
},
gps: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
|
prettier check failing - in unrelated files that weren't changed in this PR |
|
The code changes look fine. I'll complete my testing soon (early tomorrow) |
joekaufmanexpensify
left a comment
There was a problem hiding this comment.
Whats next project
|
🚧 @Julesssss has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
@jakubkalinski0 Please merge new main |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-02-24.at.12.52.17.moviOS: HybridAppScreen.Recording.2026-02-24.at.12.42.02.mov |
|
@jakubkalinski0 NAB: but good to update: #82432 (comment) |
|
@DylanDylann I am aware this comment is pretty valid but wasn't sure what to do with all the other magic numbers with the same values that I used. I will add those values used by me to variables now but I will leave all otherr untouched |
|
Thanks for the detailed review. I created this follow up improvement issue to help us move forward and added it to the tracker. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 @Julesssss has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/Julesssss in version: 9.3.26-0 🚀
|
|
Deploy Blocker #83526 was identified to be related to this PR. |
|
🚀 Deployed to production by https://github.com/puneetlath in version: 9.3.26-8 🚀
|
Explanation of Change
This PR implements the next part of the Odometer feature, aligned with Phase 1 task 1.D. This PR focuses on the native implementation of the image capture flow with mobile web implementation to follow
src/pages/iou/request/step/IOURequestStepOdometerImage/index.native.tsx. We intentionally did not reuseIOURequestStepScanbecause that screen includes receipt‑specific behaviors (SmartScan, receipt state logic). The implementation covers all the following aspects:Due to duplicated logic across
IOURequestStepOdometerImageandIOURequestStepScanthere is a plan to introduce more abstraction for the camera related logic/features in a followup once the Odometer project is done.Fixed Issues
$ #77266
PROPOSAL: N/A
Tests
Important
Caution
Use Android emulator or a physical device for testing - explanation above ^
Prerequisites:
Turn off camera permissions for Expensify -> the most convenient for you would probably be to set
"Ask every time"FAB-> go to"Track distance"-> choose"Odometer"tab`Thumbnail-> you should see the screen shown below"Continue"-> verify that you are shown a native prompt asking you to allow Expensify to use the camera"Only this time"(pressing"While using this app"would behave the same but will require you to turn off camera permissions for Expensify once again later) -> verify that you now have access to the camera (the screen from step 2 is gone and you see a working camera display like the one below)Important
If your device has no back camera then you will see an infinite loader (same behavior as in the scan component)

Important
If your device has no flash then the

Bolticon of flash in the top right corner will be hidden - in order to test the flash itself you have to do it on a physical device no matter if your simulator "has" a flashOdometer pagewith the photo you have just taken being displayed correctly in the thumbnailthumbnailonce again then you are brought to theImage Previewand it displays the photo correctlyThree dots menuand verify that when you press"Replace"then you are brought to theCamera photo captureand that it works correctly"While using this app"earlier)"Don't allow"when asked about camera permissions -> verify that you are still at this screen"Continue"once again and verify that you are shown this prompt belowImportant
You will probably be shown this prompt more than once when pressing

"Continue"before you are shown the prompt redirecting you to"Settings". If that happens then simply press"Don't allow"until you see a prompt from step 11"Cancel"the prompt should be simply closed but when you press"Settings"verify that you are brought to the device settingsExpensify(either to"While using this app"or"Ask every time") and verify that when you go back to theExpensifyyou are brought:Camera photo captureif you chose"While using this app""Take a photo"if you chose"Ask every time"-> then you will have to press continue and you will be prompted once again by the original prompt to"Allow Expensify...".When you are finally on the
Camera photo captureverify that it works properly.Galleryicon at the bottom left corner and verify that all the 3 options work properlyEnd odometer photo(although if everything worked properly forStart photothen it will work the same for theEnd photo)End odometer photoWarning
When testing
End odometer photoafter theStart odometer phot"your camera permissions forExpensifywere probably set to"Don't allow"after the step 9 so you will have to change it to"Ask every time"when testing the second photo flowOffline tests
Same as Tests
QA Steps
Same as Tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Steps 1 through 7:
1to7.mp4
Steps 8 through 12:
8to12.mp4
Step 13:
13.mp4
iOS: Native
iOS simulator does not support camera so you can actually observe the correct behavior for this situation (lack of back camera) mentioned in the step 4 Important section 1 - i.e. an infinite loader:
ios.mp4