Browse Source

add marker in feed to show where they've seen claims, plus other small clean-up

pull/23/head
Trent Larson 1 year ago
parent
commit
5747404fd6
  1. 29
      project.task.yaml
  2. 2
      src/db/tables/settings.ts
  3. 1
      src/router/index.ts
  4. 27
      src/views/AccountViewView.vue
  5. 10
      src/views/ContactAmountsView.vue
  6. 5
      src/views/ContactQRScanShowView.vue
  7. 22
      src/views/ContactsView.vue
  8. 39
      src/views/HomeView.vue
  9. 10
      src/views/NewEditProjectView.vue
  10. 5
      src/views/ProjectViewView.vue
  11. 5
      src/views/ProjectsView.vue
  12. 2
      src/views/StatisticsView.vue

29
project.task.yaml

@ -4,13 +4,14 @@
- add infinite scroll assignee:matthew - add infinite scroll assignee:matthew
blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time
- allow type annotations in World.js & landmarks.js (since we get this error: "Types are not supported by current JavaScript version") - allow type annotations in World.js & landmarks.js (since we get this error - "Types are not supported by current JavaScript version")
- replace user-affecting console.log & console.error with error messages (eg. catches) - replace user-affecting console.log & console.error with error messages (eg. catches)
- if there's no identity, handle it on pages which expect an identity (eg. project -- look for JSON.parse identity calls)
- stats v1 : - show project activity :
- 01 show numeric stats - Save IDs of special projects of interest
- 01 link to world for specific stats - allow to give where a project is the provider
- .5 don't load another instance of a bush if it already exists - allow to see activity where a project is provider
- contacts v1 : - contacts v1 :
- 01 Import contact info a la QR code. - 01 Import contact info a la QR code.
@ -29,19 +30,26 @@
- show pop-up confirming that settings & contacts have been downloaded - show pop-up confirming that settings & contacts have been downloaded
- Ensure each action sent to the server has a confirmation. - Ensure each action sent to the server has a confirmation - registration
- Home Feed & Quick Give screen - Home Feed & Quick Give screen :
- 01 save the feed-viewed status in settings storage ("afterQuery") - 01 save the feed-viewed status in settings storage ("afterQuery")
- 01 quick action - send action, maybe choose via canvas tool https://github.com/konvajs/vue-konva - 01 quick action - send action, maybe choose via canvas tool https://github.com/konvajs/vue-konva
- .5 customize favicon - .5 customize favicon
- .5 make advanced features harder to access; advanced build? - .5 make advanced features harder to access; advanced build?
- 40 notifications - 40 notifications :
- pull, w/ scheduled runs
- push - push
- stats v1 :
- 01 show numeric stats
- 01 link to world for specific stats
- .5 don't load another instance of a bush if it already exists
- Do we want split first name & last name?
- remove 'about' page
- Release Minimum Viable Product : - Release Minimum Viable Product :
- Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot). - Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot).
- Add disclaimers. - Add disclaimers.
@ -51,6 +59,9 @@
- Ensure public server has limits that work for group adoption. - Ensure public server has limits that work for group adoption.
- Test PWA features on Android and iOS. - Test PWA features on Android and iOS.
- 40 notifications v+ :
- pull, w/ scheduled runs
- Stats : - Stats :
- 01 point out user's location on the world - 01 point out user's location on the world
- 01 present a credential selected from the stats - 01 present a credential selected from the stats

2
src/db/tables/settings.ts

@ -1,10 +1,12 @@
// a singleton // a singleton
export type Settings = { export type Settings = {
id: number; // there's only one entry: MASTER_SETTINGS_KEY id: number; // there's only one entry: MASTER_SETTINGS_KEY
activeDid?: string; activeDid?: string;
apiServer?: string; apiServer?: string;
firstName?: string; firstName?: string;
lastName?: string; lastName?: string;
lastViewedClaimId?: string;
showContactGivesInline?: boolean; showContactGivesInline?: boolean;
}; };

1
src/router/index.ts

@ -132,6 +132,7 @@ const routes: Array<RouteRecordRaw> = [
name: "projects", name: "projects",
component: () => component: () =>
import(/* webpackChunkName: "projects" */ "../views/ProjectsView.vue"), import(/* webpackChunkName: "projects" */ "../views/ProjectsView.vue"),
beforeEnter: enterOrStart,
}, },
{ {
path: "/seed-backup", path: "/seed-backup",

27
src/views/AccountViewView.vue

@ -289,10 +289,10 @@
</div> </div>
<div> <div>
<button class="text-blue-500 px-2"> <button class="text-blue-500">
<router-link <router-link
:to="{ name: 'statistics' }" :to="{ name: 'statistics' }"
class="block text-center py-3 px-1" class="block text-center py-3"
> >
See Achievements & Statistics See Achievements & Statistics
</router-link> </router-link>
@ -427,7 +427,10 @@ export default class AccountViewView extends Vue {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
this.publicHex = identity.keys[0].publicKeyHex; this.publicHex = identity.keys[0].publicKeyHex;
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
this.derivationPath = identity.keys[0].meta.derivationPath; this.derivationPath = identity.keys[0].meta.derivationPath;
@ -437,8 +440,8 @@ export default class AccountViewView extends Vue {
}); });
} catch (err) { } catch (err) {
this.alertMessage = this.alertMessage =
"Clear your cache and start over (after data backup). See console log for more info."; "Clear your cache and start over (after data backup).";
console.error("Telling user to clear cache because:", err); console.error("Telling user to clear cache at page create because:", err);
this.alertTitle = "Error Creating Account"; this.alertTitle = "Error Creating Account";
this.isAlertVisible = true; this.isAlertVisible = true;
} }
@ -452,9 +455,12 @@ export default class AccountViewView extends Vue {
}); });
} catch (err) { } catch (err) {
this.alertMessage = this.alertMessage =
"Clear your cache and start over (after data backup). See console log for more info."; "Clear your cache and start over (after data backup).";
console.error("Telling user to clear cache because:", err); console.error(
this.alertTitle = "Error Creating Account"; "Telling user to clear cache after contact setting update because:",
err
);
this.alertTitle = "Error Updating Contact Setting";
this.isAlertVisible = true; this.isAlertVisible = true;
} }
} }
@ -487,7 +493,10 @@ export default class AccountViewView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",

10
src/views/ContactAmountsView.vue

@ -168,7 +168,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts); const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
// load all the time I have given to them // load all the time I have given to them
try { try {
@ -267,7 +270,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
if (identity.keys[0].privateKeyHex !== null) { if (identity.keys[0].privateKeyHex !== null) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const privateKeyHex: string = identity.keys[0].privateKeyHex!; const privateKeyHex: string = identity.keys[0].privateKeyHex!;

5
src/views/ContactQRScanShowView.vue

@ -113,7 +113,10 @@ export default class ContactQRScanShow extends Vue {
if (!account) { if (!account) {
this.alertMessage = "You have no identity yet."; this.alertMessage = "You have no identity yet.";
} else { } else {
const identity = JSON.parse(account.identity); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const publicKeyHex = identity.keys[0].publicKeyHex; const publicKeyHex = identity.keys[0].publicKeyHex;
const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64"); const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64");

22
src/views/ContactsView.vue

@ -314,7 +314,7 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) { if (!identity) {
console.error( console.error(
@ -487,7 +487,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
// Make a claim // Make a claim
const vcClaim: RegisterVerifiableCredential = { const vcClaim: RegisterVerifiableCredential = {
@ -567,7 +570,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
@ -606,7 +612,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity); const token = await accessToken(identity);
const headers = { const headers = {
@ -663,7 +672,10 @@ export default class ContactsView extends Vue {
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
// if they have unconfirmed amounts, ask to confirm those first // if they have unconfirmed amounts, ask to confirm those first
if (toDid == identity?.did && this.givenToMeUnconfirmed[fromDid] > 0) { if (toDid == identity?.did && this.givenToMeUnconfirmed[fromDid] > 0) {

39
src/views/HomeView.vue

@ -57,12 +57,12 @@
v-for="contact in allContacts" v-for="contact in allContacts"
:key="contact.did" :key="contact.did"
@click="openDialog(contact)" @click="openDialog(contact)"
style="color: blue" class="text-blue-500"
> >
&nbsp;{{ contact.name }}, &nbsp;{{ contact.name }},
</button> </button>
or or
<button @click="openDialog()" style="color: blue"> <button @click="openDialog()" class="text-blue-500">
nobody in particular nobody in particular
</button> </button>
</div> </div>
@ -85,8 +85,14 @@
<li <li
class="border-b border-slate-300" class="border-b border-slate-300"
v-for="record in feedData" v-for="record in feedData"
:key="record.id" :key="record.jwtId"
> >
<div
class="border-b text-orange-400 px-8 py-4"
v-if="record.jwtId == feedLastViewedId"
>
You've seen all claims below.
</div>
{{ this.giveDescription(record) }} {{ this.giveDescription(record) }}
</li> </li>
</ul> </ul>
@ -129,8 +135,8 @@ export default class HomeView extends Vue {
feedAllLoaded = false; feedAllLoaded = false;
feedData = []; feedData = [];
feedPreviousOldestId = null; feedPreviousOldestId = null;
feedLastViewedId = null;
isHiddenSpinner = true; isHiddenSpinner = true;
showInput = false;
// 'created' hook runs when the Vue instance is first created // 'created' hook runs when the Vue instance is first created
async created() { async created() {
@ -140,6 +146,7 @@ export default class HomeView extends Vue {
const settings = await db.settings.get(MASTER_SETTINGS_KEY); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
this.activeDid = settings?.activeDid || ""; this.activeDid = settings?.activeDid || "";
this.allContacts = await db.contacts.toArray(); this.allContacts = await db.contacts.toArray();
this.feedLastViewedId = settings?.lastViewedClaimId;
} }
// 'mounted' hook runs after initial render // 'mounted' hook runs after initial render
@ -168,7 +175,19 @@ export default class HomeView extends Vue {
this.feedData = this.feedData.concat(results.data); this.feedData = this.feedData.concat(results.data);
//console.log("Feed data:", this.feedData); //console.log("Feed data:", this.feedData);
this.feedAllLoaded = results.hitLimit; this.feedAllLoaded = results.hitLimit;
this.feedPreviousOldestId = results.data[results.data.length - 1].id; this.feedPreviousOldestId =
results.data[results.data.length - 1].jwtId;
if (
this.feedLastViewedId == null ||
this.feedLastViewedId < results.data[0].jwtId
) {
// save it to storage
await db.open();
db.settings.update(MASTER_SETTINGS_KEY, {
lastViewedClaimId: results.data[0].jwtId,
});
// but not for this page because we need to remember what it was before
}
} }
}) })
.catch((e) => { .catch((e) => {
@ -190,7 +209,10 @@ export default class HomeView extends Vue {
this.allAccounts this.allAccounts
); );
//console.log("about to parse from", this.activeDid, account?.identity); //console.log("about to parse from", this.activeDid, account?.identity);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
const token = await accessToken(identity); const token = await accessToken(identity);
headers["Authorization"] = "Bearer " + token; headers["Authorization"] = "Bearer " + token;
} else { } else {
@ -278,7 +300,10 @@ export default class HomeView extends Vue {
this.allAccounts this.allAccounts
); );
//console.log("about to parse from", this.activeDid, account?.identity); //console.log("about to parse from", this.activeDid, account?.identity);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
createAndSubmitGive( createAndSubmitGive(
this.axios, this.axios,
this.apiServer, this.apiServer,

10
src/views/NewEditProjectView.vue

@ -127,7 +127,10 @@ export default class NewEditProjectView extends Vue {
} else { } else {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
this.LoadProject(identity); this.LoadProject(identity);
} }
} }
@ -259,7 +262,10 @@ export default class NewEditProjectView extends Vue {
} else { } else {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === this.activeDid, accounts); const account = R.find((acc) => acc.did === this.activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
this.SaveProject(identity); this.SaveProject(identity);
} }
} }

5
src/views/ProjectViewView.vue

@ -240,7 +240,10 @@ export default class ProjectViewView extends Vue {
} else { } else {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts); const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
this.LoadProject(identity); this.LoadProject(identity);
} }
} }

5
src/views/ProjectsView.vue

@ -167,7 +167,10 @@ export default class ProjectsView extends Vue {
} else { } else {
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts); const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error("No identity found.");
}
this.LoadProjects(identity); this.LoadProjects(identity);
} }
} }

2
src/views/StatisticsView.vue

@ -61,7 +61,7 @@
<!-- eslint-disable prettier/prettier --><!-- If we format prettier then there is extra space at the start of the line. --> <!-- eslint-disable prettier/prettier --><!-- If we format prettier then there is extra space at the start of the line. -->
<li>Each will show at their time of appearance relative to all others.</li> <li>Each will show at their time of appearance relative to all others.</li>
<li>Note that the ones on the left and right edges are randomized <li>Note that the ones on the left and right edges are randomized
because not all their positional data is visible to you. because their data isn't all visible to you.
</li> </li>
<!-- eslint-enable --> <!-- eslint-enable -->
</ul> </ul>

Loading…
Cancel
Save