|
@ -73,7 +73,7 @@ |
|
|
? "Confirmed Amounts" |
|
|
? "Confirmed Amounts" |
|
|
: "Unconfirmed Amounts" |
|
|
: "Unconfirmed Amounts" |
|
|
}} |
|
|
}} |
|
|
<fa icon="rotate" class="fa-fw" /> |
|
|
<fa icon="left-right" class="fa-fw" /> |
|
|
</button> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
@ -301,30 +301,25 @@ |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
<script lang="ts"> |
|
|
import { AxiosError } from "axios"; |
|
|
import {Axios, AxiosError} from "axios"; |
|
|
import { IndexableType } from "dexie"; |
|
|
import { IndexableType } from "dexie"; |
|
|
import * as didJwt from "did-jwt"; |
|
|
|
|
|
import * as R from "ramda"; |
|
|
import * as R from "ramda"; |
|
|
import { IIdentifier } from "@veramo/core"; |
|
|
import { IIdentifier } from "@veramo/core"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
|
|
|
import { Router } from "vue-router"; |
|
|
|
|
|
|
|
|
import { AppString, NotificationIface } from "@/constants/app"; |
|
|
import { AppString, NotificationIface } from "@/constants/app"; |
|
|
import { accountsDB, db } from "@/db/index"; |
|
|
import {accountsDB, db, NonsensitiveDexie} from "@/db/index"; |
|
|
import { Contact } from "@/db/tables/contacts"; |
|
|
import { Contact } from "@/db/tables/contacts"; |
|
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; |
|
|
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; |
|
|
import { |
|
|
import { accessToken, getContactPayloadFromJwtUrl } from "@/libs/crypto"; |
|
|
accessToken, |
|
|
|
|
|
getContactPayloadFromJwtUrl, |
|
|
|
|
|
SimpleSigner, |
|
|
|
|
|
} from "@/libs/crypto"; |
|
|
|
|
|
import { |
|
|
import { |
|
|
CONTACT_CSV_HEADER, |
|
|
CONTACT_CSV_HEADER, |
|
|
CONTACT_URL_PREFIX, |
|
|
CONTACT_URL_PREFIX, |
|
|
GiverReceiverInputInfo, |
|
|
GiverReceiverInputInfo, |
|
|
GiveSummaryRecord, |
|
|
GiveSummaryRecord, |
|
|
isDid, |
|
|
isDid, |
|
|
RegisterVerifiableCredential, |
|
|
register, |
|
|
SERVICE_ID, |
|
|
|
|
|
setVisibilityUtil, |
|
|
setVisibilityUtil, |
|
|
} from "@/libs/endorserServer"; |
|
|
} from "@/libs/endorserServer"; |
|
|
import * as libsUtil from "@/libs/util"; |
|
|
import * as libsUtil from "@/libs/util"; |
|
@ -335,6 +330,7 @@ import OfferDialog from "@/components/OfferDialog.vue"; |
|
|
import { Account } from "@/db/tables/accounts"; |
|
|
import { Account } from "@/db/tables/accounts"; |
|
|
|
|
|
|
|
|
import { Buffer } from "buffer/"; |
|
|
import { Buffer } from "buffer/"; |
|
|
|
|
|
import {getIdentity} from "@/libs/util"; |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
components: { GiftedDialog, EntityIcon, OfferDialog, QuickNav }, |
|
|
components: { GiftedDialog, EntityIcon, OfferDialog, QuickNav }, |
|
@ -360,6 +356,7 @@ export default class ContactsView extends Vue { |
|
|
givenToMeConfirmed: Record<string, number> = {}; |
|
|
givenToMeConfirmed: Record<string, number> = {}; |
|
|
// { "did:...": amount } entry for each contact |
|
|
// { "did:...": amount } entry for each contact |
|
|
givenToMeUnconfirmed: Record<string, number> = {}; |
|
|
givenToMeUnconfirmed: Record<string, number> = {}; |
|
|
|
|
|
hideRegisterPromptOnNewContact = false; |
|
|
isRegistered = false; |
|
|
isRegistered = false; |
|
|
showDidCopy = false; |
|
|
showDidCopy = false; |
|
|
showGiveNumbers = false; |
|
|
showGiveNumbers = false; |
|
@ -378,6 +375,9 @@ export default class ContactsView extends Vue { |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
|
|
|
|
|
|
this.showGiveNumbers = !!settings?.showContactGivesInline; |
|
|
this.showGiveNumbers = !!settings?.showContactGivesInline; |
|
|
|
|
|
this.hideRegisterPromptOnNewContact = |
|
|
|
|
|
!!settings?.hideRegisterPromptOnNewContact; |
|
|
|
|
|
|
|
|
if (this.showGiveNumbers) { |
|
|
if (this.showGiveNumbers) { |
|
|
this.loadGives(); |
|
|
this.loadGives(); |
|
|
} |
|
|
} |
|
@ -681,6 +681,7 @@ export default class ContactsView extends Vue { |
|
|
nextPubKeyHashB64: payload.own.nextPublicEncKeyHash, |
|
|
nextPubKeyHashB64: payload.own.nextPublicEncKeyHash, |
|
|
profileImageUrl: payload.own.profileImageUrl, |
|
|
profileImageUrl: payload.own.profileImageUrl, |
|
|
publicKeyBase64: payload.own.publicEncKey, |
|
|
publicKeyBase64: payload.own.publicEncKey, |
|
|
|
|
|
isRegistered: payload.own.isRegistered, |
|
|
} as Contact); |
|
|
} as Contact); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -705,6 +706,7 @@ export default class ContactsView extends Vue { |
|
|
let addedMessage; |
|
|
let addedMessage; |
|
|
if (this.activeDid) { |
|
|
if (this.activeDid) { |
|
|
this.setVisibility(newContact, true, false); |
|
|
this.setVisibility(newContact, true, false); |
|
|
|
|
|
newContact.seesMe = true; // didn't work inside setVisibility |
|
|
addedMessage = |
|
|
addedMessage = |
|
|
"They were added, and your activity is visible to them."; |
|
|
"They were added, and your activity is visible to them."; |
|
|
} else { |
|
|
} else { |
|
@ -712,15 +714,39 @@ export default class ContactsView extends Vue { |
|
|
} |
|
|
} |
|
|
this.contactInput = ""; |
|
|
this.contactInput = ""; |
|
|
if (this.isRegistered) { |
|
|
if (this.isRegistered) { |
|
|
this.$notify( |
|
|
if (!this.hideRegisterPromptOnNewContact && !newContact.registered) { |
|
|
{ |
|
|
setTimeout(() => { |
|
|
group: "alert", |
|
|
this.$notify( |
|
|
type: "info", |
|
|
{ |
|
|
title: "New User?", |
|
|
group: "modal", |
|
|
text: "If they are a new user, be sure to register to onboard them.", |
|
|
type: "confirm", |
|
|
}, |
|
|
title: "Register", |
|
|
-1, |
|
|
text: "Do you want to register them?", |
|
|
); |
|
|
onCancel: async (stopAsking: boolean) => { |
|
|
|
|
|
if (stopAsking) { |
|
|
|
|
|
db.settings.update(MASTER_SETTINGS_KEY, { |
|
|
|
|
|
hideRegisterPromptOnNewContact: stopAsking, |
|
|
|
|
|
}); |
|
|
|
|
|
this.hideRegisterPromptOnNewContact = stopAsking; |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
onNo: async (stopAsking: boolean) => { |
|
|
|
|
|
if (stopAsking) { |
|
|
|
|
|
db.settings.update(MASTER_SETTINGS_KEY, { |
|
|
|
|
|
hideRegisterPromptOnNewContact: stopAsking, |
|
|
|
|
|
}); |
|
|
|
|
|
this.hideRegisterPromptOnNewContact = stopAsking; |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
onYes: async () => { |
|
|
|
|
|
await this.register(newContact); |
|
|
|
|
|
}, |
|
|
|
|
|
promptToStopAsking: true, |
|
|
|
|
|
}, |
|
|
|
|
|
-1, |
|
|
|
|
|
); |
|
|
|
|
|
}, 500); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
@ -809,99 +835,65 @@ export default class ContactsView extends Vue { |
|
|
1000, |
|
|
1000, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const identity = await this.getIdentity(this.activeDid); |
|
|
try { |
|
|
|
|
|
const regResult = await register( |
|
|
const vcClaim: RegisterVerifiableCredential = { |
|
|
this.activeDid, |
|
|
"@context": "https://schema.org", |
|
|
this.apiServer, |
|
|
"@type": "RegisterAction", |
|
|
this.axios, |
|
|
agent: { identifier: identity.did }, |
|
|
contact, |
|
|
object: SERVICE_ID, |
|
|
); |
|
|
participant: { identifier: contact.did }, |
|
|
if (regResult.success) { |
|
|
}; |
|
|
contact.registered = true; |
|
|
// Make a payload for the claim |
|
|
db.contacts.update(contact.did, { registered: true }); |
|
|
const vcPayload = { |
|
|
|
|
|
vc: { |
|
|
|
|
|
"@context": ["https://www.w3.org/2018/credentials/v1"], |
|
|
|
|
|
type: ["VerifiableCredential"], |
|
|
|
|
|
credentialSubject: vcClaim, |
|
|
|
|
|
}, |
|
|
|
|
|
}; |
|
|
|
|
|
// Create a signature using private key of identity |
|
|
|
|
|
if (identity.keys[0].privateKeyHex !== null) { |
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
|
|
|
|
|
const privateKeyHex: string = identity.keys[0].privateKeyHex!; |
|
|
|
|
|
const signer = await SimpleSigner(privateKeyHex); |
|
|
|
|
|
const alg = undefined; |
|
|
|
|
|
// Create a JWT for the request |
|
|
|
|
|
const vcJwt: string = await didJwt.createJWT(vcPayload, { |
|
|
|
|
|
alg: alg, |
|
|
|
|
|
issuer: identity.did, |
|
|
|
|
|
signer: signer, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Make the xhr request payload |
|
|
|
|
|
const payload = JSON.stringify({ jwtEncoded: vcJwt }); |
|
|
|
|
|
const url = this.apiServer + "/api/v2/claim"; |
|
|
|
|
|
const headers = await this.getHeaders(identity); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
this.$notify( |
|
|
const resp = await this.axios.post(url, payload, { headers }); |
|
|
{ |
|
|
if (resp.data?.success?.embeddedRecordError) { |
|
|
group: "alert", |
|
|
let message = "There was some problem with the registration."; |
|
|
type: "success", |
|
|
if (typeof resp.data.success.embeddedRecordError == "string") { |
|
|
title: "Registration Success", |
|
|
message += " " + resp.data.success.embeddedRecordError; |
|
|
text: |
|
|
} |
|
|
(contact.name || "That unnamed person") + " has been registered.", |
|
|
this.$notify( |
|
|
}, |
|
|
{ |
|
|
5000, |
|
|
group: "alert", |
|
|
); |
|
|
type: "danger", |
|
|
} else { |
|
|
title: "Registration Still Unknown", |
|
|
|
|
|
text: message, |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} else if (resp.data?.success?.handleId) { |
|
|
|
|
|
contact.registered = true; |
|
|
|
|
|
db.contacts.update(contact.did, { registered: true }); |
|
|
|
|
|
|
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "success", |
|
|
|
|
|
title: "Registration Success", |
|
|
|
|
|
text: |
|
|
|
|
|
(contact.name || "That unnamed person") + |
|
|
|
|
|
" has been registered.", |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error("Error when registering:", error); |
|
|
|
|
|
let userMessage = "There was an error. See logs for more info."; |
|
|
|
|
|
const serverError = error as AxiosError; |
|
|
|
|
|
if (serverError) { |
|
|
|
|
|
if (serverError.response?.data?.error?.message) { |
|
|
|
|
|
userMessage = serverError.response.data.error.message; |
|
|
|
|
|
} else if (serverError.message) { |
|
|
|
|
|
userMessage = serverError.message; // Info for the user |
|
|
|
|
|
} else { |
|
|
|
|
|
userMessage = JSON.stringify(serverError.toJSON()); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
userMessage = error as string; |
|
|
|
|
|
} |
|
|
|
|
|
// Now set that error for the user to see. |
|
|
|
|
|
this.$notify( |
|
|
this.$notify( |
|
|
{ |
|
|
{ |
|
|
group: "alert", |
|
|
group: "alert", |
|
|
type: "danger", |
|
|
type: "danger", |
|
|
title: "Registration Error", |
|
|
title: "Registration Error", |
|
|
text: userMessage, |
|
|
text: |
|
|
|
|
|
(regResult.error as string) || |
|
|
|
|
|
"Something went wrong during registration.", |
|
|
}, |
|
|
}, |
|
|
5000, |
|
|
5000, |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error("Error when registering:", error); |
|
|
|
|
|
let userMessage = "There was an error. See logs for more info."; |
|
|
|
|
|
const serverError = error as AxiosError; |
|
|
|
|
|
if (serverError) { |
|
|
|
|
|
if (serverError.response?.data?.error?.message) { |
|
|
|
|
|
userMessage = serverError.response.data.error.message; |
|
|
|
|
|
} else if (serverError.message) { |
|
|
|
|
|
userMessage = serverError.message; // Info for the user |
|
|
|
|
|
} else { |
|
|
|
|
|
userMessage = JSON.stringify(serverError.toJSON()); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
userMessage = error as string; |
|
|
|
|
|
} |
|
|
|
|
|
// Now set that error for the user to see. |
|
|
|
|
|
this.$notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: "Registration Error", |
|
|
|
|
|
text: userMessage, |
|
|
|
|
|
}, |
|
|
|
|
|
5000, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -980,6 +972,13 @@ export default class ContactsView extends Vue { |
|
|
if (resp.status === 200) { |
|
|
if (resp.status === 200) { |
|
|
const visibility = resp.data; |
|
|
const visibility = resp.data; |
|
|
contact.seesMe = visibility; |
|
|
contact.seesMe = visibility; |
|
|
|
|
|
console.log( |
|
|
|
|
|
"Visibility checked:", |
|
|
|
|
|
visibility, |
|
|
|
|
|
contact.did, |
|
|
|
|
|
contact.name, |
|
|
|
|
|
); // eslint-disable-line no-console |
|
|
|
|
|
console.log(this.contacts); // eslint-disable-line no-console |
|
|
db.contacts.update(contact.did, { seesMe: visibility }); |
|
|
db.contacts.update(contact.did, { seesMe: visibility }); |
|
|
|
|
|
|
|
|
this.$notify( |
|
|
this.$notify( |
|
@ -1064,7 +1063,7 @@ export default class ContactsView extends Vue { |
|
|
this.showGiftedDialog(giverDid, recipientDid); |
|
|
this.showGiftedDialog(giverDid, recipientDid); |
|
|
}, |
|
|
}, |
|
|
onYes: async () => { |
|
|
onYes: async () => { |
|
|
this.$router.push({ |
|
|
(this.$router as Router).push({ |
|
|
name: "contact-amounts", |
|
|
name: "contact-amounts", |
|
|
query: { contactDid: giverDid }, |
|
|
query: { contactDid: giverDid }, |
|
|
}); |
|
|
}); |
|
@ -1159,6 +1158,18 @@ export default class ContactsView extends Vue { |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
this.showGiveNumbers = newShowValue; |
|
|
this.showGiveNumbers = newShowValue; |
|
|
|
|
|
if ( |
|
|
|
|
|
newShowValue && |
|
|
|
|
|
Object.keys(this.givenByMeDescriptions).length === 0 && |
|
|
|
|
|
Object.keys(this.givenByMeConfirmed).length === 0 && |
|
|
|
|
|
Object.keys(this.givenByMeUnconfirmed).length === 0 && |
|
|
|
|
|
Object.keys(this.givenToMeDescriptions).length === 0 && |
|
|
|
|
|
Object.keys(this.givenToMeConfirmed).length === 0 && |
|
|
|
|
|
Object.keys(this.givenToMeUnconfirmed).length === 0 |
|
|
|
|
|
) { |
|
|
|
|
|
// assume we should load it all |
|
|
|
|
|
this.loadGives(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
public toggleShowGiveTotals() { |
|
|
public toggleShowGiveTotals() { |
|
|
if (this.showGiveTotals) { |
|
|
if (this.showGiveTotals) { |
|
|