- Add LocaleProvider + useLocale() hook with AsyncStorage persistence to mobile i18n
- Replace all hardcoded English strings in every screen with t() calls
- Add missing profile.settings/language keys to all 5 translation files (en/de/es/fr/it)
- Wrap app root in LocaleProvider; apply server preferred_language on login in AuthGuard
- Tab labels and header titles now re-render on language switch
- ProfileScreen: use useLocale() context, sync language to server via POST /api/i18n/language
- Backend: add preferred_language field to GET /api/mobile/whoami response
- Mobile API: add preferred_language to WhoAmIResponse type + setServerLanguage() method
- Tests: add test_whoami_returns_preferred_language and test_whoami_no_profile_preferred_language_is_null
- Docs: update MobileApp.md with language sync priority and whoami response format
Language priority: server preference > AsyncStorage > device locale > English fallback
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Add Privacy Policy, Terms of Service, and Imprint links to WelcomeScreen
and LoginScreen for GDPR/Apple compliance (pre-login access)
- Enable multiple image selection in photo library picker
- Add HEIC/HEIF image support to backend (allowed_types, convert_to_pdf, upload handler)
- Create FileDetailScreen with processing status and logs
- Add search bar to FilesScreen with debounced search
- Set up i18n with expo-localization (EN, DE, ES, FR, IT)
- Add language selector to ProfileScreen settings
- Add Imprint link to ProfileScreen legal section
- Update docs and tests
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Address code review feedback:
- Extract normalizeFileUri to mobile/src/utils/normalizeUri.ts
- Import shared function in ShareContext and UploadScreen
- Move os import to top of test file
- Update test docstring to reflect new behavior
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Move duplicate check before task enqueue in ui_upload endpoint
- Clean up temp file and return status "duplicate" for exact duplicates
- Add URI-level dedup guard in mobile UploadScreen to prevent repeated uploads
- Improve ShareContext URI normalization (collapse slashes, decode percent-encoding)
- Guard +not-found.tsx effect against re-firing for the same pathname
- Update mobile UploadResponse type and handlers for duplicate status
- Update web frontend upload.js to show duplicate status
- Update API and Configuration docs
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Extract EXT_TO_MIME map and mimeTypeFromFilename to shared module
at mobile/src/utils/mimeTypes.ts (used by _layout.tsx and +not-found.tsx)
- Add error logging to ensureLocalUri catch block for debugging
- Add error handling to Linking.openURL calls in ProfileScreen
- Fix incorrect LSSupportsOpeningDocumentsInPlace docs in audit report
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Files shared via iOS Share Sheet / "Open In…" may reference paths
outside the app's sandbox or use security-scoped URLs that React
Native's fetch cannot read. This caused uploads to hang indefinitely
with a spinning indicator.
Fixes:
- Set LSSupportsOpeningDocumentsInPlace to false so iOS copies shared
files to the app's accessible Inbox directory
- Use expo-file-system to copy external file:// URIs to the app's
cache directory before uploading (ensureLocalUri helper)
- Apply ensureLocalUri to both initial uploads and retries
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Skip known deep-link paths (qr-login, callback) in makeUrlHandler
to prevent docuelevate://qr-login URLs from being treated as shared
files and creating phantom upload errors
- Infer MIME type from file extension for files shared via iOS Share
Sheet / "Open In…" so the server receives correct Content-Type
instead of application/octet-stream
- Default login screen server URL to https://app.docuelevate.org
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Show alert dialogs when Privacy Policy, Terms of Service, or
account deletion links cannot be opened due to missing server URL.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
When iOS delivers a file via "Open In…", expo-router strips the
docuelevate:// scheme and routes to +not-found.tsx. Previously, this
screen only redirected to the Upload tab and relied on the Linking
handler in _layout.tsx to add the file to ShareContext. This was
unreliable because expo-router may consume the URL event before the
Linking handler fires.
Now +not-found.tsx directly reconstructs the file:// URI from the
pathname and adds it to ShareContext before redirecting. ShareContext
deduplicates by URI to prevent double uploads if both mechanisms fire.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
The QR login page countdown timer compared the server's UTC expiration
timestamp against the client's local clock, causing the QR code to appear
immediately expired when the client clock was ahead of the server.
Changes:
- Add ttl_seconds field to CreateChallengeResponse (seconds until expiry)
- Frontend countdown now uses relative elapsed time since response was
received, eliminating clock-skew issues
- Mobile app: replace alert-only QR button with actual camera-based
QR code scanner using expo-camera
- Add QRScannerScreen with barcode scanning, permission handling, and
scan area overlay
- Update camera permission description to mention QR code scanning
- Add tests for ttl_seconds computation
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Without a root app/index.tsx in the repo, a stale default Expo Router
scaffold file (showing "Hello World") could be picked up from a previous
build or CLI scaffolding and displayed instead of the real app.
The new index.tsx immediately redirects to /(auth)/, and the existing
AuthGuard in _layout.tsx forwards authenticated users to /(tabs)/.
Also registers the index screen in the root Stack and updates
docs/MobileApp.md with an expanded project structure and a new
troubleshooting entry.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Extract APP_SCHEME_PREFIX constant for the custom URL scheme string,
and derive the photo library fallback filename extension from the
asset's MIME type instead of always using .jpg.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
On iOS the Share Sheet / "Open In" action may deliver the file path
under the app's custom docuelevate:// scheme instead of a file:// URL,
causing an "Unmatched Route" error. The URL handler now detects this
and rewrites the URL to file:// before processing.
Also adds a Photo Library button to the Upload screen so users can
select existing photos from their device library, not just capture
new ones with the camera.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Change `appVersionSource` from `"local"` to `"remote"` in `eas.json` so EAS
tracks build numbers on its servers. This ensures every CI build gets a
unique, ever-increasing version without needing to commit bumps back to
the repo — fixing the App Store Connect "bundle version already used"
rejection.
Also bump `ios.buildNumber` to "7" and `android.versionCode` to 7 in
`app.json` (above the previously uploaded version "6") so the remote
version initializes correctly.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Add paths filter so builds only trigger when mobile/ files change
- Add submit_ios job to automatically submit iOS builds to App Store Connect
- Update docs/MobileApp.md and mobile/README.md with CI/CD documentation
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- app.json: add CFBundleDocumentTypes to iOS infoPlist so the app
appears in the iOS Share Sheet; add ACTION_SEND/SEND_MULTIPLE
intentFilters for Android share intent support
- src/context/ShareContext.tsx (new): React context that queues files
received from the share sheet and delivers them to UploadScreen
- app/_layout.tsx: wrap in ShareProvider; add Linking handler
(makeUrlHandler factory + getInitialURL cold-start + addEventListener
warm-start) to capture file:// and content:// URLs
- src/services/api.ts: fix FileRecord interface (original_filename,
nested ProcessingStatus, mime_type); fix UploadResponse interface;
fix listFiles() (per_page param, unwrap data.files); add
getFileStatus(fileId) for single-file status polling
- src/screens/FilesScreen.tsx: use file.original_filename and
file.processing_status.status; fix statusEmoji to use actual backend
status values (completed/pending/duplicate)
- src/screens/UploadScreen.tsx: consume ShareContext for auto-upload of
shared files; add 5-second polling loop (search by filename → file_id
→ getFileStatus) to show real-time server processing status;
uploadFile wrapped in useCallback; proper effect dependency arrays
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Add NODE_NO_WARNINGS=1 to all eas.json build profiles (development,
preview, production) to suppress [DEP0169] url.parse() deprecation
warnings emitted by EAS CLI when the build image's system Node is 22+
- Add NODE_NO_WARNINGS=1 env to both EAS Cloud Workflow jobs
(.eas/workflows/create-builds.yml) with explanatory comments
- Fix outdated Node.js prerequisite in docs/MobileApp.md (was "18 or
later", now "20.19.4 or later" with nvm guidance)
- Add troubleshooting sections in docs/MobileApp.md and mobile/README.md
covering both the "Session expired Local session" error (Apple ID
session expiry + App Store Connect API key recommendation) and the
[DEP0169] Node.js deprecation warning
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Fix fatal crash: require('../../../assets/logo.png') in LoginScreen and
WelcomeScreen resolved 3 levels above mobile/src/screens/ — outside the
mobile/ directory. Changed to ../../assets/logo.png which correctly
resolves to the existing mobile/assets/logo.png.
- Add expo-router app/ directory (root cause of missing welcome screen and
web support): app/_layout.tsx, (auth)/, (tabs)/ with all route files
- Add WelcomeScreen.tsx: branded intro screen with feature highlights
- Update LoginScreen/WelcomeScreen to use useRouter() (expo-router style)
- Add react-native-web ~0.20.0 and react-dom 19.2.4 for web channel
- Add expo-device ~7.0.3 (was imported but missing from package.json)
- Remove android.googleServicesFile from app.json (file is gitignored;
README documents how to restore it for Android FCM builds)
- Add web.bundler: metro and web.output: single to app.json
- Fix aria-hidden to explicit boolean value in WelcomeScreen
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
metro-config@0.83.3 uses Array.prototype.toReversed() which was added
in Node.js 20.0.0. The README incorrectly stated "Node.js 18+" causing
users to run `npx expo start` with Node 18 and hit:
TypeError: configs.toReversed is not a function
- Update README.md to say "Node.js 20.19.4+" with nvm hint
- Update package.json engines from >=20.16.0 to >=20.19.4 to match
metro-config's exact minimum (as declared in package-lock.json)
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
EAS builds run `npm ci` which validates integrity hashes for every package.
The react entry was updated to 19.2.4 but its integrity hash was removed,
causing the EAS iOS build to fail at the Install dependencies phase.
- Runs npm install --package-lock-only to regenerate full lockfile
(also adds react-dom@19.2.4 entry with its integrity hash)
- Patches node_modules/react entry with canonical integrity hash from
the npm registry: sha512-9nfp...
- Verified with npm ci --dry-run: 938 packages, exit 0
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- UploadScreen.tsx: replace deprecated ImagePicker.MediaTypeOptions.Images
with new array syntax ['images'] — the old enum is not accepted by the
expo-image-picker v17 TurboModule on iOS, causing SIGABRT on Thread 2
- package.json: bump react 19.1.0 → 19.2.4 so react-dom@19.2.4 (peerOptional
of @expo/metro-runtime) no longer conflicts; update @types/react to ~19.2.0
- package-lock.json: update react entry to 19.2.4 (integrity removed, will be
regenerated by npm install on the next EAS build)
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Expo SDK 54 switched to precompiled React Native XCFrameworks by default
for faster iOS builds. However, the precompiled frameworks do not expose
legacy bridge headers (RCTBridge, RCTViewManager, RCTSurfaceHostingProxyRootView,
RCTPackagerConnection, RCTDevSettings.isDebuggingRemotely, rootViewFactory)
that some native modules (e.g. expo-dev-client) still reference.
Add expo-build-properties (v1.0.10, the SDK 54-compatible version) and
configure buildReactNativeFromSource: true for iOS. This compiles React
Native from source, making all native headers available to linked modules
and resolving the Xcode compilation errors seen in the EAS production build.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Regenerate all app icons (icon.png, adaptive-icon.png, splash.png,
favicon.png, notification-icon.png) using the DocuElevate folder+gear
SVG logo on brand blue (#1e40af) background
- Add assets/logo.png (200×200 circular logo) for the login screen
- Update LoginScreen.tsx to display the DocuElevate logo image above
the brand name text instead of plain text only
- Replace emoji tab bar icons (⬆️📄👤) with Ionicons vector icons
(cloud-upload-outline, document-text-outline, person-circle-outline)
from the already-installed @expo/vector-icons package
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>