Skip to main content

Overview

The mobile app now ships first-party workspace surfaces for:
  • Drive at Routes.drive
  • CRM at Routes.crm
  • Education at Routes.education
Both modules are registered in the Apps hub and available through the mini-app navigation system. The mobile Apps hub now uses a single-column list of equal-priority app cards instead of the older grid treatment. Keep module ordering explicit in the hub view rather than relying on registry insertion order. For workspace modules that need dense controls, prefer the same shell-owned chrome used by Calendar:
  • ShellMiniNav for mode or section switching
  • ShellChromeActions for quick actions such as filters, create, sort, and view toggles
  • A finance-style overview hero card inside the page body for stats and search
  • Compact panel sections in the page body instead of raw admin Card/ListTile stacks, so module pages read like first-class mobile products rather than direct dashboard ports
Background warmups for optional products should also respect access state before fetching. Do not let Apps hub or boot-time cache warmups call product endpoints blindly; skip the warmup when the corresponding access cubit is not loaded and enabled, and fail soft on opportunistic 404 responses.

Mobile App Lock And Web QR Approval

The mobile app supports a device-level app lock from Settings > Session. The setting is stored in secure storage and uses the OS local authentication prompt with device PIN/passcode fallback allowed. When enabled, the app locks on an authenticated cold launch and after at least 30 seconds in the background. QR session handoff approval is gated by this setting:
  • The scanner lives at Routes.settingsQrLoginScan.
  • The scanner only accepts tuturuuu://auth/qr-login payloads from trusted Tuturuuu or local development origins.
  • The QR challenge must have been created by the same account that is active in the mobile app. Public unauthenticated QR sign-in is not supported because it would let an attacker poll a victim-approved challenge.
  • Before approval, mobile runs local authentication and then calls /api/v1/auth/qr-login/challenges/:id/approve with the current Bearer session.
If a user wants to approve QR session handoff for a different account, both the creator session and the mobile app must be using that account before scanning. The mobile app can approve web MFA challenges when the active mobile Supabase session is already at aal2:
  • The web MFA screen creates an /api/v1/auth/mfa/mobile/challenges request and shows a pair code.
  • MobileMfaApprovalListener is mounted at the app shell and polls /api/v1/auth/mfa/mobile/approvals while the user is authenticated, the app is in the foreground, and app lock is not covering the UI.
  • Pending pair codes open an app-level approval dialog. The Settings > Session tile remains a manual fallback surface for the same pending approvals.
  • Mobile approves through /api/v1/auth/mfa/mobile/challenges/:id/approve.
  • The web poller consumes the approved challenge and sets an HTTP-only ttr_mfa_mobile_approval cookie. The central auth proxy accepts that cookie only for the same user, the hidden web challenge secret, and an unexpired consumed mfa_mobile_approval challenge.
  • After a mobile TOTP challenge succeeds, AuthCubit.verifyMfa must sync the refreshed aal2 Supabase session into the active multi-account store. Without that sync, later mobile API calls can restore/send an older aal1 token and the approvals endpoint will respond with requiresMobileMfa.
  • Multi-account removal and all-account logout must treat Supabase auth.signOut() as required before clearing the local account store or emitting an unauthenticated Cubit state. Google/provider logout and stored refresh-token revocation can be best-effort, but a failed Supabase logout must leave the account visible with an error so the persisted session cannot silently reappear later.
  • Auth account models may serialize refresh tokens and session JSON only for the encrypted secure-storage store. Do not include those raw secrets in Equatable.props, toString, or other Cubit/Bloc state debug output because the global Bloc observer logs state changes during development.

Mobile Drive Coverage

The Drive screen in apps/mobile supports the main dashboard Drive flow:
  • Workspace permission-gated loading
  • Storage analytics summary
  • Folder navigation and breadcrumb-style path movement
  • Search, sort, grid/list toggle, and pagination
  • Folder creation, rename, delete, bulk delete, and upload
  • File preview, external open, share, and path copy
  • Folder export-link generation and sharing
The mobile implementation talks to the same authenticated workspace storage routes used by the web product:
  • /api/v1/workspaces/:wsId/storage/list
  • /api/v1/workspaces/:wsId/storage/analytics
  • /api/v1/workspaces/:wsId/storage/folders
  • /api/v1/workspaces/:wsId/storage/rename
  • /api/v1/workspaces/:wsId/storage/object
  • /api/v1/workspaces/:wsId/storage/share
  • /api/v1/workspaces/:wsId/storage/export-links
  • /api/v1/workspaces/:wsId/storage/upload-url
  • /api/v1/workspaces/:wsId/storage/finalize-upload

Mobile CRM Coverage

The CRM screen in apps/mobile supports:
  • Workspace permission-gated users and audit tabs
  • Search, filters, pagination, and audit range filters
  • Create, edit, and delete user records
  • Avatar upload during create/edit flows
  • Feedback create/update/delete flows
  • Duplicate detection and merge flows
  • CSV import with preview and deduping by email
  • CSV export of the currently filtered users dataset
The mobile CRM uses the existing authenticated routes:
  • /api/v1/workspaces/:wsId/users/database
  • /api/v1/workspaces/:wsId/users
  • /api/v1/workspaces/:wsId/users/:userId
  • /api/v1/workspaces/:wsId/users/groups
  • /api/v1/workspaces/:wsId/users/feedbacks
  • /api/v1/workspaces/:wsId/users/audit-logs
  • /api/v1/workspaces/:wsId/users/duplicates/detect
  • /api/v1/workspaces/:wsId/users/merge
  • /api/v1/workspaces/:wsId/users/bulk-import
  • /api/v1/workspaces/:wsId/users/avatar
For CRM list and duplicate/merge routes, keep workspace resolution request-aware on the web side. Mobile calls authenticate with Bearer tokens rather than dashboard cookies, so routes that resolve the workspace with cookie-only helpers can fail as Workspace not found even for valid workspace members.

Mobile Education Coverage

The Education screen in apps/mobile supports:
  • An overview dashboard with counts for courses, quizzes, quiz sets, flashcards, and attempts
  • Course search, create, edit, delete, and paginated browsing
  • Library management for quizzes, quiz sets, and flashcards
  • Quiz authoring with answer options, correctness flags, and explanations
  • Attempt browsing with status and quiz-set filters plus attempt-detail review
The mobile Education module uses authenticated workspace routes from apps/web:
  • /api/v1/workspaces/:wsId/courses
  • /api/v1/workspaces/:wsId/courses/:courseId
  • /api/v1/workspaces/:wsId/quizzes
  • /api/v1/workspaces/:wsId/quizzes/:quizId
  • /api/v1/workspaces/:wsId/quiz-sets
  • /api/v1/workspaces/:wsId/quiz-sets/:setId
  • /api/v1/workspaces/:wsId/flashcards
  • /api/v1/workspaces/:wsId/flashcards/:flashcardId
  • /api/v1/workspaces/:wsId/education/attempts
  • /api/v1/workspaces/:wsId/education/attempts/:attemptId
For mobile-facing collection APIs, keep list routes request-aware and list-shaped. A cookie-only helper or .single() response on a collection endpoint can work in the web dashboard but break mobile Bearer-token callers or pagination assumptions. When an Education mobile list needs derived counts from workspace-protected join tables such as course_module_quiz_sets, verify workspace membership first with the request-scoped client, then run the joined read on an admin-backed client. Also select real join columns such as module_id; do not assume those bridge tables expose a synthetic id.

Import And Export Format Notes

  • The web CRM page still has richer spreadsheet tooling, including XLSX-oriented flows.
  • Mobile import and export currently use CSV plus native share sheets.
  • Keep the server contract for bulk import as a raw JSON array of { email, fullName } rows.
This format choice is intentional for now: the current Flutter dependency graph in apps/mobile does not accept a stable XLSX package cleanly without conflicts, so mobile should prefer CSV/share flows unless spreadsheet generation is moved server-side later.

Verification

For Drive, CRM, or Education mobile changes:
  1. Update apps/mobile/lib/l10n/arb/*.arb for every new user-facing string.
  2. Run flutter gen-l10n.
  3. Run bun check:mobile.