Complete DIDView.vue triple migration and refactor template handlers

- Fix DIDView.vue notification migration: add missing NOTIFY_SERVER_ACCESS_ERROR and NOTIFY_NO_IDENTITY_ERROR imports
- Refactor 5 inline template handlers to proper class methods (goBack, toggleDidDetails, showLargeProfileImage, showLargeIdenticon, hideLargeImage)
- Update notification validation script to exclude createNotifyHelpers initialization patterns
- DIDView.vue now fully compliant: database migration + SQL abstraction + notification migration complete

Improves code organization, testability, and follows Vue.js best practices for template/class separation. All linting passes without errors.
This commit is contained in:
Matthew Raymer
2025-07-07 05:44:34 +00:00
parent 3d124e13bb
commit ea851a7dfd
9 changed files with 877 additions and 343 deletions

View File

@@ -10,7 +10,7 @@
<!-- Back -->
<button
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
@click="$router.go(-1)"
@click="goBack"
>
<font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
</button>
@@ -32,10 +32,7 @@
<font-awesome icon="pen" class="text-sm text-blue-500 ml-2 mb-1" />
</router-link>
</h2>
<button
class="ml-2 mr-2 mt-4"
@click="showDidDetails = !showDidDetails"
>
<button class="ml-2 mr-2 mt-4" @click="toggleDidDetails">
Details
<font-awesome
v-if="showDidDetails"
@@ -60,7 +57,7 @@
:icon-size="96"
:profile-image-url="contactFromDid?.profileImageUrl"
class="inline-block align-text-bottom border border-slate-300 rounded"
@click="showLargeIdenticonUrl = contactFromDid?.profileImageUrl"
@click="showLargeProfileImage"
/>
</span>
</div>
@@ -160,7 +157,7 @@
:entity-id="viewingDid"
:icon-size="64"
class="inline-block align-middle border border-slate-300 rounded-md mr-1"
@click="showLargeIdenticonId = viewingDid"
@click="showLargeIdenticon"
/>
</div>
</div>
@@ -177,10 +174,7 @@
:icon-size="512"
:profile-image-url="showLargeIdenticonUrl"
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
@click="
showLargeIdenticonId = undefined;
showLargeIdenticonUrl = undefined;
"
@click="hideLargeImage"
/>
</div>
</div>
@@ -266,7 +260,7 @@ import TopMessage from "../components/TopMessage.vue";
import { NotificationIface } from "../constants/app";
import { Contact } from "../db/tables/contacts";
import { BoundingBox } from "../db/tables/settings";
import * as databaseUtil from "../db/databaseUtil";
import {
GenericCredWrapper,
GenericVerifiableCredential,
@@ -284,6 +278,16 @@ import * as libsUtil from "../libs/util";
import EntityIcon from "../components/EntityIcon.vue";
import { logger } from "../utils/logger";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import {
NOTIFY_DEFAULT_TO_ACTIVE_DID,
NOTIFY_CONTACT_DELETED,
NOTIFY_CONTACT_DELETE_FAILED,
NOTIFY_REGISTRATION_SUCCESS,
NOTIFY_REGISTRATION_ERROR,
NOTIFY_SERVER_ACCESS_ERROR,
NOTIFY_NO_IDENTITY_ERROR,
} from "@/constants/notifications";
/**
* DIDView Component
@@ -310,6 +314,8 @@ export default class DIDView extends Vue {
$route!: RouteLocationNormalizedLoaded;
$router!: Router;
notify!: ReturnType<typeof createNotifyHelpers>;
libsUtil = libsUtil;
yaml = yaml;
@@ -331,6 +337,13 @@ export default class DIDView extends Vue {
didInfoForContact = didInfoForContact;
displayAmount = displayAmount;
/**
* Initializes notification helpers
*/
created() {
this.notify = createNotifyHelpers(this.$notify);
}
/**
* Initializes the view with DID information
*
@@ -355,7 +368,7 @@ export default class DIDView extends Vue {
* Initializes component settings from active account
*/
private async initializeSettings() {
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
const settings = await this.$accountSettings();
this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || "";
}
@@ -384,14 +397,10 @@ export default class DIDView extends Vue {
* Notifies user that we're showing their DID info by default
*/
private notifyDefaultToActiveDID() {
this.$notify(
{
group: "alert",
type: "toast",
title: "Your Info",
text: "No user was specified so showing your info.",
},
3000,
this.notify.toast(
NOTIFY_DEFAULT_TO_ACTIVE_DID.title,
NOTIFY_DEFAULT_TO_ACTIVE_DID.message,
TIMEOUTS.SHORT,
);
}
@@ -402,17 +411,10 @@ export default class DIDView extends Vue {
private async loadContactInformation() {
if (!this.viewingDid) return;
const dbContacts = await this.$dbQuery(
"SELECT * FROM contacts WHERE did = ?",
[this.viewingDid],
);
const contacts = databaseUtil.mapQueryResultToValues(
dbContacts,
) as unknown as Contact[];
const contact = await this.$getContact(this.viewingDid);
// Safely check if contact exists before assigning
if (contacts && contacts.length > 0) {
this.contactFromDid = contacts[0];
if (contact) {
this.contactFromDid = contact;
this.contactYaml = yaml.dump(this.contactFromDid);
} else {
this.contactFromDid = undefined;
@@ -442,6 +444,33 @@ export default class DIDView extends Vue {
}
}
/**
* Navigation helper methods
*/
goBack() {
this.$router.go(-1);
}
/**
* UI state helper methods
*/
toggleDidDetails() {
this.showDidDetails = !this.showDidDetails;
}
showLargeProfileImage() {
this.showLargeIdenticonUrl = this.contactFromDid?.profileImageUrl;
}
showLargeIdenticon() {
this.showLargeIdenticonId = this.viewingDid;
}
hideLargeImage() {
this.showLargeIdenticonId = undefined;
this.showLargeIdenticonUrl = undefined;
}
/**
* Prompts user to confirm contact deletion
* Shows additional warning if contact has visibility permissions
@@ -457,18 +486,9 @@ export default class DIDView extends Vue {
message +=
" Note that they can see your activity, so if you want to hide your activity from them then you should do that first.";
}
this.$notify(
{
group: "modal",
type: "confirm",
title: "Delete",
text: message,
onYes: async () => {
await this.deleteContact(contact);
},
},
-1,
);
this.notify.confirm(message, async () => {
await this.deleteContact(contact);
});
}
/**
@@ -477,17 +497,13 @@ export default class DIDView extends Vue {
* @param contact - Contact object to be deleted
*/
async deleteContact(contact: Contact) {
await this.$dbExec("DELETE FROM contacts WHERE did = ?", [contact.did]);
this.$notify(
{
group: "alert",
type: "success",
title: "Deleted",
text: "Contact has been removed.",
},
3000,
);
this.$router.push({ name: "contacts" });
const success = await this.$deleteContact(contact.did);
if (success) {
this.notify.success(NOTIFY_CONTACT_DELETED.message, TIMEOUTS.SHORT);
this.$router.push({ name: "contacts" });
} else {
this.notify.error(NOTIFY_CONTACT_DELETE_FAILED.message, TIMEOUTS.LONG);
}
}
/**
@@ -497,24 +513,16 @@ export default class DIDView extends Vue {
* @param contact - Contact to be registered
*/
async confirmRegister(contact: Contact) {
this.$notify(
{
group: "modal",
type: "confirm",
title: "Register",
text:
"Are you sure you want to register " +
libsUtil.nameForContact(this.contactFromDid, false) +
(contact.registered
? " -- especially since they are already marked as registered"
: "") +
"?",
onYes: async () => {
await this.register(contact);
},
},
-1,
);
const message =
"Are you sure you want to register " +
libsUtil.nameForContact(this.contactFromDid, false) +
(contact.registered
? " -- especially since they are already marked as registered"
: "") +
"?";
this.notify.confirm(message, async () => {
await this.register(contact);
});
}
/**
@@ -524,7 +532,7 @@ export default class DIDView extends Vue {
* @param contact - Contact to register
*/
async register(contact: Contact) {
this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);
this.notify.toast("Processing", "Sent...", TIMEOUTS.SHORT);
try {
const regResult = await register(
@@ -535,32 +543,17 @@ export default class DIDView extends Vue {
);
if (regResult.success) {
contact.registered = true;
await this.$dbExec("UPDATE contacts SET registered = ? WHERE did = ?", [
true,
contact.did,
]);
await this.$updateContact(contact.did, { registered: true });
this.$notify(
{
group: "alert",
type: "success",
title: "Registration Success",
text:
(contact.name || "That unnamed person") + " has been registered.",
},
5000,
const name = contact.name || "That unnamed person";
this.notify.success(
`${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`,
TIMEOUTS.LONG,
);
} else {
this.$notify(
{
group: "alert",
type: "danger",
title: "Registration Error",
text:
(regResult.error as string) ||
"Something went wrong during registration.",
},
5000,
this.notify.error(
(regResult.error as string) || NOTIFY_REGISTRATION_ERROR.message,
TIMEOUTS.LONG,
);
}
} catch (error) {
@@ -582,15 +575,7 @@ export default class DIDView extends Vue {
userMessage = error as string;
}
// Now set that error for the user to see.
this.$notify(
{
group: "alert",
type: "danger",
title: "Registration Error",
text: userMessage,
},
5000,
);
this.notify.error(userMessage, TIMEOUTS.LONG);
}
}
@@ -624,15 +609,7 @@ export default class DIDView extends Vue {
if (response.status !== 200) {
const details = await response.text();
logger.error("Problem with full search:", details);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `There was a problem accessing the server. Try again later.`,
},
5000,
);
this.notify.error(NOTIFY_SERVER_ACCESS_ERROR.message, TIMEOUTS.LONG);
return;
}
@@ -642,14 +619,9 @@ export default class DIDView extends Vue {
} catch (e: unknown) {
logger.error("Error with feed load:", e);
const error = e as { userMessage?: string };
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: error.userMessage || "There was a problem retrieving claims.",
},
3000,
this.notify.error(
error.userMessage || "There was a problem retrieving claims.",
TIMEOUTS.SHORT,
);
} finally {
this.isLoading = false;
@@ -728,21 +700,12 @@ export default class DIDView extends Vue {
const visibilityPrompt = visibility
? "Are you sure you want to make your activity visible to them?"
: "Are you sure you want to hide all your activity from them?";
this.$notify(
{
group: "modal",
type: "confirm",
title: "Set Visibility",
text: visibilityPrompt,
onYes: async () => {
const success = await this.setVisibility(contact, visibility, true);
if (success) {
contact.seesMe = visibility; // didn't work inside setVisibility
}
},
},
-1,
);
this.notify.confirm(visibilityPrompt, async () => {
const success = await this.setVisibility(contact, visibility, true);
if (success) {
contact.seesMe = visibility; // didn't work inside setVisibility
}
});
}
/**
@@ -758,27 +721,16 @@ export default class DIDView extends Vue {
visibility: boolean,
showSuccessAlert: boolean,
) {
// TODO: Implement proper visibility setting using mixin methods
// For now, just update local database
await this.$dbExec("UPDATE contacts SET seesMe = ? WHERE did = ?", [
visibility,
contact.did,
]);
// Update contact visibility using mixin method
await this.$updateContact(contact.did, { seesMe: visibility });
if (showSuccessAlert) {
this.$notify(
{
group: "alert",
type: "success",
title: "Visibility Set",
text:
(contact.name || "That user") +
" can " +
(visibility ? "" : "not ") +
"see your activity.",
},
3000,
);
const message =
(contact.name || "That user") +
" can " +
(visibility ? "" : "not ") +
"see your activity.";
this.notify.success(message, TIMEOUTS.SHORT);
}
return true;
}
@@ -796,15 +748,7 @@ export default class DIDView extends Vue {
encodeURIComponent(contact.did);
const headers = await getHeaders(this.activeDid);
if (!headers["Authorization"]) {
this.$notify(
{
group: "alert",
type: "danger",
title: "No Identity",
text: "There is no identity to use to check visibility.",
},
3000,
);
this.notify.error(NOTIFY_NO_IDENTITY_ERROR.message, TIMEOUTS.SHORT);
return;
}
@@ -814,48 +758,22 @@ export default class DIDView extends Vue {
const visibility = resp.data;
contact.seesMe = visibility;
//console.log("Visi check:", visibility, contact.seesMe, contact.did);
await this.$dbExec("UPDATE contacts SET seesMe = ? WHERE did = ?", [
visibility,
contact.did,
]);
await this.$updateContact(contact.did, { seesMe: visibility });
this.$notify(
{
group: "alert",
type: "info",
title: "Visibility Refreshed",
text:
libsUtil.nameForContact(contact, true) +
" can" +
(visibility ? "" : " not") +
" see your activity.",
},
3000,
);
const message =
libsUtil.nameForContact(contact, true) +
" can" +
(visibility ? "" : " not") +
" see your activity.";
this.notify.info(message, TIMEOUTS.SHORT);
} else {
logger.error("Got bad server response checking visibility:", resp);
const message = resp.data.error?.message || "Got bad server response.";
this.$notify(
{
group: "alert",
type: "danger",
title: "Error Checking Visibility",
text: message,
},
5000,
);
this.notify.error(message, TIMEOUTS.LONG);
}
} catch (err) {
logger.error("Caught error from request to check visibility:", err);
this.$notify(
{
group: "alert",
type: "danger",
title: "Error Checking Visibility",
text: "Check connectivity and try again.",
},
3000,
);
this.notify.error("Check connectivity and try again.", TIMEOUTS.SHORT);
}
}
@@ -869,21 +787,12 @@ export default class DIDView extends Vue {
const contentVisibilityPrompt = view
? "Are you sure you want to see their content?"
: "Are you sure you want to hide their content from you?";
this.$notify(
{
group: "modal",
type: "confirm",
title: "Set Content Visibility",
text: contentVisibilityPrompt,
onYes: async () => {
const success = await this.setViewContent(contact, view);
if (success) {
contact.iViewContent = view; // see visibility note about not working inside setVisibility
}
},
},
-1,
);
this.notify.confirm(contentVisibilityPrompt, async () => {
const success = await this.setViewContent(contact, view);
if (success) {
contact.iViewContent = view; // see visibility note about not working inside setVisibility
}
});
}
/**
@@ -894,22 +803,12 @@ export default class DIDView extends Vue {
* @returns Boolean indicating success
*/
async setViewContent(contact: Contact, visibility: boolean) {
await this.$dbExec("UPDATE contacts SET iViewContent = ? WHERE did = ?", [
visibility,
contact.did,
]);
this.$notify(
{
group: "alert",
type: "success",
title: "Visibility Set",
text:
"You will" +
(visibility ? "" : " not") +
` see ${contact.name}'s activity.`,
},
3000,
);
await this.$updateContact(contact.did, { iViewContent: visibility });
const message =
"You will" +
(visibility ? "" : " not") +
` see ${contact.name}'s activity.`;
this.notify.success(message, TIMEOUTS.SHORT);
return true;
}
}