Compare commits
7 Commits
claimview-
...
registrati
| Author | SHA1 | Date | |
|---|---|---|---|
| 631aa468e6 | |||
| ee29b517ce | |||
| f34c567ab4 | |||
| bd072d95eb | |||
|
|
ca1190aa47 | ||
|
|
f38ec1daff | ||
|
|
ec2cab768b |
181
doc/seed-phrase-reminder-implementation.md
Normal file
181
doc/seed-phrase-reminder-implementation.md
Normal file
@@ -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.
|
||||||
@@ -16,6 +16,12 @@ messages * - Conditional UI based on platform capabilities * * @component *
|
|||||||
:to="{ name: 'seed-backup' }"
|
:to="{ name: 'seed-backup' }"
|
||||||
:class="backupButtonClasses"
|
:class="backupButtonClasses"
|
||||||
>
|
>
|
||||||
|
<!-- Notification dot - show while the user has not yet backed up their seed phrase -->
|
||||||
|
<font-awesome
|
||||||
|
v-if="!hasBackedUpSeed"
|
||||||
|
icon="circle"
|
||||||
|
class="absolute -right-[8px] -top-[8px] text-rose-500 text-[14px] border border-white rounded-full"
|
||||||
|
></font-awesome>
|
||||||
Backup Identifier Seed
|
Backup Identifier Seed
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
@@ -98,6 +104,12 @@ export default class DataExportSection extends Vue {
|
|||||||
*/
|
*/
|
||||||
isExporting = false;
|
isExporting = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating if the user has backed up their seed phrase
|
||||||
|
* Used to control the visibility of the notification dot
|
||||||
|
*/
|
||||||
|
hasBackedUpSeed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification helper for consistent notification patterns
|
* Notification helper for consistent notification patterns
|
||||||
* Created as a getter to ensure $notify is available when called
|
* Created as a getter to ensure $notify is available when called
|
||||||
@@ -129,7 +141,7 @@ export default class DataExportSection extends Vue {
|
|||||||
* CSS classes for the backup button (router link)
|
* CSS classes for the backup button (router link)
|
||||||
*/
|
*/
|
||||||
get backupButtonClasses(): string {
|
get backupButtonClasses(): string {
|
||||||
return "block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2";
|
return "block relative w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,6 +230,22 @@ export default class DataExportSection extends Vue {
|
|||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
this.notify = createNotifyHelpers(this.$notify);
|
||||||
|
this.loadSeedBackupStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the seed backup status from account settings
|
||||||
|
* Updates the hasBackedUpSeed flag to control notification dot visibility
|
||||||
|
*/
|
||||||
|
private async loadSeedBackupStatus(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
this.hasBackedUpSeed = !!settings.hasBackedUpSeed;
|
||||||
|
} catch (err: unknown) {
|
||||||
|
logger.error("Failed to load seed backup status:", err);
|
||||||
|
// Default to false (show notification dot) if we can't load the setting
|
||||||
|
this.hasBackedUpSeed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ import GiftDetailsStep from "../components/GiftDetailsStep.vue";
|
|||||||
import { PlanData } from "../interfaces/records";
|
import { PlanData } from "../interfaces/records";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
import {
|
import {
|
||||||
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT,
|
NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT,
|
||||||
NOTIFY_GIFT_ERROR_NO_DESCRIPTION,
|
NOTIFY_GIFT_ERROR_NO_DESCRIPTION,
|
||||||
@@ -411,6 +412,15 @@ export default class GiftedDialog extends Vue {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.safeNotify.success("That gift was recorded.", TIMEOUTS.VERY_LONG);
|
this.safeNotify.success("That gift was recorded.", TIMEOUTS.VERY_LONG);
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.callbackOnSuccess) {
|
if (this.callbackOnSuccess) {
|
||||||
this.callbackOnSuccess(amount);
|
this.callbackOnSuccess(amount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import * as libsUtil from "../libs/util";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
import {
|
import {
|
||||||
NOTIFY_OFFER_SETTINGS_ERROR,
|
NOTIFY_OFFER_SETTINGS_ERROR,
|
||||||
NOTIFY_OFFER_RECORDING,
|
NOTIFY_OFFER_RECORDING,
|
||||||
@@ -299,6 +300,14 @@ export default class OfferDialog extends Vue {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.notify.success(NOTIFY_OFFER_SUCCESS.message, TIMEOUTS.VERY_LONG);
|
this.notify.success(NOTIFY_OFFER_SUCCESS.message, TIMEOUTS.VERY_LONG);
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -124,6 +124,12 @@ const MIGRATIONS = [
|
|||||||
ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE;
|
ALTER TABLE contacts ADD COLUMN iViewContent BOOLEAN DEFAULT TRUE;
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "003_add_hasBackedUpSeed_to_settings",
|
||||||
|
sql: `
|
||||||
|
ALTER TABLE settings ADD COLUMN hasBackedUpSeed BOOLEAN DEFAULT FALSE;
|
||||||
|
`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type Settings = {
|
|||||||
finishedOnboarding?: boolean; // the user has completed the onboarding process
|
finishedOnboarding?: boolean; // the user has completed the onboarding process
|
||||||
|
|
||||||
firstName?: string; // user's full name, may be null if unwanted for a particular account
|
firstName?: string; // user's full name, may be null if unwanted for a particular account
|
||||||
|
hasBackedUpSeed?: boolean; // tracks whether the user has backed up their seed phrase
|
||||||
hideRegisterPromptOnNewContact?: boolean;
|
hideRegisterPromptOnNewContact?: boolean;
|
||||||
isRegistered?: boolean;
|
isRegistered?: boolean;
|
||||||
// imageServer?: string; // if we want to allow modification then we should make image functionality optional -- or at least customizable
|
// imageServer?: string; // if we want to allow modification then we should make image functionality optional -- or at least customizable
|
||||||
|
|||||||
90
src/utils/seedPhraseReminder.ts
Normal file
90
src/utils/seedPhraseReminder.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -811,6 +811,7 @@ import { logger } from "../utils/logger";
|
|||||||
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
|
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
import {
|
import {
|
||||||
AccountSettings,
|
AccountSettings,
|
||||||
isApiError,
|
isApiError,
|
||||||
@@ -1695,6 +1696,14 @@ export default class AccountViewView extends Vue {
|
|||||||
);
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED);
|
this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED);
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR);
|
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import { Router, RouteLocationNormalizedLoaded } from "vue-router";
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
|
|
||||||
// Type guard for API responses
|
// Type guard for API responses
|
||||||
function isApiResponse(response: unknown): response is AxiosResponse {
|
function isApiResponse(response: unknown): response is AxiosResponse {
|
||||||
@@ -223,6 +224,14 @@ export default class ClaimAddRawView extends Vue {
|
|||||||
);
|
);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.notify.success("Claim submitted.", TIMEOUTS.LONG);
|
this.notify.success("Claim submitted.", TIMEOUTS.LONG);
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error("Got error submitting the claim:", result);
|
logger.error("Got error submitting the claim:", result);
|
||||||
this.notify.error(
|
this.notify.error(
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ import {
|
|||||||
QR_TIMEOUT_LONG,
|
QR_TIMEOUT_LONG,
|
||||||
} from "@/constants/notifications";
|
} from "@/constants/notifications";
|
||||||
import { createNotifyHelpers, NotifyFunction } from "../utils/notify";
|
import { createNotifyHelpers, NotifyFunction } from "../utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
|
|
||||||
interface QRScanResult {
|
interface QRScanResult {
|
||||||
rawValue?: string;
|
rawValue?: string;
|
||||||
@@ -622,6 +623,15 @@ export default class ContactQRScanFull extends Vue {
|
|||||||
*/
|
*/
|
||||||
async handleBack() {
|
async handleBack() {
|
||||||
await this.cleanupScanner();
|
await this.cleanupScanner();
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.back();
|
this.$router.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ import { QRScannerFactory } from "@/services/QRScanner/QRScannerFactory";
|
|||||||
import { CameraState } from "@/services/QRScanner/types";
|
import { CameraState } from "@/services/QRScanner/types";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers } from "@/utils/notify";
|
import { createNotifyHelpers } from "@/utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
import {
|
import {
|
||||||
NOTIFY_QR_INITIALIZATION_ERROR,
|
NOTIFY_QR_INITIALIZATION_ERROR,
|
||||||
NOTIFY_QR_CAMERA_IN_USE,
|
NOTIFY_QR_CAMERA_IN_USE,
|
||||||
@@ -319,6 +320,15 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
|
|
||||||
async handleBack(): Promise<void> {
|
async handleBack(): Promise<void> {
|
||||||
await this.cleanupScanner();
|
await this.cleanupScanner();
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.back();
|
this.$router.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,24 +748,17 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
!contact.registered
|
!contact.registered
|
||||||
) {
|
) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.notify.confirm(
|
this.$notify(
|
||||||
"Do you want to register them?",
|
|
||||||
{
|
{
|
||||||
|
group: "modal",
|
||||||
|
type: "confirm",
|
||||||
|
title: "Register",
|
||||||
|
text: "Do you want to register them?",
|
||||||
onCancel: async (stopAsking?: boolean) => {
|
onCancel: async (stopAsking?: boolean) => {
|
||||||
if (stopAsking) {
|
await this.handleRegistrationPromptResponse(stopAsking);
|
||||||
await this.$updateSettings({
|
|
||||||
hideRegisterPromptOnNewContact: stopAsking,
|
|
||||||
});
|
|
||||||
this.hideRegisterPromptOnNewContact = stopAsking;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onNo: async (stopAsking?: boolean) => {
|
onNo: async (stopAsking?: boolean) => {
|
||||||
if (stopAsking) {
|
await this.handleRegistrationPromptResponse(stopAsking);
|
||||||
await this.$updateSettings({
|
|
||||||
hideRegisterPromptOnNewContact: stopAsking,
|
|
||||||
});
|
|
||||||
this.hideRegisterPromptOnNewContact = stopAsking;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onYes: async () => {
|
onYes: async () => {
|
||||||
await this.register(contact);
|
await this.register(contact);
|
||||||
@@ -885,6 +888,17 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
videoElement.style.transform = shouldMirror ? "scaleX(-1)" : "none";
|
videoElement.style.transform = shouldMirror ? "scaleX(-1)" : "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleRegistrationPromptResponse(
|
||||||
|
stopAsking?: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (stopAsking) {
|
||||||
|
await this.$saveSettings({
|
||||||
|
hideRegisterPromptOnNewContact: stopAsking,
|
||||||
|
});
|
||||||
|
this.hideRegisterPromptOnNewContact = stopAsking;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ import { logger } from "../utils/logger";
|
|||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
|
import { showSeedPhraseReminder } from "@/utils/seedPhraseReminder";
|
||||||
import {
|
import {
|
||||||
NOTIFY_GIFTED_DETAILS_RETRIEVAL_ERROR,
|
NOTIFY_GIFTED_DETAILS_RETRIEVAL_ERROR,
|
||||||
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM,
|
NOTIFY_GIFTED_DETAILS_DELETE_IMAGE_CONFIRM,
|
||||||
@@ -770,6 +771,15 @@ export default class GiftedDetails extends Vue {
|
|||||||
NOTIFY_GIFTED_DETAILS_GIFT_RECORDED.message,
|
NOTIFY_GIFTED_DETAILS_GIFT_RECORDED.message,
|
||||||
TIMEOUTS.SHORT,
|
TIMEOUTS.SHORT,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Show seed phrase backup reminder if needed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
showSeedPhraseReminder(!!settings.hasBackedUpSeed, this.$notify);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking seed backup status:", error);
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.removeItem("imageUrl");
|
localStorage.removeItem("imageUrl");
|
||||||
if (this.destinationPathAfter) {
|
if (this.destinationPathAfter) {
|
||||||
(this.$router as Router).push({ path: this.destinationPathAfter });
|
(this.$router as Router).push({ path: this.destinationPathAfter });
|
||||||
|
|||||||
@@ -231,9 +231,24 @@ export default class SeedBackupView extends Vue {
|
|||||||
/**
|
/**
|
||||||
* Reveals the seed phrase to the user
|
* Reveals the seed phrase to the user
|
||||||
* Sets showSeed to true to display the sensitive seed phrase data
|
* Sets showSeed to true to display the sensitive seed phrase data
|
||||||
|
* Updates the hasBackedUpSeed setting to true to track that user has backed up
|
||||||
*/
|
*/
|
||||||
revealSeed(): void {
|
async revealSeed(): Promise<void> {
|
||||||
this.showSeed = true;
|
this.showSeed = true;
|
||||||
|
|
||||||
|
// Update the account setting to track that user has backed up their seed
|
||||||
|
try {
|
||||||
|
const settings = await this.$accountSettings();
|
||||||
|
if (settings.activeDid) {
|
||||||
|
await this.$saveUserSettings(settings.activeDid, {
|
||||||
|
hasBackedUpSeed: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err: unknown) {
|
||||||
|
logger.error("Failed to update hasBackedUpSeed setting:", err);
|
||||||
|
// Don't show error to user as this is not critical to the main functionality
|
||||||
|
// The seed phrase is still revealed, just the tracking won't work
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user