# kickstart-for-time-pwa

## Project setup
```
npm install
```

### Compiles and hot-reloads for development
```
npm run serve
```

### Compiles and minifies for production

If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",`

```
npm run build
```

### Lints and fixes files
```
npm run lint
```

### Test key contents

See [this page](openssl_signing_console.rst)

### Register new user on test server

New users require registration. This can be done with a claim payload like this
by an existing user:

```
  const vcClaim = {
    "@context": "https://schema.org",
    "@type": "RegisterAction",
    agent: { identifier: identity0.did },
    object: SERVICE_ID,
    participant: { identifier: newIdentity.did },
  };
```

On the test server, User #0 has rights to register others, so you can start
playing one of two ways:

- Import the keys for the test User `did:ethr:0x000Ee5654b9742f6Fe18ea970e32b97ee2247B51` by importing this seed phrase:
  `seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control`
  (Other test users are found [here](https://github.com/trentlarson/endorser-ch/blob/master/test/util.js).)

- Alternatively, register someone else under User #0 automatically:

  * In the `src/views/AccountViewView.vue` file, uncomment the lines referring to "testServerRegisterUser".

  * Visit the `/account` page.

### Create multiple identifiers

Go to /start and create or import a new one. Then switch identifiers on the bottom of the Your Identity page.

### Create keys with alternate tools

See [this page](openssl_signing_console.rst)

### Customize Vue configuration
See [Configuration Reference](https://cli.vuejs.org/config/).


## Scenarios

- Create a new identity as prompted. Go to "Your Identity" screen and copy the ID to the clipboard.

- Go back to /start and import test User `did:ethr:0x000Ee5654b9742f6Fe18ea970e32b97ee2247B51` with this this seed phrase:
  `seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control`
  (Other test users are found [here](https://github.com/trentlarson/endorser-ch/blob/master/test/util.js).)

- Go to "Your Contacts" screen and add the ID you copied to the clipboard, and hit "+" to add them.

- Click on the "Registration Unknown" button and register that person to be able to make claims as them.

### Clear data & restart

Clear cache for localhost, then go to http://localhost:8080/start
(because it'll generate a new one automatically if you start on the `/account` page).



## Dependencies

See https://tea.xyz

| Project    | Version   |
| ---------- | --------- |
| nodejs.org | ^16.0.0   |
| npmjs.com  | ^8.0.0    |

## Other

### Reference Material

* Notifications can be type of `toast` (self-dismiss), `info`, `success`, `warning`, and `danger`.
  They are done via [notiwind](https://www.npmjs.com/package/notiwind) and set up in App.vue.

```
// reference material from https://github.com/trentlarson/endorser-mobile/blob/8dc8e0353e0cc80ffa7ed89ded15c8b0da92726b/src/utility/idUtility.ts#L83

// Import an existing ID
export const importAndStoreIdentifier = async (mnemonic: string, mnemonicPassword: string, toLowercase: boolean, previousIdentifiers: Array<IIdentifier>) => {

  // just to get rid of variability that might cause an error
  mnemonic = mnemonic.trim().toLowerCase()

  /**
  // an approach I pieced together
  // requires: yarn add elliptic
  // ... plus:
  // const EC = require('elliptic').ec
  // const secp256k1 = new EC('secp256k1')
  //
  const keyHex: string = bip39.mnemonicToEntropy(mnemonic)
  // returns a KeyPair from the elliptic.ec library
  const keyPair = secp256k1.keyFromPrivate(keyHex, 'hex')
  // this code is from did-provider-eth createIdentifier
  const privateHex = keyPair.getPrivate('hex')
  const publicHex = keyPair.getPublic('hex')
  const address = didJwt.toEthereumAddress(publicHex)
  **/

  /**
  // from https://github.com/uport-project/veramo/discussions/346#discussioncomment-302234
  // ... which almost works but the didJwt.toEthereumAddress is wrong
  // requires: yarn add bip32
  // ... plus: import * as bip32 from 'bip32'
  //
  const seed: Buffer = await bip39.mnemonicToSeed(mnemonic)
  const root = bip32.fromSeed(seed)
  const node = root.derivePath(UPORT_ROOT_DERIVATION_PATH)
  const privateHex = node.privateKey.toString("hex")
  const publicHex = node.publicKey.toString("hex")
  const address = didJwt.toEthereumAddress('0x' + publicHex)
  **/

  /**
  // from https://github.com/uport-project/veramo/discussions/346#discussioncomment-302234
  // requires: yarn add @ethersproject/hdnode
  // ... plus: import { HDNode } from '@ethersproject/hdnode'
  **/
  const hdnode: HDNode = HDNode.fromMnemonic(mnemonic)
  const rootNode: HDNode = hdnode.derivePath(UPORT_ROOT_DERIVATION_PATH)
  const privateHex = rootNode.privateKey.substring(2) // original starts with '0x'
  const publicHex = rootNode.publicKey.substring(2) // original starts with '0x'
  let address = rootNode.address

  const prevIds = previousIdentifiers || [];

  if (toLowercase) {
    const foundEqual = R.find(
      (id) => utility.rawAddressOfDid(id.did) === address,
      prevIds
    )
    if (foundEqual) {
      // They're trying to create a lowercase version of one that exists in normal case.
      // (We really should notify the user.)
      appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a normal-case version of the DID since a regular version exists."}))
    } else {
      address = address.toLowerCase()
    }
  } else {
    // They're not trying to convert to lowercase.
    const foundLower = R.find((id) =>
      utility.rawAddressOfDid(id.did) === address.toLowerCase(),
      prevIds
    )
    if (foundLower) {
      // They're trying to create a normal case version of one that exists in lowercase.
      // (We really should notify the user.)
      appStore.dispatch(appSlice.actions.addLog({log: true, msg: "Will create a lowercase version of the DID since a lowercase version exists."}))
      address = address.toLowerCase()
    }
  }

  appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... derived keys and address..."}))

  const newId = newIdentifier(address, publicHex, privateHex, UPORT_ROOT_DERIVATION_PATH)
  appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... created new ID..."}))

  // awaiting because otherwise the UI may not see that a mnemonic was created
  const savedId = await storeIdentifier(newId, mnemonic, mnemonicPassword)
  appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... stored new ID..."}))
  return savedId
}

// Create a totally new ID
export const createAndStoreIdentifier = async (mnemonicPassword) => {

  // This doesn't give us the entropy/seed.
  //const id = await agent.didManagerCreate()

  const entropy = crypto.randomBytes(32)
  const mnemonic = bip39.entropyToMnemonic(entropy)
  appStore.dispatch(appSlice.actions.addLog({log: false, msg: "... generated mnemonic..."}))

  return importAndStoreIdentifier(mnemonic, mnemonicPassword, false, [])
}
```

## Kudos

Gifts make the world go 'round!

* [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80)
* [Many tools & libraries]() such as Nodejs.org, IntelliJ Idea, Veramo.io, Vuejs.org, threejs.org
* [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439)
* [Forest floor image](https://www.goodfreephotos.com/albums/textures/leafy-autumn-forest-floor.jpg)
