forked from trent_larson/crowd-funder-for-time-pwa
catch more errors if something catastrophic happens to encrypted data
This commit is contained in:
22
src/App.vue
22
src/App.vue
@@ -309,6 +309,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
|
||||
>
|
||||
<div
|
||||
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="w-full px-6 py-6 text-slate-900 text-center">
|
||||
<p class="text-lg mb-4">
|
||||
Something has gone very wrong. We'd appreciate if you'd
|
||||
contact us and let us know how you got here. Thank you!
|
||||
</p>
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Notification>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<div
|
||||
v-if="isVisible"
|
||||
class="fixed z-[100] top-0 inset-x-0 w-full absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
|
||||
class="fixed z-[100] top-0 inset-x-0 w-full inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
|
||||
>
|
||||
<div
|
||||
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
|
||||
@@ -276,9 +276,9 @@ export default class PushNotificationPermission extends Vue {
|
||||
}
|
||||
|
||||
private async askPermission(): Promise<NotificationPermission> {
|
||||
logConsoleAndDb(
|
||||
"Requesting permission for notifications: " + JSON.stringify(navigator),
|
||||
);
|
||||
// console.log(
|
||||
// "Requesting permission for notifications: " + JSON.stringify(navigator),
|
||||
// );
|
||||
if (
|
||||
!("serviceWorker" in navigator && navigator.serviceWorker?.controller)
|
||||
) {
|
||||
@@ -344,7 +344,7 @@ export default class PushNotificationPermission extends Vue {
|
||||
},
|
||||
-1,
|
||||
);
|
||||
throw new Error("We weren't granted permission.");
|
||||
throw new Error("Permission was not granted to this app.");
|
||||
}
|
||||
return permission;
|
||||
},
|
||||
|
||||
@@ -90,6 +90,12 @@
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="circle-user" class="fa-fw" />
|
||||
<!--
|
||||
We used to say "account", so we'll keep that in the code,
|
||||
but it isn't accurate because we don't hold anything for them.
|
||||
We'll say "profile" to the users.
|
||||
(Or: settings, face, registry, cache, repo, vault... or separate preferences from identity.)
|
||||
-->
|
||||
<span class="text-xs mt-1">profile</span>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
@@ -4,10 +4,10 @@ import { sha256 } from "ethereum-cryptography/sha256";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import * as R from "ramda";
|
||||
|
||||
import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
|
||||
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { accessToken, deriveAddress, nextDerivationPath } from "@/libs/crypto";
|
||||
import { NonsensitiveDexie } from "@/db/index";
|
||||
import { logConsoleAndDb, NonsensitiveDexie } from "@/db/index";
|
||||
import {
|
||||
retrieveAccountMetadata,
|
||||
retrieveFullyDecryptedAccount,
|
||||
@@ -501,35 +501,72 @@ export function tokenExpiryTimeDescription() {
|
||||
/**
|
||||
* Get the headers for a request, potentially including Authorization
|
||||
*/
|
||||
export async function getHeaders(did?: string) {
|
||||
export async function getHeaders(
|
||||
did?: string,
|
||||
$notify?: (notification: NotificationIface, timeout?: number) => void,
|
||||
failureMessage?: string,
|
||||
) {
|
||||
const headers: { "Content-Type": string; Authorization?: string } = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (did) {
|
||||
let token;
|
||||
const account = await retrieveAccountMetadata(did);
|
||||
if (account?.passkeyCredIdHex) {
|
||||
if (
|
||||
passkeyAccessToken &&
|
||||
passkeyTokenExpirationEpochSeconds > Date.now() / 1000
|
||||
) {
|
||||
// there's an active current passkey token
|
||||
token = passkeyAccessToken;
|
||||
} else {
|
||||
// there's no current passkey token or it's expired
|
||||
token = await accessToken(did);
|
||||
try {
|
||||
let token;
|
||||
const account = await retrieveAccountMetadata(did);
|
||||
if (account?.passkeyCredIdHex) {
|
||||
if (
|
||||
passkeyAccessToken &&
|
||||
passkeyTokenExpirationEpochSeconds > Date.now() / 1000
|
||||
) {
|
||||
// there's an active current passkey token
|
||||
token = passkeyAccessToken;
|
||||
} else {
|
||||
// there's no current passkey token or it's expired
|
||||
token = await accessToken(did);
|
||||
|
||||
passkeyAccessToken = token;
|
||||
const passkeyExpirationSeconds = await getPasskeyExpirationSeconds();
|
||||
passkeyTokenExpirationEpochSeconds =
|
||||
Date.now() / 1000 + passkeyExpirationSeconds;
|
||||
passkeyAccessToken = token;
|
||||
const passkeyExpirationSeconds = await getPasskeyExpirationSeconds();
|
||||
passkeyTokenExpirationEpochSeconds =
|
||||
Date.now() / 1000 + passkeyExpirationSeconds;
|
||||
}
|
||||
} else {
|
||||
token = await accessToken(did);
|
||||
}
|
||||
headers["Authorization"] = "Bearer " + token;
|
||||
} catch (error) {
|
||||
// This rarely happens: we've seen it when they have account info but the
|
||||
// encryption secret got lost. But in most cases we want users to at
|
||||
// least see their feed -- and anything else that returns results for
|
||||
// anonymous users.
|
||||
|
||||
// We'll continue with an anonymous request... still want to show feed and other things, but ideally let them know.
|
||||
logConsoleAndDb(
|
||||
"Something failed in getHeaders call (will proceed anonymously" +
|
||||
($notify ? " and notify user" : "") +
|
||||
"): " +
|
||||
// IntelliJ type system complains about getCircularReplacer() with: Argument of type '(obj: any, key: string, value: any) => any' is not assignable to parameter of type '(this: any, key: string, value: any) => any'.
|
||||
//JSON.stringify(error, getCircularReplacer()), // JSON.stringify(error) on a Dexie error throws another error about: Converting circular structure to JSON
|
||||
error,
|
||||
true,
|
||||
);
|
||||
if ($notify) {
|
||||
// remember: only want to do this if they supplied a DID, expecting personal results
|
||||
const notifyMessage =
|
||||
failureMessage ||
|
||||
"Showing anonymous data. See the Help page for help with personal data.";
|
||||
$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Personal Data Error",
|
||||
text: notifyMessage,
|
||||
},
|
||||
3000,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
token = await accessToken(did);
|
||||
}
|
||||
headers["Authorization"] = "Bearer " + token;
|
||||
} else {
|
||||
// it's often OK to request without auth; we assume necessary checks are done earlier
|
||||
// it's usually OK to request without auth; we assume we're only here when allowed
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
@@ -611,6 +648,7 @@ export async function getNewOffersToUser(
|
||||
url += "&beforeId=" + beforeOfferJwtId;
|
||||
}
|
||||
const headers = await getHeaders(activeDid);
|
||||
console.log("Using headers: ", headers);
|
||||
const response = await axios.get(url, { headers });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,23 @@ export interface ConfirmerData {
|
||||
numConfsNotVisible: number;
|
||||
}
|
||||
|
||||
// // This is meant to be a second argument to JSON.stringify to avoid circular references.
|
||||
// // Usage: JSON.stringify(error, getCircularReplacer())
|
||||
// // Beware: we've seen this return "undefined" when there is actually a message, eg: DatabaseClosedError: Error DEXIE ENCRYPT ADDON: Encryption key has changed
|
||||
// function getCircularReplacer() {
|
||||
// const seen = new WeakSet();
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// return (obj: any, key: string, value: any): any => {
|
||||
// if (typeof value === "object" && value !== null) {
|
||||
// if (seen.has(value)) {
|
||||
// return "[circular ref]";
|
||||
// }
|
||||
// seen.add(value);
|
||||
// }
|
||||
// return value;
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* @return only confirmers, excluding the issuer and hidden DIDs
|
||||
*/
|
||||
@@ -423,11 +440,13 @@ export function findAllVisibleToDids(
|
||||
export interface AccountKeyInfo extends Account, KeyMeta {}
|
||||
|
||||
export const retrieveAccountCount = async (): Promise<number> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
return await accountsDB.accounts.count();
|
||||
};
|
||||
|
||||
export const retrieveAccountDids = async (): Promise<string[]> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const allAccounts = await accountsDB.accounts.toArray();
|
||||
const allDids = allAccounts.map((acc) => acc.did);
|
||||
@@ -439,6 +458,7 @@ export const retrieveAccountDids = async (): Promise<string[]> => {
|
||||
export const retrieveAccountMetadata = async (
|
||||
activeDid: string,
|
||||
): Promise<AccountKeyInfo | undefined> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const account = (await accountsDB.accounts
|
||||
.where("did")
|
||||
@@ -454,6 +474,7 @@ export const retrieveAccountMetadata = async (
|
||||
};
|
||||
|
||||
export const retrieveAllAccountsMetadata = async (): Promise<Account[]> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const array = await accountsDB.accounts.toArray();
|
||||
return array.map((account) => {
|
||||
@@ -466,6 +487,7 @@ export const retrieveAllAccountsMetadata = async (): Promise<Account[]> => {
|
||||
export const retrieveFullyDecryptedAccount = async (
|
||||
activeDid: string,
|
||||
): Promise<AccountKeyInfo | undefined> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const account = (await accountsDB.accounts
|
||||
.where("did")
|
||||
@@ -496,6 +518,7 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
|
||||
const newId = newIdentifier(address, publicHex, privateHex, derivationPath);
|
||||
const identity = JSON.stringify(newId);
|
||||
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.add({
|
||||
dateCreated: new Date().toISOString(),
|
||||
@@ -527,6 +550,7 @@ export const registerAndSavePasskey = async (
|
||||
passkeyCredIdHex,
|
||||
publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"),
|
||||
};
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.add(account);
|
||||
return account;
|
||||
|
||||
@@ -18,6 +18,7 @@ const enterOrStart = async (
|
||||
from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext,
|
||||
) => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const num_accounts = await accountsDB.accounts.count();
|
||||
if (num_accounts > 0) {
|
||||
@@ -263,6 +264,9 @@ const errorHandler = (
|
||||
) => {
|
||||
// Handle the error here
|
||||
console.error("Caught in top level error handler:", error, to, from);
|
||||
alert(
|
||||
"Something is very wrong. We'd love if you contacted us and let us know how you got here. Thank you!",
|
||||
);
|
||||
|
||||
// You can also perform additional actions, such as displaying an error message or redirecting the user to a specific page
|
||||
};
|
||||
|
||||
@@ -951,8 +951,8 @@ export default class AccountViewView extends Vue {
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Account",
|
||||
text: "Clear your cache and start over (after data backup).",
|
||||
title: "Error Loading Profile",
|
||||
text: "See the Help page about errors with your personal data.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
|
||||
@@ -484,7 +484,11 @@ import { useClipboard } from "@vueuse/core";
|
||||
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { db, retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import {
|
||||
db,
|
||||
logConsoleAndDb,
|
||||
retrieveSettingsForActiveAccount,
|
||||
} from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import * as serverUtil from "@/libs/endorserServer";
|
||||
import {
|
||||
@@ -559,7 +563,24 @@ export default class ClaimView extends Vue {
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
this.isRegistered = settings.isRegistered || false;
|
||||
|
||||
this.allMyDids = await libsUtil.retrieveAccountDids();
|
||||
try {
|
||||
this.allMyDids = await libsUtil.retrieveAccountDids();
|
||||
} catch (error) {
|
||||
// continue because we want to see claims, even anonymously
|
||||
logConsoleAndDb(
|
||||
"Error retrieving all account DIDs on home page:" + error,
|
||||
true,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Profile",
|
||||
text: "See the Help page for problems with your personal data.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
const pathParam = window.location.pathname.substring("/claim/".length);
|
||||
let claimId;
|
||||
|
||||
@@ -171,8 +171,9 @@
|
||||
<fa icon="circle-info" class="text-xl text-blue-500 ml-4" />
|
||||
</router-link>
|
||||
|
||||
<span class="ml-4 text-sm overflow-hidden"
|
||||
>{{ shortDid(contact.did) }}...</span
|
||||
<span class="ml-4 text-sm overflow-hidden">{{
|
||||
shortDid(contact.did)
|
||||
}}</span
|
||||
><!-- The first 18 characters of did:peer are the same. -->
|
||||
</div>
|
||||
<div id="ContactActions" class="flex gap-1.5 mt-2">
|
||||
@@ -425,7 +426,7 @@ export default class ContactsView extends Vue {
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Blank Invite",
|
||||
text: "The invite was not included. This can happen when your device cuts off the link, so you might try pasting the full link into a browser.",
|
||||
text: "The invite was not included, which can happen when your iOS device cuts off the link. Try pasting the full link into a browser.",
|
||||
},
|
||||
7000,
|
||||
);
|
||||
@@ -601,7 +602,7 @@ export default class ContactsView extends Vue {
|
||||
};
|
||||
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const headers = await getHeaders(this.activeDid, this.$notify);
|
||||
const givenByUrl =
|
||||
this.apiServer +
|
||||
"/api/v2/report/gives?agentDid=" +
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
<button class="text-blue-500" @click="showNotificationChoice()">
|
||||
Click here.
|
||||
</button>
|
||||
<PushNotificationPermission ref="pushNotificationPermission" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -193,14 +194,18 @@
|
||||
<h2 class="text-xl font-semibold mt-4">Reinstall</h2>
|
||||
<div>
|
||||
<p>
|
||||
If all else fails, uninstall the app, ensure all the browser tabs with
|
||||
it are closed, and clear out caches and storage.
|
||||
If all else fails, it's best to start over.
|
||||
</p>
|
||||
<p>
|
||||
Of course, you'll want to back up all your data first -- all seeds as
|
||||
well as the contacts & settings -- on the Account
|
||||
well as the contacts & settings -- on the Profile
|
||||
<fa icon="circle-user" /> page.
|
||||
</p>
|
||||
<p>
|
||||
Here are instructions to uninstall the app and clear out caches and storage.
|
||||
Note that you should first ensure check that the browser tabs with Time Safari are closed.
|
||||
(If any are open then that will interfere with your refresh.)
|
||||
</p>
|
||||
<ul class="ml-4 list-disc">
|
||||
<li>
|
||||
Clear cache.
|
||||
@@ -304,9 +309,12 @@ import { Component, Vue } from "vue-facing-decorator";
|
||||
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { sendTestThroughPushServer } from "@/libs/util";
|
||||
import { DIRECT_PUSH_TITLE, sendTestThroughPushServer } from "@/libs/util";
|
||||
import PushNotificationPermission from "@/components/PushNotificationPermission.vue";
|
||||
import { db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
|
||||
@Component({ components: { QuickNav } })
|
||||
@Component({ components: { PushNotificationPermission, QuickNav } })
|
||||
export default class HelpNotificationsView extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
|
||||
@@ -407,14 +415,19 @@ export default class HelpNotificationsView extends Vue {
|
||||
}
|
||||
|
||||
showNotificationChoice() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "modal",
|
||||
type: "notification-permission",
|
||||
title: "", // unused, only here to satisfy type check
|
||||
text: "", // unused, only here to satisfy type check
|
||||
(this.$refs.pushNotificationPermission as PushNotificationPermission).open(
|
||||
DIRECT_PUSH_TITLE,
|
||||
async (success: boolean, timeText: string, message?: string) => {
|
||||
if (success) {
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingReminderMessage: message,
|
||||
notifyingReminderTime: timeText,
|
||||
});
|
||||
this.notifyingReminder = true;
|
||||
this.notifyingReminderMessage = message || "";
|
||||
this.notifyingReminderTime = timeText;
|
||||
}
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
How do I access even more functionality?
|
||||
</h2>
|
||||
<p>
|
||||
There is an "Advanced" section at the bottom of the Account
|
||||
There is an "Advanced" section at the bottom of the Profile
|
||||
<fa icon="circle-user" /> page.
|
||||
</p>
|
||||
<p>
|
||||
@@ -422,19 +422,19 @@
|
||||
</p>
|
||||
|
||||
<h2 class="text-xl font-semibold">
|
||||
My app is misbehaving, like showing me a blank screen or failing to show a feed.
|
||||
This app is misbehaving, like showing me a blank screen or failing to show my personal data.
|
||||
What can I do?
|
||||
</h2>
|
||||
<p>
|
||||
First, note that clearing the cache will clear all your identity and contact info,
|
||||
so we recommend doing other things first (unless you know you have your backups ready).
|
||||
so we recommend doing other things first -- and only clearing when have your backups ready.
|
||||
</p>
|
||||
<ul class="list-disc list-outside ml-4">
|
||||
<li>
|
||||
Drag down on the screen to refresh it; do that multiple times, because
|
||||
it sometimes takes multiple tries for the app to refresh to the current version.
|
||||
it sometimes takes multiple tries for the app to refresh to the latest version.
|
||||
You can see the version information at the bottom of this page; the best
|
||||
way to determine the current version is to open this page in an incognito
|
||||
way to determine the latest version is to open this page in an incognito/private
|
||||
browser window and look at the version there.
|
||||
</li>
|
||||
<li>
|
||||
@@ -498,7 +498,7 @@
|
||||
</a>
|
||||
<br />
|
||||
For notifications, this service stores push token data; that can be revoked at any time
|
||||
by disabling notifications on the Account <fa icon="circle-user" class="fa-fw" /> page.
|
||||
by disabling notifications on the Profile <fa icon="circle-user" class="fa-fw" /> page.
|
||||
<br />
|
||||
For all other claim data,
|
||||
<a href="https://endorser.ch/privacy-policy" target="_blank" class="text-blue-500">
|
||||
@@ -520,9 +520,9 @@
|
||||
class="text-blue-500 ml-2"
|
||||
>
|
||||
bc1q90v4ted6cpt63tjfh2lvd5xzfc67sd4g9w8xma
|
||||
<fa v-show="!showDidCopy" icon="copy" class="text-slate-400 fa-fw" />
|
||||
<fa v-show="!showDidCopy" icon="copy" class="text-sm text-slate-400 fa-fw" />
|
||||
<fa v-show="showDidCopy" icon="circle-check" class="text-sm text-green-500 fa-fw"/>
|
||||
</button>
|
||||
<span v-show="showDidCopy" class="ml-2 text-sm text-green-500">Copied</span>
|
||||
You can donate online via
|
||||
<a href="https://www.patreon.com/TimeSafari" target="_blank" class="text-blue-500">Patreon here</a>.
|
||||
For other donations, contact us.
|
||||
@@ -541,7 +541,7 @@
|
||||
<p>{{ package.version }} ({{ commitHash }})</p>
|
||||
|
||||
<h2 class="text-xl font-semibold">
|
||||
I have other questions or feedback, like getting a new account or removing my data or requesting an improvement.
|
||||
I have other questions or feedback, like getting a new profile or removing my data or requesting an improvement.
|
||||
</h2>
|
||||
<p>
|
||||
Contact us at
|
||||
|
||||
@@ -389,6 +389,7 @@ import {
|
||||
} from "@/constants/app";
|
||||
import {
|
||||
db,
|
||||
logConsoleAndDb,
|
||||
retrieveSettingsForActiveAccount,
|
||||
updateAccountSettings,
|
||||
} from "@/db/index";
|
||||
@@ -486,12 +487,21 @@ export default class HomeView extends Vue {
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
if (this.allMyDids.length === 0) {
|
||||
this.isCreatingIdentifier = true;
|
||||
const newDid = await generateSaveAndActivateIdentity();
|
||||
this.isCreatingIdentifier = false;
|
||||
this.allMyDids = [newDid];
|
||||
try {
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
if (this.allMyDids.length === 0) {
|
||||
this.isCreatingIdentifier = true;
|
||||
const newDid = await generateSaveAndActivateIdentity();
|
||||
this.isCreatingIdentifier = false;
|
||||
this.allMyDids = [newDid];
|
||||
}
|
||||
} catch (error) {
|
||||
// continue because we want the feed to work, even anonymously
|
||||
logConsoleAndDb(
|
||||
"Error retrieving all account DIDs on home page:" + error,
|
||||
true,
|
||||
);
|
||||
// some other piece will display an error about personal info
|
||||
}
|
||||
|
||||
const settings = await retrieveSettingsForActiveAccount();
|
||||
@@ -546,6 +556,7 @@ export default class HomeView extends Vue {
|
||||
this.activeDid,
|
||||
this.lastAckedOfferToUserJwtId,
|
||||
);
|
||||
console.log("offersToUserData", offersToUserData);
|
||||
this.numNewOffersToUser = offersToUserData.data.length;
|
||||
this.newOffersToUserHitLimit = offersToUserData.hitLimit;
|
||||
}
|
||||
@@ -563,7 +574,7 @@ export default class HomeView extends Vue {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings or feed.", err);
|
||||
logConsoleAndDb("Error retrieving settings or feed: " + err, true);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -760,13 +771,19 @@ export default class HomeView extends Vue {
|
||||
*/
|
||||
async retrieveGives(endorserApiServer: string, beforeId?: string) {
|
||||
const beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId;
|
||||
const doNotShowErrorAgain = !!beforeId; // don't show error again if we're loading more
|
||||
const headers = await getHeaders(
|
||||
this.activeDid,
|
||||
doNotShowErrorAgain ? undefined : this.$notify,
|
||||
);
|
||||
// retrieve headers for this user, but if an error happens then report it but proceed with the fetch with no header
|
||||
const response = await fetch(
|
||||
endorserApiServer +
|
||||
"/api/v2/report/gives?giftNotTrade=true" +
|
||||
beforeQuery,
|
||||
{
|
||||
method: "GET",
|
||||
headers: await getHeaders(this.activeDid),
|
||||
headers: headers,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ export default class IdentitySwitcherView extends Vue {
|
||||
title: "Delete Identity?",
|
||||
text: "Are you sure you want to erase this identity? (There is no undo. You may want to select it and back it up just in case.)",
|
||||
onYes: async () => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.delete(id);
|
||||
this.otherIdentities = this.otherIdentities.filter(
|
||||
|
||||
@@ -489,7 +489,11 @@ import QuickNav from "@/components/QuickNav.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
import ProjectIcon from "@/components/ProjectIcon.vue";
|
||||
import { NotificationIface } from "@/constants/app";
|
||||
import { db, retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import {
|
||||
db,
|
||||
logConsoleAndDb,
|
||||
retrieveSettingsForActiveAccount,
|
||||
} from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import * as libsUtil from "@/libs/util";
|
||||
import {
|
||||
@@ -557,7 +561,24 @@ export default class ProjectViewView extends Vue {
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
try {
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
} catch (error) {
|
||||
// continue because we want to see claims, even anonymously
|
||||
logConsoleAndDb(
|
||||
"Error retrieving all account DIDs on home page:" + error,
|
||||
true,
|
||||
);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Profile",
|
||||
text: "See the Help page to fix problems with your personal data.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
const pathParam = window.location.pathname.substring("/project/".length);
|
||||
if (pathParam) {
|
||||
|
||||
@@ -355,7 +355,7 @@ export default class ProjectsView extends Vue {
|
||||
**/
|
||||
async projectDataLoader(url: string) {
|
||||
try {
|
||||
const headers = await getHeaders(this.activeDid);
|
||||
const headers = await getHeaders(this.activeDid, this.$notify);
|
||||
this.isLoading = true;
|
||||
const resp = await this.axios.get(url, { headers } as AxiosRequestConfig);
|
||||
if (resp.status === 200 && resp.data.data) {
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>You do not have an active identifier.</div>
|
||||
<div v-else>You do not have an active identity.</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -135,7 +135,7 @@ export default class SeedBackupView extends Vue {
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Loading Account",
|
||||
title: "Error Loading Profile",
|
||||
text: "Got an error loading your seed data.",
|
||||
},
|
||||
-1,
|
||||
|
||||
Reference in New Issue
Block a user