IndexedDB migration: fix loading of data, fix object comparisons, add unmodified, etc
This commit is contained in:
@@ -19,6 +19,10 @@ export interface Contact {
|
|||||||
registered?: boolean; // cached value of the server setting
|
registered?: boolean; // cached value of the server setting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ContactWithJsonStrings = Contact & {
|
||||||
|
contactMethods?: 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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ export type Settings = {
|
|||||||
webPushServer?: string; // Web Push server URL
|
webPushServer?: string; // Web Push server URL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// type of settings where the searchBoxes are JSON strings instead of objects
|
||||||
|
export type SettingsWithJsonStrings = Settings & {
|
||||||
|
searchBoxes: string;
|
||||||
|
};
|
||||||
|
|
||||||
export function checkIsAnyFeedFilterOn(settings: Settings): boolean {
|
export function checkIsAnyFeedFilterOn(settings: Settings): boolean {
|
||||||
return !!(settings?.filterFeedByNearby || settings?.filterFeedByVisible);
|
return !!(settings?.filterFeedByNearby || settings?.filterFeedByVisible);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ import "dexie-export-import";
|
|||||||
import { PlatformServiceFactory } from "./PlatformServiceFactory";
|
import { PlatformServiceFactory } from "./PlatformServiceFactory";
|
||||||
import { db, accountsDBPromise } from "../db/index";
|
import { db, accountsDBPromise } from "../db/index";
|
||||||
import { Contact, ContactMethod } from "../db/tables/contacts";
|
import { Contact, ContactMethod } from "../db/tables/contacts";
|
||||||
import { Settings, MASTER_SETTINGS_KEY } from "../db/tables/settings";
|
import { Settings, MASTER_SETTINGS_KEY, SettingsWithJsonStrings, BoundingBox } from "../db/tables/settings";
|
||||||
import { Account } from "../db/tables/accounts";
|
import { Account, AccountEncrypted } from "../db/tables/accounts";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { parseJsonField } from "../db/databaseUtil";
|
import { mapColumnsToValues, parseJsonField } from "../db/databaseUtil";
|
||||||
import { importFromMnemonic } from "../libs/util";
|
import { importFromMnemonic } from "../libs/util";
|
||||||
|
import { IIdentifier } from "@veramo/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for data comparison results between Dexie and SQLite databases
|
* Interface for data comparison results between Dexie and SQLite databases
|
||||||
@@ -66,22 +67,24 @@ export interface DataComparison {
|
|||||||
dexieSettings: Settings[];
|
dexieSettings: Settings[];
|
||||||
sqliteSettings: Settings[];
|
sqliteSettings: Settings[];
|
||||||
dexieAccounts: Account[];
|
dexieAccounts: Account[];
|
||||||
sqliteAccounts: Account[];
|
sqliteAccounts: string[];
|
||||||
differences: {
|
differences: {
|
||||||
contacts: {
|
contacts: {
|
||||||
added: Contact[];
|
added: Contact[];
|
||||||
modified: Contact[];
|
modified: Contact[];
|
||||||
|
unmodified: Contact[];
|
||||||
missing: Contact[];
|
missing: Contact[];
|
||||||
};
|
};
|
||||||
settings: {
|
settings: {
|
||||||
added: Settings[];
|
added: Settings[];
|
||||||
modified: Settings[];
|
modified: Settings[];
|
||||||
|
unmodified: Settings[];
|
||||||
missing: Settings[];
|
missing: Settings[];
|
||||||
};
|
};
|
||||||
accounts: {
|
accounts: {
|
||||||
added: Account[];
|
added: Account[];
|
||||||
modified: Account[];
|
unmodified: Account[];
|
||||||
missing: Account[];
|
missing: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -184,22 +187,14 @@ export async function getSqliteContacts(): Promise<Contact[]> {
|
|||||||
|
|
||||||
let contacts: Contact[] = [];
|
let contacts: Contact[] = [];
|
||||||
if (result?.values?.length) {
|
if (result?.values?.length) {
|
||||||
contacts = result.values.map((row) => {
|
const preContacts = mapColumnsToValues(result.columns, result.values) as unknown as Contact[];
|
||||||
const contact = parseJsonField(row, {}) as Contact;
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
||||||
return {
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
||||||
did: contact.did || "",
|
contacts = preContacts.map((contact) => {
|
||||||
name: contact.name || "",
|
if (contact.contactMethods) {
|
||||||
contactMethods: parseJsonField(
|
contact.contactMethods = parseJsonField(contact.contactMethods, []) as ContactMethod[];
|
||||||
contact.contactMethods,
|
}
|
||||||
[],
|
return contact;
|
||||||
) as ContactMethod[],
|
|
||||||
nextPubKeyHashB64: contact.nextPubKeyHashB64 || "",
|
|
||||||
notes: contact.notes || "",
|
|
||||||
profileImageUrl: contact.profileImageUrl || "",
|
|
||||||
publicKeyBase64: contact.publicKeyBase64 || "",
|
|
||||||
seesMe: contact.seesMe || false,
|
|
||||||
registered: contact.registered || false,
|
|
||||||
} as Contact;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,37 +276,16 @@ export async function getSqliteSettings(): Promise<Settings[]> {
|
|||||||
|
|
||||||
let settings: Settings[] = [];
|
let settings: Settings[] = [];
|
||||||
if (result?.values?.length) {
|
if (result?.values?.length) {
|
||||||
settings = result.values.map((row) => {
|
const presettings =
|
||||||
const setting = parseJsonField(row, {}) as Settings;
|
mapColumnsToValues(result.columns, result.values) as Settings[];
|
||||||
return {
|
// This is redundant since absurd-sql auto-parses JSON strings to objects.
|
||||||
id: setting.id,
|
// But we started it, and it should be known everywhere, so we're keeping it.
|
||||||
accountDid: setting.accountDid || "",
|
settings = presettings.map((setting) => {
|
||||||
activeDid: setting.activeDid || "",
|
if (setting.searchBoxes) {
|
||||||
apiServer: setting.apiServer || "",
|
setting.searchBoxes = parseJsonField(setting.searchBoxes, []) as Array<{ name: string, bbox: BoundingBox }>;
|
||||||
filterFeedByNearby: setting.filterFeedByNearby || false,
|
}
|
||||||
filterFeedByVisible: setting.filterFeedByVisible || false,
|
return setting;
|
||||||
finishedOnboarding: setting.finishedOnboarding || false,
|
})
|
||||||
firstName: setting.firstName || "",
|
|
||||||
hideRegisterPromptOnNewContact: setting.hideRegisterPromptOnNewContact || false,
|
|
||||||
isRegistered: setting.isRegistered || false,
|
|
||||||
lastName: setting.lastName || "",
|
|
||||||
lastAckedOfferToUserJwtId: setting.lastAckedOfferToUserJwtId || "",
|
|
||||||
lastAckedOfferToUserProjectsJwtId: setting.lastAckedOfferToUserProjectsJwtId || "",
|
|
||||||
lastNotifiedClaimId: setting.lastNotifiedClaimId || "",
|
|
||||||
lastViewedClaimId: setting.lastViewedClaimId || "",
|
|
||||||
notifyingNewActivityTime: setting.notifyingNewActivityTime || "",
|
|
||||||
notifyingReminderMessage: setting.notifyingReminderMessage || "",
|
|
||||||
notifyingReminderTime: setting.notifyingReminderTime || "",
|
|
||||||
partnerApiServer: setting.partnerApiServer || "",
|
|
||||||
passkeyExpirationMinutes: setting.passkeyExpirationMinutes,
|
|
||||||
profileImageUrl: setting.profileImageUrl || "",
|
|
||||||
searchBoxes: parseJsonField(setting.searchBoxes, []),
|
|
||||||
showContactGivesInline: setting.showContactGivesInline || false,
|
|
||||||
showGeneralAdvanced: setting.showGeneralAdvanced || false,
|
|
||||||
showShortcutBvc: setting.showShortcutBvc || false,
|
|
||||||
vapid: setting.vapid || "",
|
|
||||||
} as Settings;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -336,7 +310,7 @@ export async function getSqliteSettings(): Promise<Settings[]> {
|
|||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
* @function getSqliteAccounts
|
* @function getSqliteAccounts
|
||||||
* @returns {Promise<Account[]>} Array of all accounts from SQLite database
|
* @returns {Promise<string[]>} Array of all accounts from SQLite database
|
||||||
* @throws {Error} If database query fails or data conversion fails
|
* @throws {Error} If database query fails or data conversion fails
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
@@ -348,32 +322,20 @@ export async function getSqliteSettings(): Promise<Settings[]> {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export async function getSqliteAccounts(): Promise<Account[]> {
|
export async function getSqliteAccounts(): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const platformService = PlatformServiceFactory.getInstance();
|
const platformService = PlatformServiceFactory.getInstance();
|
||||||
const result = await platformService.dbQuery("SELECT * FROM accounts");
|
const result = await platformService.dbQuery("SELECT did FROM accounts");
|
||||||
|
|
||||||
let accounts: Account[] = [];
|
let dids: string[] = [];
|
||||||
if (result?.values?.length) {
|
if (result?.values?.length) {
|
||||||
accounts = result.values.map((row) => {
|
dids = result.values.map((row) => row[0] as string);
|
||||||
const account = parseJsonField(row, {}) as Account;
|
|
||||||
return {
|
|
||||||
id: account.id,
|
|
||||||
dateCreated: account.dateCreated || "",
|
|
||||||
derivationPath: account.derivationPath || "",
|
|
||||||
did: account.did || "",
|
|
||||||
identity: account.identity || "",
|
|
||||||
mnemonic: account.mnemonic || "",
|
|
||||||
passkeyCredIdHex: account.passkeyCredIdHex || "",
|
|
||||||
publicKeyHex: account.publicKeyHex || "",
|
|
||||||
} as Account;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`[MigrationService] Retrieved ${accounts.length} accounts from SQLite`,
|
`[MigrationService] Retrieved ${dids.length} accounts from SQLite`,
|
||||||
);
|
);
|
||||||
return accounts;
|
return dids;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("[MigrationService] Error retrieving SQLite accounts:", error);
|
logger.error("[MigrationService] Error retrieving SQLite accounts:", error);
|
||||||
throw new Error(`Failed to retrieve SQLite accounts: ${error}`);
|
throw new Error(`Failed to retrieve SQLite accounts: ${error}`);
|
||||||
@@ -532,6 +494,7 @@ export async function compareDatabases(): Promise<DataComparison> {
|
|||||||
function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) {
|
function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) {
|
||||||
const added: Contact[] = [];
|
const added: Contact[] = [];
|
||||||
const modified: Contact[] = [];
|
const modified: Contact[] = [];
|
||||||
|
const unmodified: Contact[] = [];
|
||||||
const missing: Contact[] = [];
|
const missing: Contact[] = [];
|
||||||
|
|
||||||
// Find contacts that exist in Dexie but not in SQLite
|
// Find contacts that exist in Dexie but not in SQLite
|
||||||
@@ -543,6 +506,8 @@ function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) {
|
|||||||
added.push(dexieContact);
|
added.push(dexieContact);
|
||||||
} else if (!contactsEqual(dexieContact, sqliteContact)) {
|
} else if (!contactsEqual(dexieContact, sqliteContact)) {
|
||||||
modified.push(dexieContact);
|
modified.push(dexieContact);
|
||||||
|
} else {
|
||||||
|
unmodified.push(dexieContact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,7 +519,7 @@ function compareContacts(dexieContacts: Contact[], sqliteContacts: Contact[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { added, modified, missing };
|
return { added, modified, unmodified, missing };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -588,27 +553,30 @@ function compareSettings(
|
|||||||
) {
|
) {
|
||||||
const added: Settings[] = [];
|
const added: Settings[] = [];
|
||||||
const modified: Settings[] = [];
|
const modified: Settings[] = [];
|
||||||
|
const unmodified: Settings[] = [];
|
||||||
const missing: Settings[] = [];
|
const missing: Settings[] = [];
|
||||||
|
|
||||||
// Find settings that exist in Dexie but not in SQLite
|
// Find settings that exist in Dexie but not in SQLite
|
||||||
for (const dexieSetting of dexieSettings) {
|
for (const dexieSetting of dexieSettings) {
|
||||||
const sqliteSetting = sqliteSettings.find((s) => s.id === dexieSetting.id);
|
const sqliteSetting = sqliteSettings.find((s) => s.accountDid === dexieSetting.accountDid);
|
||||||
if (!sqliteSetting) {
|
if (!sqliteSetting) {
|
||||||
added.push(dexieSetting);
|
added.push(dexieSetting);
|
||||||
} else if (!settingsEqual(dexieSetting, sqliteSetting)) {
|
} else if (!settingsEqual(dexieSetting, sqliteSetting)) {
|
||||||
modified.push(dexieSetting);
|
modified.push(dexieSetting);
|
||||||
|
} else {
|
||||||
|
unmodified.push(dexieSetting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find settings that exist in SQLite but not in Dexie
|
// Find settings that exist in SQLite but not in Dexie
|
||||||
for (const sqliteSetting of sqliteSettings) {
|
for (const sqliteSetting of sqliteSettings) {
|
||||||
const dexieSetting = dexieSettings.find((s) => s.id === sqliteSetting.id);
|
const dexieSetting = dexieSettings.find((s) => s.accountDid === sqliteSetting.accountDid);
|
||||||
if (!dexieSetting) {
|
if (!dexieSetting) {
|
||||||
missing.push(sqliteSetting);
|
missing.push(sqliteSetting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { added, modified, missing };
|
return { added, modified, unmodified, missing };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -623,11 +591,11 @@ function compareSettings(
|
|||||||
*
|
*
|
||||||
* @function compareAccounts
|
* @function compareAccounts
|
||||||
* @param {Account[]} dexieAccounts - Accounts from Dexie database
|
* @param {Account[]} dexieAccounts - Accounts from Dexie database
|
||||||
* @param {Account[]} sqliteAccounts - Accounts from SQLite database
|
* @param {Account[]} sqliteDids - Accounts from SQLite database
|
||||||
* @returns {Object} Object containing added, modified, and missing accounts
|
* @returns {Object} Object containing added, modified, and missing accounts
|
||||||
* @returns {Account[]} returns.added - Accounts in Dexie but not SQLite
|
* @returns {Account[]} returns.added - Accounts in Dexie but not SQLite
|
||||||
* @returns {Account[]} returns.modified - Accounts that differ between databases
|
* @returns {Account[]} returns.modified - always 0 because we don't check
|
||||||
* @returns {Account[]} returns.missing - Accounts in SQLite but not Dexie
|
* @returns {string[]} returns.missing - Accounts in SQLite but not Dexie
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* const differences = compareAccounts(dexieAccounts, sqliteAccounts);
|
* const differences = compareAccounts(dexieAccounts, sqliteAccounts);
|
||||||
@@ -636,30 +604,30 @@ function compareSettings(
|
|||||||
* console.log(`Missing: ${differences.missing.length}`);
|
* console.log(`Missing: ${differences.missing.length}`);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function compareAccounts(dexieAccounts: Account[], sqliteAccounts: Account[]) {
|
function compareAccounts(dexieAccounts: Account[], sqliteDids: string[]) {
|
||||||
const added: Account[] = [];
|
const added: Account[] = [];
|
||||||
const modified: Account[] = [];
|
const unmodified: Account[] = [];
|
||||||
const missing: Account[] = [];
|
const missing: string[] = [];
|
||||||
|
|
||||||
// Find accounts that exist in Dexie but not in SQLite
|
// Find accounts that exist in Dexie but not in SQLite
|
||||||
for (const dexieAccount of dexieAccounts) {
|
for (const dexieAccount of dexieAccounts) {
|
||||||
const sqliteAccount = sqliteAccounts.find((a) => a.id === dexieAccount.id);
|
const sqliteDid = sqliteDids.find((a) => a === dexieAccount.did);
|
||||||
if (!sqliteAccount) {
|
if (!sqliteDid) {
|
||||||
added.push(dexieAccount);
|
added.push(dexieAccount);
|
||||||
} else if (!accountsEqual(dexieAccount, sqliteAccount)) {
|
} else {
|
||||||
modified.push(dexieAccount);
|
unmodified.push(dexieAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find accounts that exist in SQLite but not in Dexie
|
// Find accounts that exist in SQLite but not in Dexie
|
||||||
for (const sqliteAccount of sqliteAccounts) {
|
for (const sqliteDid of sqliteDids) {
|
||||||
const dexieAccount = dexieAccounts.find((a) => a.id === sqliteAccount.id);
|
const dexieAccount = dexieAccounts.find((a) => a.did === sqliteDid);
|
||||||
if (!dexieAccount) {
|
if (!dexieAccount) {
|
||||||
missing.push(sqliteAccount);
|
missing.push(sqliteDid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { added, modified, missing };
|
return { added, unmodified, missing };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -688,15 +656,15 @@ function compareAccounts(dexieAccounts: Account[], sqliteAccounts: Account[]) {
|
|||||||
*/
|
*/
|
||||||
function contactsEqual(contact1: Contact, contact2: Contact): boolean {
|
function contactsEqual(contact1: Contact, contact2: Contact): boolean {
|
||||||
return (
|
return (
|
||||||
contact1.did === contact2.did &&
|
contact1.did == contact2.did &&
|
||||||
contact1.name === contact2.name &&
|
contact1.name == contact2.name &&
|
||||||
contact1.notes === contact2.notes &&
|
contact1.notes == contact2.notes &&
|
||||||
contact1.profileImageUrl === contact2.profileImageUrl &&
|
contact1.profileImageUrl == contact2.profileImageUrl &&
|
||||||
contact1.publicKeyBase64 === contact2.publicKeyBase64 &&
|
contact1.publicKeyBase64 == contact2.publicKeyBase64 &&
|
||||||
contact1.nextPubKeyHashB64 === contact2.nextPubKeyHashB64 &&
|
contact1.nextPubKeyHashB64 == contact2.nextPubKeyHashB64 &&
|
||||||
contact1.seesMe === contact2.seesMe &&
|
contact1.seesMe == contact2.seesMe &&
|
||||||
contact1.registered === contact2.registered &&
|
contact1.registered == contact2.registered &&
|
||||||
JSON.stringify(contact1.contactMethods) ===
|
JSON.stringify(contact1.contactMethods) ==
|
||||||
JSON.stringify(contact2.contactMethods)
|
JSON.stringify(contact2.contactMethods)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -727,38 +695,38 @@ function contactsEqual(contact1: Contact, contact2: Contact): boolean {
|
|||||||
*/
|
*/
|
||||||
function settingsEqual(settings1: Settings, settings2: Settings): boolean {
|
function settingsEqual(settings1: Settings, settings2: Settings): boolean {
|
||||||
return (
|
return (
|
||||||
settings1.id === settings2.id &&
|
settings1.id == settings2.id &&
|
||||||
settings1.accountDid === settings2.accountDid &&
|
settings1.accountDid == settings2.accountDid &&
|
||||||
settings1.activeDid === settings2.activeDid &&
|
settings1.activeDid == settings2.activeDid &&
|
||||||
settings1.apiServer === settings2.apiServer &&
|
settings1.apiServer == settings2.apiServer &&
|
||||||
settings1.filterFeedByNearby === settings2.filterFeedByNearby &&
|
settings1.filterFeedByNearby == settings2.filterFeedByNearby &&
|
||||||
settings1.filterFeedByVisible === settings2.filterFeedByVisible &&
|
settings1.filterFeedByVisible == settings2.filterFeedByVisible &&
|
||||||
settings1.finishedOnboarding === settings2.finishedOnboarding &&
|
settings1.finishedOnboarding == settings2.finishedOnboarding &&
|
||||||
settings1.firstName === settings2.firstName &&
|
settings1.firstName == settings2.firstName &&
|
||||||
settings1.hideRegisterPromptOnNewContact ===
|
settings1.hideRegisterPromptOnNewContact ==
|
||||||
settings2.hideRegisterPromptOnNewContact &&
|
settings2.hideRegisterPromptOnNewContact &&
|
||||||
settings1.isRegistered === settings2.isRegistered &&
|
settings1.isRegistered == settings2.isRegistered &&
|
||||||
settings1.lastName === settings2.lastName &&
|
settings1.lastName == settings2.lastName &&
|
||||||
settings1.lastAckedOfferToUserJwtId ===
|
settings1.lastAckedOfferToUserJwtId ==
|
||||||
settings2.lastAckedOfferToUserJwtId &&
|
settings2.lastAckedOfferToUserJwtId &&
|
||||||
settings1.lastAckedOfferToUserProjectsJwtId ===
|
settings1.lastAckedOfferToUserProjectsJwtId ==
|
||||||
settings2.lastAckedOfferToUserProjectsJwtId &&
|
settings2.lastAckedOfferToUserProjectsJwtId &&
|
||||||
settings1.lastNotifiedClaimId === settings2.lastNotifiedClaimId &&
|
settings1.lastNotifiedClaimId == settings2.lastNotifiedClaimId &&
|
||||||
settings1.lastViewedClaimId === settings2.lastViewedClaimId &&
|
settings1.lastViewedClaimId == settings2.lastViewedClaimId &&
|
||||||
settings1.notifyingNewActivityTime === settings2.notifyingNewActivityTime &&
|
settings1.notifyingNewActivityTime == settings2.notifyingNewActivityTime &&
|
||||||
settings1.notifyingReminderMessage === settings2.notifyingReminderMessage &&
|
settings1.notifyingReminderMessage == settings2.notifyingReminderMessage &&
|
||||||
settings1.notifyingReminderTime === settings2.notifyingReminderTime &&
|
settings1.notifyingReminderTime == settings2.notifyingReminderTime &&
|
||||||
settings1.partnerApiServer === settings2.partnerApiServer &&
|
settings1.partnerApiServer == settings2.partnerApiServer &&
|
||||||
settings1.passkeyExpirationMinutes === settings2.passkeyExpirationMinutes &&
|
settings1.passkeyExpirationMinutes == settings2.passkeyExpirationMinutes &&
|
||||||
settings1.profileImageUrl === settings2.profileImageUrl &&
|
settings1.profileImageUrl == settings2.profileImageUrl &&
|
||||||
settings1.showContactGivesInline === settings2.showContactGivesInline &&
|
settings1.showContactGivesInline == settings2.showContactGivesInline &&
|
||||||
settings1.showGeneralAdvanced === settings2.showGeneralAdvanced &&
|
settings1.showGeneralAdvanced == settings2.showGeneralAdvanced &&
|
||||||
settings1.showShortcutBvc === settings2.showShortcutBvc &&
|
settings1.showShortcutBvc == settings2.showShortcutBvc &&
|
||||||
settings1.vapid === settings2.vapid &&
|
settings1.vapid == settings2.vapid &&
|
||||||
settings1.warnIfProdServer === settings2.warnIfProdServer &&
|
settings1.warnIfProdServer == settings2.warnIfProdServer &&
|
||||||
settings1.warnIfTestServer === settings2.warnIfTestServer &&
|
settings1.warnIfTestServer == settings2.warnIfTestServer &&
|
||||||
settings1.webPushServer === settings2.webPushServer &&
|
settings1.webPushServer == settings2.webPushServer &&
|
||||||
JSON.stringify(settings1.searchBoxes) ===
|
JSON.stringify(settings1.searchBoxes) ==
|
||||||
JSON.stringify(settings2.searchBoxes)
|
JSON.stringify(settings2.searchBoxes)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -830,23 +798,25 @@ export function generateComparisonYaml(comparison: DataComparison): string {
|
|||||||
dexieSettings: comparison.dexieSettings.length,
|
dexieSettings: comparison.dexieSettings.length,
|
||||||
sqliteSettings: comparison.sqliteSettings.filter(s => s.accountDid || s.activeDid).length,
|
sqliteSettings: comparison.sqliteSettings.filter(s => s.accountDid || s.activeDid).length,
|
||||||
dexieAccounts: comparison.dexieAccounts.length,
|
dexieAccounts: comparison.dexieAccounts.length,
|
||||||
sqliteAccounts: comparison.sqliteAccounts.filter(a => a.did).length,
|
sqliteAccounts: comparison.sqliteAccounts.filter(a => a).length,
|
||||||
},
|
},
|
||||||
differences: {
|
differences: {
|
||||||
contacts: {
|
contacts: {
|
||||||
added: comparison.differences.contacts.added.length,
|
added: comparison.differences.contacts.added.length,
|
||||||
modified: comparison.differences.contacts.modified.length,
|
modified: comparison.differences.contacts.modified.length,
|
||||||
|
unmodified: comparison.differences.contacts.unmodified.length,
|
||||||
missing: comparison.differences.contacts.missing.filter(c => c.did).length,
|
missing: comparison.differences.contacts.missing.filter(c => c.did).length,
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
added: comparison.differences.settings.added.length,
|
added: comparison.differences.settings.added.length,
|
||||||
modified: comparison.differences.settings.modified.length,
|
modified: comparison.differences.settings.modified.length,
|
||||||
|
unmodified: comparison.differences.settings.unmodified.length,
|
||||||
missing: comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length,
|
missing: comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length,
|
||||||
},
|
},
|
||||||
accounts: {
|
accounts: {
|
||||||
added: comparison.differences.accounts.added.length,
|
added: comparison.differences.accounts.added.length,
|
||||||
modified: comparison.differences.accounts.modified.length,
|
unmodified: comparison.differences.accounts.unmodified.length,
|
||||||
missing: comparison.differences.accounts.missing.filter(a => a.did).length,
|
missing: comparison.differences.accounts.missing.filter(a => a).length,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
@@ -888,14 +858,8 @@ export function generateComparisonYaml(comparison: DataComparison): string {
|
|||||||
hasIdentity: !!a.identity,
|
hasIdentity: !!a.identity,
|
||||||
hasMnemonic: !!a.mnemonic,
|
hasMnemonic: !!a.mnemonic,
|
||||||
})),
|
})),
|
||||||
sqlite: comparison.sqliteAccounts
|
sqlite: comparison.sqliteAccounts.map((a) => ({
|
||||||
.filter(a => a.did)
|
did: a,
|
||||||
.map((a) => ({
|
|
||||||
id: a.id,
|
|
||||||
did: a.did,
|
|
||||||
dateCreated: a.dateCreated,
|
|
||||||
hasIdentity: !!a.identity,
|
|
||||||
hasMnemonic: !!a.mnemonic,
|
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p v-if="downloadMnemonic" class="text-green-500">
|
<p v-if="downloadMnemonic" class="text-green-500">
|
||||||
Here is your seed. Write it down!
|
Here is your seed for account {{ downloadMnemonicAddress?.substring('did:ethr:0x'.length).substring(0, 3) }} -- write it down!
|
||||||
<br />
|
<br />
|
||||||
{{ downloadMnemonic }}
|
{{ downloadMnemonic }}
|
||||||
</p>
|
</p>
|
||||||
@@ -506,15 +506,15 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<IconRenderer
|
<IconRenderer
|
||||||
icon-name="edit"
|
icon-name="check"
|
||||||
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
/>
|
/>
|
||||||
<span class="text-sm font-medium text-yellow-900"
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
>Modify</span
|
>Unmodified</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-bold text-yellow-900">{{
|
<span class="text-sm font-bold text-yellow-900">{{
|
||||||
comparison.differences.accounts.modified.length
|
comparison.differences.accounts.unmodified.length
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -559,17 +559,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Modify Accounts -->
|
<!-- Unmodified Accounts -->
|
||||||
<div
|
<div
|
||||||
v-if="comparison.differences.accounts.modified.length > 0"
|
v-if="comparison.differences.accounts.unmodified.length > 0"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
>
|
>
|
||||||
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
||||||
Modify Accounts ({{ comparison.differences.accounts.modified.length }}):
|
Unmodified Accounts ({{ comparison.differences.accounts.unmodified.length }}):
|
||||||
</h4>
|
</h4>
|
||||||
|
</div>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<div
|
<div
|
||||||
v-for="account in comparison.differences.accounts.modified"
|
v-for="account in comparison.differences.accounts.unmodified"
|
||||||
:key="account.id"
|
:key="account.id"
|
||||||
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
||||||
>
|
>
|
||||||
@@ -580,27 +581,22 @@
|
|||||||
<div class="text-gray-400">Has Mnemonic: {{ getAccountHasMnemonic(account) ? 'Yes' : 'No' }}</div>
|
<div class="text-gray-400">Has Mnemonic: {{ getAccountHasMnemonic(account) ? 'Yes' : 'No' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Keep Accounts -->
|
<!-- Keep Accounts -->
|
||||||
<div
|
<div
|
||||||
v-if="comparison.differences.accounts.missing.filter(a => a.did).length > 0"
|
v-if="comparison.differences.accounts.missing.filter(a => a).length > 0"
|
||||||
class="mt-4"
|
class="mt-4"
|
||||||
>
|
>
|
||||||
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
||||||
Keep Accounts ({{ comparison.differences.accounts.missing.filter(a => a.did).length }}):
|
Keep Accounts ({{ comparison.differences.accounts.missing.filter(a => a).length }}):
|
||||||
</h4>
|
</h4>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<div
|
<div
|
||||||
v-for="account in comparison.differences.accounts.missing.filter(a => a.did)"
|
v-for="did in comparison.differences.accounts.missing.filter(a => a)"
|
||||||
:key="account.id"
|
:key="did"
|
||||||
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
||||||
>
|
>
|
||||||
<div class="font-medium">ID: {{ account.id }}</div>
|
<div class="text-gray-500">{{ did }}</div>
|
||||||
<div class="text-gray-500">{{ account.did }}</div>
|
|
||||||
<div class="text-gray-400">Created: {{ account.dateCreated }}</div>
|
|
||||||
<div class="text-gray-400">Has Identity: {{ getAccountHasIdentity(account) ? 'Yes' : 'No' }}</div>
|
|
||||||
<div class="text-gray-400">Has Mnemonic: {{ getAccountHasMnemonic(account) ? 'Yes' : 'No' }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -647,6 +643,23 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<IconRenderer
|
||||||
|
icon-name="check"
|
||||||
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
|
/>
|
||||||
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
|
>Unmodified</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<span class="text-sm font-bold text-yellow-900">{{
|
||||||
|
comparison.differences.settings.unmodified.length
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||||
>
|
>
|
||||||
@@ -707,6 +720,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Unmodified Settings -->
|
||||||
|
<div
|
||||||
|
v-if="comparison.differences.settings.unmodified.length > 0"
|
||||||
|
class="mt-4"
|
||||||
|
>
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
||||||
|
Unmodified Settings ({{ comparison.differences.settings.unmodified.length }}):
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div
|
||||||
|
v-for="setting in comparison.differences.settings.unmodified"
|
||||||
|
:key="setting.id"
|
||||||
|
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
||||||
|
>
|
||||||
|
<div class="font-medium">{{ getSettingDisplayName(setting) }}</div>
|
||||||
|
<div class="text-gray-500">ID: {{ setting.id }}</div>
|
||||||
|
<div class="text-gray-400">Registered: {{ setting.isRegistered ? 'Yes' : 'No' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Keep Settings -->
|
<!-- Keep Settings -->
|
||||||
<div
|
<div
|
||||||
v-if="comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length > 0"
|
v-if="comparison.differences.settings.missing.filter(s => s.accountDid || s.activeDid).length > 0"
|
||||||
@@ -770,6 +804,23 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<IconRenderer
|
||||||
|
icon-name="check"
|
||||||
|
svg-class="h-5 w-5 text-yellow-600 mr-2"
|
||||||
|
/>
|
||||||
|
<span class="text-sm font-medium text-yellow-900"
|
||||||
|
>Unmodified</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<span class="text-sm font-bold text-yellow-900">{{
|
||||||
|
comparison.differences.contacts.unmodified.length
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
class="flex items-center justify-between p-3 bg-red-50 rounded-lg"
|
||||||
>
|
>
|
||||||
@@ -830,6 +881,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Unmodified Contacts -->
|
||||||
|
<div
|
||||||
|
v-if="comparison.differences.contacts.unmodified.length > 0"
|
||||||
|
class="mt-4"
|
||||||
|
>
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 mb-2">
|
||||||
|
Unmodified Contacts ({{ comparison.differences.contacts.unmodified.length }}):
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div
|
||||||
|
v-for="contact in comparison.differences.contacts.unmodified"
|
||||||
|
:key="contact.did"
|
||||||
|
class="text-xs text-gray-600 bg-gray-50 p-2 rounded"
|
||||||
|
>
|
||||||
|
<div class="font-medium">{{ contact.name || '<empty>' }}</div>
|
||||||
|
<div class="text-gray-500">{{ contact.did }}</div>
|
||||||
|
<div class="text-gray-400">{{ contact.contactMethods?.length || 0 }} contact methods</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Keep Contacts -->
|
<!-- Keep Contacts -->
|
||||||
<div
|
<div
|
||||||
v-if="comparison.differences.contacts.missing.filter(c => c.did).length > 0"
|
v-if="comparison.differences.contacts.missing.filter(c => c.did).length > 0"
|
||||||
@@ -871,7 +943,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ref } from "vue";
|
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
import { useClipboard } from "@vueuse/core";
|
import { useClipboard } from "@vueuse/core";
|
||||||
|
|
||||||
@@ -923,6 +994,7 @@ export default class DatabaseMigration extends Vue {
|
|||||||
private cannotfindMainAccount = false;
|
private cannotfindMainAccount = false;
|
||||||
private downloadSettingsContactsBlob?: Blob;
|
private downloadSettingsContactsBlob?: Blob;
|
||||||
private downloadMnemonic?: string;
|
private downloadMnemonic?: string;
|
||||||
|
private downloadMnemonicAddress?: string;
|
||||||
private hasMultipleMnemonics = false;
|
private hasMultipleMnemonics = false;
|
||||||
private isLoading = false;
|
private isLoading = false;
|
||||||
private loadingMessage = "";
|
private loadingMessage = "";
|
||||||
@@ -1024,6 +1096,7 @@ export default class DatabaseMigration extends Vue {
|
|||||||
}
|
}
|
||||||
if (primaryAccount) {
|
if (primaryAccount) {
|
||||||
this.downloadMnemonic = primaryAccount.mnemonic;
|
this.downloadMnemonic = primaryAccount.mnemonic;
|
||||||
|
this.downloadMnemonicAddress = primaryAccount.did;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,15 @@
|
|||||||
>
|
>
|
||||||
Accounts
|
Accounts
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="text-sm text-blue-600 hover:text-blue-800 underline"
|
||||||
|
@click="
|
||||||
|
sqlQuery = 'SELECT * FROM contacts;';
|
||||||
|
executeSql();
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Contacts
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="text-sm text-blue-600 hover:text-blue-800 underline"
|
class="text-sm text-blue-600 hover:text-blue-800 underline"
|
||||||
@click="
|
@click="
|
||||||
|
|||||||
Reference in New Issue
Block a user