Browse Source
- 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.pull/142/head
7 changed files with 1000 additions and 699 deletions
@ -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; |
@ -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; |
||||
|
} |
Loading…
Reference in new issue