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:
@@ -1030,24 +1030,18 @@ import {
|
||||
import { UserProfile } from "@/libs/partnerServer";
|
||||
import { logger } from "../utils/logger";
|
||||
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>();
|
||||
|
||||
// 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
|
||||
function extractErrorMessage(error: unknown): string {
|
||||
if (isApiError(error)) {
|
||||
@@ -1085,59 +1079,72 @@ export default class AccountViewView extends Vue {
|
||||
$route!: RouteLocationNormalizedLoaded;
|
||||
$router!: Router;
|
||||
|
||||
AppConstants = AppString;
|
||||
DEFAULT_PUSH_SERVER = DEFAULT_PUSH_SERVER;
|
||||
DEFAULT_IMAGE_API_SERVER = DEFAULT_IMAGE_API_SERVER;
|
||||
DEFAULT_PARTNER_API_SERVER = DEFAULT_PARTNER_API_SERVER;
|
||||
// Add notification helpers
|
||||
private notify = createNotifyHelpers(this.$notify);
|
||||
|
||||
activeDid = "";
|
||||
apiServer = "";
|
||||
apiServerInput = "";
|
||||
derivationPath = "";
|
||||
downloadUrl = ""; // because DuckDuckGo doesn't download on automated call to "click" on the anchor
|
||||
endorserLimits: EndorserRateLimits | null = null;
|
||||
givenName = "";
|
||||
hideRegisterPromptOnNewContact = false;
|
||||
imageLimits: ImageRateLimits | null = null;
|
||||
includeUserProfileLocation = false;
|
||||
isRegistered = false;
|
||||
isSearchAreasSet = false;
|
||||
limitsMessage = "";
|
||||
loadingLimits = false;
|
||||
loadingProfile = true;
|
||||
notifyingNewActivity = false;
|
||||
notifyingNewActivityTime = "";
|
||||
notifyingReminder = false;
|
||||
notifyingReminderMessage = "";
|
||||
notifyingReminderTime = "";
|
||||
partnerApiServer = DEFAULT_PARTNER_API_SERVER;
|
||||
partnerApiServerInput = DEFAULT_PARTNER_API_SERVER;
|
||||
passkeyExpirationDescription = "";
|
||||
passkeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||
previousPasskeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||
// Constants
|
||||
readonly AppConstants: typeof AppString = AppString;
|
||||
readonly DEFAULT_PUSH_SERVER: string = DEFAULT_PUSH_SERVER;
|
||||
readonly DEFAULT_IMAGE_API_SERVER: string = DEFAULT_IMAGE_API_SERVER;
|
||||
readonly DEFAULT_PARTNER_API_SERVER: string = DEFAULT_PARTNER_API_SERVER;
|
||||
|
||||
// Identity and settings properties
|
||||
activeDid: string = "";
|
||||
apiServer: string = "";
|
||||
apiServerInput: string = "";
|
||||
derivationPath: string = "";
|
||||
givenName: string = "";
|
||||
hideRegisterPromptOnNewContact: boolean = false;
|
||||
isRegistered: boolean = false;
|
||||
isSearchAreasSet: boolean = false;
|
||||
partnerApiServer: string = DEFAULT_PARTNER_API_SERVER;
|
||||
partnerApiServerInput: string = DEFAULT_PARTNER_API_SERVER;
|
||||
passkeyExpirationDescription: string = "";
|
||||
passkeyExpirationMinutes: number = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||
previousPasskeyExpirationMinutes: number = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||
profileImageUrl?: string;
|
||||
publicHex = "";
|
||||
publicBase64 = "";
|
||||
savingProfile = false;
|
||||
showAdvanced = false;
|
||||
showB64Copy = false;
|
||||
showContactGives = false;
|
||||
showDidCopy = false;
|
||||
showDerCopy = false;
|
||||
showGeneralAdvanced = false;
|
||||
publicHex: string = "";
|
||||
publicBase64: string = "";
|
||||
webPushServer: string = DEFAULT_PUSH_SERVER;
|
||||
webPushServerInput: string = DEFAULT_PUSH_SERVER;
|
||||
|
||||
// Profile properties
|
||||
userProfileDesc: string = "";
|
||||
userProfileLatitude: number = 0;
|
||||
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;
|
||||
showLargeIdenticonUrl?: string;
|
||||
showPubCopy = false;
|
||||
showShortcutBvc = false;
|
||||
subscription: PushSubscription | null = null;
|
||||
warnIfProdServer = false;
|
||||
warnIfTestServer = false;
|
||||
webPushServer = DEFAULT_PUSH_SERVER;
|
||||
webPushServerInput = DEFAULT_PUSH_SERVER;
|
||||
userProfileDesc = "";
|
||||
userProfileLatitude = 0;
|
||||
userProfileLongitude = 0;
|
||||
zoom = 2;
|
||||
showPubCopy: boolean = false;
|
||||
showShortcutBvc: boolean = false;
|
||||
warnIfProdServer: boolean = false;
|
||||
warnIfTestServer: boolean = false;
|
||||
zoom: number = 2;
|
||||
|
||||
// Limits and validation properties
|
||||
endorserLimits: EndorserRateLimits | null = null;
|
||||
imageLimits: ImageRateLimits | null = null;
|
||||
limitsMessage: string = "";
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
async mounted() {
|
||||
async mounted(): Promise<void> {
|
||||
try {
|
||||
// Initialize component state with values from the database or defaults
|
||||
await this.initializeState();
|
||||
@@ -1156,7 +1163,7 @@ export default class AccountViewView extends Vue {
|
||||
if (this.isRegistered) {
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const response = await this.axios.get(
|
||||
const response = await this.axios.get<UserProfileResponse>(
|
||||
this.partnerApiServer +
|
||||
"/api/partner/userProfileForIssuer/" +
|
||||
this.activeDid,
|
||||
@@ -1171,7 +1178,7 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
} else {
|
||||
// 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) {
|
||||
if (isApiError(error) && error.response?.status === 404) {
|
||||
@@ -1180,14 +1187,8 @@ export default class AccountViewView extends Vue {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
"Error loading profile: " + errorStringForLog(error),
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Profile",
|
||||
text: "Your server profile is not available.",
|
||||
},
|
||||
5000,
|
||||
this.notify.error(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_AVAILABLE,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1203,15 +1204,7 @@ export default class AccountViewView extends Vue {
|
||||
"To repeat with concatenated error: telling user to clear cache at page create because: " +
|
||||
error,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Profile",
|
||||
text: "See the Help page about errors with your personal data.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_LOAD_ERROR);
|
||||
} finally {
|
||||
this.loadingProfile = false;
|
||||
}
|
||||
@@ -1231,14 +1224,9 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
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,
|
||||
this.notify.warning(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.BROWSER_NOTIFICATIONS_UNSUPPORTED,
|
||||
TIMEOUTS.VERY_LONG,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -1249,7 +1237,7 @@ export default class AccountViewView extends Vue {
|
||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||
}
|
||||
|
||||
beforeUnmount() {
|
||||
beforeUnmount(): void {
|
||||
if (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.
|
||||
*/
|
||||
async initializeState() {
|
||||
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
async initializeState(): Promise<void> {
|
||||
const settings: AccountSettings =
|
||||
await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
|
||||
this.activeDid = settings.activeDid || "";
|
||||
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
|
||||
doCopyTwoSecRedo(text: string, fn: () => void) {
|
||||
doCopyTwoSecRedo(text: string, fn: () => void): void {
|
||||
fn();
|
||||
useClipboard()
|
||||
.copy(text)
|
||||
.then(() => setTimeout(fn, 2000));
|
||||
}
|
||||
|
||||
async toggleShowContactAmounts() {
|
||||
async toggleShowContactAmounts(): Promise<void> {
|
||||
this.showContactGives = !this.showContactGives;
|
||||
await this.$saveSettings({
|
||||
showContactGivesInline: this.showContactGives,
|
||||
});
|
||||
}
|
||||
|
||||
async toggleShowGeneralAdvanced() {
|
||||
async toggleShowGeneralAdvanced(): Promise<void> {
|
||||
this.showGeneralAdvanced = !this.showGeneralAdvanced;
|
||||
await this.$saveSettings({
|
||||
showGeneralAdvanced: this.showGeneralAdvanced,
|
||||
});
|
||||
}
|
||||
|
||||
async toggleProdWarning() {
|
||||
async toggleProdWarning(): Promise<void> {
|
||||
this.warnIfProdServer = !this.warnIfProdServer;
|
||||
await this.$saveSettings({
|
||||
warnIfProdServer: this.warnIfProdServer,
|
||||
});
|
||||
}
|
||||
|
||||
async toggleTestWarning() {
|
||||
async toggleTestWarning(): Promise<void> {
|
||||
this.warnIfTestServer = !this.warnIfTestServer;
|
||||
await this.$saveSettings({
|
||||
warnIfTestServer: this.warnIfTestServer,
|
||||
});
|
||||
}
|
||||
|
||||
async toggleShowShortcutBvc() {
|
||||
async toggleShowShortcutBvc(): Promise<void> {
|
||||
this.showShortcutBvc = !this.showShortcutBvc;
|
||||
await this.$saveSettings({
|
||||
showShortcutBvc: this.showShortcutBvc,
|
||||
});
|
||||
}
|
||||
|
||||
readableDate(timeStr: string) {
|
||||
readableDate(timeStr: string): string {
|
||||
return timeStr ? timeStr.substring(0, timeStr.indexOf("T")) : "?";
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the identity and updates the component's state.
|
||||
*/
|
||||
async processIdentity() {
|
||||
async processIdentity(): Promise<void> {
|
||||
const account = await retrieveAccountMetadata(this.activeDid);
|
||||
if (account?.identity) {
|
||||
const identity = JSON.parse(account.identity as string) as IIdentifier;
|
||||
@@ -1359,30 +1348,18 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
async showNewActivityNotificationInfo() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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({
|
||||
name: "help-notification-types",
|
||||
});
|
||||
},
|
||||
yesText: "tell me more.",
|
||||
async showNewActivityNotificationInfo(): Promise<void> {
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.NEW_ACTIVITY_INFO,
|
||||
async () => {
|
||||
await (this.$router as Router).push({
|
||||
name: "help-notification-types",
|
||||
});
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
async showNewActivityNotificationChoice() {
|
||||
async showNewActivityNotificationChoice(): Promise<void> {
|
||||
if (!this.notifyingNewActivity) {
|
||||
(
|
||||
this.$refs.pushNotificationPermission as PushNotificationPermission
|
||||
@@ -1396,51 +1373,30 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
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) {
|
||||
await this.$saveSettings({
|
||||
notifyingNewActivityTime: "",
|
||||
});
|
||||
this.notifyingNewActivity = false;
|
||||
this.notifyingNewActivityTime = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
-1,
|
||||
);
|
||||
this.notify.notificationOff(DAILY_CHECK_TITLE, async (success) => {
|
||||
if (success) {
|
||||
await this.$saveSettings({
|
||||
notifyingNewActivityTime: "",
|
||||
});
|
||||
this.notifyingNewActivity = false;
|
||||
this.notifyingNewActivityTime = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async showReminderNotificationInfo() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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({
|
||||
name: "help-notification-types",
|
||||
});
|
||||
},
|
||||
yesText: "tell me more.",
|
||||
async showReminderNotificationInfo(): Promise<void> {
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.REMINDER_INFO,
|
||||
async () => {
|
||||
await (this.$router as Router).push({
|
||||
name: "help-notification-types",
|
||||
});
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
async showReminderNotificationChoice() {
|
||||
async showReminderNotificationChoice(): Promise<void> {
|
||||
if (!this.notifyingReminder) {
|
||||
(
|
||||
this.$refs.pushNotificationPermission as PushNotificationPermission
|
||||
@@ -1459,30 +1415,21 @@ export default class AccountViewView extends Vue {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
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) {
|
||||
await this.$saveSettings({
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
this.notifyingReminder = false;
|
||||
this.notifyingReminderMessage = "";
|
||||
this.notifyingReminderTime = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
-1,
|
||||
);
|
||||
this.notify.notificationOff(DIRECT_PUSH_TITLE, async (success) => {
|
||||
if (success) {
|
||||
await this.$saveSettings({
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
this.notifyingReminder = false;
|
||||
this.notifyingReminderMessage = "";
|
||||
this.notifyingReminderTime = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async toggleHideRegisterPromptOnNewContact() {
|
||||
public async toggleHideRegisterPromptOnNewContact(): Promise<void> {
|
||||
const newSetting = !this.hideRegisterPromptOnNewContact;
|
||||
await this.$saveSettings({
|
||||
hideRegisterPromptOnNewContact: newSetting,
|
||||
@@ -1490,7 +1437,7 @@ export default class AccountViewView extends Vue {
|
||||
this.hideRegisterPromptOnNewContact = newSetting;
|
||||
}
|
||||
|
||||
public async updatePasskeyExpiration() {
|
||||
public async updatePasskeyExpiration(): Promise<void> {
|
||||
await this.$saveSettings({
|
||||
passkeyExpirationMinutes: this.passkeyExpirationMinutes,
|
||||
});
|
||||
@@ -1498,7 +1445,7 @@ export default class AccountViewView extends Vue {
|
||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||
}
|
||||
|
||||
public async turnOffNotifyingFlags() {
|
||||
public async turnOffNotifyingFlags(): Promise<void> {
|
||||
// should tell the push server as well
|
||||
await this.$saveSettings({
|
||||
notifyingNewActivityTime: "",
|
||||
@@ -1586,15 +1533,7 @@ export default class AccountViewView extends Vue {
|
||||
* Notifies the user that the download has started.
|
||||
*/
|
||||
private notifyDownloadStarted() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Download Started",
|
||||
text: "See your downloads directory for the backup. It is in the Dexie format.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
this.notify.downloadStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1602,42 +1541,29 @@ export default class AccountViewView extends Vue {
|
||||
*
|
||||
* @param {Error} error - The error object.
|
||||
*/
|
||||
private handleExportError(error: unknown) {
|
||||
private handleExportError(error: unknown): void {
|
||||
logger.error("Export Error:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Export Error",
|
||||
text: "There was an error exporting the data.",
|
||||
},
|
||||
3000,
|
||||
this.notify.error(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.EXPORT_ERROR,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
|
||||
async uploadImportFile(event: Event) {
|
||||
async uploadImportFile(event: Event): Promise<void> {
|
||||
inputImportFileNameRef.value = (
|
||||
event.target as HTMLInputElement
|
||||
).files?.[0];
|
||||
}
|
||||
|
||||
showContactImport() {
|
||||
showContactImport(): boolean {
|
||||
return !!inputImportFileNameRef.value;
|
||||
}
|
||||
|
||||
confirmSubmitImportFile() {
|
||||
confirmSubmitImportFile(): void {
|
||||
if (inputImportFileNameRef.value != null) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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,
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.WARNINGS.IMPORT_REPLACE_WARNING,
|
||||
this.submitImportFile,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1647,18 +1573,18 @@ export default class AccountViewView extends Vue {
|
||||
*
|
||||
* @throws Will notify the user if there is an export error.
|
||||
*/
|
||||
async submitImportFile() {
|
||||
async submitImportFile(): Promise<void> {
|
||||
if (inputImportFileNameRef.value != null) {
|
||||
// TODO: implement this for SQLite
|
||||
}
|
||||
}
|
||||
|
||||
async checkContactImports() {
|
||||
async checkContactImports(): Promise<void> {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const fileContent: string = (event.target?.result as string) || "{}";
|
||||
try {
|
||||
const contents = JSON.parse(fileContent);
|
||||
const contents: ImportContent = JSON.parse(fileContent);
|
||||
const contactTableRows: Array<Contact> = (
|
||||
contents.data?.data as [{ tableName: string; rows: Array<Contact> }]
|
||||
)?.find((table) => table.tableName === "contacts")
|
||||
@@ -1673,45 +1599,34 @@ export default class AccountViewView extends Vue {
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Error checking contact imports:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Importing",
|
||||
text: "There was an error reading that Dexie file.",
|
||||
},
|
||||
3000,
|
||||
this.notify.error(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.IMPORT_ERROR,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
};
|
||||
reader.readAsText(inputImportFileNameRef.value as Blob);
|
||||
}
|
||||
|
||||
private progressCallback(progress: ImportProgress) {
|
||||
private progressCallback(progress: ImportProgress): boolean {
|
||||
logger.log(
|
||||
`Import progress: ${progress.completedRows} of ${progress.totalRows} rows completed.`,
|
||||
);
|
||||
if (progress.done) {
|
||||
// console.log(`Imported ${progress.completedTables} tables.`);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Import Complete",
|
||||
text: "",
|
||||
},
|
||||
5000,
|
||||
this.notify.success(
|
||||
ACCOUNT_VIEW_CONSTANTS.SUCCESS.IMPORT_COMPLETE,
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async checkLimits() {
|
||||
async checkLimits(): Promise<void> {
|
||||
if (this.activeDid) {
|
||||
this.checkLimitsFor(this.activeDid);
|
||||
} else {
|
||||
this.limitsMessage =
|
||||
"You have no identifier, or your data has been corrupted.";
|
||||
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1722,7 +1637,7 @@ export default class AccountViewView extends Vue {
|
||||
*
|
||||
* Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`.
|
||||
*/
|
||||
private async checkLimitsFor(did: string) {
|
||||
private async checkLimitsFor(did: string): Promise<void> {
|
||||
this.loadingLimits = true;
|
||||
this.limitsMessage = "";
|
||||
|
||||
@@ -1743,14 +1658,8 @@ export default class AccountViewView extends Vue {
|
||||
this.isRegistered = true;
|
||||
} catch (err) {
|
||||
logger.error("Got an error updating settings:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Update Error",
|
||||
text: "Unable to update your settings. Check claim limits again.",
|
||||
},
|
||||
5000,
|
||||
this.notify.error(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.SETTINGS_UPDATE_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1759,10 +1668,11 @@ export default class AccountViewView extends Vue {
|
||||
if (imageResp.status === 200) {
|
||||
this.imageLimits = imageResp.data;
|
||||
} else {
|
||||
this.limitsMessage = "You don't have access to upload images.";
|
||||
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IMAGE_ACCESS;
|
||||
}
|
||||
} catch {
|
||||
this.limitsMessage = "You cannot upload images.";
|
||||
this.limitsMessage =
|
||||
ACCOUNT_VIEW_CONSTANTS.LIMITS.CANNOT_UPLOAD_IMAGES;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1777,7 +1687,7 @@ export default class AccountViewView extends Vue {
|
||||
*
|
||||
* @param {AxiosError | Error} error - The error object.
|
||||
*/
|
||||
private handleRateLimitsError(error: unknown) {
|
||||
private handleRateLimitsError(error: unknown): void {
|
||||
if (error instanceof AxiosError) {
|
||||
if (error.status == 400 || error.status == 404) {
|
||||
// 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:",
|
||||
error,
|
||||
);
|
||||
this.limitsMessage = "No limits were found, so no actions are allowed.";
|
||||
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_LIMITS_FOUND;
|
||||
} else {
|
||||
const data = error.response?.data as ErrorResponse;
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
async onClickSaveApiServer() {
|
||||
async onClickSaveApiServer(): Promise<void> {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
apiServer: this.apiServerInput,
|
||||
});
|
||||
this.apiServer = this.apiServerInput;
|
||||
}
|
||||
|
||||
async onClickSavePartnerServer() {
|
||||
async onClickSavePartnerServer(): Promise<void> {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
partnerApiServer: this.partnerApiServerInput,
|
||||
});
|
||||
this.partnerApiServer = this.partnerApiServerInput;
|
||||
}
|
||||
|
||||
async onClickSavePushServer() {
|
||||
async onClickSavePushServer(): Promise<void> {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
webPushServer: this.webPushServerInput,
|
||||
});
|
||||
this.webPushServer = this.webPushServerInput;
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Reload",
|
||||
text: "Now reload the app to get a new VAPID to use with this push server.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
this.notify.warning(ACCOUNT_VIEW_CONSTANTS.INFO.RELOAD_VAPID);
|
||||
}
|
||||
|
||||
openImageDialog() {
|
||||
openImageDialog(): void {
|
||||
(this.$refs.imageMethodDialog as ImageMethodDialog).open(
|
||||
async (imgUrl) => {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
@@ -1842,21 +1746,14 @@ export default class AccountViewView extends Vue {
|
||||
);
|
||||
}
|
||||
|
||||
confirmDeleteImage() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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,
|
||||
confirmDeleteImage(): void {
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.WARNINGS.IMAGE_DELETE_WARNING,
|
||||
this.deleteImage,
|
||||
);
|
||||
}
|
||||
|
||||
async deleteImage() {
|
||||
async deleteImage(): Promise<void> {
|
||||
if (!this.profileImageUrl) {
|
||||
return;
|
||||
}
|
||||
@@ -1882,15 +1779,7 @@ export default class AccountViewView extends Vue {
|
||||
// (either they'll simply continue or they're canceling and going back)
|
||||
} else {
|
||||
logger.error("Non-success deleting image:", response);
|
||||
this.$notify(
|
||||
{
|
||||
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,
|
||||
);
|
||||
this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.IMAGE_DELETE_PROBLEM);
|
||||
// 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
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "There was an error deleting the image.",
|
||||
},
|
||||
3000,
|
||||
this.notify.error(
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.IMAGE_DELETE_ERROR,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2;
|
||||
map.setView([this.userProfileLatitude, this.userProfileLongitude], zoom);
|
||||
}
|
||||
|
||||
showProfileInfo() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
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,
|
||||
showProfileInfo(): void {
|
||||
this.notify.info(
|
||||
ACCOUNT_VIEW_CONSTANTS.INFO.PROFILE_INFO,
|
||||
TIMEOUTS.VERY_LONG,
|
||||
);
|
||||
}
|
||||
|
||||
async saveProfile() {
|
||||
async saveProfile(): Promise<void> {
|
||||
this.savingProfile = true;
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
@@ -1955,14 +1834,10 @@ export default class AccountViewView extends Vue {
|
||||
payload.locLat = this.userProfileLatitude;
|
||||
payload.locLon = this.userProfileLongitude;
|
||||
} else if (this.includeUserProfileLocation) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "toast",
|
||||
title: "",
|
||||
text: "No profile location is saved.",
|
||||
},
|
||||
3000,
|
||||
this.notify.toast(
|
||||
"",
|
||||
ACCOUNT_VIEW_CONSTANTS.INFO.NO_PROFILE_LOCATION,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
const response = await this.axios.post(
|
||||
@@ -1971,40 +1846,28 @@ export default class AccountViewView extends Vue {
|
||||
{ headers },
|
||||
);
|
||||
if (response.status === 201) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Profile Saved",
|
||||
text: "Your profile has been updated successfully.",
|
||||
},
|
||||
3000,
|
||||
this.notify.success(
|
||||
ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_SAVED,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} else {
|
||||
// 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) {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
"Error saving profile: " + errorStringForLog(error),
|
||||
);
|
||||
const errorMessage: string =
|
||||
extractErrorMessage(error) || "There was an error saving your profile.";
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Saving Profile",
|
||||
text: errorMessage,
|
||||
},
|
||||
3000,
|
||||
);
|
||||
extractErrorMessage(error) ||
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR;
|
||||
this.notify.error(errorMessage, TIMEOUTS.STANDARD);
|
||||
} finally {
|
||||
this.savingProfile = false;
|
||||
}
|
||||
}
|
||||
|
||||
toggleUserProfileLocation() {
|
||||
toggleUserProfileLocation(): void {
|
||||
this.includeUserProfileLocation = !this.includeUserProfileLocation;
|
||||
if (!this.includeUserProfileLocation) {
|
||||
this.userProfileLatitude = 0;
|
||||
@@ -2013,42 +1876,30 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
confirmEraseLatLong() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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();
|
||||
},
|
||||
confirmEraseLatLong(): void {
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.WARNINGS.ERASE_LOCATION_WARNING,
|
||||
async () => {
|
||||
this.eraseLatLong();
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
eraseLatLong() {
|
||||
eraseLatLong(): void {
|
||||
this.userProfileLatitude = 0;
|
||||
this.userProfileLongitude = 0;
|
||||
this.zoom = 2;
|
||||
this.includeUserProfileLocation = false;
|
||||
}
|
||||
|
||||
async confirmDeleteProfile() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
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 confirmDeleteProfile(): Promise<void> {
|
||||
this.notify.confirm(
|
||||
ACCOUNT_VIEW_CONSTANTS.WARNINGS.DELETE_PROFILE_WARNING,
|
||||
this.deleteProfile,
|
||||
);
|
||||
}
|
||||
|
||||
async deleteProfile() {
|
||||
async deleteProfile(): Promise<void> {
|
||||
this.savingProfile = true;
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
@@ -2061,17 +1912,12 @@ export default class AccountViewView extends Vue {
|
||||
this.userProfileLatitude = 0;
|
||||
this.userProfileLongitude = 0;
|
||||
this.includeUserProfileLocation = false;
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Profile Deleted",
|
||||
text: "Your profile has been deleted successfully.",
|
||||
},
|
||||
3000,
|
||||
this.notify.success(
|
||||
ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_DELETED,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} else {
|
||||
throw Error("Profile not deleted");
|
||||
throw Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_DELETED);
|
||||
}
|
||||
} catch (error) {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
@@ -2079,16 +1925,8 @@ export default class AccountViewView extends Vue {
|
||||
);
|
||||
const errorMessage: string =
|
||||
extractErrorMessage(error) ||
|
||||
"There was an error deleting your profile.";
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Deleting Profile",
|
||||
text: errorMessage,
|
||||
},
|
||||
3000,
|
||||
);
|
||||
ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_DELETE_ERROR;
|
||||
this.notify.error(errorMessage, TIMEOUTS.STANDARD);
|
||||
} finally {
|
||||
this.savingProfile = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user