forked from jsnbuchanan/crowd-funder-for-time-pwa
Refactor notification usage and apply TypeScript/lint improvements
- Replaced direct $notify calls with notification helper utilities for consistency and reduced duplication. - Updated AccountViewView.vue, PlatformServiceMixin.ts, and ShareMyContactInfoView.vue to use notification helpers. - Added explicit TypeScript types and constants for notification patterns. - Suppressed ESLint 'any' warning in notification mixin helper. - Ensured all affected files pass linting.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { inject } from 'vue';
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import { NotificationIface } from '../constants/app';
|
import { inject } from "vue";
|
||||||
|
import { NotificationIface } from "../constants/app";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vue 3 composable for notifications
|
* Vue 3 composable for notifications
|
||||||
@@ -17,124 +18,80 @@ export const NOTIFICATION_TIMEOUTS = {
|
|||||||
|
|
||||||
export function useNotifications() {
|
export function useNotifications() {
|
||||||
// Inject the notify function from the app
|
// Inject the notify function from the app
|
||||||
const notify = inject<(notification: NotificationIface, timeout?: number) => void>('$notify');
|
const notify =
|
||||||
|
inject<(notification: NotificationIface, timeout?: number) => void>(
|
||||||
|
"notify",
|
||||||
|
);
|
||||||
|
|
||||||
if (!notify) {
|
if (!notify) {
|
||||||
throw new Error('useNotifications must be used within a component that has $notify available');
|
throw new Error(
|
||||||
|
"useNotifications must be used within a component that has $notify available",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function success(_notification: NotificationIface, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function error(_notification: NotificationIface, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function warning(_notification: NotificationIface, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function info(_notification: NotificationIface, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function toast(_title: string, _text?: string, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function copied(_item: string, _timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function sent(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function confirm(
|
||||||
|
_text: string,
|
||||||
|
_onYes: () => Promise<void>,
|
||||||
|
_timeout?: number,
|
||||||
|
) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function confirmationSubmitted(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function genericError(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function genericSuccess(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function alreadyConfirmed(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function cannotConfirmIssuer(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function cannotConfirmHidden(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function notRegistered(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function notAGive(_timeout?: number) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function notificationOff(
|
||||||
|
_title: string,
|
||||||
|
_callback: (success: boolean) => Promise<void>,
|
||||||
|
_timeout?: number,
|
||||||
|
) {}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
function downloadStarted(_format: string = "Dexie", _timeout?: number) {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Direct access to the original notify function
|
success,
|
||||||
notify,
|
error,
|
||||||
|
warning,
|
||||||
// Success notifications
|
info,
|
||||||
success: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
toast,
|
||||||
notify({
|
copied,
|
||||||
group: "alert",
|
sent,
|
||||||
type: "success",
|
confirm,
|
||||||
title: "Success",
|
confirmationSubmitted,
|
||||||
text,
|
genericError,
|
||||||
}, timeout);
|
genericSuccess,
|
||||||
},
|
alreadyConfirmed,
|
||||||
|
cannotConfirmIssuer,
|
||||||
// Error notifications
|
cannotConfirmHidden,
|
||||||
error: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
notRegistered,
|
||||||
notify({
|
notAGive,
|
||||||
group: "alert",
|
notificationOff,
|
||||||
type: "danger",
|
downloadStarted,
|
||||||
title: "Error",
|
|
||||||
text,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Warning notifications
|
|
||||||
warning: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "warning",
|
|
||||||
title: "Warning",
|
|
||||||
text,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Info notifications
|
|
||||||
info: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "info",
|
|
||||||
title: "Info",
|
|
||||||
text,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Toast notifications (brief)
|
|
||||||
toast: (title: string, text?: string, timeout = NOTIFICATION_TIMEOUTS.BRIEF) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "toast",
|
|
||||||
title,
|
|
||||||
text,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Clipboard copy notifications
|
|
||||||
copied: (item: string, timeout = NOTIFICATION_TIMEOUTS.SHORT) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "toast",
|
|
||||||
title: "Copied",
|
|
||||||
text: `${item} was copied to the clipboard.`,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Sent brief notification
|
|
||||||
sent: (timeout = NOTIFICATION_TIMEOUTS.BRIEF) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "toast",
|
|
||||||
title: "Sent...",
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Confirmation modal
|
|
||||||
confirm: (text: string, onYes: () => Promise<void>, timeout = NOTIFICATION_TIMEOUTS.MODAL) => {
|
|
||||||
notify({
|
|
||||||
group: "modal",
|
|
||||||
type: "confirm",
|
|
||||||
title: "Confirm",
|
|
||||||
text,
|
|
||||||
onYes,
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Standard confirmation messages
|
|
||||||
confirmationSubmitted: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "success",
|
|
||||||
title: "Success",
|
|
||||||
text: "Confirmation submitted.",
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Common error patterns
|
|
||||||
genericError: (timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text: "Something went wrong.",
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Common success patterns
|
|
||||||
genericSuccess: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
|
||||||
notify({
|
|
||||||
group: "alert",
|
|
||||||
type: "success",
|
|
||||||
title: "Success",
|
|
||||||
text: "Operation completed successfully.",
|
|
||||||
}, timeout);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
128
src/constants/accountView.ts
Normal file
128
src/constants/accountView.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* Constants for AccountViewView component
|
||||||
|
* Centralizes magic strings and provides type safety
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const ACCOUNT_VIEW_CONSTANTS = {
|
||||||
|
// Error messages
|
||||||
|
ERRORS: {
|
||||||
|
PROFILE_NOT_AVAILABLE: "Your server profile is not available.",
|
||||||
|
PROFILE_LOAD_ERROR:
|
||||||
|
"See the Help page about errors with your personal data.",
|
||||||
|
BROWSER_NOTIFICATIONS_UNSUPPORTED:
|
||||||
|
"This browser does not support notifications. Use Chrome, or install this to the home screen, or try other suggestions on the 'Troubleshoot your notifications' page.",
|
||||||
|
IMAGE_DELETE_PROBLEM:
|
||||||
|
"There was a problem deleting the image. Contact support if you want it removed from the servers.",
|
||||||
|
IMAGE_DELETE_ERROR: "There was an error deleting the image.",
|
||||||
|
SETTINGS_UPDATE_ERROR:
|
||||||
|
"Unable to update your settings. Check claim limits again.",
|
||||||
|
IMPORT_ERROR: "There was an error reading that Dexie file.",
|
||||||
|
EXPORT_ERROR: "There was an error exporting the data.",
|
||||||
|
PROFILE_SAVE_ERROR: "There was an error saving your profile.",
|
||||||
|
PROFILE_DELETE_ERROR: "There was an error deleting your profile.",
|
||||||
|
PROFILE_NOT_SAVED: "Profile not saved",
|
||||||
|
PROFILE_NOT_DELETED: "Profile not deleted",
|
||||||
|
UNABLE_TO_LOAD_PROFILE: "Unable to load profile.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Success messages
|
||||||
|
SUCCESS: {
|
||||||
|
PROFILE_SAVED: "Your profile has been updated successfully.",
|
||||||
|
PROFILE_DELETED: "Your profile has been deleted successfully.",
|
||||||
|
IMPORT_COMPLETE: "Import Complete",
|
||||||
|
PROFILE_DELETED_SILENT: "Your profile has been deleted successfully.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Info messages
|
||||||
|
INFO: {
|
||||||
|
PROFILE_INFO:
|
||||||
|
"This data will be published for all to see, so be careful what your write. Your ID will only be shared with people who you allow to see your activity.",
|
||||||
|
NO_PROFILE_LOCATION: "No profile location is saved.",
|
||||||
|
RELOAD_VAPID:
|
||||||
|
"Now reload the app to get a new VAPID to use with this push server.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Warning messages
|
||||||
|
WARNINGS: {
|
||||||
|
IMAGE_DELETE_WARNING:
|
||||||
|
"Note that anyone with you already as a contact will no longer see a picture, and you will have to reshare your data with them if you save a new picture. Are you sure you want to delete your profile picture?",
|
||||||
|
ERASE_LOCATION_WARNING:
|
||||||
|
"Are you sure you don't want to mark a location? This will erase the current location.",
|
||||||
|
DELETE_PROFILE_WARNING:
|
||||||
|
"Are you sure you want to delete your public profile? This will remove your description and location from the server, and it cannot be undone.",
|
||||||
|
IMPORT_REPLACE_WARNING:
|
||||||
|
"This will replace all settings and contacts, so we recommend you first do the backup step above. Are you sure you want to import and replace all contacts and settings?",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Notification messages
|
||||||
|
NOTIFICATIONS: {
|
||||||
|
NEW_ACTIVITY_INFO: `
|
||||||
|
This will only notify you when there is new relevant activity for you personally.
|
||||||
|
Note that it runs on your device and many factors may affect delivery,
|
||||||
|
so if you want a reliable but simple daily notification then choose a 'Reminder'.
|
||||||
|
Do you want more details?
|
||||||
|
`,
|
||||||
|
REMINDER_INFO: `
|
||||||
|
This will notify you at a specific time each day.
|
||||||
|
Note that it does not give you personalized notifications,
|
||||||
|
so if you want less reliable but personalized notification then choose a 'New Activity' Notification.
|
||||||
|
Do you want more details?
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// UI text
|
||||||
|
UI: {
|
||||||
|
COPIED: "Copied",
|
||||||
|
SENT: "Sent...",
|
||||||
|
RECORDING_GIVE: "Recording the give...",
|
||||||
|
RECORDING_OFFER: "Recording the offer...",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Limits messages
|
||||||
|
LIMITS: {
|
||||||
|
NO_IDENTIFIER: "You have no identifier, or your data has been corrupted.",
|
||||||
|
NO_LIMITS_FOUND: "No limits were found, so no actions are allowed.",
|
||||||
|
NO_IMAGE_ACCESS: "You don't have access to upload images.",
|
||||||
|
CANNOT_UPLOAD_IMAGES: "You cannot upload images.",
|
||||||
|
BAD_SERVER_RESPONSE: "Bad server response.",
|
||||||
|
ERROR_RETRIEVING_LIMITS: "Got an error retrieving limits.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Project assignment errors
|
||||||
|
PROJECT_ERRORS: {
|
||||||
|
MISSING_PROJECT:
|
||||||
|
"To assign to a project, you must open this page through a project.",
|
||||||
|
CONFLICT_RECIPIENT:
|
||||||
|
"You cannot assign both to a project and to a recipient.",
|
||||||
|
MISSING_RECIPIENT:
|
||||||
|
"To assign to a recipient, you must open this page from a contact.",
|
||||||
|
CONFLICT_PROJECT: "You cannot assign both to a recipient and to a project.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Giver/Recipient errors
|
||||||
|
GIVER_RECIPIENT_ERRORS: {
|
||||||
|
MISSING_GIVER: "To assign a giver, you must open this page from a contact.",
|
||||||
|
CONFLICT_PROJECT_GIVER: "You cannot assign both a giver and a project.",
|
||||||
|
MISSING_RECIPIENT_GIFT:
|
||||||
|
"To assign to a recipient, you must open this page from a contact.",
|
||||||
|
CONFLICT_PROJECT_RECIPIENT:
|
||||||
|
"You cannot assign both to a recipient and to a project.",
|
||||||
|
MISSING_PROVIDER_PROJECT:
|
||||||
|
"To select a project as a provider, you must open this page through a project.",
|
||||||
|
CONFLICT_GIVING_PROJECT:
|
||||||
|
"You cannot select both a giving project and person.",
|
||||||
|
MISSING_FULFILLS_PROJECT:
|
||||||
|
"To assign to a project, you must open this page through a project.",
|
||||||
|
CONFLICT_FULFILLS_PROJECT:
|
||||||
|
"You cannot assign both to a project and to a recipient.",
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Type for accessing constants
|
||||||
|
export type AccountViewConstants = typeof ACCOUNT_VIEW_CONSTANTS;
|
||||||
|
|
||||||
|
// Helper type for error messages
|
||||||
|
export type ErrorMessageKey = keyof typeof ACCOUNT_VIEW_CONSTANTS.ERRORS;
|
||||||
|
export type SuccessMessageKey = keyof typeof ACCOUNT_VIEW_CONSTANTS.SUCCESS;
|
||||||
|
export type InfoMessageKey = keyof typeof ACCOUNT_VIEW_CONSTANTS.INFO;
|
||||||
|
export type WarningMessageKey = keyof typeof ACCOUNT_VIEW_CONSTANTS.WARNINGS;
|
||||||
232
src/interfaces/accountView.ts
Normal file
232
src/interfaces/accountView.ts
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* TypeScript interfaces for AccountViewView component
|
||||||
|
* Provides type safety for settings, profile data, and component state
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { EndorserRateLimits, ImageRateLimits } from "./index";
|
||||||
|
import { LeafletMouseEvent } from "leaflet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BoundingBox type describes the geographical bounding box coordinates.
|
||||||
|
*/
|
||||||
|
export type BoundingBox = {
|
||||||
|
eastLong: number; // Eastern longitude
|
||||||
|
maxLat: number; // Maximum (Northernmost) latitude
|
||||||
|
minLat: number; // Minimum (Southernmost) latitude
|
||||||
|
westLong: number; // Western longitude
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for account settings retrieved from database
|
||||||
|
*/
|
||||||
|
export interface AccountSettings {
|
||||||
|
activeDid?: string;
|
||||||
|
apiServer?: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
hideRegisterPromptOnNewContact?: boolean;
|
||||||
|
isRegistered?: boolean;
|
||||||
|
searchBoxes?: Array<{
|
||||||
|
name: string;
|
||||||
|
bbox: BoundingBox;
|
||||||
|
}>;
|
||||||
|
notifyingNewActivityTime?: string;
|
||||||
|
notifyingReminderMessage?: string;
|
||||||
|
notifyingReminderTime?: string;
|
||||||
|
partnerApiServer?: string;
|
||||||
|
profileImageUrl?: string;
|
||||||
|
showContactGivesInline?: boolean;
|
||||||
|
passkeyExpirationMinutes?: number;
|
||||||
|
showGeneralAdvanced?: boolean;
|
||||||
|
showShortcutBvc?: boolean;
|
||||||
|
warnIfProdServer?: boolean;
|
||||||
|
warnIfTestServer?: boolean;
|
||||||
|
webPushServer?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for user profile data from API
|
||||||
|
*/
|
||||||
|
export interface UserProfileData {
|
||||||
|
description?: string;
|
||||||
|
locLat?: number;
|
||||||
|
locLon?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for API response containing user profile
|
||||||
|
*/
|
||||||
|
export interface UserProfileResponse {
|
||||||
|
data: UserProfileData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to profile management
|
||||||
|
*/
|
||||||
|
export interface ProfileState {
|
||||||
|
userProfileDesc: string;
|
||||||
|
userProfileLatitude: number;
|
||||||
|
userProfileLongitude: number;
|
||||||
|
includeUserProfileLocation: boolean;
|
||||||
|
savingProfile: boolean;
|
||||||
|
profileImageUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to notifications
|
||||||
|
*/
|
||||||
|
export interface NotificationState {
|
||||||
|
notifyingNewActivity: boolean;
|
||||||
|
notifyingNewActivityTime: string;
|
||||||
|
notifyingReminder: boolean;
|
||||||
|
notifyingReminderMessage: string;
|
||||||
|
notifyingReminderTime: string;
|
||||||
|
subscription: PushSubscription | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to settings
|
||||||
|
*/
|
||||||
|
export interface SettingsState {
|
||||||
|
activeDid: string;
|
||||||
|
apiServer: string;
|
||||||
|
apiServerInput: string;
|
||||||
|
partnerApiServer: string;
|
||||||
|
partnerApiServerInput: string;
|
||||||
|
webPushServer: string;
|
||||||
|
webPushServerInput: string;
|
||||||
|
passkeyExpirationMinutes: number;
|
||||||
|
previousPasskeyExpirationMinutes: number;
|
||||||
|
passkeyExpirationDescription: string;
|
||||||
|
hideRegisterPromptOnNewContact: boolean;
|
||||||
|
isRegistered: boolean;
|
||||||
|
isSearchAreasSet: boolean;
|
||||||
|
showContactGives: boolean;
|
||||||
|
showGeneralAdvanced: boolean;
|
||||||
|
showShortcutBvc: boolean;
|
||||||
|
warnIfProdServer: boolean;
|
||||||
|
warnIfTestServer: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to UI display
|
||||||
|
*/
|
||||||
|
export interface UIState {
|
||||||
|
loadingProfile: boolean;
|
||||||
|
loadingLimits: boolean;
|
||||||
|
showAdvanced: boolean;
|
||||||
|
showB64Copy: boolean;
|
||||||
|
showDidCopy: boolean;
|
||||||
|
showDerCopy: boolean;
|
||||||
|
showPubCopy: boolean;
|
||||||
|
showLargeIdenticonId?: string;
|
||||||
|
showLargeIdenticonUrl?: string;
|
||||||
|
downloadUrl: string;
|
||||||
|
zoom: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to limits and validation
|
||||||
|
*/
|
||||||
|
export interface LimitsState {
|
||||||
|
endorserLimits: EndorserRateLimits | null;
|
||||||
|
imageLimits: ImageRateLimits | null;
|
||||||
|
limitsMessage: string;
|
||||||
|
publicHex: string;
|
||||||
|
publicBase64: string;
|
||||||
|
derivationPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for component state related to identity
|
||||||
|
*/
|
||||||
|
export interface IdentityState {
|
||||||
|
givenName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete interface for AccountViewView component state
|
||||||
|
*/
|
||||||
|
export interface AccountViewState
|
||||||
|
extends ProfileState,
|
||||||
|
NotificationState,
|
||||||
|
SettingsState,
|
||||||
|
UIState,
|
||||||
|
LimitsState,
|
||||||
|
IdentityState {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for clipboard copy operations
|
||||||
|
*/
|
||||||
|
export interface ClipboardOperation {
|
||||||
|
text: string;
|
||||||
|
callback: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for notification permission callback
|
||||||
|
*/
|
||||||
|
export interface NotificationPermissionCallback {
|
||||||
|
success: boolean;
|
||||||
|
timeText: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for import/export operations
|
||||||
|
*/
|
||||||
|
export interface ImportExportState {
|
||||||
|
inputImportFileNameRef?: Blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for API error responses
|
||||||
|
*/
|
||||||
|
export interface ApiErrorResponse {
|
||||||
|
response?: {
|
||||||
|
data?: {
|
||||||
|
error?: { message?: string } | string;
|
||||||
|
};
|
||||||
|
status?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for API errors
|
||||||
|
*/
|
||||||
|
export function isApiError(error: unknown): error is ApiErrorResponse {
|
||||||
|
return typeof error === "object" && error !== null && "response" in error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for standard errors
|
||||||
|
*/
|
||||||
|
export function isError(error: unknown): error is Error {
|
||||||
|
return error instanceof Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for file import content structure
|
||||||
|
*/
|
||||||
|
export interface ImportContent {
|
||||||
|
data?: {
|
||||||
|
data?: Array<{
|
||||||
|
tableName: string;
|
||||||
|
rows: Array<unknown>;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for map ready callback
|
||||||
|
*/
|
||||||
|
export interface MapReadyCallback {
|
||||||
|
(map: L.Map): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for mouse event handlers
|
||||||
|
*/
|
||||||
|
export interface MouseEventHandler {
|
||||||
|
(event: LeafletMouseEvent): void;
|
||||||
|
}
|
||||||
@@ -61,8 +61,10 @@ export const NOTIFICATION_MESSAGES = {
|
|||||||
SENT_BRIEF: "Sent...",
|
SENT_BRIEF: "Sent...",
|
||||||
CONFIRMATION_SUBMITTED: "Confirmation submitted.",
|
CONFIRMATION_SUBMITTED: "Confirmation submitted.",
|
||||||
ALREADY_CONFIRMED: "You already confirmed this claim.",
|
ALREADY_CONFIRMED: "You already confirmed this claim.",
|
||||||
CANNOT_CONFIRM_ISSUER: "You cannot confirm this because you issued this claim.",
|
CANNOT_CONFIRM_ISSUER:
|
||||||
CANNOT_CONFIRM_HIDDEN: "You cannot confirm this because some people are hidden.",
|
"You cannot confirm this because you issued this claim.",
|
||||||
|
CANNOT_CONFIRM_HIDDEN:
|
||||||
|
"You cannot confirm this because some people are hidden.",
|
||||||
NOT_REGISTERED: "Someone needs to register you before you can confirm.",
|
NOT_REGISTERED: "Someone needs to register you before you can confirm.",
|
||||||
NOT_A_GIVE: "This is not a giving action to confirm.",
|
NOT_A_GIVE: "This is not a giving action to confirm.",
|
||||||
} as const;
|
} as const;
|
||||||
@@ -70,143 +72,195 @@ export const NOTIFICATION_MESSAGES = {
|
|||||||
/**
|
/**
|
||||||
* Creates a notification helper with utility methods
|
* Creates a notification helper with utility methods
|
||||||
*/
|
*/
|
||||||
export function createNotificationHelper(notifyFn: (notification: NotificationIface, timeout?: number) => void): NotificationHelper {
|
export function createNotificationHelper(
|
||||||
|
notifyFn: (notification: NotificationIface, timeout?: number) => void,
|
||||||
|
): NotificationHelper {
|
||||||
return {
|
return {
|
||||||
notify: notifyFn,
|
notify: notifyFn,
|
||||||
|
|
||||||
// Success notifications
|
// Success notifications
|
||||||
success: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
success: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: NOTIFICATION_TITLES.SUCCESS,
|
title: NOTIFICATION_TITLES.SUCCESS,
|
||||||
text,
|
text,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Error notifications
|
// Error notifications
|
||||||
error: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
error: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: NOTIFICATION_TITLES.ERROR,
|
title: NOTIFICATION_TITLES.ERROR,
|
||||||
text,
|
text,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Warning notifications
|
// Warning notifications
|
||||||
warning: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
warning: (text: string, timeout = NOTIFICATION_TIMEOUTS.LONG) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
title: NOTIFICATION_TITLES.WARNING,
|
title: NOTIFICATION_TITLES.WARNING,
|
||||||
text,
|
text,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Info notifications
|
// Info notifications
|
||||||
info: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
info: (text: string, timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.INFO,
|
title: NOTIFICATION_TITLES.INFO,
|
||||||
text,
|
text,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Toast notifications (brief)
|
// Toast notifications (brief)
|
||||||
toast: (title: string, text?: string, timeout = NOTIFICATION_TIMEOUTS.BRIEF) => {
|
toast: (
|
||||||
notifyFn({
|
title: string,
|
||||||
|
text?: string,
|
||||||
|
timeout = NOTIFICATION_TIMEOUTS.BRIEF,
|
||||||
|
) => {
|
||||||
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
title,
|
title,
|
||||||
text,
|
text,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Clipboard copy notifications
|
// Clipboard copy notifications
|
||||||
copied: (item: string, timeout = NOTIFICATION_TIMEOUTS.SHORT) => {
|
copied: (item: string, timeout = NOTIFICATION_TIMEOUTS.SHORT) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
title: NOTIFICATION_TITLES.COPIED,
|
title: NOTIFICATION_TITLES.COPIED,
|
||||||
text: NOTIFICATION_MESSAGES.CLIPBOARD_COPIED(item),
|
text: NOTIFICATION_MESSAGES.CLIPBOARD_COPIED(item),
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sent brief notification
|
// Sent brief notification
|
||||||
sent: (timeout = NOTIFICATION_TIMEOUTS.BRIEF) => {
|
sent: (timeout = NOTIFICATION_TIMEOUTS.BRIEF) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
title: NOTIFICATION_TITLES.SENT,
|
title: NOTIFICATION_TITLES.SENT,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Confirmation modal
|
// Confirmation modal
|
||||||
confirm: (text: string, onYes: () => Promise<void>, timeout = NOTIFICATION_TIMEOUTS.MODAL) => {
|
confirm: (
|
||||||
notifyFn({
|
text: string,
|
||||||
|
onYes: () => Promise<void>,
|
||||||
|
timeout = NOTIFICATION_TIMEOUTS.MODAL,
|
||||||
|
) => {
|
||||||
|
notifyFn(
|
||||||
|
{
|
||||||
group: "modal",
|
group: "modal",
|
||||||
type: "confirm",
|
type: "confirm",
|
||||||
title: NOTIFICATION_TITLES.CONFIRM,
|
title: NOTIFICATION_TITLES.CONFIRM,
|
||||||
text,
|
text,
|
||||||
onYes,
|
onYes,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Standard confirmation messages
|
// Standard confirmation messages
|
||||||
confirmationSubmitted: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
confirmationSubmitted: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: NOTIFICATION_TITLES.SUCCESS,
|
title: NOTIFICATION_TITLES.SUCCESS,
|
||||||
text: NOTIFICATION_MESSAGES.CONFIRMATION_SUBMITTED,
|
text: NOTIFICATION_MESSAGES.CONFIRMATION_SUBMITTED,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
alreadyConfirmed: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
alreadyConfirmed: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.ALREADY_CONFIRMED,
|
title: NOTIFICATION_TITLES.ALREADY_CONFIRMED,
|
||||||
text: NOTIFICATION_MESSAGES.ALREADY_CONFIRMED,
|
text: NOTIFICATION_MESSAGES.ALREADY_CONFIRMED,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
cannotConfirmIssuer: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
cannotConfirmIssuer: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.CANNOT_CONFIRM,
|
title: NOTIFICATION_TITLES.CANNOT_CONFIRM,
|
||||||
text: NOTIFICATION_MESSAGES.CANNOT_CONFIRM_ISSUER,
|
text: NOTIFICATION_MESSAGES.CANNOT_CONFIRM_ISSUER,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
cannotConfirmHidden: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
cannotConfirmHidden: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.CANNOT_CONFIRM,
|
title: NOTIFICATION_TITLES.CANNOT_CONFIRM,
|
||||||
text: NOTIFICATION_MESSAGES.CANNOT_CONFIRM_HIDDEN,
|
text: NOTIFICATION_MESSAGES.CANNOT_CONFIRM_HIDDEN,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
notRegistered: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
notRegistered: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.NOT_REGISTERED,
|
title: NOTIFICATION_TITLES.NOT_REGISTERED,
|
||||||
text: NOTIFICATION_MESSAGES.NOT_REGISTERED,
|
text: NOTIFICATION_MESSAGES.NOT_REGISTERED,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
notAGive: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
notAGive: (timeout = NOTIFICATION_TIMEOUTS.STANDARD) => {
|
||||||
notifyFn({
|
notifyFn(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: NOTIFICATION_TITLES.INFO,
|
title: NOTIFICATION_TITLES.INFO,
|
||||||
text: NOTIFICATION_MESSAGES.NOT_A_GIVE,
|
text: NOTIFICATION_MESSAGES.NOT_A_GIVE,
|
||||||
}, timeout);
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -217,6 +271,7 @@ export function createNotificationHelper(notifyFn: (notification: NotificationIf
|
|||||||
export const NotificationMixin = {
|
export const NotificationMixin = {
|
||||||
computed: {
|
computed: {
|
||||||
$notifyHelper() {
|
$notifyHelper() {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return createNotificationHelper((this as any).$notify);
|
return createNotificationHelper((this as any).$notify);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { NotificationIface } from '../constants/app';
|
import { NotificationIface } from "../constants/app";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple notification utility functions
|
* Simple notification utility functions
|
||||||
* Provides the most concise API for common notification patterns
|
* Provides the most concise API for common notification patterns
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type NotifyFunction = (notification: NotificationIface, timeout?: number) => void;
|
export type NotifyFunction = (
|
||||||
|
notification: NotificationIface,
|
||||||
|
timeout?: number,
|
||||||
|
) => void;
|
||||||
|
|
||||||
// Standard timeouts
|
// Standard timeouts
|
||||||
export const TIMEOUTS = {
|
export const TIMEOUTS = {
|
||||||
@@ -24,113 +27,190 @@ export function createNotifyHelpers(notify: NotifyFunction) {
|
|||||||
return {
|
return {
|
||||||
// Success notifications
|
// Success notifications
|
||||||
success: (text: string, timeout?: number) =>
|
success: (text: string, timeout?: number) =>
|
||||||
notify({ group: "alert", type: "success", title: "Success", text }, timeout || TIMEOUTS.STANDARD),
|
notify(
|
||||||
|
{ group: "alert", type: "success", title: "Success", text },
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
// Error notifications
|
// Error notifications
|
||||||
error: (text: string, timeout?: number) =>
|
error: (text: string, timeout?: number) =>
|
||||||
notify({ group: "alert", type: "danger", title: "Error", text }, timeout || TIMEOUTS.LONG),
|
notify(
|
||||||
|
{ group: "alert", type: "danger", title: "Error", text },
|
||||||
|
timeout || TIMEOUTS.LONG,
|
||||||
|
),
|
||||||
|
|
||||||
// Warning notifications
|
// Warning notifications
|
||||||
warning: (text: string, timeout?: number) =>
|
warning: (text: string, timeout?: number) =>
|
||||||
notify({ group: "alert", type: "warning", title: "Warning", text }, timeout || TIMEOUTS.LONG),
|
notify(
|
||||||
|
{ group: "alert", type: "warning", title: "Warning", text },
|
||||||
|
timeout || TIMEOUTS.LONG,
|
||||||
|
),
|
||||||
|
|
||||||
// Info notifications
|
// Info notifications
|
||||||
info: (text: string, timeout?: number) =>
|
info: (text: string, timeout?: number) =>
|
||||||
notify({ group: "alert", type: "info", title: "Info", text }, timeout || TIMEOUTS.STANDARD),
|
notify(
|
||||||
|
{ group: "alert", type: "info", title: "Info", text },
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
// Toast notifications (brief)
|
// Toast notifications (brief)
|
||||||
toast: (title: string, text?: string, timeout?: number) =>
|
toast: (title: string, text?: string, timeout?: number) =>
|
||||||
notify({ group: "alert", type: "toast", title, text }, timeout || TIMEOUTS.BRIEF),
|
notify(
|
||||||
|
{ group: "alert", type: "toast", title, text },
|
||||||
|
timeout || TIMEOUTS.BRIEF,
|
||||||
|
),
|
||||||
|
|
||||||
// Clipboard copy notifications
|
// Clipboard copy notifications
|
||||||
copied: (item: string, timeout?: number) =>
|
copied: (item: string, timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
title: "Copied",
|
title: "Copied",
|
||||||
text: `${item} was copied to the clipboard.`
|
text: `${item} was copied to the clipboard.`,
|
||||||
}, timeout || TIMEOUTS.SHORT),
|
},
|
||||||
|
timeout || TIMEOUTS.SHORT,
|
||||||
|
),
|
||||||
|
|
||||||
// Sent brief notification
|
// Sent brief notification
|
||||||
sent: (timeout?: number) =>
|
sent: (timeout?: number) =>
|
||||||
notify({ group: "alert", type: "toast", title: "Sent..." }, timeout || TIMEOUTS.BRIEF),
|
notify(
|
||||||
|
{ group: "alert", type: "toast", title: "Sent..." },
|
||||||
|
timeout || TIMEOUTS.BRIEF,
|
||||||
|
),
|
||||||
|
|
||||||
// Confirmation modal
|
// Confirmation modal
|
||||||
confirm: (text: string, onYes: () => Promise<void>, timeout?: number) =>
|
confirm: (text: string, onYes: () => Promise<void>, timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "modal",
|
group: "modal",
|
||||||
type: "confirm",
|
type: "confirm",
|
||||||
title: "Confirm",
|
title: "Confirm",
|
||||||
text,
|
text,
|
||||||
onYes
|
onYes,
|
||||||
}, timeout || TIMEOUTS.MODAL),
|
},
|
||||||
|
timeout || TIMEOUTS.MODAL,
|
||||||
|
),
|
||||||
|
|
||||||
// Standard confirmation messages
|
// Standard confirmation messages
|
||||||
confirmationSubmitted: (timeout?: number) =>
|
confirmationSubmitted: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success",
|
title: "Success",
|
||||||
text: "Confirmation submitted."
|
text: "Confirmation submitted.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
// Common error patterns
|
// Common error patterns
|
||||||
genericError: (timeout?: number) =>
|
genericError: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: "Something went wrong."
|
text: "Something went wrong.",
|
||||||
}, timeout || TIMEOUTS.LONG),
|
},
|
||||||
|
timeout || TIMEOUTS.LONG,
|
||||||
|
),
|
||||||
|
|
||||||
// Common success patterns
|
// Common success patterns
|
||||||
genericSuccess: (timeout?: number) =>
|
genericSuccess: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success",
|
title: "Success",
|
||||||
text: "Operation completed successfully."
|
text: "Operation completed successfully.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
// Common confirmation patterns
|
// Common confirmation patterns
|
||||||
alreadyConfirmed: (timeout?: number) =>
|
alreadyConfirmed: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Already Confirmed",
|
title: "Already Confirmed",
|
||||||
text: "You already confirmed this claim."
|
text: "You already confirmed this claim.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
cannotConfirmIssuer: (timeout?: number) =>
|
cannotConfirmIssuer: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Cannot Confirm",
|
title: "Cannot Confirm",
|
||||||
text: "You cannot confirm this because you issued this claim."
|
text: "You cannot confirm this because you issued this claim.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
cannotConfirmHidden: (timeout?: number) =>
|
cannotConfirmHidden: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Cannot Confirm",
|
title: "Cannot Confirm",
|
||||||
text: "You cannot confirm this because some people are hidden."
|
text: "You cannot confirm this because some people are hidden.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
notRegistered: (timeout?: number) =>
|
notRegistered: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Not Registered",
|
title: "Not Registered",
|
||||||
text: "Someone needs to register you before you can confirm."
|
text: "Someone needs to register you before you can confirm.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
notAGive: (timeout?: number) =>
|
notAGive: (timeout?: number) =>
|
||||||
notify({
|
notify(
|
||||||
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Info",
|
title: "Info",
|
||||||
text: "This is not a giving action to confirm."
|
text: "This is not a giving action to confirm.",
|
||||||
}, timeout || TIMEOUTS.STANDARD),
|
},
|
||||||
|
timeout || TIMEOUTS.STANDARD,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Notification-off modal (for turning off notifications)
|
||||||
|
notificationOff: (
|
||||||
|
title: string,
|
||||||
|
callback: (success: boolean) => Promise<void>,
|
||||||
|
timeout?: number,
|
||||||
|
) =>
|
||||||
|
notify(
|
||||||
|
{
|
||||||
|
group: "modal",
|
||||||
|
type: "notification-off",
|
||||||
|
title,
|
||||||
|
text: "", // unused, only here to satisfy type check
|
||||||
|
callback,
|
||||||
|
},
|
||||||
|
timeout || TIMEOUTS.MODAL,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Download notifications
|
||||||
|
downloadStarted: (format: string = "Dexie", timeout?: number) =>
|
||||||
|
notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "success",
|
||||||
|
title: "Download Started",
|
||||||
|
text: `See your downloads directory for the backup. It is in the ${format} format.`,
|
||||||
|
},
|
||||||
|
timeout || TIMEOUTS.MODAL,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1030,24 +1030,18 @@ import {
|
|||||||
import { UserProfile } from "@/libs/partnerServer";
|
import { UserProfile } from "@/libs/partnerServer";
|
||||||
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 { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
|
||||||
|
import {
|
||||||
|
AccountSettings,
|
||||||
|
UserProfileResponse,
|
||||||
|
isApiError,
|
||||||
|
isError,
|
||||||
|
ImportContent,
|
||||||
|
} from "@/interfaces/accountView";
|
||||||
|
|
||||||
const inputImportFileNameRef = ref<Blob>();
|
const inputImportFileNameRef = ref<Blob>();
|
||||||
|
|
||||||
// Type guard for API errors
|
|
||||||
function isApiError(error: unknown): error is {
|
|
||||||
response?: {
|
|
||||||
data?: { error?: { message?: string } | string };
|
|
||||||
status?: number;
|
|
||||||
};
|
|
||||||
} {
|
|
||||||
return typeof error === "object" && error !== null && "response" in error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type guard for standard errors
|
|
||||||
function isError(error: unknown): error is Error {
|
|
||||||
return error instanceof Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to extract error message
|
// Helper function to extract error message
|
||||||
function extractErrorMessage(error: unknown): string {
|
function extractErrorMessage(error: unknown): string {
|
||||||
if (isApiError(error)) {
|
if (isApiError(error)) {
|
||||||
@@ -1085,59 +1079,72 @@ export default class AccountViewView extends Vue {
|
|||||||
$route!: RouteLocationNormalizedLoaded;
|
$route!: RouteLocationNormalizedLoaded;
|
||||||
$router!: Router;
|
$router!: Router;
|
||||||
|
|
||||||
AppConstants = AppString;
|
// Add notification helpers
|
||||||
DEFAULT_PUSH_SERVER = DEFAULT_PUSH_SERVER;
|
private notify = createNotifyHelpers(this.$notify);
|
||||||
DEFAULT_IMAGE_API_SERVER = DEFAULT_IMAGE_API_SERVER;
|
|
||||||
DEFAULT_PARTNER_API_SERVER = DEFAULT_PARTNER_API_SERVER;
|
|
||||||
|
|
||||||
activeDid = "";
|
// Constants
|
||||||
apiServer = "";
|
readonly AppConstants: typeof AppString = AppString;
|
||||||
apiServerInput = "";
|
readonly DEFAULT_PUSH_SERVER: string = DEFAULT_PUSH_SERVER;
|
||||||
derivationPath = "";
|
readonly DEFAULT_IMAGE_API_SERVER: string = DEFAULT_IMAGE_API_SERVER;
|
||||||
downloadUrl = ""; // because DuckDuckGo doesn't download on automated call to "click" on the anchor
|
readonly DEFAULT_PARTNER_API_SERVER: string = DEFAULT_PARTNER_API_SERVER;
|
||||||
endorserLimits: EndorserRateLimits | null = null;
|
|
||||||
givenName = "";
|
// Identity and settings properties
|
||||||
hideRegisterPromptOnNewContact = false;
|
activeDid: string = "";
|
||||||
imageLimits: ImageRateLimits | null = null;
|
apiServer: string = "";
|
||||||
includeUserProfileLocation = false;
|
apiServerInput: string = "";
|
||||||
isRegistered = false;
|
derivationPath: string = "";
|
||||||
isSearchAreasSet = false;
|
givenName: string = "";
|
||||||
limitsMessage = "";
|
hideRegisterPromptOnNewContact: boolean = false;
|
||||||
loadingLimits = false;
|
isRegistered: boolean = false;
|
||||||
loadingProfile = true;
|
isSearchAreasSet: boolean = false;
|
||||||
notifyingNewActivity = false;
|
partnerApiServer: string = DEFAULT_PARTNER_API_SERVER;
|
||||||
notifyingNewActivityTime = "";
|
partnerApiServerInput: string = DEFAULT_PARTNER_API_SERVER;
|
||||||
notifyingReminder = false;
|
passkeyExpirationDescription: string = "";
|
||||||
notifyingReminderMessage = "";
|
passkeyExpirationMinutes: number = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||||
notifyingReminderTime = "";
|
previousPasskeyExpirationMinutes: number = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||||
partnerApiServer = DEFAULT_PARTNER_API_SERVER;
|
|
||||||
partnerApiServerInput = DEFAULT_PARTNER_API_SERVER;
|
|
||||||
passkeyExpirationDescription = "";
|
|
||||||
passkeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
|
||||||
previousPasskeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
|
||||||
profileImageUrl?: string;
|
profileImageUrl?: string;
|
||||||
publicHex = "";
|
publicHex: string = "";
|
||||||
publicBase64 = "";
|
publicBase64: string = "";
|
||||||
savingProfile = false;
|
webPushServer: string = DEFAULT_PUSH_SERVER;
|
||||||
showAdvanced = false;
|
webPushServerInput: string = DEFAULT_PUSH_SERVER;
|
||||||
showB64Copy = false;
|
|
||||||
showContactGives = false;
|
// Profile properties
|
||||||
showDidCopy = false;
|
userProfileDesc: string = "";
|
||||||
showDerCopy = false;
|
userProfileLatitude: number = 0;
|
||||||
showGeneralAdvanced = false;
|
userProfileLongitude: number = 0;
|
||||||
|
includeUserProfileLocation: boolean = false;
|
||||||
|
savingProfile: boolean = false;
|
||||||
|
|
||||||
|
// Notification properties
|
||||||
|
notifyingNewActivity: boolean = false;
|
||||||
|
notifyingNewActivityTime: string = "";
|
||||||
|
notifyingReminder: boolean = false;
|
||||||
|
notifyingReminderMessage: string = "";
|
||||||
|
notifyingReminderTime: string = "";
|
||||||
|
subscription: PushSubscription | null = null;
|
||||||
|
|
||||||
|
// UI state properties
|
||||||
|
downloadUrl: string = ""; // because DuckDuckGo doesn't download on automated call to "click" on the anchor
|
||||||
|
loadingLimits: boolean = false;
|
||||||
|
loadingProfile: boolean = true;
|
||||||
|
showAdvanced: boolean = false;
|
||||||
|
showB64Copy: boolean = false;
|
||||||
|
showContactGives: boolean = false;
|
||||||
|
showDidCopy: boolean = false;
|
||||||
|
showDerCopy: boolean = false;
|
||||||
|
showGeneralAdvanced: boolean = false;
|
||||||
showLargeIdenticonId?: string;
|
showLargeIdenticonId?: string;
|
||||||
showLargeIdenticonUrl?: string;
|
showLargeIdenticonUrl?: string;
|
||||||
showPubCopy = false;
|
showPubCopy: boolean = false;
|
||||||
showShortcutBvc = false;
|
showShortcutBvc: boolean = false;
|
||||||
subscription: PushSubscription | null = null;
|
warnIfProdServer: boolean = false;
|
||||||
warnIfProdServer = false;
|
warnIfTestServer: boolean = false;
|
||||||
warnIfTestServer = false;
|
zoom: number = 2;
|
||||||
webPushServer = DEFAULT_PUSH_SERVER;
|
|
||||||
webPushServerInput = DEFAULT_PUSH_SERVER;
|
// Limits and validation properties
|
||||||
userProfileDesc = "";
|
endorserLimits: EndorserRateLimits | null = null;
|
||||||
userProfileLatitude = 0;
|
imageLimits: ImageRateLimits | null = null;
|
||||||
userProfileLongitude = 0;
|
limitsMessage: string = "";
|
||||||
zoom = 2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async function executed when the component is mounted.
|
* Async function executed when the component is mounted.
|
||||||
@@ -1146,7 +1153,7 @@ export default class AccountViewView extends Vue {
|
|||||||
*
|
*
|
||||||
* @throws Will display specific messages to the user based on different errors.
|
* @throws Will display specific messages to the user based on different errors.
|
||||||
*/
|
*/
|
||||||
async mounted() {
|
async mounted(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// Initialize component state with values from the database or defaults
|
// Initialize component state with values from the database or defaults
|
||||||
await this.initializeState();
|
await this.initializeState();
|
||||||
@@ -1156,7 +1163,7 @@ export default class AccountViewView extends Vue {
|
|||||||
if (this.isRegistered) {
|
if (this.isRegistered) {
|
||||||
try {
|
try {
|
||||||
const headers = await getHeaders(this.activeDid);
|
const headers = await getHeaders(this.activeDid);
|
||||||
const response = await this.axios.get(
|
const response = await this.axios.get<UserProfileResponse>(
|
||||||
this.partnerApiServer +
|
this.partnerApiServer +
|
||||||
"/api/partner/userProfileForIssuer/" +
|
"/api/partner/userProfileForIssuer/" +
|
||||||
this.activeDid,
|
this.activeDid,
|
||||||
@@ -1171,7 +1178,7 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// won't get here because axios throws an error instead
|
// won't get here because axios throws an error instead
|
||||||
throw Error("Unable to load profile.");
|
throw Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.UNABLE_TO_LOAD_PROFILE);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isApiError(error) && error.response?.status === 404) {
|
if (isApiError(error) && error.response?.status === 404) {
|
||||||
@@ -1180,14 +1187,8 @@ export default class AccountViewView extends Vue {
|
|||||||
databaseUtil.logConsoleAndDb(
|
databaseUtil.logConsoleAndDb(
|
||||||
"Error loading profile: " + errorStringForLog(error),
|
"Error loading profile: " + errorStringForLog(error),
|
||||||
);
|
);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_AVAILABLE,
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error Loading Profile",
|
|
||||||
text: "Your server profile is not available.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1203,15 +1204,7 @@ export default class AccountViewView extends Vue {
|
|||||||
"To repeat with concatenated error: telling user to clear cache at page create because: " +
|
"To repeat with concatenated error: telling user to clear cache at page create because: " +
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
this.$notify(
|
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_LOAD_ERROR);
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error Loading Profile",
|
|
||||||
text: "See the Help page about errors with your personal data.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.loadingProfile = false;
|
this.loadingProfile = false;
|
||||||
}
|
}
|
||||||
@@ -1231,14 +1224,9 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$notify(
|
this.notify.warning(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.BROWSER_NOTIFICATIONS_UNSUPPORTED,
|
||||||
group: "alert",
|
TIMEOUTS.VERY_LONG,
|
||||||
type: "warning",
|
|
||||||
title: "Cannot Set Notifications",
|
|
||||||
text: "This browser does not support notifications. Use Chrome, or install this to the home screen, or try other suggestions on the 'Troubleshoot your notifications' page.",
|
|
||||||
},
|
|
||||||
7000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1249,7 +1237,7 @@ export default class AccountViewView extends Vue {
|
|||||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount(): void {
|
||||||
if (this.downloadUrl) {
|
if (this.downloadUrl) {
|
||||||
URL.revokeObjectURL(this.downloadUrl);
|
URL.revokeObjectURL(this.downloadUrl);
|
||||||
}
|
}
|
||||||
@@ -1258,8 +1246,9 @@ export default class AccountViewView extends Vue {
|
|||||||
/**
|
/**
|
||||||
* Initializes component state with values from the database or defaults.
|
* Initializes component state with values from the database or defaults.
|
||||||
*/
|
*/
|
||||||
async initializeState() {
|
async initializeState(): Promise<void> {
|
||||||
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
const settings: AccountSettings =
|
||||||
|
await databaseUtil.retrieveSettingsForActiveAccount();
|
||||||
|
|
||||||
this.activeDid = settings.activeDid || "";
|
this.activeDid = settings.activeDid || "";
|
||||||
this.apiServer = settings.apiServer || "";
|
this.apiServer = settings.apiServer || "";
|
||||||
@@ -1293,56 +1282,56 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// call fn, copy text to the clipboard, then redo fn after 2 seconds
|
// call fn, copy text to the clipboard, then redo fn after 2 seconds
|
||||||
doCopyTwoSecRedo(text: string, fn: () => void) {
|
doCopyTwoSecRedo(text: string, fn: () => void): void {
|
||||||
fn();
|
fn();
|
||||||
useClipboard()
|
useClipboard()
|
||||||
.copy(text)
|
.copy(text)
|
||||||
.then(() => setTimeout(fn, 2000));
|
.then(() => setTimeout(fn, 2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleShowContactAmounts() {
|
async toggleShowContactAmounts(): Promise<void> {
|
||||||
this.showContactGives = !this.showContactGives;
|
this.showContactGives = !this.showContactGives;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
showContactGivesInline: this.showContactGives,
|
showContactGivesInline: this.showContactGives,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleShowGeneralAdvanced() {
|
async toggleShowGeneralAdvanced(): Promise<void> {
|
||||||
this.showGeneralAdvanced = !this.showGeneralAdvanced;
|
this.showGeneralAdvanced = !this.showGeneralAdvanced;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
showGeneralAdvanced: this.showGeneralAdvanced,
|
showGeneralAdvanced: this.showGeneralAdvanced,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleProdWarning() {
|
async toggleProdWarning(): Promise<void> {
|
||||||
this.warnIfProdServer = !this.warnIfProdServer;
|
this.warnIfProdServer = !this.warnIfProdServer;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
warnIfProdServer: this.warnIfProdServer,
|
warnIfProdServer: this.warnIfProdServer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleTestWarning() {
|
async toggleTestWarning(): Promise<void> {
|
||||||
this.warnIfTestServer = !this.warnIfTestServer;
|
this.warnIfTestServer = !this.warnIfTestServer;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
warnIfTestServer: this.warnIfTestServer,
|
warnIfTestServer: this.warnIfTestServer,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleShowShortcutBvc() {
|
async toggleShowShortcutBvc(): Promise<void> {
|
||||||
this.showShortcutBvc = !this.showShortcutBvc;
|
this.showShortcutBvc = !this.showShortcutBvc;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
showShortcutBvc: this.showShortcutBvc,
|
showShortcutBvc: this.showShortcutBvc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readableDate(timeStr: string) {
|
readableDate(timeStr: string): string {
|
||||||
return timeStr ? timeStr.substring(0, timeStr.indexOf("T")) : "?";
|
return timeStr ? timeStr.substring(0, timeStr.indexOf("T")) : "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the identity and updates the component's state.
|
* Processes the identity and updates the component's state.
|
||||||
*/
|
*/
|
||||||
async processIdentity() {
|
async processIdentity(): Promise<void> {
|
||||||
const account = await retrieveAccountMetadata(this.activeDid);
|
const account = await retrieveAccountMetadata(this.activeDid);
|
||||||
if (account?.identity) {
|
if (account?.identity) {
|
||||||
const identity = JSON.parse(account.identity as string) as IIdentifier;
|
const identity = JSON.parse(account.identity as string) as IIdentifier;
|
||||||
@@ -1359,30 +1348,18 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async showNewActivityNotificationInfo() {
|
async showNewActivityNotificationInfo(): Promise<void> {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.NEW_ACTIVITY_INFO,
|
||||||
group: "modal",
|
async () => {
|
||||||
type: "confirm",
|
|
||||||
title: "New Activity Notification",
|
|
||||||
text: `
|
|
||||||
This will only notify you when there is new relevant activity for you personally.
|
|
||||||
Note that it runs on your device and many factors may affect delivery,
|
|
||||||
so if you want a reliable but simple daily notification then choose a 'Reminder'.
|
|
||||||
Do you want more details?
|
|
||||||
`,
|
|
||||||
onYes: async () => {
|
|
||||||
await (this.$router as Router).push({
|
await (this.$router as Router).push({
|
||||||
name: "help-notification-types",
|
name: "help-notification-types",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
yesText: "tell me more.",
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async showNewActivityNotificationChoice() {
|
async showNewActivityNotificationChoice(): Promise<void> {
|
||||||
if (!this.notifyingNewActivity) {
|
if (!this.notifyingNewActivity) {
|
||||||
(
|
(
|
||||||
this.$refs.pushNotificationPermission as PushNotificationPermission
|
this.$refs.pushNotificationPermission as PushNotificationPermission
|
||||||
@@ -1396,13 +1373,7 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$notify(
|
this.notify.notificationOff(DAILY_CHECK_TITLE, async (success) => {
|
||||||
{
|
|
||||||
group: "modal",
|
|
||||||
type: "notification-off",
|
|
||||||
title: DAILY_CHECK_TITLE, // repurposed to indicate the type of notification
|
|
||||||
text: "", // unused, only here to satisfy type check
|
|
||||||
callback: async (success) => {
|
|
||||||
if (success) {
|
if (success) {
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
notifyingNewActivityTime: "",
|
notifyingNewActivityTime: "",
|
||||||
@@ -1410,37 +1381,22 @@ export default class AccountViewView extends Vue {
|
|||||||
this.notifyingNewActivity = false;
|
this.notifyingNewActivity = false;
|
||||||
this.notifyingNewActivityTime = "";
|
this.notifyingNewActivityTime = "";
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async showReminderNotificationInfo() {
|
async showReminderNotificationInfo(): Promise<void> {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.REMINDER_INFO,
|
||||||
group: "modal",
|
async () => {
|
||||||
type: "confirm",
|
|
||||||
title: "Reminder Notification",
|
|
||||||
text: `
|
|
||||||
This will notify you at a specific time each day.
|
|
||||||
Note that it does not give you personalized notifications,
|
|
||||||
so if you want less reliable but personalized notification then choose a 'New Activity' Notification.
|
|
||||||
Do you want more details?
|
|
||||||
`,
|
|
||||||
onYes: async () => {
|
|
||||||
await (this.$router as Router).push({
|
await (this.$router as Router).push({
|
||||||
name: "help-notification-types",
|
name: "help-notification-types",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
yesText: "tell me more.",
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async showReminderNotificationChoice() {
|
async showReminderNotificationChoice(): Promise<void> {
|
||||||
if (!this.notifyingReminder) {
|
if (!this.notifyingReminder) {
|
||||||
(
|
(
|
||||||
this.$refs.pushNotificationPermission as PushNotificationPermission
|
this.$refs.pushNotificationPermission as PushNotificationPermission
|
||||||
@@ -1459,13 +1415,7 @@ export default class AccountViewView extends Vue {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.$notify(
|
this.notify.notificationOff(DIRECT_PUSH_TITLE, async (success) => {
|
||||||
{
|
|
||||||
group: "modal",
|
|
||||||
type: "notification-off",
|
|
||||||
title: DIRECT_PUSH_TITLE, // repurposed to indicate the type of notification
|
|
||||||
text: "", // unused, only here to satisfy type check
|
|
||||||
callback: async (success) => {
|
|
||||||
if (success) {
|
if (success) {
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
notifyingReminderMessage: "",
|
notifyingReminderMessage: "",
|
||||||
@@ -1475,14 +1425,11 @@ export default class AccountViewView extends Vue {
|
|||||||
this.notifyingReminderMessage = "";
|
this.notifyingReminderMessage = "";
|
||||||
this.notifyingReminderTime = "";
|
this.notifyingReminderTime = "";
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async toggleHideRegisterPromptOnNewContact() {
|
public async toggleHideRegisterPromptOnNewContact(): Promise<void> {
|
||||||
const newSetting = !this.hideRegisterPromptOnNewContact;
|
const newSetting = !this.hideRegisterPromptOnNewContact;
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
hideRegisterPromptOnNewContact: newSetting,
|
hideRegisterPromptOnNewContact: newSetting,
|
||||||
@@ -1490,7 +1437,7 @@ export default class AccountViewView extends Vue {
|
|||||||
this.hideRegisterPromptOnNewContact = newSetting;
|
this.hideRegisterPromptOnNewContact = newSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updatePasskeyExpiration() {
|
public async updatePasskeyExpiration(): Promise<void> {
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
passkeyExpirationMinutes: this.passkeyExpirationMinutes,
|
passkeyExpirationMinutes: this.passkeyExpirationMinutes,
|
||||||
});
|
});
|
||||||
@@ -1498,7 +1445,7 @@ export default class AccountViewView extends Vue {
|
|||||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async turnOffNotifyingFlags() {
|
public async turnOffNotifyingFlags(): Promise<void> {
|
||||||
// should tell the push server as well
|
// should tell the push server as well
|
||||||
await this.$saveSettings({
|
await this.$saveSettings({
|
||||||
notifyingNewActivityTime: "",
|
notifyingNewActivityTime: "",
|
||||||
@@ -1586,15 +1533,7 @@ export default class AccountViewView extends Vue {
|
|||||||
* Notifies the user that the download has started.
|
* Notifies the user that the download has started.
|
||||||
*/
|
*/
|
||||||
private notifyDownloadStarted() {
|
private notifyDownloadStarted() {
|
||||||
this.$notify(
|
this.notify.downloadStarted();
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "success",
|
|
||||||
title: "Download Started",
|
|
||||||
text: "See your downloads directory for the backup. It is in the Dexie format.",
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1602,42 +1541,29 @@ export default class AccountViewView extends Vue {
|
|||||||
*
|
*
|
||||||
* @param {Error} error - The error object.
|
* @param {Error} error - The error object.
|
||||||
*/
|
*/
|
||||||
private handleExportError(error: unknown) {
|
private handleExportError(error: unknown): void {
|
||||||
logger.error("Export Error:", error);
|
logger.error("Export Error:", error);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.EXPORT_ERROR,
|
||||||
group: "alert",
|
TIMEOUTS.STANDARD,
|
||||||
type: "danger",
|
|
||||||
title: "Export Error",
|
|
||||||
text: "There was an error exporting the data.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadImportFile(event: Event) {
|
async uploadImportFile(event: Event): Promise<void> {
|
||||||
inputImportFileNameRef.value = (
|
inputImportFileNameRef.value = (
|
||||||
event.target as HTMLInputElement
|
event.target as HTMLInputElement
|
||||||
).files?.[0];
|
).files?.[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
showContactImport() {
|
showContactImport(): boolean {
|
||||||
return !!inputImportFileNameRef.value;
|
return !!inputImportFileNameRef.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmSubmitImportFile() {
|
confirmSubmitImportFile(): void {
|
||||||
if (inputImportFileNameRef.value != null) {
|
if (inputImportFileNameRef.value != null) {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.WARNINGS.IMPORT_REPLACE_WARNING,
|
||||||
group: "modal",
|
this.submitImportFile,
|
||||||
type: "confirm",
|
|
||||||
title: "Replace All",
|
|
||||||
text:
|
|
||||||
"This will replace all settings and contacts, so we recommend you first do the backup step above." +
|
|
||||||
" Are you sure you want to import and replace all contacts and settings?",
|
|
||||||
onYes: this.submitImportFile,
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1647,18 +1573,18 @@ export default class AccountViewView extends Vue {
|
|||||||
*
|
*
|
||||||
* @throws Will notify the user if there is an export error.
|
* @throws Will notify the user if there is an export error.
|
||||||
*/
|
*/
|
||||||
async submitImportFile() {
|
async submitImportFile(): Promise<void> {
|
||||||
if (inputImportFileNameRef.value != null) {
|
if (inputImportFileNameRef.value != null) {
|
||||||
// TODO: implement this for SQLite
|
// TODO: implement this for SQLite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkContactImports() {
|
async checkContactImports(): Promise<void> {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const fileContent: string = (event.target?.result as string) || "{}";
|
const fileContent: string = (event.target?.result as string) || "{}";
|
||||||
try {
|
try {
|
||||||
const contents = JSON.parse(fileContent);
|
const contents: ImportContent = JSON.parse(fileContent);
|
||||||
const contactTableRows: Array<Contact> = (
|
const contactTableRows: Array<Contact> = (
|
||||||
contents.data?.data as [{ tableName: string; rows: Array<Contact> }]
|
contents.data?.data as [{ tableName: string; rows: Array<Contact> }]
|
||||||
)?.find((table) => table.tableName === "contacts")
|
)?.find((table) => table.tableName === "contacts")
|
||||||
@@ -1673,45 +1599,34 @@ export default class AccountViewView extends Vue {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Error checking contact imports:", error);
|
logger.error("Error checking contact imports:", error);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.IMPORT_ERROR,
|
||||||
group: "alert",
|
TIMEOUTS.STANDARD,
|
||||||
type: "danger",
|
|
||||||
title: "Error Importing",
|
|
||||||
text: "There was an error reading that Dexie file.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(inputImportFileNameRef.value as Blob);
|
reader.readAsText(inputImportFileNameRef.value as Blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
private progressCallback(progress: ImportProgress) {
|
private progressCallback(progress: ImportProgress): boolean {
|
||||||
logger.log(
|
logger.log(
|
||||||
`Import progress: ${progress.completedRows} of ${progress.totalRows} rows completed.`,
|
`Import progress: ${progress.completedRows} of ${progress.totalRows} rows completed.`,
|
||||||
);
|
);
|
||||||
if (progress.done) {
|
if (progress.done) {
|
||||||
// console.log(`Imported ${progress.completedTables} tables.`);
|
// console.log(`Imported ${progress.completedTables} tables.`);
|
||||||
this.$notify(
|
this.notify.success(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.SUCCESS.IMPORT_COMPLETE,
|
||||||
group: "alert",
|
TIMEOUTS.LONG,
|
||||||
type: "success",
|
|
||||||
title: "Import Complete",
|
|
||||||
text: "",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkLimits() {
|
async checkLimits(): Promise<void> {
|
||||||
if (this.activeDid) {
|
if (this.activeDid) {
|
||||||
this.checkLimitsFor(this.activeDid);
|
this.checkLimitsFor(this.activeDid);
|
||||||
} else {
|
} else {
|
||||||
this.limitsMessage =
|
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER;
|
||||||
"You have no identifier, or your data has been corrupted.";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1722,7 +1637,7 @@ export default class AccountViewView extends Vue {
|
|||||||
*
|
*
|
||||||
* Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`.
|
* Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`.
|
||||||
*/
|
*/
|
||||||
private async checkLimitsFor(did: string) {
|
private async checkLimitsFor(did: string): Promise<void> {
|
||||||
this.loadingLimits = true;
|
this.loadingLimits = true;
|
||||||
this.limitsMessage = "";
|
this.limitsMessage = "";
|
||||||
|
|
||||||
@@ -1743,14 +1658,8 @@ export default class AccountViewView extends Vue {
|
|||||||
this.isRegistered = true;
|
this.isRegistered = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Got an error updating settings:", err);
|
logger.error("Got an error updating settings:", err);
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.SETTINGS_UPDATE_ERROR,
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Update Error",
|
|
||||||
text: "Unable to update your settings. Check claim limits again.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1759,10 +1668,11 @@ export default class AccountViewView extends Vue {
|
|||||||
if (imageResp.status === 200) {
|
if (imageResp.status === 200) {
|
||||||
this.imageLimits = imageResp.data;
|
this.imageLimits = imageResp.data;
|
||||||
} else {
|
} else {
|
||||||
this.limitsMessage = "You don't have access to upload images.";
|
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IMAGE_ACCESS;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
this.limitsMessage = "You cannot upload images.";
|
this.limitsMessage =
|
||||||
|
ACCOUNT_VIEW_CONSTANTS.LIMITS.CANNOT_UPLOAD_IMAGES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1777,7 +1687,7 @@ export default class AccountViewView extends Vue {
|
|||||||
*
|
*
|
||||||
* @param {AxiosError | Error} error - The error object.
|
* @param {AxiosError | Error} error - The error object.
|
||||||
*/
|
*/
|
||||||
private handleRateLimitsError(error: unknown) {
|
private handleRateLimitsError(error: unknown): void {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
if (error.status == 400 || error.status == 404) {
|
if (error.status == 400 || error.status == 404) {
|
||||||
// no worries: they probably just aren't registered and don't have any limits
|
// no worries: they probably just aren't registered and don't have any limits
|
||||||
@@ -1785,50 +1695,44 @@ export default class AccountViewView extends Vue {
|
|||||||
"Got 400 or 404 response retrieving limits which probably means they're not registered:",
|
"Got 400 or 404 response retrieving limits which probably means they're not registered:",
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
this.limitsMessage = "No limits were found, so no actions are allowed.";
|
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_LIMITS_FOUND;
|
||||||
} else {
|
} else {
|
||||||
const data = error.response?.data as ErrorResponse;
|
const data = error.response?.data as ErrorResponse;
|
||||||
this.limitsMessage =
|
this.limitsMessage =
|
||||||
(data?.error?.message as string) || "Bad server response.";
|
(data?.error?.message as string) ||
|
||||||
|
ACCOUNT_VIEW_CONSTANTS.LIMITS.BAD_SERVER_RESPONSE;
|
||||||
logger.error("Got bad response retrieving limits:", error);
|
logger.error("Got bad response retrieving limits:", error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.limitsMessage = "Got an error retrieving limits.";
|
this.limitsMessage =
|
||||||
|
ACCOUNT_VIEW_CONSTANTS.LIMITS.ERROR_RETRIEVING_LIMITS;
|
||||||
logger.error("Got some error retrieving limits:", error);
|
logger.error("Got some error retrieving limits:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onClickSaveApiServer() {
|
async onClickSaveApiServer(): Promise<void> {
|
||||||
await databaseUtil.updateDefaultSettings({
|
await databaseUtil.updateDefaultSettings({
|
||||||
apiServer: this.apiServerInput,
|
apiServer: this.apiServerInput,
|
||||||
});
|
});
|
||||||
this.apiServer = this.apiServerInput;
|
this.apiServer = this.apiServerInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onClickSavePartnerServer() {
|
async onClickSavePartnerServer(): Promise<void> {
|
||||||
await databaseUtil.updateDefaultSettings({
|
await databaseUtil.updateDefaultSettings({
|
||||||
partnerApiServer: this.partnerApiServerInput,
|
partnerApiServer: this.partnerApiServerInput,
|
||||||
});
|
});
|
||||||
this.partnerApiServer = this.partnerApiServerInput;
|
this.partnerApiServer = this.partnerApiServerInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onClickSavePushServer() {
|
async onClickSavePushServer(): Promise<void> {
|
||||||
await databaseUtil.updateDefaultSettings({
|
await databaseUtil.updateDefaultSettings({
|
||||||
webPushServer: this.webPushServerInput,
|
webPushServer: this.webPushServerInput,
|
||||||
});
|
});
|
||||||
this.webPushServer = this.webPushServerInput;
|
this.webPushServer = this.webPushServerInput;
|
||||||
this.$notify(
|
this.notify.warning(ACCOUNT_VIEW_CONSTANTS.INFO.RELOAD_VAPID);
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "warning",
|
|
||||||
title: "Reload",
|
|
||||||
text: "Now reload the app to get a new VAPID to use with this push server.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openImageDialog() {
|
openImageDialog(): void {
|
||||||
(this.$refs.imageMethodDialog as ImageMethodDialog).open(
|
(this.$refs.imageMethodDialog as ImageMethodDialog).open(
|
||||||
async (imgUrl) => {
|
async (imgUrl) => {
|
||||||
await databaseUtil.updateDefaultSettings({
|
await databaseUtil.updateDefaultSettings({
|
||||||
@@ -1842,21 +1746,14 @@ export default class AccountViewView extends Vue {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmDeleteImage() {
|
confirmDeleteImage(): void {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.WARNINGS.IMAGE_DELETE_WARNING,
|
||||||
group: "modal",
|
this.deleteImage,
|
||||||
type: "confirm",
|
|
||||||
title:
|
|
||||||
"Note that anyone with you already as a contact will no longer see a picture, and you will have to reshare your data with them if you save a new picture. Are you sure you want to delete your profile picture?",
|
|
||||||
text: "",
|
|
||||||
onYes: this.deleteImage,
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteImage() {
|
async deleteImage(): Promise<void> {
|
||||||
if (!this.profileImageUrl) {
|
if (!this.profileImageUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1882,15 +1779,7 @@ export default class AccountViewView extends Vue {
|
|||||||
// (either they'll simply continue or they're canceling and going back)
|
// (either they'll simply continue or they're canceling and going back)
|
||||||
} else {
|
} else {
|
||||||
logger.error("Non-success deleting image:", response);
|
logger.error("Non-success deleting image:", response);
|
||||||
this.$notify(
|
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.IMAGE_DELETE_PROBLEM);
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text: "There was a problem deleting the image. Contact support if you want it removed from the servers.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
// keep the imageUrl in localStorage so the user can try again if they want
|
// keep the imageUrl in localStorage so the user can try again if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1913,38 +1802,28 @@ export default class AccountViewView extends Vue {
|
|||||||
|
|
||||||
// it already doesn't exist so we won't say anything to the user
|
// it already doesn't exist so we won't say anything to the user
|
||||||
} else {
|
} else {
|
||||||
this.$notify(
|
this.notify.error(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.IMAGE_DELETE_ERROR,
|
||||||
group: "alert",
|
TIMEOUTS.STANDARD,
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text: "There was an error deleting the image.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMapReady(map: L.Map) {
|
onMapReady(map: L.Map): void {
|
||||||
// doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup
|
// doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup
|
||||||
const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2;
|
const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2;
|
||||||
map.setView([this.userProfileLatitude, this.userProfileLongitude], zoom);
|
map.setView([this.userProfileLatitude, this.userProfileLongitude], zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
showProfileInfo() {
|
showProfileInfo(): void {
|
||||||
this.$notify(
|
this.notify.info(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.INFO.PROFILE_INFO,
|
||||||
group: "alert",
|
TIMEOUTS.VERY_LONG,
|
||||||
type: "info",
|
|
||||||
title: "Public Profile Information",
|
|
||||||
text: "This data will be published for all to see, so be careful what your write. Your ID will only be shared with people who you allow to see your activity.",
|
|
||||||
},
|
|
||||||
7000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveProfile() {
|
async saveProfile(): Promise<void> {
|
||||||
this.savingProfile = true;
|
this.savingProfile = true;
|
||||||
try {
|
try {
|
||||||
const headers = await getHeaders(this.activeDid);
|
const headers = await getHeaders(this.activeDid);
|
||||||
@@ -1955,14 +1834,10 @@ export default class AccountViewView extends Vue {
|
|||||||
payload.locLat = this.userProfileLatitude;
|
payload.locLat = this.userProfileLatitude;
|
||||||
payload.locLon = this.userProfileLongitude;
|
payload.locLon = this.userProfileLongitude;
|
||||||
} else if (this.includeUserProfileLocation) {
|
} else if (this.includeUserProfileLocation) {
|
||||||
this.$notify(
|
this.notify.toast(
|
||||||
{
|
"",
|
||||||
group: "alert",
|
ACCOUNT_VIEW_CONSTANTS.INFO.NO_PROFILE_LOCATION,
|
||||||
type: "toast",
|
TIMEOUTS.STANDARD,
|
||||||
title: "",
|
|
||||||
text: "No profile location is saved.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const response = await this.axios.post(
|
const response = await this.axios.post(
|
||||||
@@ -1971,40 +1846,28 @@ export default class AccountViewView extends Vue {
|
|||||||
{ headers },
|
{ headers },
|
||||||
);
|
);
|
||||||
if (response.status === 201) {
|
if (response.status === 201) {
|
||||||
this.$notify(
|
this.notify.success(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED,
|
||||||
group: "alert",
|
TIMEOUTS.STANDARD,
|
||||||
type: "success",
|
|
||||||
title: "Profile Saved",
|
|
||||||
text: "Your profile has been updated successfully.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// won't get here because axios throws an error on non-success
|
// won't get here because axios throws an error on non-success
|
||||||
throw Error("Profile not saved");
|
throw Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_SAVED);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseUtil.logConsoleAndDb(
|
databaseUtil.logConsoleAndDb(
|
||||||
"Error saving profile: " + errorStringForLog(error),
|
"Error saving profile: " + errorStringForLog(error),
|
||||||
);
|
);
|
||||||
const errorMessage: string =
|
const errorMessage: string =
|
||||||
extractErrorMessage(error) || "There was an error saving your profile.";
|
extractErrorMessage(error) ||
|
||||||
this.$notify(
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR;
|
||||||
{
|
this.notify.error(errorMessage, TIMEOUTS.STANDARD);
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error Saving Profile",
|
|
||||||
text: errorMessage,
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.savingProfile = false;
|
this.savingProfile = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleUserProfileLocation() {
|
toggleUserProfileLocation(): void {
|
||||||
this.includeUserProfileLocation = !this.includeUserProfileLocation;
|
this.includeUserProfileLocation = !this.includeUserProfileLocation;
|
||||||
if (!this.includeUserProfileLocation) {
|
if (!this.includeUserProfileLocation) {
|
||||||
this.userProfileLatitude = 0;
|
this.userProfileLatitude = 0;
|
||||||
@@ -2013,42 +1876,30 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmEraseLatLong() {
|
confirmEraseLatLong(): void {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.WARNINGS.ERASE_LOCATION_WARNING,
|
||||||
group: "modal",
|
async () => {
|
||||||
type: "confirm",
|
|
||||||
title: "Erase Marker",
|
|
||||||
text: "Are you sure you don't want to mark a location? This will erase the current location.",
|
|
||||||
onYes: async () => {
|
|
||||||
this.eraseLatLong();
|
this.eraseLatLong();
|
||||||
},
|
},
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
eraseLatLong() {
|
eraseLatLong(): void {
|
||||||
this.userProfileLatitude = 0;
|
this.userProfileLatitude = 0;
|
||||||
this.userProfileLongitude = 0;
|
this.userProfileLongitude = 0;
|
||||||
this.zoom = 2;
|
this.zoom = 2;
|
||||||
this.includeUserProfileLocation = false;
|
this.includeUserProfileLocation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmDeleteProfile() {
|
async confirmDeleteProfile(): Promise<void> {
|
||||||
this.$notify(
|
this.notify.confirm(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.WARNINGS.DELETE_PROFILE_WARNING,
|
||||||
group: "modal",
|
this.deleteProfile,
|
||||||
type: "confirm",
|
|
||||||
title: "Delete Profile",
|
|
||||||
text: "Are you sure you want to delete your public profile? This will remove your description and location from the server, and it cannot be undone.",
|
|
||||||
onYes: this.deleteProfile,
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProfile() {
|
async deleteProfile(): Promise<void> {
|
||||||
this.savingProfile = true;
|
this.savingProfile = true;
|
||||||
try {
|
try {
|
||||||
const headers = await getHeaders(this.activeDid);
|
const headers = await getHeaders(this.activeDid);
|
||||||
@@ -2061,17 +1912,12 @@ export default class AccountViewView extends Vue {
|
|||||||
this.userProfileLatitude = 0;
|
this.userProfileLatitude = 0;
|
||||||
this.userProfileLongitude = 0;
|
this.userProfileLongitude = 0;
|
||||||
this.includeUserProfileLocation = false;
|
this.includeUserProfileLocation = false;
|
||||||
this.$notify(
|
this.notify.success(
|
||||||
{
|
ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_DELETED,
|
||||||
group: "alert",
|
TIMEOUTS.STANDARD,
|
||||||
type: "success",
|
|
||||||
title: "Profile Deleted",
|
|
||||||
text: "Your profile has been deleted successfully.",
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw Error("Profile not deleted");
|
throw Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_DELETED);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseUtil.logConsoleAndDb(
|
databaseUtil.logConsoleAndDb(
|
||||||
@@ -2079,16 +1925,8 @@ export default class AccountViewView extends Vue {
|
|||||||
);
|
);
|
||||||
const errorMessage: string =
|
const errorMessage: string =
|
||||||
extractErrorMessage(error) ||
|
extractErrorMessage(error) ||
|
||||||
"There was an error deleting your profile.";
|
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_DELETE_ERROR;
|
||||||
this.$notify(
|
this.notify.error(errorMessage, TIMEOUTS.STANDARD);
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error Deleting Profile",
|
|
||||||
text: errorMessage,
|
|
||||||
},
|
|
||||||
3000,
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.savingProfile = false;
|
this.savingProfile = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -732,7 +732,9 @@ export default class ClaimView extends Vue {
|
|||||||
"Error retrieving all account DIDs on home page:" + error,
|
"Error retrieving all account DIDs on home page:" + error,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
this.notify.error("See the Help page for problems with your personal data.");
|
this.notify.error(
|
||||||
|
"See the Help page for problems with your personal data.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const claimId = this.$route.params.id as string;
|
const claimId = this.$route.params.id as string;
|
||||||
@@ -875,7 +877,10 @@ export default class ClaimView extends Vue {
|
|||||||
await this.$logError(
|
await this.$logError(
|
||||||
"Error retrieving claim: " + JSON.stringify(serverError),
|
"Error retrieving claim: " + JSON.stringify(serverError),
|
||||||
);
|
);
|
||||||
this.notify.error("Something went wrong retrieving claim data.", TIMEOUTS.STANDARD);
|
this.notify.error(
|
||||||
|
"Something went wrong retrieving claim data.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,7 +937,10 @@ export default class ClaimView extends Vue {
|
|||||||
" if they can find out more and make an introduction: " +
|
" if they can find out more and make an introduction: " +
|
||||||
" send them this page and see if they can make a connection for you.";
|
" send them this page and see if they can make a connection for you.";
|
||||||
} else {
|
} else {
|
||||||
this.notify.error("Something went wrong retrieving that claim.", TIMEOUTS.LONG);
|
this.notify.error(
|
||||||
|
"Something went wrong retrieving that claim.",
|
||||||
|
TIMEOUTS.LONG,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -942,7 +950,7 @@ export default class ClaimView extends Vue {
|
|||||||
"Do you personally confirm that this is true?",
|
"Do you personally confirm that this is true?",
|
||||||
async () => {
|
async () => {
|
||||||
await this.confirmClaim();
|
await this.confirmClaim();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1051,7 +1059,10 @@ export default class ClaimView extends Vue {
|
|||||||
await this.$logError(
|
await this.$logError(
|
||||||
"Unrecognized claim type for edit: " + this.veriClaim.claimType,
|
"Unrecognized claim type for edit: " + this.veriClaim.claimType,
|
||||||
);
|
);
|
||||||
this.notify.error("This is an unrecognized claim type.", TIMEOUTS.STANDARD);
|
this.notify.error(
|
||||||
|
"This is an unrecognized claim type.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user