export labels within contacts
This commit is contained in:
@@ -105,11 +105,9 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
|
|||||||
import { Router } from "vue-router";
|
import { Router } from "vue-router";
|
||||||
import * as R from "ramda";
|
import * as R from "ramda";
|
||||||
|
|
||||||
import { AppString, NotificationIface } from "../constants/app";
|
import { NotificationIface } from "../constants/app";
|
||||||
import { Contact } from "../db/tables/contacts";
|
|
||||||
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { contactsToExportJson } from "../libs/util";
|
|
||||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
|
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";
|
||||||
@@ -222,26 +220,15 @@ export default class DataExportSection extends Vue {
|
|||||||
return "list-disc list-outside ml-4";
|
return "list-disc list-outside ml-4";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computed property for the export file name
|
|
||||||
* Includes today's date for easy identification of backup files
|
|
||||||
*/
|
|
||||||
private get fileName(): string {
|
|
||||||
const today = new Date();
|
|
||||||
const dateString = today.toISOString().split("T")[0]; // YYYY-MM-DD format
|
|
||||||
return `${AppString.APP_NAME_NO_SPACES}-backup-contacts-${dateString}.json`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports the database to a JSON file
|
* Exports the database to a JSON file
|
||||||
|
* Exports contacts and contact labels tables
|
||||||
* Uses the platform service to handle platform-specific export logic
|
* Uses the platform service to handle platform-specific export logic
|
||||||
* Shows success/error notifications to user
|
* Shows success/error notifications to user
|
||||||
*
|
*
|
||||||
* @throws {Error} If export fails
|
* @throws {Error} If export fails
|
||||||
*/
|
*/
|
||||||
public async exportDatabase(): Promise<void> {
|
public async exportDatabase(): Promise<void> {
|
||||||
// Note that similar code is in ContactsView.vue exportContactData()
|
|
||||||
|
|
||||||
if (this.isExporting) {
|
if (this.isExporting) {
|
||||||
return; // Prevent multiple simultaneous exports
|
return; // Prevent multiple simultaneous exports
|
||||||
}
|
}
|
||||||
@@ -249,49 +236,11 @@ export default class DataExportSection extends Vue {
|
|||||||
try {
|
try {
|
||||||
this.isExporting = true;
|
this.isExporting = true;
|
||||||
|
|
||||||
// Fetch contacts from database using mixin's cached method
|
// Prepare export data using shared utility function
|
||||||
const allContacts = await this.$contacts();
|
await this.$saveContactExport();
|
||||||
|
|
||||||
// Convert contacts to export format
|
|
||||||
const processedContacts: Contact[] = allContacts.map((contact) => {
|
|
||||||
// first remove the contactMethods field, mostly to cast to a clear type (that will end up with JSON objects)
|
|
||||||
const exContact: Contact = R.omit(["contactMethods"], contact);
|
|
||||||
// now add contactMethods as a true array of ContactMethod objects
|
|
||||||
// $contacts() returns normalized contacts where contactMethods is already an array,
|
|
||||||
// but we handle both array and string cases for robustness
|
|
||||||
if (contact.contactMethods) {
|
|
||||||
if (Array.isArray(contact.contactMethods)) {
|
|
||||||
// Already an array, use it directly
|
|
||||||
exContact.contactMethods = contact.contactMethods;
|
|
||||||
} else {
|
|
||||||
// Check if it's a string that needs parsing (shouldn't happen with normalized contacts, but handle for robustness)
|
|
||||||
const contactMethodsValue = contact.contactMethods as unknown;
|
|
||||||
if (
|
|
||||||
typeof contactMethodsValue === "string" &&
|
|
||||||
contactMethodsValue.trim() !== ""
|
|
||||||
) {
|
|
||||||
// String that needs parsing
|
|
||||||
exContact.contactMethods = JSON.parse(contactMethodsValue);
|
|
||||||
} else {
|
|
||||||
// Invalid data, use empty array
|
|
||||||
exContact.contactMethods = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No contactMethods, use empty array
|
|
||||||
exContact.contactMethods = [];
|
|
||||||
}
|
|
||||||
return exContact;
|
|
||||||
});
|
|
||||||
|
|
||||||
const exportData = contactsToExportJson(processedContacts);
|
|
||||||
const jsonStr = JSON.stringify(exportData, null, 2);
|
|
||||||
|
|
||||||
// Use platform service to handle export (no platform-specific logic here!)
|
|
||||||
await this.platformService.writeAndShareFile(this.fileName, jsonStr);
|
|
||||||
|
|
||||||
this.notify.success(
|
this.notify.success(
|
||||||
"Contact export completed successfully. Check your downloads or share dialog.",
|
"Contact export completed successfully. Check downloads or the share dialog.",
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Export Error:", error);
|
logger.error("Export Error:", error);
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export type ContactMaybeWithJsonStrings = Omit<Contact, "contactMethods"> & {
|
|||||||
contactMethods?: string | Array<ContactMethod>;
|
contactMethods?: string | Array<ContactMethod>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ContactWithLabels = Contact & { labels?: Array<string> };
|
||||||
|
|
||||||
export const ContactSchema = {
|
export const ContactSchema = {
|
||||||
contacts: "&did, name", // no need to key by other things
|
contacts: "&did, name", // no need to key by other things
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -941,43 +941,6 @@ export const csvLineToContact = (lineRaw: string): Contact => {
|
|||||||
return newContact;
|
return newContact;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for the JSON export format of database tables
|
|
||||||
*/
|
|
||||||
export interface TableExportData {
|
|
||||||
tableName: string;
|
|
||||||
rows: Array<Record<string, unknown>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for the complete database export format
|
|
||||||
*/
|
|
||||||
export interface DatabaseExport {
|
|
||||||
data: {
|
|
||||||
data: Array<TableExportData>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an array of contacts to the export JSON format.
|
|
||||||
* This format is used for data migration and backup purposes.
|
|
||||||
*
|
|
||||||
* @param contacts - Array of Contact objects to convert
|
|
||||||
* @returns DatabaseExport object in the standardized format
|
|
||||||
*/
|
|
||||||
export const contactsToExportJson = (contacts: Contact[]): DatabaseExport => {
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
tableName: "contacts",
|
|
||||||
rows: contacts,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports an account from a mnemonic phrase
|
* Imports an account from a mnemonic phrase
|
||||||
* @param mnemonic - The seed phrase to import from
|
* @param mnemonic - The seed phrase to import from
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
* @updated 2025-06-25 - Added high-level entity operations for code reduction
|
* @updated 2025-06-25 - Added high-level entity operations for code reduction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as R from "ramda";
|
||||||
|
|
||||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
import type {
|
import type {
|
||||||
PlatformService,
|
PlatformService,
|
||||||
@@ -49,7 +51,11 @@ import {
|
|||||||
type SettingsWithJsonStrings,
|
type SettingsWithJsonStrings,
|
||||||
} from "@/db/tables/settings";
|
} from "@/db/tables/settings";
|
||||||
import { logger } from "@/utils/logger";
|
import { logger } from "@/utils/logger";
|
||||||
import { Contact, ContactMaybeWithJsonStrings } from "@/db/tables/contacts";
|
import {
|
||||||
|
Contact,
|
||||||
|
ContactMaybeWithJsonStrings,
|
||||||
|
ContactWithLabels,
|
||||||
|
} from "@/db/tables/contacts";
|
||||||
import { Account } from "@/db/tables/accounts";
|
import { Account } from "@/db/tables/accounts";
|
||||||
import { Temp } from "@/db/tables/temp";
|
import { Temp } from "@/db/tables/temp";
|
||||||
import {
|
import {
|
||||||
@@ -61,6 +67,7 @@ import {
|
|||||||
generateInsertStatement,
|
generateInsertStatement,
|
||||||
generateUpdateStatement,
|
generateUpdateStatement,
|
||||||
} from "@/utils/sqlHelpers";
|
} from "@/utils/sqlHelpers";
|
||||||
|
import { AppString } from "../constants/app";
|
||||||
|
|
||||||
// =================================================
|
// =================================================
|
||||||
// TYPESCRIPT INTERFACES
|
// TYPESCRIPT INTERFACES
|
||||||
@@ -86,6 +93,23 @@ interface VueComponentWithMixin {
|
|||||||
platformService(): PlatformService;
|
platformService(): PlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the JSON export format of database tables
|
||||||
|
*/
|
||||||
|
export interface TableExportData {
|
||||||
|
tableName: string;
|
||||||
|
rows: Array<Record<string, unknown>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the complete database export format
|
||||||
|
*/
|
||||||
|
export interface DatabaseExport {
|
||||||
|
data: {
|
||||||
|
data: Array<TableExportData>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Global cache store for mixin instances
|
// * Global cache store for mixin instances
|
||||||
// * Uses WeakMap to avoid memory leaks when components are destroyed
|
// * Uses WeakMap to avoid memory leaks when components are destroyed
|
||||||
@@ -1501,7 +1525,7 @@ export const PlatformServiceMixin = {
|
|||||||
* @param did Contact DID
|
* @param did Contact DID
|
||||||
* @returns Promise<string[]> Array of labels
|
* @returns Promise<string[]> Array of labels
|
||||||
*/
|
*/
|
||||||
async $getContactLabels(did: string): Promise<string[]> {
|
async $getContactLabelsForDid(did: string): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const results = (await this.$dbQuery(
|
const results = (await this.$dbQuery(
|
||||||
"SELECT label FROM contact_labels WHERE did = ? ORDER BY label",
|
"SELECT label FROM contact_labels WHERE did = ? ORDER BY label",
|
||||||
@@ -1952,6 +1976,133 @@ export const PlatformServiceMixin = {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of contacts to the export JSON format.
|
||||||
|
* This format is used for data migration and backup purposes.
|
||||||
|
*
|
||||||
|
* @param contacts - Array of Contact objects to convert
|
||||||
|
* @returns DatabaseExport object in the standardized format
|
||||||
|
*/
|
||||||
|
$contactsToExportJson(contacts: ContactWithLabels[]): DatabaseExport {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
tableName: "contacts",
|
||||||
|
rows: contacts,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares contact and label data for export to a JSON file.
|
||||||
|
* Handles normalization of contact data and generates a timestamped filename.
|
||||||
|
*
|
||||||
|
* @param appName - Application name for filename (defaults to "TimeSafari")
|
||||||
|
* @returns Object containing the JSON string and filename
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const contacts = await $contacts();
|
||||||
|
* const labelsResult = await $dbQuery("SELECT did, label FROM contact_labels");
|
||||||
|
* const labels = mapQueryResultToValues(labelsResult);
|
||||||
|
* const { jsonString, fileName } = saveContactExport(contacts, labels);
|
||||||
|
* await platformService.writeAndShareFile(fileName, jsonString);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async $saveContactExport(): Promise<string> {
|
||||||
|
const contacts = await this.$contacts();
|
||||||
|
|
||||||
|
// Fetch all contact labels from database
|
||||||
|
const labelsResult = await this.$dbQuery(
|
||||||
|
"SELECT did, label FROM contact_labels ORDER BY did, label",
|
||||||
|
);
|
||||||
|
// create a map of did to labels
|
||||||
|
const contactToLabelsMap = new Map<string, string[]>();
|
||||||
|
// iterate over the labelsResult and accumulate the labels for each did
|
||||||
|
labelsResult?.values?.forEach((contactLabel: [string, string]) => {
|
||||||
|
const did = contactLabel[0];
|
||||||
|
const label = contactLabel[1];
|
||||||
|
if (!contactToLabelsMap.has(did)) {
|
||||||
|
contactToLabelsMap.set(did, []);
|
||||||
|
}
|
||||||
|
contactToLabelsMap.get(did)?.push(label);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process contacts to normalize contactMethods field
|
||||||
|
// Handle both array format (from normalized contacts) and string format (legacy/database)
|
||||||
|
const processedContacts: ContactWithLabels[] = contacts.map((contact) => {
|
||||||
|
// Remove contactMethods field temporarily to get a clean type
|
||||||
|
const exContact: ContactWithLabels = R.omit(
|
||||||
|
["contactMethods"],
|
||||||
|
contact,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add contactMethods as a proper array of ContactMethod objects
|
||||||
|
if (contact.contactMethods) {
|
||||||
|
if (Array.isArray(contact.contactMethods)) {
|
||||||
|
// Already an array, use it directly
|
||||||
|
exContact.contactMethods = contact.contactMethods;
|
||||||
|
} else {
|
||||||
|
// Check if it's a string that needs parsing
|
||||||
|
const contactMethodsValue = contact.contactMethods as unknown;
|
||||||
|
if (
|
||||||
|
typeof contactMethodsValue === "string" &&
|
||||||
|
contactMethodsValue.trim() !== ""
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// String that needs parsing
|
||||||
|
exContact.contactMethods = JSON.parse(contactMethodsValue);
|
||||||
|
} catch (error) {
|
||||||
|
// Invalid JSON, use empty array
|
||||||
|
logger.warn(
|
||||||
|
`Invalid contactMethods JSON for contact ${contact.did}:`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
exContact.contactMethods = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Invalid data, use empty array
|
||||||
|
exContact.contactMethods = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No contactMethods, use empty array
|
||||||
|
exContact.contactMethods = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the labels to the contact
|
||||||
|
exContact.labels = contactToLabelsMap.get(contact.did) || [];
|
||||||
|
|
||||||
|
return exContact;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build export data with contacts
|
||||||
|
const exportData = this.$contactsToExportJson(processedContacts);
|
||||||
|
|
||||||
|
// Generate JSON string
|
||||||
|
const jsonString = JSON.stringify(exportData, null, 2);
|
||||||
|
|
||||||
|
// Generate filename with current date
|
||||||
|
const today = new Date();
|
||||||
|
const dateString = today.toISOString().split("T")[0]; // YYYY-MM-DD format
|
||||||
|
const appName = AppString.APP_NAME_NO_SPACES;
|
||||||
|
const fileName = `${appName}-backup-contacts-${dateString}.json`;
|
||||||
|
|
||||||
|
// Use platform service to handle export
|
||||||
|
await (
|
||||||
|
this as unknown as IPlatformServiceMixin
|
||||||
|
).platformService.writeAndShareFile(fileName, jsonString);
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
},
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// DEBUGGING
|
||||||
|
// =================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug method to verify settings for a specific DID
|
* Debug method to verify settings for a specific DID
|
||||||
* Useful for troubleshooting settings propagation issues
|
* Useful for troubleshooting settings propagation issues
|
||||||
@@ -2086,7 +2237,7 @@ export interface IPlatformServiceMixin {
|
|||||||
$getContact(did: string): Promise<Contact | null>;
|
$getContact(did: string): Promise<Contact | null>;
|
||||||
$deleteContact(did: string): Promise<boolean>;
|
$deleteContact(did: string): Promise<boolean>;
|
||||||
$contactCount(): Promise<number>;
|
$contactCount(): Promise<number>;
|
||||||
$getContactLabels(did: string): Promise<string[]>;
|
$getContactLabelsForDid(did: string): Promise<string[]>;
|
||||||
$getContactIdsWithAllLabels(labels: string[]): Promise<string[]>;
|
$getContactIdsWithAllLabels(labels: string[]): Promise<string[]>;
|
||||||
$addContactLabel(did: string, label: string): Promise<boolean>;
|
$addContactLabel(did: string, label: string): Promise<boolean>;
|
||||||
$deleteContactLabel(did: string, label: string): Promise<boolean>;
|
$deleteContactLabel(did: string, label: string): Promise<boolean>;
|
||||||
@@ -2146,6 +2297,9 @@ export interface IPlatformServiceMixin {
|
|||||||
values: unknown[][],
|
values: unknown[][],
|
||||||
): Array<Record<string, unknown>>;
|
): Array<Record<string, unknown>>;
|
||||||
|
|
||||||
|
// Contact export methods
|
||||||
|
$saveContactExport(): Promise<string>;
|
||||||
|
|
||||||
// Debug methods
|
// Debug methods
|
||||||
$debugDidSettings(did: string): Promise<Settings | null>;
|
$debugDidSettings(did: string): Promise<Settings | null>;
|
||||||
$debugMergedSettings(did: string): Promise<void>;
|
$debugMergedSettings(did: string): Promise<void>;
|
||||||
@@ -2233,7 +2387,7 @@ declare module "@vue/runtime-core" {
|
|||||||
$getContact(did: string): Promise<Contact | null>;
|
$getContact(did: string): Promise<Contact | null>;
|
||||||
$deleteContact(did: string): Promise<boolean>;
|
$deleteContact(did: string): Promise<boolean>;
|
||||||
$contactCount(): Promise<number>;
|
$contactCount(): Promise<number>;
|
||||||
$getContactLabels(did: string): Promise<string[]>;
|
$getContactLabelsForDid(did: string): Promise<string[]>;
|
||||||
$getContactIdsWithAllLabels(labels: string[]): Promise<string[]>;
|
$getContactIdsWithAllLabels(labels: string[]): Promise<string[]>;
|
||||||
$addContactLabel(did: string, label: string): Promise<boolean>;
|
$addContactLabel(did: string, label: string): Promise<boolean>;
|
||||||
$deleteContactLabel(did: string, label: string): Promise<boolean>;
|
$deleteContactLabel(did: string, label: string): Promise<boolean>;
|
||||||
@@ -2293,6 +2447,9 @@ declare module "@vue/runtime-core" {
|
|||||||
values: unknown[][],
|
values: unknown[][],
|
||||||
): Array<Record<string, unknown>>;
|
): Array<Record<string, unknown>>;
|
||||||
|
|
||||||
|
// Contact export methods
|
||||||
|
$saveContactExport(): Promise<string>;
|
||||||
|
|
||||||
// Debug methods
|
// Debug methods
|
||||||
$debugDidSettings(did: string): Promise<Settings | null>;
|
$debugDidSettings(did: string): Promise<Settings | null>;
|
||||||
$debugMergedSettings(did: string): Promise<void>;
|
$debugMergedSettings(did: string): Promise<void>;
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ export default class ContactEditView extends Vue {
|
|||||||
this.contactMethods = contact.contactMethods || [];
|
this.contactMethods = contact.contactMethods || [];
|
||||||
|
|
||||||
// Load labels
|
// Load labels
|
||||||
const labels = await this.$getContactLabels(contactDid);
|
const labels = await this.$getContactLabelsForDid(contactDid);
|
||||||
this.contactLabels = labels;
|
this.contactLabels = labels;
|
||||||
this.originalLabels = [...labels];
|
this.originalLabels = [...labels];
|
||||||
|
|
||||||
|
|||||||
@@ -226,10 +226,7 @@ import {
|
|||||||
VerifiableCredential,
|
VerifiableCredential,
|
||||||
} from "@/interfaces";
|
} from "@/interfaces";
|
||||||
import * as libsUtil from "../libs/util";
|
import * as libsUtil from "../libs/util";
|
||||||
import {
|
import { generateSaveAndActivateIdentity } from "../libs/util";
|
||||||
generateSaveAndActivateIdentity,
|
|
||||||
contactsToExportJson,
|
|
||||||
} from "../libs/util";
|
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
// No longer needed - using PlatformServiceMixin methods
|
// No longer needed - using PlatformServiceMixin methods
|
||||||
// import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
// import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||||
@@ -1455,28 +1452,17 @@ export default class ContactsView extends Vue {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Export contact data to JSON file
|
* Export contact data to JSON file
|
||||||
|
* Exports contacts and contact labels tables
|
||||||
* Uses platform service to handle platform-specific export logic
|
* Uses platform service to handle platform-specific export logic
|
||||||
*/
|
*/
|
||||||
private async exportContactData(): Promise<void> {
|
private async exportContactData(): Promise<void> {
|
||||||
// Note that similar code is in DataExportSection.vue exportDatabase()
|
|
||||||
try {
|
try {
|
||||||
// Fetch all contacts from database
|
// Fetch all contacts from database
|
||||||
const allContacts = await this.$contacts();
|
// Prepare export data using shared utility function
|
||||||
|
await this.$saveContactExport();
|
||||||
// Convert contacts to export format
|
|
||||||
const exportData = contactsToExportJson(allContacts);
|
|
||||||
const jsonStr = JSON.stringify(exportData, null, 2);
|
|
||||||
|
|
||||||
// Generate filename with current date
|
|
||||||
const today = new Date();
|
|
||||||
const dateString = today.toISOString().split("T")[0]; // YYYY-MM-DD format
|
|
||||||
const fileName = `timesafari-backup-contacts-${dateString}.json`;
|
|
||||||
|
|
||||||
// Use platform service to handle export
|
|
||||||
await this.platformService.writeAndShareFile(fileName, jsonStr);
|
|
||||||
|
|
||||||
this.notify.success(
|
this.notify.success(
|
||||||
"Contact export completed successfully. Check your downloads or share dialog.",
|
"Contact export completed successfully. Check downloads or the share dialog.",
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Export Error:", error);
|
logger.error("Export Error:", error);
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ export default class DIDView extends Vue {
|
|||||||
this.contactYaml = yaml.dump(this.contactFromDid);
|
this.contactYaml = yaml.dump(this.contactFromDid);
|
||||||
|
|
||||||
// Load labels for this contact
|
// Load labels for this contact
|
||||||
this.contactLabels = await this.$getContactLabels(this.viewingDid);
|
this.contactLabels = await this.$getContactLabelsForDid(this.viewingDid);
|
||||||
} else {
|
} else {
|
||||||
this.contactFromDid = undefined;
|
this.contactFromDid = undefined;
|
||||||
this.contactYaml = "";
|
this.contactYaml = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user