Browse Source
			
			
			
			
				
		Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/kick-starter-for-time-pwa/pulls/56project-map-link
				 8 changed files with 244 additions and 13 deletions
			
			
		@ -0,0 +1,163 @@ | 
				
			|||
<template> | 
				
			|||
  <section id="Content" class="p-6 pb-24"> | 
				
			|||
    <!-- Breadcrumb --> | 
				
			|||
    <div id="ViewBreadcrumb" class="mb-8"> | 
				
			|||
      <h1 class="text-lg text-center font-light relative px-7"> | 
				
			|||
        <!-- Cancel --> | 
				
			|||
        <button | 
				
			|||
          @click="$router.go(-1)" | 
				
			|||
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" | 
				
			|||
        > | 
				
			|||
          <fa icon="chevron-left"></fa> | 
				
			|||
        </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 derivation path from the existing seed. | 
				
			|||
      </p> | 
				
			|||
 | 
				
			|||
      <p v-if="didArrays.length > 1"> | 
				
			|||
        Choose existing DIDs from same seed phrase to compute derivation. | 
				
			|||
      </p> | 
				
			|||
      <ul class="mb-4"> | 
				
			|||
        <li | 
				
			|||
          class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2" | 
				
			|||
          v-for="dids in didArrays" | 
				
			|||
          :key="dids[0]" | 
				
			|||
          @click="switchAccount(dids[0])" | 
				
			|||
        > | 
				
			|||
          <fa | 
				
			|||
            v-if="dids[0] == selectedArrayFirstDid" | 
				
			|||
            icon="circle" | 
				
			|||
            class="fa-fw text-blue-400 text-xl mr-3" | 
				
			|||
          ></fa> | 
				
			|||
          <fa | 
				
			|||
            v-else | 
				
			|||
            icon="circle" | 
				
			|||
            class="fa-fw text-slate-400 text-xl mr-3" | 
				
			|||
          ></fa> | 
				
			|||
          <span class="overflow-hidden"> | 
				
			|||
            <div class="text-sm text-slate-500 truncate"> | 
				
			|||
              <code>{{ dids.join(",") }}</code> | 
				
			|||
            </div> | 
				
			|||
          </span> | 
				
			|||
        </li> | 
				
			|||
      </ul> | 
				
			|||
    </div> | 
				
			|||
    <div class="mt-8"> | 
				
			|||
      <button | 
				
			|||
        @click="incrementDerivation()" | 
				
			|||
        class="block w-full text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2" | 
				
			|||
      > | 
				
			|||
        Increment and Import | 
				
			|||
      </button> | 
				
			|||
      <button | 
				
			|||
        @click="onCancelClick()" | 
				
			|||
        type="button" | 
				
			|||
        class="block w-full text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md" | 
				
			|||
      > | 
				
			|||
        Cancel | 
				
			|||
      </button> | 
				
			|||
    </div> | 
				
			|||
  </section> | 
				
			|||
</template> | 
				
			|||
 | 
				
			|||
<script lang="ts"> | 
				
			|||
import { Component, Vue } from "vue-facing-decorator"; | 
				
			|||
import { | 
				
			|||
  DEFAULT_ROOT_DERIVATION_PATH, | 
				
			|||
  deriveAddress, | 
				
			|||
  newIdentifier, | 
				
			|||
} from "../libs/crypto"; | 
				
			|||
import { accountsDB, db } from "@/db"; | 
				
			|||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; | 
				
			|||
 | 
				
			|||
@Component({ | 
				
			|||
  components: {}, | 
				
			|||
}) | 
				
			|||
export default class ImportAccountView extends Vue { | 
				
			|||
  derivationPath = DEFAULT_ROOT_DERIVATION_PATH; | 
				
			|||
  didArrays: Array<Array<string>> = []; | 
				
			|||
  selectedArrayFirstDid = ""; | 
				
			|||
 | 
				
			|||
  async mounted() { | 
				
			|||
    await accountsDB.open(); | 
				
			|||
    const accounts = await accountsDB.accounts.toArray(); | 
				
			|||
    const seedDids = {}; | 
				
			|||
    accounts.forEach((account) => { | 
				
			|||
      const prevDids = seedDids[account.mnemonic] || []; | 
				
			|||
      seedDids[account.mnemonic] = prevDids.concat([account.did]); | 
				
			|||
    }); | 
				
			|||
    this.didArrays = Object.values(seedDids); | 
				
			|||
    this.selectedArrayFirstDid = this.didArrays[0][0]; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  public onCancelClick() { | 
				
			|||
    this.$router.back(); | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  public switchAccount(did: string) { | 
				
			|||
    this.selectedArrayFirstDid = did; | 
				
			|||
  } | 
				
			|||
 | 
				
			|||
  public async incrementDerivation() { | 
				
			|||
    await accountsDB.open(); | 
				
			|||
    // find the maximum derivation path for the selected DIDs | 
				
			|||
    const selectedArray: Array<string> = this.didArrays.find( | 
				
			|||
      (dids) => dids[0] === this.selectedArrayFirstDid, | 
				
			|||
    ); | 
				
			|||
    const allMatchingAccounts = await accountsDB.accounts | 
				
			|||
      .where("did") | 
				
			|||
      .anyOf(...selectedArray) | 
				
			|||
      .toArray(); | 
				
			|||
    const accountWithMaxDeriv = allMatchingAccounts[0]; | 
				
			|||
    allMatchingAccounts.slice(1).forEach((account) => { | 
				
			|||
      if (account.derivationPath > accountWithMaxDeriv.derivationPath) { | 
				
			|||
        accountWithMaxDeriv.derivationPath = account.derivationPath; | 
				
			|||
      } | 
				
			|||
    }); | 
				
			|||
    // increment the last number in that max derivation path | 
				
			|||
    let lastStr = accountWithMaxDeriv.derivationPath.split("/").slice(-1)[0]; | 
				
			|||
    if (lastStr.endsWith("'")) { | 
				
			|||
      lastStr = lastStr.slice(0, -1); | 
				
			|||
    } | 
				
			|||
    const lastNum = parseInt(lastStr, 10); | 
				
			|||
    const newLastNum = lastNum + 1; | 
				
			|||
    const newDerivPath = accountWithMaxDeriv.derivationPath | 
				
			|||
      .split("/") | 
				
			|||
      .slice(0, -1) | 
				
			|||
      .concat([newLastNum.toString() + "'"]) | 
				
			|||
      .join("/"); | 
				
			|||
 | 
				
			|||
    const mne: string = accountWithMaxDeriv.mnemonic; | 
				
			|||
 | 
				
			|||
    const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath); | 
				
			|||
 | 
				
			|||
    const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); | 
				
			|||
 | 
				
			|||
    try { | 
				
			|||
      await accountsDB.accounts.add({ | 
				
			|||
        dateCreated: new Date().toISOString(), | 
				
			|||
        derivationPath: newDerivPath, | 
				
			|||
        did: newId.did, | 
				
			|||
        identity: JSON.stringify(newId), | 
				
			|||
        mnemonic: mne, | 
				
			|||
        publicKeyHex: newId.keys[0].publicKeyHex, | 
				
			|||
      }); | 
				
			|||
 | 
				
			|||
      // record that as the active DID | 
				
			|||
      await db.open(); | 
				
			|||
      db.settings.update(MASTER_SETTINGS_KEY, { | 
				
			|||
        activeDid: newId.did, | 
				
			|||
      }); | 
				
			|||
      this.$router.push({ name: "account" }); | 
				
			|||
    } catch (err) { | 
				
			|||
      console.error("Error saving mnemonic & updating settings:", err); | 
				
			|||
    } | 
				
			|||
  } | 
				
			|||
} | 
				
			|||
</script> | 
				
			|||
					Loading…
					
					
				
		Reference in new issue