PWA, FCM, and web Stripe checkout#18
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR expands the project’s web/PWA and notification capabilities by adding Firebase Cloud Messaging (FCM) support end-to-end (Flutter client + web service worker + Supabase functions/migrations), improving the web shell/manifest, and extending marketplace checkout to use Stripe Hosted Checkout on web. It also includes tooling/docs updates and Cursor ignore/config changes.
Changes:
- Add Firebase/FCM integration across Flutter (token sync + routing + local display), web (service worker), and Supabase (dispatch functions + DB triggers/outbox).
- Improve web/PWA UX and installability (manifest
start_url, screenshots/shortcuts, iOS splash assets, updated PWA banner, loading timeout). - Add web-specific platform abstractions (notifications, media picking, image caching, payments) and adjust marketplace flow for web Stripe checkout.
Reviewed changes
Copilot reviewed 106 out of 115 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| web/pwa_banner.js | Refactors iOS install banner injection/styling and delays it until Flutter first frame. |
| web/push_register.js | Adds a JS helper for VAPID Web Push registration (currently not wired/used). |
| web/manifest.json | Updates PWA metadata (id, start_url, screenshots, shortcuts). |
| web/index.html | Adds splash timeout/error handling, startup images, and registers Firebase messaging SW. |
| web/firebase-messaging-sw.js | Adds Firebase Messaging service worker for background notifications. |
| web/firebase-config.js.example | Example Firebase web config for the SW. |
| web/firebase-config.js | Firebase web config used by the SW. |
| tool/sync_firebase_web_config.dart | Tool to generate web/firebase-config.js from .env. |
| tool/sync_firebase_from_cli.ps1 | PowerShell helper to regenerate Firebase configs via CLI/FlutterFire. |
| tool/set_fcm_supabase_secrets.ps1 | PowerShell helper to upload FCM secrets to Supabase and sync dispatch secret. |
| test/core/firebase/fcm_message_router_test.dart | Unit tests for push payload → route mapping behavior. |
| supabase/migrations/20260607140000_fcm_dispatch_system.sql | Adds FCM dispatch tables, triggers, outbox, pg_net + cron schedules. |
| supabase/migrations/20260606120000_user_fcm_devices.sql | Adds user_fcm_devices table + RLS for storing FCM tokens. |
| supabase/migrations/20260605130000_waitlist_insert_policy_hardening.sql | Tightens waitlist insert policy with basic email validation. |
| supabase/migrations/20260605120000_restore_matching_discovery_candidates_cursor.sql | Restores keyset/cursor-based matching discovery RPC signature + grants. |
| supabase/migrations/20260605100000_pwa_phase2.sql | Adds checkout/care web tables and web push subscription table + RLS. |
| supabase/migrations/20260604000000_fix_matching_discovery_candidates_offset_signature.sql | Converts a previously conflicting migration into a no-op placeholder. |
| supabase/functions/stripe-webhook/index.ts | Adds Checkout Session events and shared idempotent fulfillment helper. |
| supabase/functions/send-fcm-notification/index.ts | Adds authenticated (secret header) FCM send endpoint. |
| supabase/functions/register-web-push-subscription/index.ts | Adds edge function to store web push subscriptions for a user. |
| supabase/functions/process-fcm-outbox/index.ts | Adds dispatcher to send queued FCM notifications from fcm_push_outbox. |
| supabase/functions/process-care-fcm-reminders/index.ts | Adds cron-driven care reminder dispatch via FCM. |
| supabase/functions/create-payment-intent/index.ts | Adds checkout_mode to create Stripe Hosted Checkout sessions on web. |
| supabase/functions/_shared/fcm_send.ts | Adds Firebase HTTP v1 sender with service-account JWT + token cleanup. |
| supabase/.temp/cli-latest | Bumps stored Supabase CLI marker. |
| PWA_WEB_AUDIT.md | Adds a detailed web/PWA audit document and recommended roadmap. |
| pubspec.yaml | Adds firebase_core and firebase_messaging. |
| progress.md | Logs PWA + backend phases and operational notes. |
| lib/main.dart | Initializes Firebase/FCM, adjusts Stripe init (non-web at startup), and token sync on auth changes. |
| lib/firebase_options.dart | Adds FlutterFire-style Firebase options (currently misconfigured for iOS). |
| lib/features/social/presentation/widgets/post_comments_bottom_sheet.dart | Adds semantics + key for comment input (web/a11y/testing). |
| lib/features/social/presentation/screens/story_viewer_screen.dart | Applies web image cache sizing helpers for story images. |
| lib/features/social/presentation/screens/social_screen.dart | Applies web image cache sizing helpers for feed images. |
| lib/features/social/presentation/screens/social_profile_screen.dart | Applies web image cache sizing helpers for grid thumbnails. |
| lib/features/social/presentation/screens/post_detail_screen.dart | Applies web image cache sizing helpers for detail images. |
| lib/features/social/presentation/screens/create_story_screen.dart | Uses platform media picker on web and adjusts thumbnail caching. |
| lib/features/social/presentation/screens/create_post_screen.dart | Adds web media picker branch (but still uses dart:io preview logic). |
| lib/features/pet_profile/presentation/screens/edit_profile_screen.dart | Switches to platform media picker for profile image selection. |
| lib/features/matching/presentation/widgets/match_celebration_overlay.dart | Applies web image cache sizing for avatars. |
| lib/features/matching/presentation/screens/matching_screen.dart | Improves web matching behavior and error messaging + caching. |
| lib/features/matching/presentation/screens/matches_inbox_screen.dart | Uses safer back navigation (popOrGo). |
| lib/features/matching/presentation/screens/chat_screen.dart | Uses safer back navigation (popOrGo) and avoids router import cycle. |
| lib/features/matching/presentation/matching_navigation.dart | Adds popOrGo helper. |
| lib/features/matching/presentation/controllers/discovery_candidates_controller.dart | Improves error handling when replenishing candidates fails. |
| lib/features/matching/data/datasources/matching_supabase_data_source.dart | Wraps PostgREST errors in app DatabaseException. |
| lib/features/marketplace/presentation/widgets/web_checkout_resume_listener.dart | Adds web lifecycle listener to resume/cancel checkout flows. |
| lib/features/marketplace/presentation/screens/vendor/edit_shop_screen.dart | Uses platform media picker for shop image selection. |
| lib/features/marketplace/presentation/screens/order_confirmation_screen.dart | Adds web confirmation polling when returning from Stripe. |
| lib/features/marketplace/presentation/screens/marketplace_screen.dart | Adds web checkout cancel handling + improves tap targets/caching. |
| lib/features/marketplace/presentation/screens/cart_screen.dart | Wraps with web checkout resume listener. |
| lib/features/marketplace/presentation/controllers/manual_kyc_controller.dart | Uses platform media picker for KYC image selection. |
| lib/features/marketplace/presentation/controllers/checkout_controller.dart | Adds hosted checkout path for web + resume logic; defers Stripe settings init. |
| lib/features/marketplace/data/repositories/order_repository.dart | Adds createCheckoutSession() API for hosted checkout. |
| lib/features/care/presentation/widgets/web_push_enable_banner.dart | Adds web UI prompt to enable push (via FCM token). |
| lib/features/care/presentation/screens/medical_vault_screen.dart | Uses platform media picker for record uploads. |
| lib/features/care/presentation/screens/care_screen.dart | Adds web push banner and a11y/tap improvements (InkWell/Semantics). |
| lib/features/care/data/repositories/pet_care_repository.dart | Routes care reminders through PlatformNotifications. |
| lib/features/auth/presentation/widgets/auth_widgets.dart | Adds autofillHints support to auth fields. |
| lib/features/auth/presentation/screens/login_screen.dart | Sets email/password autofill hints. |
| lib/features/auth/data/repositories/auth_repository.dart | Clears FCM token on sign out before Supabase sign-out. |
| lib/core/widgets/pet_avatar.dart | Adds web cache sizing for avatar images. |
| lib/core/widgets/app_shell.dart | Adjusts web shell layout to use bottomNavigationBar (web tap reliability). |
| lib/core/services/stripe_init_service.dart | Adds ensureStripeReady() helper to centralize Stripe settings init. |
| lib/core/services/notification_service.dart | Adds push display channels/sound, tap routing payload support, and background init path. |
| lib/core/services/location_service.dart | Enables web geolocator paths and adjusts timeouts. |
| lib/core/router.dart | Adds router error screen, overlay dismissal on route change, and root redirects. |
| lib/core/platform/web_push_registration.dart | Adds conditional export for JS web push registration bridge. |
| lib/core/platform/web_push_registration_web.dart | Implements JS interop bridge to window.PetfolioPush. |
| lib/core/platform/web_push_registration_stub.dart | Stub for non-web builds. |
| lib/core/platform/web_image_cache.dart | Adds DPI-aware cache width helpers with web caps. |
| lib/core/platform/web_checkout_redirect.dart | Conditional export for direct web redirect helper. |
| lib/core/platform/web_checkout_redirect_web.dart | Implements window.location.href redirect helper. |
| lib/core/platform/web_checkout_redirect_stub.dart | Stub for non-web builds. |
| lib/core/platform/web_app_url.dart | Adds helper to build app URLs for hosted checkout redirects (currently uses hash). |
| lib/core/platform/platform_services.dart | Barrel export for platform abstractions. |
| lib/core/platform/platform_payments.dart | Adds useStripeHostedCheckout platform flag (web). |
| lib/core/platform/platform_notifications.dart | Adds abstraction for notifications across web/io. |
| lib/core/platform/platform_notifications_web.dart | Web implementation uses Supabase care_web_reminders. |
| lib/core/platform/platform_notifications_io.dart | IO implementation schedules local notifications and syncs reminders to Supabase. |
| lib/core/platform/platform_location.dart | Barrel exports for location services/providers. |
| lib/core/platform/media_picker.dart | Conditional export for media picking on web/io. |
| lib/core/platform/media_picker_web.dart | Web media picking implementation. |
| lib/core/platform/media_picker_io.dart | IO media picking implementation. |
| lib/core/platform/care_fcm_reminder_sync.dart | Upserts/deletes care reminders into Supabase for FCM dispatch. |
| lib/core/navigation/route_overlay_dismissal.dart | Adds helper to dismiss popup routes on navigation changes. |
| lib/core/firebase/firebase_env.dart | Adds Firebase env helpers (platform label, VAPID key access). |
| lib/core/firebase/fcm_token_repository.dart | Adds Supabase persistence for FCM tokens. |
| lib/core/firebase/fcm_service.dart | Adds core FCM lifecycle: permissions, token sync, routing, foreground handling. |
| lib/core/firebase/fcm_push_display.dart | Shows FCM foreground messages via local notifications. |
| lib/core/firebase/fcm_message_router.dart | Maps push payloads to app routes and chooses push vs go navigation. |
| lib/core/firebase/fcm_lifecycle.dart | Adds a Riverpod-based lifecycle wrapper (appears unused now). |
| lib/core/firebase/fcm_background_handler.dart | Adds background handler to show notifications and init Firebase/plugins. |
| ios/Runner.xcodeproj/project.pbxproj | Bundles chat notification sound resource in iOS build. |
| firebase.json | Adds FlutterFire/Firebase config artifact. |
| firebase-instructions.md | Adds setup/deploy/runbook for Firebase FCM + Supabase integration. |
| android/settings.gradle.kts | Adds Google Services Gradle plugin version. |
| android/app/src/main/AndroidManifest.xml | Sets default FCM notification channel id meta-data. |
| android/app/google-services.json | Adds Firebase Android config. |
| android/app/build.gradle.kts | Applies Google Services plugin. |
| AGENTS.md | Updates agent guidance with web/FCM conventions. |
| .gitignore | Ignores Firebase Admin SDK JSON and Supabase FCM secrets env file. |
| .firebaserc | Adds default Firebase project mapping. |
| .env.example | Adds Firebase/FCM-related env vars and dispatch secret placeholder. |
| .cursorignore | Reworks Cursor ignore rules (more aggressive platform/build/media ignores). |
| .cursor/settings.json | Enables Cursor plugins (Stripe/Supabase/Firebase/modern web guidance). |
| .cursor/hooks/state/continual-learning.json | Updates local Cursor hook state. |
| .cursor/hooks/state/continual-learning-index.json | Updates local Cursor hook state index (machine-specific paths). |
| .claudeignore | Adjusts Claude ignore patterns (removes html/jsx ignores). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+11
to
+14
| if (kIsWeb) { | ||
| return '${Uri.base.origin}/#$normalized$query'; | ||
| } | ||
| return '${Uri.base.origin}$normalized$query'; |
Comment on lines
2
to
6
| import 'package:flutter/material.dart'; | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import 'package:go_router/go_router.dart'; | ||
| import 'package:flutter/foundation.dart'; | ||
| import 'package:image_picker/image_picker.dart'; |
Comment on lines
+42
to
+49
| static const FirebaseOptions ios = FirebaseOptions( | ||
| apiKey: 'AIzaSyDW3jiVahlDqTey6aj0zsj9Z-Dj-kvamgE', | ||
| appId: '1:86798095066:android:5b5d6008f7ab957f68cbe3', | ||
| messagingSenderId: '86798095066', | ||
| projectId: 'petfolio-v1', | ||
| storageBucket: 'petfolio-v1.firebasestorage.app', | ||
| iosBundleId: 'com.example.petfolio', | ||
| ); |
Comment on lines
+5
to
+7
| if (!await envFile.exists()) { | ||
| stderr.writeln('.env not found. Copy .env.example and set FIREBASE_VAPID_KEY.'); | ||
| exitCode = 1; |
Comment on lines
+191
to
+194
| if (checkout_mode) { | ||
| if (!success_url || !cancel_url) { | ||
| return json({ error: 'success_url and cancel_url are required for checkout_mode' }, 400); | ||
| } |
Comment on lines
+82
to
+84
| await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); | ||
| FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); | ||
| await FcmService.instance.initialize(); |
Comment on lines
+1
to
+5
| { | ||
| "version": 1, | ||
| "files": { | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\ce1b2c8d-81a4-4639-90f8-3721e6c503a7\\ce1b2c8d-81a4-4639-90f8-3721e6c503a7.jsonl": 1779035133681, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\94643488-4659-4d0e-9545-49a79437f020\\94643488-4659-4d0e-9545-49a79437f020.jsonl": 1778967578761, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\36e3088e-0d5c-45e5-b1ba-d1054303de94\\36e3088e-0d5c-45e5-b1ba-d1054303de94.jsonl": 1779039991962, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\bced3497-a066-4257-b439-2b857e493c49\\bced3497-a066-4257-b439-2b857e493c49.jsonl": 1778888950579, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\80eedde5-7184-424c-b917-a680f1ff70a2\\80eedde5-7184-424c-b917-a680f1ff70a2.jsonl": 1778892745538, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\87c931dd-27e3-40aa-bdc6-3274831f9214\\87c931dd-27e3-40aa-bdc6-3274831f9214.jsonl": 1778958857308, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\60e1a481-cb4c-4ac2-aa03-bfae7cefa5fe\\60e1a481-cb4c-4ac2-aa03-bfae7cefa5fe.jsonl": 1778881486521, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\bb710a65-baaf-4764-9eae-dacedace3006\\bb710a65-baaf-4764-9eae-dacedace3006.jsonl": 1778786794129, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\274c391d-7a36-49d5-87be-4df8b522bd16\\274c391d-7a36-49d5-87be-4df8b522bd16.jsonl": 1778800364415, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\194eaf83-5b12-43e4-b1ea-318bbf90c26c\\194eaf83-5b12-43e4-b1ea-318bbf90c26c.jsonl": 1778954732395, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\25b68db3-c204-4def-aead-73ff3500c794\\25b68db3-c204-4def-aead-73ff3500c794.jsonl": 1778876459233, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\8c5433a9-d387-4e9b-ba3a-1e1e006c37bf\\8c5433a9-d387-4e9b-ba3a-1e1e006c37bf.jsonl": 1778856406418, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\e6552f8a-c6c0-4b71-b118-14f55640ae1f\\e6552f8a-c6c0-4b71-b118-14f55640ae1f.jsonl": 1778776581760, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\16628f4f-057e-4274-96e0-ec9ad3d893be\\16628f4f-057e-4274-96e0-ec9ad3d893be.jsonl": 1778882210597, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\0148be8a-b886-48f4-bdd3-67d076c8182c\\0148be8a-b886-48f4-bdd3-67d076c8182c.jsonl": 1778792789903, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\7abfd4c7-d454-4cf6-a391-333321a013ad\\7abfd4c7-d454-4cf6-a391-333321a013ad.jsonl": 1779058557683, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\5acfed98-7cbf-4420-ac3d-091356b8e44f\\5acfed98-7cbf-4420-ac3d-091356b8e44f.jsonl": 1778871687683, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\d7bd1b6a-f748-47f6-87ac-953246413613\\d7bd1b6a-f748-47f6-87ac-953246413613.jsonl": 1778799809267, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\2d86f00f-6bfb-451f-928c-ff0fea1f79d2\\2d86f00f-6bfb-451f-928c-ff0fea1f79d2.jsonl": 1779034182814, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\6460028d-51b8-402d-8b25-5fbcae4596f2\\6460028d-51b8-402d-8b25-5fbcae4596f2.jsonl": 1778961242286, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\88c2a075-4e77-4d24-81f3-b7ba5b774ac4\\88c2a075-4e77-4d24-81f3-b7ba5b774ac4.jsonl": 1778891774309, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\9ebdd98e-260d-475c-999c-dd06afa742cd\\9ebdd98e-260d-475c-999c-dd06afa742cd.jsonl": 1778802413316, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\5bead4b5-9471-461d-ab9a-957f4c1c2490\\5bead4b5-9471-461d-ab9a-957f4c1c2490.jsonl": 1778892105848, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\dab4e7c9-f531-4cb9-a593-62d5c029e54b\\dab4e7c9-f531-4cb9-a593-62d5c029e54b.jsonl": 1778879297617, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\8ea6431f-f572-411a-98e3-0dc5552c5a51\\8ea6431f-f572-411a-98e3-0dc5552c5a51.jsonl": 1778783272217, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\babe61d3-90ce-4696-be96-37e1e4e0f971\\babe61d3-90ce-4696-be96-37e1e4e0f971.jsonl": 1778891057278, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\09fd1bb0-4e5e-4e5d-a591-a22fac645ab3\\09fd1bb0-4e5e-4e5d-a591-a22fac645ab3.jsonl": 1778873095891 | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\46b1bc04-342c-4e30-a6dd-b55acfed523f\\46b1bc04-342c-4e30-a6dd-b55acfed523f.jsonl": 1780583489178, | ||
| "C:\\Users\\syedr\\.cursor\\projects\\g-GitHub-petfolio\\agent-transcripts\\ce1b2c8d-81a4-4639-90f8-3721e6c503a7\\ce1b2c8d-81a4-4639-90f8-3721e6c503a7.jsonl": 1779013533682, |
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds end-to-end web/PWA support: Firebase Cloud Messaging (Flutter + service worker + Supabase dispatch), improved installability and web shell, platform abstractions for notifications/media/payments, and Stripe Hosted Checkout on web. Includes Cursor ignore/config cleanup and deployment workflow updates.
Scope
Test plan
flutter analyzeandflutter test/marketplace/order/:id?stripe=successuser_fcm_devicesPUBLIC_APP_ORIGIN(comma-separated origins allowed viaALLOWED_REDIRECT_ORIGINS) before production hosted checkout