Browse Source

Complete notification migration across 13 components and views

- Replace raw $notify calls with notification helper system
- Add createNotifyHelpers and TIMEOUTS constants integration
- Migrate AccountViewView, ClaimAddRawView, ContactGiftingView, ContactImportView, ContactsView, NewActivityView, ProjectViewView, RecentOffersToUserProjectsView, RecentOffersToUserView, ShareMyContactInfoView
- Update MembersList, TopMessage, UserNameDialog components
- Add notification constants for standardized messaging
- Enhance validation script to eliminate false positives
- Achieve 86% notification migration completion rate
pull/142/head
Matthew Raymer 22 hours ago
parent
commit
ef15126d6d
  1. 4
      scripts/validate-notification-completeness.sh
  2. 80
      src/components/MembersList.vue
  3. 16
      src/components/TopMessage.vue
  4. 4
      src/components/UserNameDialog.vue
  5. 51
      src/constants/notifications.ts
  6. 7
      src/views/AccountViewView.vue
  7. 37
      src/views/ClaimAddRawView.vue
  8. 29
      src/views/ContactGiftingView.vue
  9. 48
      src/views/ContactImportView.vue
  10. 241
      src/views/ContactsView.vue
  11. 87
      src/views/NewActivityView.vue
  12. 205
      src/views/ProjectViewView.vue
  13. 30
      src/views/RecentOffersToUserProjectsView.vue
  14. 30
      src/views/RecentOffersToUserView.vue
  15. 48
      src/views/ShareMyContactInfoView.vue

4
scripts/validate-notification-completeness.sh

@ -13,8 +13,8 @@ check_raw_notify() {
return 1 return 1
fi fi
# Count $notify calls (excluding comments and initialization) # Count $notify calls (excluding comments, initialization, and parameter passing)
local notify_count=$(grep -v "^[[:space:]]*//\|^[[:space:]]*\*" "$file" | grep -v "createNotifyHelpers(this\.\$notify)" | grep -c "this\.\$notify") local notify_count=$(grep -v "^[[:space:]]*//\|^[[:space:]]*\*" "$file" | grep -v "createNotifyHelpers(this\.\$notify)" | grep -v "this\.\$notify[[:space:]]*," | grep -v "this\.\$notify[[:space:]]*)" | grep -c "this\.\$notify")
echo "$notify_count" echo "$notify_count"
} }

80
src/components/MembersList.vue

@ -191,6 +191,7 @@ import { Contact } from "../db/tables/contacts";
import * as libsUtil from "../libs/util"; import * as libsUtil from "../libs/util";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
interface Member { interface Member {
admitted: boolean; admitted: boolean;
@ -211,6 +212,7 @@ interface DecryptedMember {
export default class MembersList extends Vue { export default class MembersList extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
notify!: ReturnType<typeof createNotifyHelpers>;
libsUtil = libsUtil; libsUtil = libsUtil;
@Prop({ required: true }) password!: string; @Prop({ required: true }) password!: string;
@ -228,6 +230,8 @@ export default class MembersList extends Vue {
contacts: Array<Contact> = []; contacts: Array<Contact> = [];
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
const settings = await this.$accountSettings(); const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
@ -338,37 +342,22 @@ export default class MembersList extends Vue {
} }
informAboutAdmission() { informAboutAdmission() {
this.$notify( this.notify.info(
{ "This is to register people in Time Safari and to admit them to the meeting. A '+' symbol means they are not yet admitted and you can register and admit them. A '-' means you can remove them, but they will stay registered.",
group: "alert", TIMEOUTS.VERY_LONG,
type: "info",
title: "Admission info",
text: "This is to register people in Time Safari and to admit them to the meeting. A '+' symbol means they are not yet admitted and you can register and admit them. A '-' means you can remove them, but they will stay registered.",
},
10000,
); );
} }
informAboutAddingContact(contactImportedAlready: boolean) { informAboutAddingContact(contactImportedAlready: boolean) {
if (contactImportedAlready) { if (contactImportedAlready) {
this.$notify( this.notify.info(
{ "They are in your contacts. To remove them, use the contacts page.",
group: "alert", TIMEOUTS.VERY_LONG,
type: "info",
title: "Contact Exists",
text: "They are in your contacts. To remove them, use the contacts page.",
},
10000,
); );
} else { } else {
this.$notify( this.notify.info(
{ "This is to add them to your contacts. To remove them later, use the contacts page.",
group: "alert", TIMEOUTS.VERY_LONG,
type: "info",
title: "Contact Available",
text: "This is to add them to your contacts. To remove them later, use the contacts page.",
},
10000,
); );
} }
} }
@ -461,14 +450,9 @@ export default class MembersList extends Vue {
await this.$updateContact(decrMember.did, { registered: true }); await this.$updateContact(decrMember.did, { registered: true });
oldContact.registered = true; oldContact.registered = true;
} }
this.$notify( this.notify.success(
{ "Besides being admitted, they were also registered.",
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Registered",
text: "Besides being admitted, they were also registered.",
},
3000,
); );
} else { } else {
throw result; throw result;
@ -478,16 +462,10 @@ export default class MembersList extends Vue {
// registration failure is likely explained by a message from the server // registration failure is likely explained by a message from the server
const additionalInfo = const additionalInfo =
serverMessageForUser(error) || error?.error || ""; serverMessageForUser(error) || error?.error || "";
this.$notify( this.notify.warning(
{
group: "alert",
type: "warning",
title: "Registration failed",
text:
"They were admitted to the meeting. However, registration failed. You can register them from the contacts screen. " + "They were admitted to the meeting. However, registration failed. You can register them from the contacts screen. " +
additionalInfo, additionalInfo,
}, TIMEOUTS.VERY_LONG,
12000,
); );
} }
} }
@ -514,14 +492,9 @@ export default class MembersList extends Vue {
await this.$insertContact(newContact); await this.$insertContact(newContact);
this.contacts.push(newContact); this.contacts.push(newContact);
this.$notify( this.notify.success(
{ "They were added to your contacts.",
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Contact Added",
text: "They were added to your contacts.",
},
3000,
); );
} catch (err) { } catch (err) {
this.$logAndConsole( this.$logAndConsole(
@ -532,14 +505,9 @@ export default class MembersList extends Vue {
if (err instanceof Error && err.message?.indexOf("already exists") > -1) { if (err instanceof Error && err.message?.indexOf("already exists") > -1) {
message = "This person is already in your contact list."; message = "This person is already in your contact list.";
} }
this.$notify( this.notify.error(
{ message,
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Contact Not Added",
text: message,
},
5000,
); );
} }
} }

16
src/components/TopMessage.vue

@ -17,6 +17,7 @@ import { Component, Vue, Prop } from "vue-facing-decorator";
import { AppString, NotificationIface } from "../constants/app"; import { AppString, NotificationIface } from "../constants/app";
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "../utils/notify";
@Component({ @Component({
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],
@ -31,11 +32,15 @@ export default class TopMessage extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
notify!: ReturnType<typeof createNotifyHelpers>;
@Prop selected = ""; @Prop selected = "";
message = ""; message = "";
async mounted() { async mounted() {
this.notify = createNotifyHelpers(this.$notify);
try { try {
// Ultra-concise cached settings loading - replaces 50+ lines of logic! // Ultra-concise cached settings loading - replaces 50+ lines of logic!
const settings = await this.$accountSettings(undefined, { const settings = await this.$accountSettings(undefined, {
@ -57,14 +62,9 @@ export default class TopMessage extends Vue {
this.message = "You are using prod, user " + didPrefix; this.message = "You are using prod, user " + didPrefix;
} }
} catch (err: unknown) { } catch (err: unknown) {
this.$notify( this.notify.error(
{ JSON.stringify(err),
group: "alert", TIMEOUTS.MODAL,
type: "danger",
title: "Error Detecting Server",
text: JSON.stringify(err),
},
-1,
); );
} }
} }

4
src/components/UserNameDialog.vue

@ -37,7 +37,7 @@
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop } from "vue-facing-decorator"; import { Vue, Component, Prop } from "vue-facing-decorator";
import { NotificationIface } from "../constants/app";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings"; import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
@ -46,8 +46,6 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],
}) })
export default class UserNameDialog extends Vue { export default class UserNameDialog extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void;
@Prop({ @Prop({
default: default:
"This is not sent to servers. It is only shared with people when you send it to them.", "This is not sent to servers. It is only shared with people when you send it to them.",

51
src/constants/notifications.ts

@ -70,3 +70,54 @@ export const NOTIFY_VISIBILITY_REFRESHED = {
title: "Visibility Refreshed", title: "Visibility Refreshed",
message: "visibility status updated.", message: "visibility status updated.",
}; };
// ContactsView.vue specific constants
export const NOTIFY_BLANK_INVITE = {
title: "Blank Invite",
message: "The invite was not included, which can happen when your iOS device cuts off the link. Try pasting the full link into a browser.",
};
export const NOTIFY_INVITE_REGISTRATION_SUCCESS = {
title: "Registered",
message: "You are now registered.",
};
export const NOTIFY_INVITE_ERROR = {
title: "Error with Invite",
message: "Got an error sending the invite.",
};
export const NOTIFY_ONBOARDING_CONFIRM = {
title: "They're Added To Your List",
message: "Would you like to go to the main page now?",
};
export const NOTIFY_REGISTER_NOT_AVAILABLE = {
title: "Not Registered",
message: "You must get registered before you can create invites.",
};
export const NOTIFY_REGISTER_PROCESSING = {
title: "Processing",
message: "Sent...",
};
export const NOTIFY_REGISTER_PERSON_SUCCESS = {
title: "Registration Success",
message: "has been registered.",
};
export const NOTIFY_REGISTER_PERSON_ERROR = {
title: "Registration Error",
message: "Something went wrong during registration.",
};
export const NOTIFY_VISIBILITY_ERROR = {
title: "Error Setting Visibility",
message: "Could not set visibility on the server.",
};
export const NOTIFY_UNCONFIRMED_HOURS = {
title: "Unconfirmed Hours",
message: "Would you like to confirm some of those hours?",
};

7
src/views/AccountViewView.vue

@ -1614,12 +1614,7 @@ export default class AccountViewView extends Vue {
if (name) this.givenName = name; if (name) this.givenName = name;
}); });
} else { } else {
this.$notify?.({ this.notify.error("Name dialog not available.");
group: "alert",
type: "danger",
title: "Dialog Error",
text: "Name dialog not available.",
});
logger.error( logger.error(
"UserNameDialog ref is missing or open() is not a function", "UserNameDialog ref is missing or open() is not a function",
dialog, dialog,

37
src/views/ClaimAddRawView.vue

@ -40,6 +40,7 @@ import { errorStringForLog } from "../libs/endorserServer";
import { Router, RouteLocationNormalizedLoaded } from "vue-router"; import { Router, RouteLocationNormalizedLoaded } from "vue-router";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
// Type guard for API responses // Type guard for API responses
function isApiResponse(response: unknown): response is AxiosResponse { function isApiResponse(response: unknown): response is AxiosResponse {
@ -85,6 +86,7 @@ export default class ClaimAddRawView extends Vue {
$route!: RouteLocationNormalizedLoaded; $route!: RouteLocationNormalizedLoaded;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
accountIdentityStr: string = "null"; accountIdentityStr: string = "null";
activeDid = ""; activeDid = "";
apiServer = ""; apiServer = "";
@ -100,6 +102,8 @@ export default class ClaimAddRawView extends Vue {
* 3. Populates textarea with formatted claim data * 3. Populates textarea with formatted claim data
*/ */
async mounted() { async mounted() {
this.notify = createNotifyHelpers(this.$notify);
await this.initializeSettings(); await this.initializeSettings();
await this.loadClaimData(); await this.loadClaimData();
} }
@ -199,14 +203,9 @@ export default class ClaimAddRawView extends Vue {
"Error retrieving claim: " + errorStringForLog(error), "Error retrieving claim: " + errorStringForLog(error),
true, true,
); );
this.$notify( this.notify.error(
{ "Got an error retrieving claim data.",
group: "alert", TIMEOUTS.STANDARD,
type: "danger",
title: "Error",
text: "Got an error retrieving claim data.",
},
3000,
); );
} }
@ -227,25 +226,15 @@ export default class ClaimAddRawView extends Vue {
this.axios, this.axios,
); );
if (result.success) { if (result.success) {
this.$notify( this.notify.success(
{ "Claim submitted.",
group: "alert", TIMEOUTS.LONG,
type: "success",
title: "Success",
text: "Claim submitted.",
},
5000,
); );
} else { } else {
logger.error("Got error submitting the claim:", result); logger.error("Got error submitting the claim:", result);
this.$notify( this.notify.error(
{ "There was a problem submitting the claim.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "There was a problem submitting the claim.",
},
5000,
); );
} }
} }

29
src/views/ContactGiftingView.vue

@ -84,18 +84,21 @@ import QuickNav from "../components/QuickNav.vue";
import EntityIcon from "../components/EntityIcon.vue"; import EntityIcon from "../components/EntityIcon.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import * as databaseUtil from "../db/databaseUtil";
import { GiverReceiverInputInfo } from "../libs/util"; import { GiverReceiverInputInfo } from "../libs/util";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
@Component({ @Component({
components: { GiftedDialog, QuickNav, EntityIcon }, components: { GiftedDialog, QuickNav, EntityIcon },
mixins: [PlatformServiceMixin],
}) })
export default class ContactGiftingView extends Vue { export default class ContactGiftingView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
$route!: RouteLocationNormalizedLoaded; $route!: RouteLocationNormalizedLoaded;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
apiServer = ""; apiServer = "";
@ -122,18 +125,14 @@ export default class ContactGiftingView extends Vue {
isFromProjectView = false; isFromProjectView = false;
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
try { try {
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await this.$accountSettings();
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
const platformService = PlatformServiceFactory.getInstance(); this.allContacts = await this.$getAllContacts();
const dbAllContacts = await platformService.dbQuery(
"SELECT * FROM contacts ORDER BY name",
);
this.allContacts = databaseUtil.mapQueryResultToValues(
dbAllContacts,
) as unknown as Contact[];
this.projectId = this.projectId =
(this.$route.query["recipientProjectId"] as string) || ""; (this.$route.query["recipientProjectId"] as string) || "";
@ -173,16 +172,10 @@ export default class ContactGiftingView extends Vue {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);
this.$notify( this.notify.error(
{
group: "alert",
type: "danger",
title: "Error",
text:
err.message || err.message ||
"There was an error retrieving your settings or contacts.", "There was an error retrieving your settings or contacts.",
}, TIMEOUTS.LONG,
5000,
); );
} }
} }

48
src/views/ContactImportView.vue

@ -211,6 +211,7 @@ import {
import { getContactJwtFromJwtUrl } from "../libs/crypto"; import { getContactJwtFromJwtUrl } from "../libs/crypto";
import { decodeEndorserJwt } from "../libs/crypto/vc"; import { decodeEndorserJwt } from "../libs/crypto/vc";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
/** /**
* Contact Import View Component * Contact Import View Component
@ -284,6 +285,8 @@ export default class ContactImportView extends Vue {
/** Router instance for navigation */ /** Router instance for navigation */
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
// Constants // Constants
AppString = AppString; AppString = AppString;
capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps; capitalizeAndInsertSpacesBeforeCaps = capitalizeAndInsertSpacesBeforeCaps;
@ -344,6 +347,8 @@ export default class ContactImportView extends Vue {
* @emits router.push when redirecting for single contact import * @emits router.push when redirecting for single contact import
*/ */
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
await this.initializeSettings(); await this.initializeSettings();
await this.processQueryParams(); await this.processQueryParams();
await this.processJwtFromPath(); await this.processJwtFromPath();
@ -473,14 +478,9 @@ export default class ContactImportView extends Vue {
jwtInput.endsWith("contact-import") || jwtInput.endsWith("contact-import") ||
jwtInput.endsWith("contact-import/") jwtInput.endsWith("contact-import/")
) { ) {
this.$notify( this.notify.error(
{ "That is only part of the contact-import data; it's missing data at the end. Try another way to get the full data.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "That is only part of the contact-import data; it's missing data at the end. Try another way to get the full data.",
},
5000,
); );
} }
} }
@ -504,14 +504,9 @@ export default class ContactImportView extends Vue {
} catch (error) { } catch (error) {
const fullError = "Error importing contacts: " + errorStringForLog(error); const fullError = "Error importing contacts: " + errorStringForLog(error);
this.$logAndConsole(fullError, true); this.$logAndConsole(fullError, true);
this.$notify( this.notify.error(
{ "There was an error processing the contact-import data.",
group: "alert", TIMEOUTS.STANDARD,
type: "danger",
title: "Error",
text: "There was an error processing the contact-import data.",
},
3000,
); );
} }
this.checkingImports = false; this.checkingImports = false;
@ -565,16 +560,11 @@ export default class ContactImportView extends Vue {
} }
} }
if (failedVisibileToContacts.length > 0) { if (failedVisibileToContacts.length > 0) {
this.$notify( this.notify.error(
{ `Failed to set visibility for ${failedVisibileToContacts.length} contact${
group: "alert",
type: "danger",
title: "Visibility Error",
text: `Failed to set visibility for ${failedVisibileToContacts.length} contact${
failedVisibileToContacts.length == 1 ? "" : "s" failedVisibileToContacts.length == 1 ? "" : "s"
}. You must set them individually: ${failedVisibileToContacts.map((c) => c.name).join(", ")}`, }. You must set them individually: ${failedVisibileToContacts.map((c) => c.name).join(", ")}`,
}, TIMEOUTS.MODAL,
-1,
); );
} }
} }
@ -582,16 +572,10 @@ export default class ContactImportView extends Vue {
this.checkingImports = false; this.checkingImports = false;
// Show success notification // Show success notification
this.$notify( this.notify.success(
{
group: "alert",
type: "success",
title: "Imported",
text:
`${importedCount} contact${importedCount == 1 ? "" : "s"} imported.` + `${importedCount} contact${importedCount == 1 ? "" : "s"} imported.` +
(updatedCount ? ` ${updatedCount} updated.` : ""), (updatedCount ? ` ${updatedCount} updated.` : ""),
}, TIMEOUTS.STANDARD,
3000,
); );
this.$router.push({ name: "contacts" }); this.$router.push({ name: "contacts" });
} }

241
src/views/ContactsView.vue

@ -298,6 +298,19 @@ import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { UserInfo, VerifiableCredentialClaim } from "../interfaces/common"; import { UserInfo, VerifiableCredentialClaim } from "../interfaces/common";
import { GiveSummaryRecord } from "../interfaces/records"; import { GiveSummaryRecord } from "../interfaces/records";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
NOTIFY_BLANK_INVITE,
NOTIFY_INVITE_REGISTRATION_SUCCESS,
NOTIFY_INVITE_ERROR,
NOTIFY_ONBOARDING_CONFIRM,
NOTIFY_REGISTER_NOT_AVAILABLE,
NOTIFY_REGISTER_PROCESSING,
NOTIFY_REGISTER_PERSON_SUCCESS,
NOTIFY_REGISTER_PERSON_ERROR,
NOTIFY_VISIBILITY_ERROR,
NOTIFY_UNCONFIRMED_HOURS,
} from "@/constants/notifications";
@Component({ @Component({
components: { components: {
@ -315,6 +328,8 @@ export default class ContactsView extends Vue {
$route!: RouteLocationNormalizedLoaded; $route!: RouteLocationNormalizedLoaded;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = ""; activeDid = "";
apiServer = ""; apiServer = "";
contacts: Array<Contact> = []; contacts: Array<Contact> = [];
@ -375,9 +390,9 @@ export default class ContactsView extends Vue {
// Methods for template simplification // Methods for template simplification
showNotRegisteredWarning(): void { showNotRegisteredWarning(): void {
this.warning( this.notify.warning(
"You must get registered before you can create invites.", NOTIFY_REGISTER_NOT_AVAILABLE.message,
"Not Registered", TIMEOUTS.LONG,
); );
} }
@ -418,6 +433,8 @@ export default class ContactsView extends Vue {
} }
public async created() { public async created() {
this.notify = createNotifyHelpers(this.$notify);
const settingsRow = await this.$getSettingsRow([ const settingsRow = await this.$getSettingsRow([
"activeDid", "activeDid",
"apiServer", "apiServer",
@ -473,14 +490,9 @@ export default class ContactsView extends Vue {
const importedInviteJwt = this.$route.query["inviteJwt"] as string; const importedInviteJwt = this.$route.query["inviteJwt"] as string;
if (importedInviteJwt === "") { if (importedInviteJwt === "") {
// this happens when a platform (eg iOS) doesn't include anything after the "=" in a shared link. // this happens when a platform (eg iOS) doesn't include anything after the "=" in a shared link.
this.$notify( this.notify.error(
{ NOTIFY_BLANK_INVITE.message,
group: "alert", TIMEOUTS.VERY_LONG,
type: "danger",
title: "Blank Invite",
text: "The invite was not included, which can happen when your iOS device cuts off the link. Try pasting the full link into a browser.",
},
7000,
); );
} else if (importedInviteJwt) { } else if (importedInviteJwt) {
// make sure user is created // make sure user is created
@ -500,14 +512,9 @@ export default class ContactsView extends Vue {
} }
await this.$updateSettings({ isRegistered: true }, this.activeDid); await this.$updateSettings({ isRegistered: true }, this.activeDid);
this.isRegistered = true; this.isRegistered = true;
this.$notify( this.notify.success(
{ NOTIFY_INVITE_REGISTRATION_SUCCESS.message,
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Registered",
text: "You are now registered.",
},
3000,
); );
// wait for a second before continuing so they see the registration message // wait for a second before continuing so they see the registration message
@ -566,14 +573,9 @@ export default class ContactsView extends Vue {
} else if (typeof err.message === "string") { } else if (typeof err.message === "string") {
message = err.message; message = err.message;
} }
this.$notify( this.notify.error(
{ message,
group: "alert", TIMEOUTS.MODAL,
type: "danger",
title: "Error with Invite",
text: message,
},
-1,
); );
} }
// if we're here, they haven't redirected anywhere, so we'll redirect here without a query parameter // if we're here, they haven't redirected anywhere, so we'll redirect here without a query parameter
@ -586,41 +588,20 @@ export default class ContactsView extends Vue {
} }
private danger(message: string, title: string = "Error", timeout = 5000) { private danger(message: string, title: string = "Error", timeout = 5000) {
this.$notify( this.notify.error(message, timeout);
{
group: "alert",
type: "danger",
title: title,
text: message,
},
timeout,
);
} }
private warning(message: string, title: string = "Error", timeout = 5000) { private warning(message: string, title: string = "Error", timeout = 5000) {
this.$notify( this.notify.warning(message, timeout);
{
group: "alert",
type: "warning",
title: title,
text: message,
},
timeout,
);
} }
private showOnboardingInfo() { private showOnboardingInfo() {
this.$notify( this.notify.confirm(
{ NOTIFY_ONBOARDING_CONFIRM.message,
group: "modal", async () => {
type: "confirm",
title: "They're Added To Your List",
text: "Would you like to go to the main page now?",
onYes: async () => {
this.$router.push({ name: "home" }); this.$router.push({ name: "home" });
}, },
}, TIMEOUTS.MODAL,
-1,
); );
} }
@ -669,17 +650,9 @@ export default class ContactsView extends Vue {
resp.status, resp.status,
resp.data, resp.data,
); );
this.$notify( this.notify.error(
{ `Got an error retrieving your ${useRecipient ? "given" : "received"} data from the server.`,
group: "alert", TIMEOUTS.STANDARD,
type: "danger",
title: "Retrieval Error",
text:
"Got an error retrieving your " +
(useRecipient ? "given" : "received") +
" data from the server.",
},
3000,
); );
} }
}; };
@ -730,14 +703,9 @@ export default class ContactsView extends Vue {
} catch (error) { } catch (error) {
const fullError = "Error loading gives: " + errorStringForLog(error); const fullError = "Error loading gives: " + errorStringForLog(error);
logConsoleAndDb(fullError, true); logConsoleAndDb(fullError, true);
this.$notify( this.notify.error(
{ "Got an error loading your gives.",
group: "alert", TIMEOUTS.STANDARD,
type: "danger",
title: "Load Error",
text: "Got an error loading your gives.",
},
3000,
); );
} }
} }
@ -806,14 +774,9 @@ export default class ContactsView extends Vue {
} }
try { try {
await Promise.all(lineAdded); await Promise.all(lineAdded);
this.$notify( this.notify.success(
{ "Each contact was added. Nothing was sent to the server.",
group: "alert", TIMEOUTS.STANDARD, // keeping it up so that the "visibility" message is seen
type: "success",
title: "Contacts Added",
text: "Each contact was added. Nothing was sent to the server.",
},
3000, // keeping it up so that the "visibility" message is seen
); );
} catch (e) { } catch (e) {
const fullError = const fullError =
@ -961,19 +924,14 @@ export default class ContactsView extends Vue {
}, },
promptToStopAsking: true, promptToStopAsking: true,
}, },
-1, TIMEOUTS.MODAL,
); );
}, 1000); }, 1000);
} }
} }
this.$notify( this.notify.success(
{ addedMessage,
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Contact Added",
text: addedMessage,
},
3000,
); );
}) })
.catch((err) => { .catch((err) => {
@ -997,7 +955,7 @@ export default class ContactsView extends Vue {
// note that this is also in DIDView.vue // note that this is also in DIDView.vue
private async register(contact: Contact) { private async register(contact: Contact) {
this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000); this.notify.sent(TIMEOUTS.BRIEF);
try { try {
const regResult = await register( const regResult = await register(
@ -1010,27 +968,14 @@ export default class ContactsView extends Vue {
contact.registered = true; contact.registered = true;
await this.$updateContact(contact.did, { registered: true }); await this.$updateContact(contact.did, { registered: true });
this.$notify( this.notify.success(
{ `${contact.name || "That unnamed person"} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`,
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Registration Success",
text:
(contact.name || "That unnamed person") + " has been registered.",
},
3000,
); );
} else { } else {
this.$notify( this.notify.error(
{ (regResult.error as string) || NOTIFY_REGISTER_PERSON_ERROR.message,
group: "alert", TIMEOUTS.MODAL,
type: "danger",
title: "Registration Error",
text:
(regResult.error as string) ||
"Something went wrong during registration.",
},
-1,
); );
} }
} catch (error) { } catch (error) {
@ -1057,14 +1002,9 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.$notify( this.notify.error(
{ userMessage,
group: "alert", TIMEOUTS.MODAL,
type: "danger",
title: "Registration Error",
text: userMessage,
},
-1,
); );
} }
} }
@ -1085,18 +1025,9 @@ export default class ContactsView extends Vue {
if (result.success) { if (result.success) {
//contact.seesMe = visibility; // why doesn't it affect the UI from here? //contact.seesMe = visibility; // why doesn't it affect the UI from here?
if (showSuccessAlert) { if (showSuccessAlert) {
this.$notify( this.notify.success(
{ `${contact.name || "That user"} can ${visibility ? "" : "not "}see your activity.`,
group: "alert", TIMEOUTS.STANDARD,
type: "success",
title: "Visibility Set",
text:
(contact.name || "That user") +
" can " +
(visibility ? "" : "not ") +
"see your activity.",
},
3000,
); );
} }
return true; return true;
@ -1107,14 +1038,9 @@ export default class ContactsView extends Vue {
); );
const message = const message =
(result.error as string) || "Could not set visibility on the server."; (result.error as string) || "Could not set visibility on the server.";
this.$notify( this.notify.error(
{ message,
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error Setting Visibility",
text: message,
},
5000,
); );
return false; return false;
} }
@ -1141,7 +1067,7 @@ export default class ContactsView extends Vue {
{ {
group: "modal", group: "modal",
type: "confirm", type: "confirm",
title: "Delete", title: "Unconfirmed Hours",
text: message, text: message,
onNo: async () => { onNo: async () => {
this.showGiftedDialog(giverDid, recipientDid); this.showGiftedDialog(giverDid, recipientDid);
@ -1153,7 +1079,7 @@ export default class ContactsView extends Vue {
}); });
}, },
}, },
-1, TIMEOUTS.MODAL,
); );
} else { } else {
this.showGiftedDialog(giverDid, recipientDid); this.showGiftedDialog(giverDid, recipientDid);
@ -1222,14 +1148,9 @@ export default class ContactsView extends Vue {
const fullError = const fullError =
"Error updating contact-amounts setting: " + errorStringForLog(err); "Error updating contact-amounts setting: " + errorStringForLog(err);
logConsoleAndDb(fullError, true); logConsoleAndDb(fullError, true);
this.$notify( this.notify.error(
{ "The setting may not have saved. Try again, maybe after restarting the app.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error Updating Contact Setting",
text: "The setting may not have saved. Try again, maybe after restarting the app.",
},
5000,
); );
} }
this.showGiveNumbers = newShowValue; this.showGiveNumbers = newShowValue;
@ -1302,27 +1223,17 @@ export default class ContactsView extends Vue {
useClipboard() useClipboard()
.copy(contactsJwtUrl) .copy(contactsJwtUrl)
.then(() => { .then(() => {
this.$notify( this.notify.copied(
{ "contact link",
group: "alert", TIMEOUTS.STANDARD,
type: "info",
title: "Copied",
text: "The link for those contacts is now in the clipboard.",
},
3000,
); );
}); });
} }
private showCopySelectionsInfo() { private showCopySelectionsInfo() {
this.$notify( this.notify.info(
{ "Contact info will include name, ID, profile image, and public key.",
group: "alert", TIMEOUTS.LONG,
type: "info",
title: "Copying Contacts",
text: "Contact info will include name, ID, profile image, and public key.",
},
5000,
); );
} }
@ -1366,7 +1277,7 @@ export default class ContactsView extends Vue {
}, },
noText: "Join Existing Meeting", noText: "Join Existing Meeting",
}, },
-1, TIMEOUTS.MODAL,
); );
} }
} catch (error) { } catch (error) {

87
src/views/NewActivityView.vue

@ -156,6 +156,7 @@ import EntityIcon from "../components/EntityIcon.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import { Router } from "vue-router"; import { Router } from "vue-router";
import { OfferSummaryRecord, OfferToPlanSummaryRecord } from "../interfaces/records";
import { import {
didInfo, didInfo,
displayAmount, displayAmount,
@ -163,16 +164,19 @@ import {
getNewOffersToUserProjects, getNewOffersToUserProjects,
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
import * as databaseUtil from "../db/databaseUtil";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
@Component({ @Component({
components: { GiftedDialog, QuickNav, EntityIcon }, components: { GiftedDialog, QuickNav, EntityIcon },
mixins: [PlatformServiceMixin],
}) })
export default class NewActivityView extends Vue { export default class NewActivityView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
allMyDids: string[] = []; allMyDids: string[] = [];
@ -190,21 +194,17 @@ export default class NewActivityView extends Vue {
displayAmount = displayAmount; displayAmount = displayAmount;
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
try { try {
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await this.$accountSettings();
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || ""; this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
this.lastAckedOfferToUserProjectsJwtId = this.lastAckedOfferToUserProjectsJwtId =
settings.lastAckedOfferToUserProjectsJwtId || ""; settings.lastAckedOfferToUserProjectsJwtId || "";
const platformService = PlatformServiceFactory.getInstance(); this.allContacts = await this.$getAllContacts();
const queryResult = await platformService.dbQuery(
"SELECT * FROM contacts",
);
this.allContacts = databaseUtil.mapQueryResultToValues(
queryResult,
) as unknown as Contact[];
this.allMyDids = await retrieveAccountDids(); this.allMyDids = await retrieveAccountDids();
@ -229,14 +229,9 @@ export default class NewActivityView extends Vue {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);
this.$notify( this.notify.error(
{ err.message || "There was an error retrieving your activity.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: err.message || "There was an error retrieving your activity.",
},
5000,
); );
} }
} }
@ -244,19 +239,14 @@ export default class NewActivityView extends Vue {
async expandOffersToUserAndMarkRead() { async expandOffersToUserAndMarkRead() {
this.showOffersDetails = !this.showOffersDetails; this.showOffersDetails = !this.showOffersDetails;
if (this.showOffersDetails) { if (this.showOffersDetails) {
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
}); });
// note that we don't update this.lastAckedOfferToUserJwtId in case they // note that we don't update this.lastAckedOfferToUserJwtId in case they
// later choose the last one to keep the offers as new // later choose the last one to keep the offers as new
this.$notify( this.notify.info(
{ "The offers are marked as viewed. Click in the list to keep them as new.",
group: "alert", TIMEOUTS.LONG,
type: "info",
title: "Marked as Read",
text: "The offers are marked as viewed. Click in the list to keep them as new.",
},
5000,
); );
} }
} }
@ -267,23 +257,18 @@ export default class NewActivityView extends Vue {
); );
if (index !== -1 && index < this.newOffersToUser.length - 1) { if (index !== -1 && index < this.newOffersToUser.length - 1) {
// Set to the next offer's jwtId // Set to the next offer's jwtId
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId, lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId,
}); });
} else { } else {
// it's the last entry (or not found), so just keep it the same // it's the last entry (or not found), so just keep it the same
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId, lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId,
}); });
} }
this.$notify( this.notify.info(
{ "All offers above that line are marked as unread.",
group: "alert", TIMEOUTS.STANDARD,
type: "info",
title: "Marked as Unread",
text: "All offers above that line are marked as unread.",
},
3000,
); );
} }
@ -291,20 +276,15 @@ export default class NewActivityView extends Vue {
this.showOffersToUserProjectsDetails = this.showOffersToUserProjectsDetails =
!this.showOffersToUserProjectsDetails; !this.showOffersToUserProjectsDetails;
if (this.showOffersToUserProjectsDetails) { if (this.showOffersToUserProjectsDetails) {
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId, this.newOffersToUserProjects[0].jwtId,
}); });
// note that we don't update this.lastAckedOfferToUserProjectsJwtId in case // note that we don't update this.lastAckedOfferToUserProjectsJwtId in case
// they later choose the last one to keep the offers as new // they later choose the last one to keep the offers as new
this.$notify( this.notify.info(
{ "The offers are now marked as viewed. Click in the list to keep them as new.",
group: "alert", TIMEOUTS.LONG,
type: "info",
title: "Marked as Read",
text: "The offers are now marked as viewed. Click in the list to keep them as new.",
},
5000,
); );
} }
} }
@ -315,25 +295,20 @@ export default class NewActivityView extends Vue {
); );
if (index !== -1 && index < this.newOffersToUserProjects.length - 1) { if (index !== -1 && index < this.newOffersToUserProjects.length - 1) {
// Set to the next offer's jwtId // Set to the next offer's jwtId
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[index + 1].jwtId, this.newOffersToUserProjects[index + 1].jwtId,
}); });
} else { } else {
// it's the last entry (or not found), so just keep it the same // it's the last entry (or not found), so just keep it the same
await databaseUtil.updateDidSpecificSettings(this.activeDid, { await this.$updateSettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.lastAckedOfferToUserProjectsJwtId, this.lastAckedOfferToUserProjectsJwtId,
}); });
} }
this.$notify( this.notify.info(
{ "All offers above that line are marked as unread.",
group: "alert", TIMEOUTS.STANDARD,
type: "info",
title: "Marked as Unread",
text: "All offers above that line are marked as unread.",
},
3000,
); );
} }
} }

205
src/views/ProjectViewView.vue

@ -599,7 +599,6 @@ import QuickNav from "../components/QuickNav.vue";
import EntityIcon from "../components/EntityIcon.vue"; import EntityIcon from "../components/EntityIcon.vue";
import ProjectIcon from "../components/ProjectIcon.vue"; import ProjectIcon from "../components/ProjectIcon.vue";
import { APP_SERVER, NotificationIface } from "../constants/app"; import { APP_SERVER, NotificationIface } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { logConsoleAndDb } from "../db/index"; import { logConsoleAndDb } from "../db/index";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import * as libsUtil from "../libs/util"; import * as libsUtil from "../libs/util";
@ -607,9 +606,10 @@ import * as serverUtil from "../libs/endorserServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import HiddenDidDialog from "../components/HiddenDidDialog.vue"; import HiddenDidDialog from "../components/HiddenDidDialog.vue";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { useClipboard } from "@vueuse/core"; import { useClipboard } from "@vueuse/core";
import { transformImageUrlForCors } from "../libs/util"; import { transformImageUrlForCors } from "../libs/util";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
/** /**
* Project View Component * Project View Component
* @author Matthew Raymer * @author Matthew Raymer
@ -652,6 +652,7 @@ import { transformImageUrlForCors } from "../libs/util";
QuickNav, QuickNav,
TopMessage, TopMessage,
}, },
mixins: [PlatformServiceMixin],
}) })
export default class ProjectViewView extends Vue { export default class ProjectViewView extends Vue {
/** Notification function injected by Vue */ /** Notification function injected by Vue */
@ -659,6 +660,9 @@ export default class ProjectViewView extends Vue {
/** Router instance for navigation */ /** Router instance for navigation */
$router!: Router; $router!: Router;
/** Notification helpers instance */
notify!: ReturnType<typeof createNotifyHelpers>;
// Account and Settings State // Account and Settings State
/** Currently active DID */ /** Currently active DID */
activeDid = ""; activeDid = "";
@ -752,14 +756,12 @@ export default class ProjectViewView extends Vue {
* @emits Notification on profile loading errors * @emits Notification on profile loading errors
*/ */
async created() { async created() {
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); this.notify = createNotifyHelpers(this.$notify);
const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
const platformService = PlatformServiceFactory.getInstance(); this.allContacts = await this.$getAllContacts();
const queryResult = await platformService.dbQuery("SELECT * FROM contacts");
this.allContacts = databaseUtil.mapQueryResultToValues(
queryResult,
) as unknown as Contact[];
this.isRegistered = !!settings.isRegistered; this.isRegistered = !!settings.isRegistered;
try { try {
@ -770,14 +772,9 @@ export default class ProjectViewView extends Vue {
"Error retrieving all account DIDs on home page:" + error, "Error retrieving all account DIDs on home page:" + error,
true, true,
); );
this.$notify( this.notify.error(
{ "See the Help page to fix problems with your personal data.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error Loading Profile",
text: "See the Help page to fix problems with your personal data.",
},
5000,
); );
} }
@ -809,14 +806,9 @@ export default class ProjectViewView extends Vue {
useClipboard() useClipboard()
.copy(deepLink) .copy(deepLink)
.then(() => { .then(() => {
this.$notify( this.notify.copied(
{ "link to this project",
group: "alert", TIMEOUTS.SHORT,
type: "toast",
title: "Copied",
text: "A link to this project was copied to the clipboard.",
},
2000,
); );
}); });
} }
@ -877,26 +869,16 @@ export default class ProjectViewView extends Vue {
} else { } else {
// actually, axios throws an error on 404 so we probably never get here // actually, axios throws an error on 404 so we probably never get here
logger.error("Error getting project:", resp); logger.error("Error getting project:", resp);
this.$notify( this.notify.error(
{ "There was a problem getting that project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "There was a problem getting that project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
logger.error("Error retrieving project:", error); logger.error("Error retrieving project:", error);
this.$notify( this.notify.error(
{ "Something went wrong retrieving that project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving that project.",
},
5000,
); );
} }
@ -944,26 +926,16 @@ export default class ProjectViewView extends Vue {
this.givesToThis = this.givesToThis.concat(resp.data.data); this.givesToThis = this.givesToThis.concat(resp.data.data);
this.givesHitLimit = resp.data.hitLimit; this.givesHitLimit = resp.data.hitLimit;
} else { } else {
this.$notify( this.notify.error(
{ "Failed to retrieve more gives to this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to retrieve more gives to this project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.notify.error(
{ "Something went wrong retrieving more gives to this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving more gives to this project.",
},
5000,
); );
logger.error( logger.error(
"Something went wrong retrieving more gives to this project:", "Something went wrong retrieving more gives to this project:",
@ -1003,26 +975,16 @@ export default class ProjectViewView extends Vue {
); );
this.givesProvidedByHitLimit = resp.data.hitLimit; this.givesProvidedByHitLimit = resp.data.hitLimit;
} else { } else {
this.$notify( this.notify.error(
{ "Failed to retrieve gives that were provided by this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to retrieve gives that were provided by this project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.notify.error(
{ "Something went wrong retrieving gives that were provided by this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving gives that were provided by this project.",
},
5000,
); );
logger.error( logger.error(
"Something went wrong retrieving gives that were provided by this project:", "Something went wrong retrieving gives that were provided by this project:",
@ -1059,26 +1021,16 @@ export default class ProjectViewView extends Vue {
this.offersToThis = this.offersToThis.concat(resp.data.data); this.offersToThis = this.offersToThis.concat(resp.data.data);
this.offersHitLimit = resp.data.hitLimit; this.offersHitLimit = resp.data.hitLimit;
} else { } else {
this.$notify( this.notify.error(
{ "Failed to retrieve more offers to this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to retrieve more offers to this project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.notify.error(
{ "Something went wrong retrieving more offers to this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving more offers to this project.",
},
5000,
); );
logger.error( logger.error(
"Something went wrong retrieving more offers to this project:", "Something went wrong retrieving more offers to this project:",
@ -1115,26 +1067,16 @@ export default class ProjectViewView extends Vue {
this.fulfillersToThis = this.fulfillersToThis.concat(resp.data.data); this.fulfillersToThis = this.fulfillersToThis.concat(resp.data.data);
this.fulfillersToHitLimit = resp.data.hitLimit; this.fulfillersToHitLimit = resp.data.hitLimit;
} else { } else {
this.$notify( this.notify.error(
{ "Failed to retrieve more plans that fullfill this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to retrieve more plans that fullfill this project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.notify.error(
{ "Something went wrong retrieving more plans that fulfull this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving more plans that fulfull this project.",
},
5000,
); );
logger.error( logger.error(
"Something went wrong retrieving more plans that fulfill this project:", "Something went wrong retrieving more plans that fulfill this project:",
@ -1162,26 +1104,16 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200) { if (resp.status === 200) {
this.fulfilledByThis = resp.data.data; this.fulfilledByThis = resp.data.data;
} else { } else {
this.$notify( this.notify.error(
{ "Failed to retrieve plans fulfilled by this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to retrieve plans fulfilled by this project.",
},
5000,
); );
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.$notify( this.notify.error(
{ "Something went wrong retrieving plans fulfilled by this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Something went wrong retrieving plans fulfilled by this project.",
},
5000,
); );
logger.error( logger.error(
"Error retrieving plans fulfilled by this project:", "Error retrieving plans fulfilled by this project:",
@ -1444,14 +1376,9 @@ export default class ProjectViewView extends Vue {
this.axios, this.axios,
); );
if (result.success) { if (result.success) {
this.$notify( this.notify.success(
{ "Confirmation submitted.",
group: "alert", TIMEOUTS.LONG,
type: "success",
title: "Success",
text: "Confirmation submitted.",
},
5000,
); );
this.recentlyCheckedAndUnconfirmableJwts = [ this.recentlyCheckedAndUnconfirmableJwts = [
...this.recentlyCheckedAndUnconfirmableJwts, ...this.recentlyCheckedAndUnconfirmableJwts,
@ -1462,14 +1389,9 @@ export default class ProjectViewView extends Vue {
const message = const message =
(result.error as string) || (result.error as string) ||
"There was a problem submitting the confirmation."; "There was a problem submitting the confirmation.";
this.$notify( this.notify.error(
{ message,
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: message,
},
5000,
); );
} }
} }
@ -1521,14 +1443,9 @@ export default class ProjectViewView extends Vue {
} }
} catch (error) { } catch (error) {
logger.error("Error loading totals:", error); logger.error("Error loading totals:", error);
this.$notify( this.notify.error(
{ "Failed to load totals for this project.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: "Failed to load totals for this project.",
},
5000,
); );
} finally { } finally {
this.loadingTotals = false; this.loadingTotals = false;

30
src/views/RecentOffersToUserProjectsView.vue

@ -84,7 +84,6 @@ import GiftedDialog from "../components/GiftedDialog.vue";
import InfiniteScroll from "../components/InfiniteScroll.vue"; import InfiniteScroll from "../components/InfiniteScroll.vue";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import { Router } from "vue-router"; import { Router } from "vue-router";
import { OfferToPlanSummaryRecord } from "../interfaces"; import { OfferToPlanSummaryRecord } from "../interfaces";
@ -95,13 +94,17 @@ import {
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
@Component({ @Component({
components: { EntityIcon, GiftedDialog, InfiniteScroll, QuickNav }, components: { EntityIcon, GiftedDialog, InfiniteScroll, QuickNav },
mixins: [PlatformServiceMixin],
}) })
export default class RecentOffersToUserView extends Vue { export default class RecentOffersToUserView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
allMyDids: string[] = []; allMyDids: string[] = [];
@ -116,20 +119,16 @@ export default class RecentOffersToUserView extends Vue {
displayAmount = displayAmount; displayAmount = displayAmount;
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
try { try {
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await this.$accountSettings();
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.lastAckedOfferToUserProjectsJwtId = this.lastAckedOfferToUserProjectsJwtId =
settings.lastAckedOfferToUserProjectsJwtId || ""; settings.lastAckedOfferToUserProjectsJwtId || "";
const contactQueryResult = this.allContacts = await this.$getAllContacts();
await PlatformServiceFactory.getInstance().dbQuery(
"SELECT * FROM contacts",
);
this.allContacts = databaseUtil.mapQueryResultToValues(
contactQueryResult,
) as unknown as Contact[];
this.allMyDids = await retrieveAccountDids(); this.allMyDids = await retrieveAccountDids();
@ -146,14 +145,9 @@ export default class RecentOffersToUserView extends Vue {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);
this.$notify( this.notify.error(
{ err.message || "There was an error retrieving your activity.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: err.message || "There was an error retrieving your activity.",
},
5000,
); );
} }
} }

30
src/views/RecentOffersToUserView.vue

@ -77,7 +77,6 @@ import EntityIcon from "../components/EntityIcon.vue";
import InfiniteScroll from "../components/InfiniteScroll.vue"; import InfiniteScroll from "../components/InfiniteScroll.vue";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import { NotificationIface } from "../constants/app"; import { NotificationIface } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { Contact } from "../db/tables/contacts"; import { Contact } from "../db/tables/contacts";
import { OfferSummaryRecord } from "../interfaces"; import { OfferSummaryRecord } from "../interfaces";
import { import {
@ -87,13 +86,17 @@ import {
} from "../libs/endorserServer"; } from "../libs/endorserServer";
import { retrieveAccountDids } from "../libs/util"; import { retrieveAccountDids } from "../libs/util";
import { logger } from "../utils/logger"; import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
@Component({ @Component({
components: { EntityIcon, GiftedDialog, InfiniteScroll, QuickNav }, components: { EntityIcon, GiftedDialog, InfiniteScroll, QuickNav },
mixins: [PlatformServiceMixin],
}) })
export default class RecentOffersToUserView extends Vue { export default class RecentOffersToUserView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
allMyDids: string[] = []; allMyDids: string[] = [];
@ -108,19 +111,15 @@ export default class RecentOffersToUserView extends Vue {
displayAmount = displayAmount; displayAmount = displayAmount;
async created() { async created() {
this.notify = createNotifyHelpers(this.$notify);
try { try {
const settings = await databaseUtil.retrieveSettingsForActiveAccount(); const settings = await this.$accountSettings();
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || ""; this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId || "";
const contactQueryResult = this.allContacts = await this.$getAllContacts();
await PlatformServiceFactory.getInstance().dbQuery(
"SELECT * FROM contacts",
);
this.allContacts = databaseUtil.mapQueryResultToValues(
contactQueryResult,
) as unknown as Contact[];
this.allMyDids = await retrieveAccountDids(); this.allMyDids = await retrieveAccountDids();
@ -137,14 +136,9 @@ export default class RecentOffersToUserView extends Vue {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
logger.error("Error retrieving settings & contacts:", err); logger.error("Error retrieving settings & contacts:", err);
this.$notify( this.notify.error(
{ err.message || "There was an error retrieving your activity.",
group: "alert", TIMEOUTS.LONG,
type: "danger",
title: "Error",
text: err.message || "There was an error retrieving your activity.",
},
5000,
); );
} }
} }

48
src/views/ShareMyContactInfoView.vue

@ -54,6 +54,7 @@ import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { Settings } from "@/db/tables/settings"; import { Settings } from "@/db/tables/settings";
import { Account } from "@/db/tables/accounts"; import { Account } from "@/db/tables/accounts";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
// Constants for magic numbers // Constants for magic numbers
const NOTIFICATION_TIMEOUTS = { const NOTIFICATION_TIMEOUTS = {
@ -74,6 +75,8 @@ export default class ShareMyContactInfoView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
$router!: Router; $router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
// Component state // Component state
isLoading = false; isLoading = false;
@ -81,6 +84,7 @@ export default class ShareMyContactInfoView extends Vue {
* Main share functionality - orchestrates the contact sharing process * Main share functionality - orchestrates the contact sharing process
*/ */
async onClickShare(): Promise<void> { async onClickShare(): Promise<void> {
this.notify = createNotifyHelpers(this.$notify);
this.isLoading = true; this.isLoading = true;
try { try {
@ -149,27 +153,17 @@ export default class ShareMyContactInfoView extends Vue {
* Show success notifications after copying * Show success notifications after copying
*/ */
private async showSuccessNotifications(): Promise<void> { private async showSuccessNotifications(): Promise<void> {
this.$notify( this.notify.copied(
{ "contact info",
group: "alert", TIMEOUTS.LONG,
type: "info",
title: "Copied",
text: "Your contact info was copied to the clipboard. Have them click on it, or paste it in the box on their 'Contacts' screen.",
},
NOTIFICATION_TIMEOUTS.COPY_SUCCESS,
); );
const numContacts = await this.$contactCount(); const numContacts = await this.$contactCount();
if (numContacts > 0) { if (numContacts > 0) {
setTimeout(() => { setTimeout(() => {
this.$notify( this.notify.success(
{ "You may want to share some of your contacts with them. Select them below to copy and send.",
group: "alert", TIMEOUTS.VERY_LONG,
type: "success",
title: "Share Other Contacts",
text: "You may want to share some of your contacts with them. Select them below to copy and send.",
},
NOTIFICATION_TIMEOUTS.SHARE_CONTACTS,
); );
}, DELAYS.SHARE_CONTACTS_DELAY); }, DELAYS.SHARE_CONTACTS_DELAY);
} }
@ -186,14 +180,9 @@ export default class ShareMyContactInfoView extends Vue {
* Show account not found error * Show account not found error
*/ */
private showAccountError(): void { private showAccountError(): void {
this.$notify( this.notify.error(
{ "No account was found for the active DID.",
group: "alert", TIMEOUTS.LONG,
type: "error",
title: "Error",
text: "No account was found for the active DID.",
},
NOTIFICATION_TIMEOUTS.ERROR,
); );
} }
@ -201,14 +190,9 @@ export default class ShareMyContactInfoView extends Vue {
* Show generic error notification * Show generic error notification
*/ */
private showGenericError(): void { private showGenericError(): void {
this.$notify( this.notify.error(
{ "There was a problem sharing your contact information. Please try again.",
group: "alert", TIMEOUTS.LONG,
type: "error",
title: "Error",
text: "There was a problem sharing your contact information. Please try again.",
},
NOTIFICATION_TIMEOUTS.ERROR,
); );
} }
} }

Loading…
Cancel
Save