forked from trent_larson/crowd-funder-for-time-pwa
- Restore runMigrations functionality for database schema migrations - Remove indexedDBMigrationService.ts (was for IndexedDB to SQLite migration) - Recreate migrationService.ts and db-sql/migration.ts for schema management - Add proper TypeScript error handling with type guards in AccountViewView - Fix CreateAndSubmitClaimResult property access in QuickActionBvcBeginView - Remove LeafletMouseEvent from Vue components array (it's a type, not component) - Add null check for UserNameDialog callback to prevent undefined assignment - Implement extractErrorMessage helper function for consistent error handling - Update router to remove database-migration route The migration system now properly handles database schema evolution across app versions, while the IndexedDB to SQLite migration service has been removed as it was specific to that one-time migration.
183 lines
5.8 KiB
Vue
183 lines
5.8 KiB
Vue
<template>
|
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
|
<!-- Breadcrumb -->
|
|
<div id="ViewBreadcrumb" class="mb-8">
|
|
<h1 class="text-lg text-center font-light relative px-7">
|
|
<!-- Cancel -->
|
|
<button
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.go(-1)"
|
|
>
|
|
<font-awesome icon="chevron-left"></font-awesome>
|
|
</button>
|
|
Derive from Existing Identity
|
|
</h1>
|
|
</div>
|
|
<!-- Import Account Form -->
|
|
|
|
<div>
|
|
<p class="text-center text-xl mb-4 font-light">
|
|
Will increment the maximum known derivation path from the existing seed.
|
|
</p>
|
|
|
|
<p v-if="Object.keys(didArrays).length > 1">
|
|
Choose existing DIDs from same seed phrase to compute derivation.
|
|
</p>
|
|
<ul class="mb-4">
|
|
<li
|
|
v-for="dids in Object.values(didArrays)"
|
|
:key="dids[0].did"
|
|
class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2"
|
|
@click="switchAccount(dids[0].did)"
|
|
>
|
|
<font-awesome
|
|
v-if="dids[0].did == selectedArrayFirstDid"
|
|
icon="circle"
|
|
class="fa-fw text-blue-500 text-xl mr-3"
|
|
></font-awesome>
|
|
<font-awesome
|
|
v-else
|
|
icon="circle"
|
|
class="fa-fw text-slate-400 text-xl mr-3"
|
|
></font-awesome>
|
|
<span class="overflow-hidden">
|
|
<div class="text-sm text-slate-500">
|
|
<code>{{ dids.map((d) => d.did).join(" ") }}</code>
|
|
</div>
|
|
</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="mt-8">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
|
<button
|
|
class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
|
|
@click="incrementDerivation()"
|
|
>
|
|
Increment and Import
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
|
|
@click="onCancelClick()"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import * as R from "ramda";
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router, RouteLocationNormalizedLoaded } from "vue-router";
|
|
|
|
import {
|
|
DEFAULT_ROOT_DERIVATION_PATH,
|
|
deriveAddress,
|
|
newIdentifier,
|
|
nextDerivationPath,
|
|
} from "../libs/crypto";
|
|
import * as databaseUtil from "../db/databaseUtil";
|
|
import { db } from "../db/index";
|
|
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
|
|
import {
|
|
retrieveAllAccountsMetadata,
|
|
retrieveFullyDecryptedAccount,
|
|
saveNewIdentity,
|
|
} from "../libs/util";
|
|
import { logger } from "../utils/logger";
|
|
import { Account, AccountEncrypted } from "../db/tables/accounts";
|
|
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
|
|
|
@Component({
|
|
components: {},
|
|
})
|
|
export default class ImportAccountView extends Vue {
|
|
$route!: RouteLocationNormalizedLoaded;
|
|
$router!: Router;
|
|
|
|
derivationPath = DEFAULT_ROOT_DERIVATION_PATH;
|
|
didArrays: Record<string, Account[]> = {};
|
|
selectedArrayFirstDid = "";
|
|
|
|
async mounted() {
|
|
const accounts: AccountEncrypted[] = await retrieveAllAccountsMetadata();
|
|
const decryptedAccounts: (Account | undefined)[] = await Promise.all(
|
|
accounts.map(async (account) => {
|
|
return retrieveFullyDecryptedAccount(account.did);
|
|
}),
|
|
);
|
|
const filteredDecryptedAccounts: Account[] = decryptedAccounts.filter(
|
|
(account) => account !== undefined,
|
|
);
|
|
|
|
// group by account.mnemonic
|
|
const groupedAccounts: Record<string, Account[]> = R.groupBy(
|
|
(a) => a.mnemonic || "",
|
|
filteredDecryptedAccounts,
|
|
) as Record<string, Account[]>;
|
|
|
|
this.didArrays = groupedAccounts;
|
|
if (Object.keys(this.didArrays).length > 0) {
|
|
this.selectedArrayFirstDid = Object.values(this.didArrays)[0][0].did;
|
|
}
|
|
}
|
|
|
|
public onCancelClick() {
|
|
this.$router.back();
|
|
}
|
|
|
|
public switchAccount(did: string) {
|
|
this.selectedArrayFirstDid = did;
|
|
}
|
|
|
|
public async incrementDerivation() {
|
|
// find the maximum derivation path for the selected DIDs
|
|
const selectedArray: Array<Account> =
|
|
Object.values(this.didArrays).find(
|
|
(dids) => dids[0].did === this.selectedArrayFirstDid,
|
|
) || [];
|
|
// extract the derivationPath array and sort it
|
|
const derivationPaths = selectedArray.map(
|
|
(account) => account.derivationPath,
|
|
);
|
|
derivationPaths.sort((a, b) => {
|
|
const aParts = a?.split("/");
|
|
const aLast = aParts?.[aParts.length - 1];
|
|
const bParts = b?.split("/");
|
|
const bLast = bParts?.[bParts.length - 1];
|
|
return parseInt(aLast || "0") - parseInt(bLast || "0");
|
|
});
|
|
// we're sure there's at least one
|
|
const maxDerivPath: string = derivationPaths[
|
|
derivationPaths.length - 1
|
|
] as string;
|
|
|
|
const newDerivPath = nextDerivationPath(maxDerivPath);
|
|
|
|
const mne = selectedArray[0].mnemonic as string;
|
|
const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath);
|
|
|
|
const newId = newIdentifier(address, publicHex, privateHex, newDerivPath);
|
|
|
|
try {
|
|
await saveNewIdentity(newId, mne, newDerivPath);
|
|
|
|
// record that as the active DID
|
|
const platformService = PlatformServiceFactory.getInstance();
|
|
await platformService.dbExec("UPDATE settings SET activeDid = ?", [
|
|
newId.did,
|
|
]);
|
|
await databaseUtil.updateDidSpecificSettings(newId.did, {
|
|
isRegistered: false,
|
|
});
|
|
this.$router.push({ name: "account" });
|
|
} catch (err) {
|
|
logger.error("Error saving mnemonic & updating settings:", err);
|
|
}
|
|
}
|
|
}
|
|
</script>
|