Browse Source
			
			
			
			
				
		Add comprehensive seed phrase backup reminder system to encourage users to secure their identity after creating content. Core Features: - Modal dialog with "Backup Identifier Seed" and "Remind me Later" options - 24-hour localStorage cooldown to prevent notification fatigue - 1-second delay after success messages for better UX flow - Focuses on claim creation actions, not confirmations New Files: - src/utils/seedPhraseReminder.ts: Core utility for reminder logic - doc/seed-phrase-reminder-implementation.md: Comprehensive documentation Trigger Points Added: - Profile saving (AccountViewView) - Claim creation (ClaimAddRawView, GiftedDialog, GiftedDetailsView) - Offer creation (OfferDialog) - QR code view exit (ContactQRScanFullView, ContactQRScanShowView) Technical Implementation: - Uses existing notification group modal system from App.vue - Integrates with PlatformServiceMixin for account settings access - Graceful error handling with logging fallbacks - Non-blocking implementation that doesn't affect main functionality - Modal stays open indefinitely (timeout: -1) until user interaction User Experience: - Non-intrusive reminders that respect user preferences - Clear call-to-action for security-conscious users - Seamless integration with existing workflows - Maintains focus on content creation rather than confirmation actionspull/195/head
				 9 changed files with 338 additions and 0 deletions
			
			
		| @ -0,0 +1,181 @@ | |||
| # Seed Phrase Backup Reminder Implementation | |||
| 
 | |||
| ## Overview | |||
| 
 | |||
| This implementation adds a modal dialog that reminds users to back up their seed phrase if they haven't done so yet. The reminder appears after specific user actions and includes a 24-hour cooldown to avoid being too intrusive. | |||
| 
 | |||
| ## Features | |||
| 
 | |||
| - **Modal Dialog**: Uses the existing notification group modal system from `App.vue` | |||
| - **Smart Timing**: Only shows when `hasBackedUpSeed = false` | |||
| - **24-Hour Cooldown**: Uses localStorage to prevent showing more than once per day | |||
| - **Action-Based Triggers**: Shows after specific user actions | |||
| - **User Choice**: "Backup Identifier Seed" or "Remind me Later" options | |||
| 
 | |||
| ## Implementation Details | |||
| 
 | |||
| ### Core Utility (`src/utils/seedPhraseReminder.ts`) | |||
| 
 | |||
| The main utility provides: | |||
| 
 | |||
| - `shouldShowSeedReminder(hasBackedUpSeed)`: Checks if reminder should be shown | |||
| - `markSeedReminderShown()`: Updates localStorage timestamp | |||
| - `createSeedReminderNotification()`: Creates the modal configuration | |||
| - `showSeedPhraseReminder(hasBackedUpSeed, notifyFunction)`: Main function to show reminder | |||
| 
 | |||
| ### Trigger Points | |||
| 
 | |||
| The reminder is shown after these user actions: | |||
| 
 | |||
| **Note**: The reminder is triggered by **claim creation** actions, not claim confirmations. This focuses on when users are actively creating new content rather than just confirming existing claims. | |||
| 
 | |||
| 1. **Profile Saving** (`AccountViewView.vue`) | |||
|    - After clicking "Save Profile" button | |||
|    - Only when profile save is successful | |||
| 
 | |||
| 2. **Claim Creation** (Multiple views) | |||
|    - `ClaimAddRawView.vue`: After submitting raw claims | |||
|    - `GiftedDialog.vue`: After creating gifts/claims | |||
|    - `GiftedDetailsView.vue`: After recording gifts/claims | |||
|    - `OfferDialog.vue`: After creating offers | |||
| 
 | |||
| 3. **QR Code Views Exit** | |||
|    - `ContactQRScanFullView.vue`: When exiting via back button | |||
|    - `ContactQRScanShowView.vue`: When exiting via back button | |||
| 
 | |||
| ### Modal Configuration | |||
| 
 | |||
| ```typescript | |||
| { | |||
|   group: "modal", | |||
|   type: "confirm", | |||
|   title: "Backup Your Identifier Seed?", | |||
|   text: "It looks like you haven't backed up your identifier seed yet. It's important to back it up as soon as possible to secure your identity.", | |||
|   yesText: "Backup Identifier Seed", | |||
|   noText: "Remind me Later", | |||
|   onYes: () => navigate to /seed-backup, | |||
|   onNo: () => mark as shown for 24 hours, | |||
|   onCancel: () => mark as shown for 24 hours | |||
| } | |||
| ``` | |||
| 
 | |||
| **Important**: The modal is configured with `timeout: -1` to ensure it stays open until the user explicitly interacts with one of the buttons. This prevents the dialog from closing automatically. | |||
| 
 | |||
| ### Cooldown Mechanism | |||
| 
 | |||
| - **Storage Key**: `seedPhraseReminderLastShown` | |||
| - **Cooldown Period**: 24 hours (24 * 60 * 60 * 1000 milliseconds) | |||
| - **Implementation**: localStorage with timestamp comparison | |||
| - **Fallback**: Shows reminder if timestamp is invalid or missing | |||
| 
 | |||
| ## User Experience | |||
| 
 | |||
| ### When Reminder Appears | |||
| 
 | |||
| - User has not backed up their seed phrase (`hasBackedUpSeed = false`) | |||
| - At least 24 hours have passed since last reminder | |||
| - User performs one of the trigger actions | |||
| - **1-second delay** after the success message to allow users to see the confirmation | |||
| 
 | |||
| ### User Options | |||
| 
 | |||
| 1. **"Backup Identifier Seed"**: Navigates to `/seed-backup` page | |||
| 2. **"Remind me Later"**: Dismisses and won't show again for 24 hours | |||
| 3. **Cancel/Close**: Same behavior as "Remind me Later" | |||
| 
 | |||
| ### Frequency Control | |||
| 
 | |||
| - **First Time**: Always shows if user hasn't backed up | |||
| - **Subsequent**: Only shows after 24-hour cooldown | |||
| - **Automatic Reset**: When user completes seed backup (`hasBackedUpSeed = true`) | |||
| 
 | |||
| ## Technical Implementation | |||
| 
 | |||
| ### Error Handling | |||
| 
 | |||
| - Graceful fallback if localStorage operations fail | |||
| - Logging of errors for debugging | |||
| - Non-blocking implementation (doesn't affect main functionality) | |||
| 
 | |||
| ### Integration Points | |||
| 
 | |||
| - **Platform Service**: Uses `$accountSettings()` to check backup status | |||
| - **Notification System**: Integrates with existing `$notify` system | |||
| - **Router**: Uses `window.location.href` for navigation | |||
| 
 | |||
| ### Performance Considerations | |||
| 
 | |||
| - Minimal localStorage operations | |||
| - No blocking operations | |||
| - Efficient timestamp comparisons | |||
| - **Timing Behavior**: 1-second delay before showing reminder to improve user experience flow | |||
| 
 | |||
| ## Testing | |||
| 
 | |||
| ### Manual Testing Scenarios | |||
| 
 | |||
| 1. **First Time User** | |||
|    - Create new account | |||
|    - Perform trigger action (save profile, create claim, exit QR view) | |||
|    - Verify reminder appears | |||
| 
 | |||
| 2. **Repeat User (Within 24h)** | |||
|    - Perform trigger action | |||
|    - Verify reminder does NOT appear | |||
| 
 | |||
| 3. **Repeat User (After 24h)** | |||
|    - Wait 24+ hours | |||
|    - Perform trigger action | |||
|    - Verify reminder appears again | |||
| 
 | |||
| 4. **User Who Has Backed Up** | |||
|    - Complete seed backup | |||
|    - Perform trigger action | |||
|    - Verify reminder does NOT appear | |||
| 
 | |||
| 5. **QR Code View Exit** | |||
|    - Navigate to QR code view (full or show) | |||
|    - Exit via back button | |||
|    - Verify reminder appears (if conditions are met) | |||
| 
 | |||
| ### Browser Testing | |||
| 
 | |||
| - Test localStorage functionality | |||
| - Verify timestamp handling | |||
| - Check navigation to seed backup page | |||
| 
 | |||
| ## Future Enhancements | |||
| 
 | |||
| ### Potential Improvements | |||
| 
 | |||
| 1. **Customizable Cooldown**: Allow users to set reminder frequency | |||
| 2. **Progressive Urgency**: Increase reminder frequency over time | |||
| 3. **Analytics**: Track reminder effectiveness and user response | |||
| 4. **A/B Testing**: Test different reminder messages and timing | |||
| 
 | |||
| ### Configuration Options | |||
| 
 | |||
| - Reminder frequency settings | |||
| - Custom reminder messages | |||
| - Different trigger conditions | |||
| - Integration with other notification systems | |||
| 
 | |||
| ## Maintenance | |||
| 
 | |||
| ### Monitoring | |||
| 
 | |||
| - Check localStorage usage in browser dev tools | |||
| - Monitor user feedback about reminder frequency | |||
| - Track navigation success to seed backup page | |||
| 
 | |||
| ### Updates | |||
| 
 | |||
| - Modify reminder text in `createSeedReminderNotification()` | |||
| - Adjust cooldown period in `REMINDER_COOLDOWN_MS` constant | |||
| - Add new trigger points as needed | |||
| 
 | |||
| ## Conclusion | |||
| 
 | |||
| This implementation provides a non-intrusive way to remind users about seed phrase backup while respecting their preferences and avoiding notification fatigue. The 24-hour cooldown ensures users aren't overwhelmed while maintaining the importance of the security reminder. | |||
| 
 | |||
| The feature is fully integrated with the existing codebase architecture and follows established patterns for notifications, error handling, and user interaction. | |||
| @ -0,0 +1,90 @@ | |||
| import { NotificationIface } from "@/constants/app"; | |||
| 
 | |||
| const SEED_REMINDER_KEY = "seedPhraseReminderLastShown"; | |||
| const REMINDER_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
 | |||
| 
 | |||
| /** | |||
|  * Checks if the seed phrase backup reminder should be shown | |||
|  * @param hasBackedUpSeed - Whether the user has backed up their seed phrase | |||
|  * @returns true if the reminder should be shown, false otherwise | |||
|  */ | |||
| export function shouldShowSeedReminder(hasBackedUpSeed: boolean): boolean { | |||
|   // Don't show if user has already backed up
 | |||
|   if (hasBackedUpSeed) { | |||
|     return false; | |||
|   } | |||
| 
 | |||
|   // Check localStorage for last shown time
 | |||
|   const lastShown = localStorage.getItem(SEED_REMINDER_KEY); | |||
|   if (!lastShown) { | |||
|     return true; // First time, show the reminder
 | |||
|   } | |||
| 
 | |||
|   try { | |||
|     const lastShownTime = parseInt(lastShown, 10); | |||
|     const now = Date.now(); | |||
|     const timeSinceLastShown = now - lastShownTime; | |||
| 
 | |||
|     // Show if more than 24 hours have passed
 | |||
|     return timeSinceLastShown >= REMINDER_COOLDOWN_MS; | |||
|   } catch (error) { | |||
|     // If there's an error parsing the timestamp, show the reminder
 | |||
|     return true; | |||
|   } | |||
| } | |||
| 
 | |||
| /** | |||
|  * Marks the seed phrase reminder as shown by updating localStorage | |||
|  */ | |||
| export function markSeedReminderShown(): void { | |||
|   localStorage.setItem(SEED_REMINDER_KEY, Date.now().toString()); | |||
| } | |||
| 
 | |||
| /** | |||
|  * Creates the seed phrase backup reminder notification | |||
|  * @returns NotificationIface configuration for the reminder modal | |||
|  */ | |||
| export function createSeedReminderNotification(): NotificationIface { | |||
|   return { | |||
|     group: "modal", | |||
|     type: "confirm", | |||
|     title: "Backup Your Identifier Seed?", | |||
|     text: "It looks like you haven't backed up your identifier seed yet. It's important to back it up as soon as possible to secure your identity.", | |||
|     yesText: "Backup Identifier Seed", | |||
|     noText: "Remind me Later", | |||
|     onYes: async () => { | |||
|       // Navigate to seed backup page
 | |||
|       window.location.href = "/seed-backup"; | |||
|     }, | |||
|     onNo: async () => { | |||
|       // Mark as shown so it won't appear again for 24 hours
 | |||
|       markSeedReminderShown(); | |||
|     }, | |||
|     onCancel: async () => { | |||
|       // Mark as shown so it won't appear again for 24 hours
 | |||
|       markSeedReminderShown(); | |||
|     }, | |||
|   }; | |||
| } | |||
| 
 | |||
| /** | |||
|  * Shows the seed phrase backup reminder if conditions are met | |||
|  * @param hasBackedUpSeed - Whether the user has backed up their seed phrase | |||
|  * @param notifyFunction - Function to show notifications | |||
|  * @returns true if the reminder was shown, false otherwise | |||
|  */ | |||
| export function showSeedPhraseReminder( | |||
|   hasBackedUpSeed: boolean, | |||
|   notifyFunction: (notification: NotificationIface, timeout?: number) => void, | |||
| ): boolean { | |||
|   if (shouldShowSeedReminder(hasBackedUpSeed)) { | |||
|     const notification = createSeedReminderNotification(); | |||
|     // Add 1-second delay before showing the modal to allow success message to be visible
 | |||
|     setTimeout(() => { | |||
|       // Pass -1 as timeout to ensure modal stays open until user interaction
 | |||
|       notifyFunction(notification, -1); | |||
|     }, 1000); | |||
|     return true; | |||
|   } | |||
|   return false; | |||
| } | |||
					Loading…
					
					
				
		Reference in new issue