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