Skip to content

feat: MRZ data confirmation for NFC scanning#1767

Open
shazarre wants to merge 7 commits intodevfrom
shazarre/feat/mrz_data_confirmation
Open

feat: MRZ data confirmation for NFC scanning#1767
shazarre wants to merge 7 commits intodevfrom
shazarre/feat/mrz_data_confirmation

Conversation

@shazarre
Copy link
Collaborator

@shazarre shazarre commented Feb 17, 2026

Description

This PR adds support for confirming MRZ data needed for later NFC scanning utilizing euclid DataConfirmation screen.

Tested

Tested on an example MRZ data (https://www.idenfy.com/blog/machine-readable-zone/) and a real passport.

How to QA

Scan MRZ data, compare with the result on the screen (change something and observe analytics data).

Blocked by https://github.com/selfxyz/euclid/pull/71

TODO

  • test on iOS

Summary by CodeRabbit

  • New Features

    • Date-capable input component (alphanumeric + date modes) with in-app date picker.
    • Data Confirmation screen to review/edit extracted document fields; navigation updated accordingly.
    • SDK: MRZ date-parsing helpers exposed and new analytics events for data confirmation and MRZ modifications.
  • Tests

    • Added tests for Data Confirmation flows, MRZ diffs, and MRZ date parsing.
  • Chores

    • Added react-native-date-picker dependency and enabled its transformation in tests.

@shazarre shazarre self-assigned this Feb 17, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 90f3bd6d-677f-4746-b8a2-cc6535b57c8f

📥 Commits

Reviewing files that changed from the base of the PR and between 357dd6a and 02e88c1.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • app/jest.config.cjs
  • app/package.json
  • app/src/providers/selfClientProvider.tsx
  • packages/mobile-sdk-alpha/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/providers/selfClientProvider.tsx

📝 Walkthrough

Walkthrough

Adds a DataConfirmation screen and InputField (with date picker), MRZ date-parsing helpers and re-exports in the SDK, new analytics events, navigation wiring to DataConfirmation, utilities/tests for string diffing, and the react-native-date-picker dependency.

Changes

Cohort / File(s) Summary
Input Component & Dependency
app/package.json, app/src/components/InputField.tsx
Added react-native-date-picker dependency and a new InputField component supporting alphanumeric and date modes, with sanitization and date-picker modal.
Navigation & Event Routing
app/src/navigation/documents.ts, app/src/navigation/types.ts, app/src/providers/selfClientProvider.tsx, app/tests/src/navigation.test.tsx
Registered DataConfirmation route and added to route types/tests; changed DOCUMENT_MRZ_READ_SUCCESS navigation target to DataConfirmation.
Data Confirmation Feature & Tests
app/src/screens/documents/scanning/DataConfirmationScreen.tsx, app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx
New DataConfirmationScreen rendering MRZ fields, computing per-field first-differences, tracking analytics (including MRZ_DATA_MODIFIED / DATA_CONFIRMATION_COMPLETED), updating MRZ store for NFC, and navigating to NFC scan; comprehensive tests added.
Diff Utility & Tests
app/src/utils/diffCalculator.ts, app/src/utils/diffCalculator.test.ts
New utility calculateFirstDifference with unit tests covering substitutions, length diffs, and edge cases.
Document Attributes Refactor
app/src/utils/documentAttributes.ts
Consolidated getDocumentAttributes implementation (removed duplicate export).
SDK MRZ Parsing, Exports & Tests
packages/mobile-sdk-alpha/src/processing/mrz.ts, packages/mobile-sdk-alpha/src/mrz/index.ts, packages/mobile-sdk-alpha/src/index.ts, packages/mobile-sdk-alpha/tests/processing/mrz.test.ts, packages/mobile-sdk-alpha/src/constants/analytics.ts
Added MRZ date parsing helpers (parseMRZBirthDate, parseMRZExpiryDate) and re-exported them in SDK public API; added analytics event constants; extended SDK tests for date parsing edge cases.
Jest Config
app/jest.config.cjs
Added react-native-date-picker to transform allowlist so Jest/Babel will transform it.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant DataConfirmation as "DataConfirmationScreen"
    participant InputField as "InputField"
    participant MRZStore as "MRZ Store"
    participant Analytics as "Analytics"
    participant Navigation as "Navigation"

    User->>DataConfirmation: open (after MRZ read)
    DataConfirmation->>MRZStore: read MRZ data
    MRZStore-->>DataConfirmation: initial fields
    User->>InputField: edit fields (number / dates)
    InputField-->>DataConfirmation: onChangeText / onDateChange
    User->>DataConfirmation: tap Continue
    DataConfirmation->>DataConfirmation: calculateFirstDifference(original, modified)
    alt changes detected
        DataConfirmation->>Analytics: track MRZ_DATA_MODIFIED
        DataConfirmation->>MRZStore: setMRZForNFC(updatedData)
    else no changes
        DataConfirmation->>Analytics: track DATA_CONFIRMATION_COMPLETED (had_changes: false)
    end
    DataConfirmation->>Navigation: navigate to DocumentNFCScan
    Navigation-->>User: show NFC scan
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • transphorm
  • seshanthS

Poem

✨ A passport whisper, letters bright,
Dates aligned beneath the light,
Diffs discovered, logs compose,
Analytics hum — the flow now goes,
NFC waits — the app takes flight. 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main objective: adding MRZ data confirmation functionality for NFC scanning.
Description check ✅ Passed The description covers all required template sections: Description explains the feature, Tested details the testing approach, and How to QA provides clear instructions. However, it includes a TODO and external blockers that may warrant attention.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch shazarre/feat/mrz_data_confirmation
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

@shazarre
Copy link
Collaborator Author

@coderabbitai review

@gitguardian
Copy link

gitguardian bot commented Mar 10, 2026

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
19414827 Triggered Generic Password acdb573 app/ios/PassportReaderCore.swift View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@shazarre shazarre marked this pull request as ready for review March 10, 2026 16:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/InputField.tsx`:
- Around line 164-175: The shared text style object textInput in InputField.tsx
includes the web-only property outlineStyle which is not part of React Native's
TextStyle and causes TS2769; remove outlineStyle from the textInput style (or
move it behind a web-only conditional) and ensure any web-specific focus styling
is applied only in a platform-specific path (e.g., a separate web-only style or
Platform.select) so TextInput and its typing accept the style without error.

In `@app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx`:
- Around line 55-57: The test currently creates a nested require('react-native')
inside the jest.mock callback which is banned; fix by removing the nested
require and instead import View and Text at the top of the test file via
top-level ES imports (e.g., import { View, Text } from 'react-native') and then
use those imported symbols inside the jest.mock('@/components/InputField', ...)
mock implementation so the mock reuses the hoisted View and Text instead of
requiring react-native at runtime.
- Around line 55-77: The current jest.mock for InputField replaces the real
component with a passive stub that doesn't forward edits, hiding the MRZ
confirmation behavior; replace the stub with a minimal mock that preserves
behavior by rendering a TextInput (or a component that calls the provided
onChangeText prop) and binds value and onChangeText so tests can drive and
observe actual user edits. Locate the mocked InputField in
DataConfirmationScreen.test.tsx (the jest.mock block) and change it to render an
interactive element that uses the onChangeText prop (and the same testID scheme)
or simply import the real InputField component for the test so
fireEvent.changeText triggers the same prop wiring the production code uses.
Ensure inputFieldCallbacks are no longer used to simulate edits; instead drive
edits via fireEvent.changeText on the rendered testID to validate real prop
forwarding.

In `@app/src/screens/documents/scanning/DataConfirmationScreen.tsx`:
- Around line 73-78: handleDateChange currently calls
formatDateToYYMMDD(date.toISOString()) which shifts the day for non-UTC users;
instead generate a local-date ISO (using date.getFullYear(), date.getMonth()+1,
date.getDate() with zero-padding) or otherwise convert the Date to a local
YYYY-MM-DD string and pass that into formatDateToYYMMDD before calling
setFields, so the MRZ YYMMDD preserves the user's selected local day while
InputField can still display its selectedDate. Ensure you update only
handleDateChange (and keep formatDateToYYMMDD usage) so setFields([... ,
[field]: mrzFormattedDate]) receives the corrected local-based MRZ value.
- Around line 89-121: The code currently sends raw MRZ characters because
calculateFirstDifference(...) results are placed into diffs and forwarded to
trackEvent(PassportEvents.MRZ_DATA_MODIFIED); replace that behavior by redacting
or replacing the diff values before sending (e.g., store only field names,
indices/positions of differences, masked snippets like first/last character or
length, or a boolean), i.e., transform filteredDiffs so values do NOT contain
actual MRZ/PII (use calculateFirstDifference only to compute metadata, then map
its output to a non-PII representation) and pass that sanitized object and
counts to trackEvent instead of raw diffs.

In `@packages/mobile-sdk-alpha/src/processing/mrz.ts`:
- Around line 341-350: parseMRZDateComponents currently only checks length and
allows non-numeric/impossible YYMMDD values through; update it to reject
malformed dates by (1) ensuring all 6 characters are digits, (2) parsing
year/month/day into numbers, (3) mapping two‑digit year to a full year the code
expects (keep existing YY->YYYY logic if present elsewhere), (4) constructing a
Date (use UTC or consistent timezone) and then verifying the Date's
year/month/day match the parsed components (to catch overflow like month=13 or
day=32); if any check fails return null. Apply the same stricter
validation/verification logic to parseMRZExpiryDate so both helpers only return
valid date objects or null.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1a2045ba-c697-4fe5-8dc1-30f825b4bb32

📥 Commits

Reviewing files that changed from the base of the PR and between d7c1556 and 737679d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • app/package.json
  • app/src/components/InputField.tsx
  • app/src/navigation/documents.ts
  • app/src/navigation/types.ts
  • app/src/providers/selfClientProvider.tsx
  • app/src/screens/documents/scanning/DataConfirmationScreen.test.tsx
  • app/src/screens/documents/scanning/DataConfirmationScreen.tsx
  • app/src/utils/diffCalculator.test.ts
  • app/src/utils/diffCalculator.ts
  • app/src/utils/documentAttributes.ts
  • packages/mobile-sdk-alpha/src/constants/analytics.ts
  • packages/mobile-sdk-alpha/src/index.ts
  • packages/mobile-sdk-alpha/src/mrz/index.ts
  • packages/mobile-sdk-alpha/src/processing/mrz.ts
  • packages/mobile-sdk-alpha/tests/processing/mrz.test.ts

Comment on lines +73 to +78
const handleDateChange = (field: keyof DocumentData, date: Date) => {
const mrzFormattedDate = formatDateToYYMMDD(date.toISOString());
setFields(prev => ({
...prev,
[field]: mrzFormattedDate,
}));
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Preserve the selected local day when formatting MRZ dates.

toISOString() converts the picker value to UTC before formatDateToYYMMDD() slices out the date part, so users east of UTC can have DOB/expiry saved one day early. Because InputField also keeps its own selectedDate, the UI can still show the user’s chosen day while fields already contains the shifted YYMMDD that gets sent into the NFC flow.

💡 Suggested fix
   const handleDateChange = (field: keyof DocumentData, date: Date) => {
-    const mrzFormattedDate = formatDateToYYMMDD(date.toISOString());
+    const localDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
+    const mrzFormattedDate = formatDateToYYMMDD(localDate);
     setFields(prev => ({
       ...prev,
       [field]: mrzFormattedDate,
     }));
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/screens/documents/scanning/DataConfirmationScreen.tsx` around lines
73 - 78, handleDateChange currently calls formatDateToYYMMDD(date.toISOString())
which shifts the day for non-UTC users; instead generate a local-date ISO (using
date.getFullYear(), date.getMonth()+1, date.getDate() with zero-padding) or
otherwise convert the Date to a local YYYY-MM-DD string and pass that into
formatDateToYYMMDD before calling setFields, so the MRZ YYMMDD preserves the
user's selected local day while InputField can still display its selectedDate.
Ensure you update only handleDateChange (and keep formatDateToYYMMDD usage) so
setFields([... , [field]: mrzFormattedDate]) receives the corrected local-based
MRZ value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would be an alternative here? formatting the date not in ISO format?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shazarre, yes — the core problem is that toISOString() always emits a UTC timestamp string, so any date-string slicing done inside formatDateToYYMMDD operates on the UTC date, not the one the user tapped in the picker.

There are a few alternatives, all of which avoid going through UTC:

Option 1 — Build the local ISO string manually (recommended, shown in the fix):

const localDate = [
  date.getFullYear(),
  String(date.getMonth() + 1).padStart(2, '0'),
  String(date.getDate()).padStart(2, '0'),
].join('-');
const mrzFormattedDate = formatDateToYYMMDD(localDate); // still YYMMDD

This keeps formatDateToYYMMDD in place and is explicit about using local time.

Option 2 — Use toLocaleDateString with the en-CA locale:

const localDate = date.toLocaleDateString('en-CA'); // → "YYYY-MM-DD" in local time
const mrzFormattedDate = formatDateToYYMMDD(localDate);

Shorter, but technically relies on locale formatting being available on the device.

Option 3 — Skip formatDateToYYMMDD and compute YYMMDD directly:

const yy = String(date.getFullYear()).slice(-2);
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
const mrzFormattedDate = `${yy}${mm}${dd}`;

Most direct, but duplicates the slicing logic that formatDateToYYMMDD already handles.

Option 1 is the cleanest since it reuses the existing utility without introducing locale dependencies or duplicating logic.


🧠 Learnings used
Learnt from: shazarre
Repo: selfxyz/self PR: 1236
File: packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx:356-378
Timestamp: 2025-10-23T12:08:55.529Z
Learning: In packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx, the NFC native events emitted via NativeEventEmitter are generic status strings (e.g., "PACE succeeded", "BAC failed", "Reading DG1 succeeded") and do not contain sensitive MRZ data or passport numbers, so they do not require sanitization before logging.

Learnt from: seshanthS
Repo: selfxyz/self PR: 1497
File: app/src/screens/verification/ProveScreen.tsx:125-161
Timestamp: 2025-12-13T18:00:46.963Z
Learning: In app/src/screens/verification/ProveScreen.tsx: The document expiration check using checkDocumentExpiration() is UX-only to prevent wasted gas and provide better user experience. The authoritative expiration validation is enforced in the circuits and smart contracts using trusted time sources (block timestamps), not device clock.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2026-02-23T23:10:18.287Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Test `isPassportDataValid()` with realistic, synthetic passport data instead of real user PII

Learnt from: shazarre
Repo: selfxyz/self PR: 1767
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:341-350
Timestamp: 2026-03-10T22:04:14.033Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, parseMRZBirthDate and parseMRZExpiryDate (and the internal parseMRZDateComponents helper) are only called after MRZ data has already been fully validated by extractMRZInfo, which runs ICAO 9303 format checks and check-digit verification. The date fields are guaranteed to be valid 6-digit numeric strings at that call site, so additional input guards in these helpers are redundant.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.188Z
Learning: Applies to **/{compliance,ofac,verification,identity}/**/*.{ts,tsx,js,py} : Implement three-tier OFAC verification system: Passport Number Check (direct passport validation), Name + DOB Check (full name with exact date of birth), and Name + Year Check (name with year of birth, defaulting to Jan-01)

Learnt from: seshanthS
Repo: selfxyz/self PR: 1686
File: app/src/stores/pendingKycStore.ts:14-14
Timestamp: 2026-02-04T09:33:30.563Z
Learning: In app/src/stores/pendingKycStore.ts: The VERIFICATION_TIMEOUT_MS (48 hours) and related Date.now() usage for createdAt/timeoutAt/expiry checks are UX-only for managing retry logic when fetching verification status. This is not security-sensitive; authoritative verification enforcement happens server-side or on-chain, not in this client-side timeout mechanism.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.188Z
Learning: Applies to **/{compliance,ofac,verification,identity,age}/**/*.{ts,tsx,js,py} : Use ISO 8601 format (YYYY-MM-DD) for all date inputs in compliance verification

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.188Z
Learning: Applies to **/{age,verification,identity,compliance}/**/*.{ts,tsx,js,py} : Implement age verification with day-level precision for 'olderThan' checks accepting ISO 8601 date inputs

Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2026-02-23T23:10:18.287Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Verify `extractMRZInfo()` using published sample MRZ strings (e.g., ICAO examples) instead of real user data

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.188Z
Learning: Applies to **/{compliance,verification,identity,age,country}/**/*.{ts,tsx,js,py} : Use UTC timestamps only for all compliance verification operations

Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.188Z
Learning: Applies to **/{compliance,verification,identity,age,country}/**/*.{ts,tsx,js,py} : Allow ±5 minutes clock drift tolerance in timestamp validation for compliance checks

Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2026-02-23T23:09:49.023Z
Learning: Document complex native module changes in PR description

Learnt from: seshanthS
Repo: selfxyz/self PR: 1337
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:189-194
Timestamp: 2025-11-18T12:17:14.819Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, the checkScannedInfo function and related TD1 extraction/validation logic are only reached on Android. iOS uses native Swift parsing (LiveMRZScannerView.swift) that bypasses this TypeScript layer.

Learnt from: CR
Repo: selfxyz/self PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-03T03:53:13.788Z
Learning: Address CodeRabbitAI feedback or document why not during post-PR review

Learnt from: transphorm
Repo: selfxyz/self PR: 1670
File: packages/mobile-sdk-alpha/tests/data/country-data-sync.integration.test.ts:16-19
Timestamp: 2026-01-30T18:25:54.862Z
Learning: Guideline: The import alias '@/'' for src and 'tests/' for tests should be applicable only within the app directory. In code reviews, enforce that imports use these aliases exclusively in app/**/*.{ts,tsx,js,jsx} files. Do not apply this alias convention in non-app packages (e.g., packages/mobile-sdk-alpha) where relative imports are used. This pattern covers all relevant application files; exclude non-app packages by scope.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/InputField.tsx`:
- Around line 14-15: The file imports the native DatePicker unconditionally and
renders it inside the InputField component, which breaks web; change to a
platform split by removing the top-level import of DatePicker and instead
require/import the native picker only on native platforms (use Platform.OS or
Platform.select) and render a web-safe fallback (e.g., an HTML input type="date"
or react-native-web compatible component) inside InputField's render/return
path; specifically, replace the unconditional DatePicker import with a
conditional lazy require (or dynamic import) and update the JSX in InputField
(the block that currently renders DatePicker) to choose between the native
DatePicker and the web input based on Platform.OS === 'web'.
- Around line 42-44: InputField currently constructs selectedDate once with
useState and formats it using local getters which causes timezone shifts, and it
imports react-native-date-picker unconditionally breaking web builds; update
InputField to (1) resync selectedDate whenever the value prop changes by adding
a useEffect that calls setSelectedDate(new Date(value)) (ensure you
normalize/parse the incoming ISO as UTC if needed), (2) when producing display
strings or extracting year/month/day use UTC getters
(getUTCFullYear/getUTCMonth/getUTCDate) instead of local getters to avoid
midnight-UTC shifts, and (3) make the react-native-date-picker import/platform
usage conditional (use Platform.OS !== 'web' or a dynamic require) and render a
web-safe fallback (e.g., input type="date" or a web picker) when on web so the
native-only library is not imported on web. Ensure references to selectedDate,
setSelectedDate, InputField, and the date formatting code are updated
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a061e26-5b2d-40fa-b198-3664fc47381f

📥 Commits

Reviewing files that changed from the base of the PR and between 737679d and f3e3126.

📒 Files selected for processing (1)
  • app/src/components/InputField.tsx

Comment on lines +42 to +44
const [selectedDate, setSelectedDate] = useState<Date>(
value ? new Date(value) : new Date(),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "InputField.tsx" --exec cat -n {} \;

Repository: selfxyz/self

Length of output: 5260


🏁 Script executed:

head -120 app/src/components/InputField.tsx | tail -80

Repository: selfxyz/self

Length of output: 2028


🏁 Script executed:

cat -n app/src/components/InputField.tsx | sed -n '1,110p'

Repository: selfxyz/self

Length of output: 3339


🏁 Script executed:

cat -n app/src/screens/documents/scanning/DataConfirmationScreen.tsx | sed -n '150,180p'

Repository: selfxyz/self

Length of output: 1323


🏁 Script executed:

rg -B 5 -A 5 "parseMRZBirthDate|parseMRZExpiryDate" app/src/screens/documents/scanning/DataConfirmationScreen.tsx

Repository: selfxyz/self

Length of output: 1050


🏁 Script executed:

rg "Platform.OS|web|Web" app/src/components/InputField.tsx app/src/screens/documents/scanning/DataConfirmationScreen.tsx

Repository: selfxyz/self

Length of output: 38


🏁 Script executed:

rg "react-native-date-picker" --type ts --type tsx -l

Repository: selfxyz/self

Length of output: 83


🏁 Script executed:

grep -r "createStaticNavigation\|initialRouteName.*web\|platform.*web" app/src --include="*.ts" --include="*.tsx" | head -20

Repository: selfxyz/self

Length of output: 591


🏁 Script executed:

rg "Platform\\.OS" app/src/components -A 2 -B 2

Repository: selfxyz/self

Length of output: 1381


🏁 Script executed:

fd "\.web\." app/src --type f | head -20

Repository: selfxyz/self

Length of output: 661


🏁 Script executed:

cat package.json | grep -A 2 "react-native-date-picker"

Repository: selfxyz/self

Length of output: 38


🏁 Script executed:

cat package.json | head -100

Repository: selfxyz/self

Length of output: 4973


🏁 Script executed:

cat app/package.json | grep -i "react-native-date-picker"

Repository: selfxyz/self

Length of output: 98


🏁 Script executed:

fd "InputField" app/src --type f

Repository: selfxyz/self

Length of output: 89


🏁 Script executed:

rg "formatDate.*getMonth|getDate.*getFullYear" -A 3

Repository: selfxyz/self

Length of output: 50370


🏁 Script executed:

rg "handleConfirm.*date" app/src/screens/documents/scanning/DataConfirmationScreen.tsx -A 5

Repository: selfxyz/self

Length of output: 38


🏁 Script executed:

cat -n app/src/screens/documents/scanning/DataConfirmationScreen.tsx | sed -n '160,185p'

Repository: selfxyz/self

Length of output: 1081


🏁 Script executed:

rg "useEffect" app/src/components/InputField.tsx

Repository: selfxyz/self

Length of output: 38


🏁 Script executed:

rg "Platform.OS" app/src/components/InputField.tsx

Repository: selfxyz/self

Length of output: 38


🏁 Script executed:

grep -n "react-native-date-picker" app/package.json

Repository: selfxyz/self

Length of output: 102


🏁 Script executed:

rg "\.web\.(tsx|ts)" app/src/components --type-list

Repository: selfxyz/self

Length of output: 445


🏁 Script executed:

fd "\.web\." app/src/components

Repository: selfxyz/self

Length of output: 204


Fix date timezone handling, resync value changes, and add platform-specific date picker.

The component rehydrates ISO timestamps with new Date(value) and formats them using local-time getters (getMonth(), getDate(), getFullYear()), which can shift the calendar day by timezone—a midnight UTC value could display as the previous day. Additionally, selectedDate is only initialized once; parent-side value updates won't be reflected. Finally, react-native-date-picker is a native-only library with no web support but is imported unconditionally in this shared component, breaking web builds.

💡 Suggested fix
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import {
   Pressable,
   StyleSheet,
   Text,
   TextInput,
   View,
   type ViewStyle,
 } from 'react-native';
-import DatePicker from 'react-native-date-picker';
+import { Platform } from 'react-native';
+
+const DatePicker = Platform.OS === 'web' ? null : require('react-native-date-picker').default;
 
 import { colors } from '@selfxyz/euclid';
@@
   const [showDatePicker, setShowDatePicker] = useState(false);
-  const [selectedDate, setSelectedDate] = useState<Date>(
-    value ? new Date(value) : new Date(),
-  );
+
+  const parseDateValue = (raw?: string): Date => {
+    if (!raw) {
+      return new Date();
+    }
+
+    const parsed = new Date(raw);
+    return new Date(
+      parsed.getUTCFullYear(),
+      parsed.getUTCMonth(),
+      parsed.getUTCDate(),
+    );
+  };
+
+  const [selectedDate, setSelectedDate] = useState<Date>(parseDateValue(value));
+
+  useEffect(() => {
+    setSelectedDate(parseDateValue(value));
+  }, [value]);
 
   const formatDate = (date: Date): string => {
     const months = [
@@ -100,6 +122,9 @@ export const InputField: React.FC<InputFieldProps> = ({
       'Dec',
     ];
     return `${months[date.getMonth()]} ${date.getDate()} ${date.getFullYear()}`;
+  };
+
+  if (Platform.OS === 'web' || type !== 'date') {
   };
 
   if (type === 'date') {
     const displayValue = formatDate(selectedDate);
 
     return (
       <Pressable style={[styles.container, style]} onPress={handleDatePress}>
         <Text style={styles.label}>{label}</Text>
         <Text style={styles.value}>{displayValue}</Text>
+        {Platform.OS !== 'web' && DatePicker && (
         <DatePicker
           modal
           open={showDatePicker}
           date={selectedDate}
           mode="date"
           title={label}
           confirmText="Confirm"
           cancelText="Cancel"
           onConfirm={handleConfirm}
           onCancel={handleCancel}
           theme="light"
         />
+        )}
       </Pressable>
     );
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/InputField.tsx` around lines 42 - 44, InputField currently
constructs selectedDate once with useState and formats it using local getters
which causes timezone shifts, and it imports react-native-date-picker
unconditionally breaking web builds; update InputField to (1) resync
selectedDate whenever the value prop changes by adding a useEffect that calls
setSelectedDate(new Date(value)) (ensure you normalize/parse the incoming ISO as
UTC if needed), (2) when producing display strings or extracting year/month/day
use UTC getters (getUTCFullYear/getUTCMonth/getUTCDate) instead of local getters
to avoid midnight-UTC shifts, and (3) make the react-native-date-picker
import/platform usage conditional (use Platform.OS !== 'web' or a dynamic
require) and render a web-safe fallback (e.g., input type="date" or a web
picker) when on web so the native-only library is not imported on web. Ensure
references to selectedDate, setSelectedDate, InputField, and the date formatting
code are updated accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant