Browse Source
- Consolidate 5 notification-system-* files into doc/notification-system.md - Add web-push cleanup guide and Start-on-Login glossary entry - Configure markdownlint for consistent formatting - Remove web-push references, focus on native OS scheduling Reduces maintenance overhead while preserving all essential information in a single, well-formatted reference document.pull/188/head
17 changed files with 631 additions and 161 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