forked from jsnbuchanan/crowd-funder-for-time-pwa
Merge branch 'master' into misc2
This commit is contained in:
@@ -9,6 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
|
||||||
## [0.1.1] - 2023.10.31
|
## [0.1.2] - 2023.11.01
|
||||||
### Added
|
### Added
|
||||||
- Basics: create ID, record a give, declare a project, search, and get notifications.
|
- Basics: create ID, record a give, declare a project, search, and get notifications.
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "kickstart-for-time-pwa",
|
"name": "kickstart-for-time-pwa",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kickstart-for-time-pwa",
|
"name": "kickstart-for-time-pwa",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersproject/hdnode": "^5.7.0",
|
"@ethersproject/hdnode": "^5.7.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kickstart-for-time-pwa",
|
"name": "kickstart-for-time-pwa",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { library } from "@fortawesome/fontawesome-svg-core";
|
|||||||
import {
|
import {
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
faArrowRight,
|
faArrowRight,
|
||||||
|
faBan,
|
||||||
faBurst,
|
faBurst,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
@@ -59,6 +60,7 @@ import {
|
|||||||
library.add(
|
library.add(
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
faArrowRight,
|
faArrowRight,
|
||||||
|
faBan,
|
||||||
faBurst,
|
faBurst,
|
||||||
faCalendar,
|
faCalendar,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
|
|||||||
@@ -244,7 +244,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- id used by puppeteer test script -->
|
||||||
<h3
|
<h3
|
||||||
|
id="advanced"
|
||||||
class="text-sm uppercase font-semibold mb-3"
|
class="text-sm uppercase font-semibold mb-3"
|
||||||
@click="showAdvanced = !showAdvanced"
|
@click="showAdvanced = !showAdvanced"
|
||||||
>
|
>
|
||||||
@@ -277,7 +279,9 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="flex py-2">
|
<div class="flex py-2">
|
||||||
|
<!-- id used by puppeteer test script -->
|
||||||
<router-link
|
<router-link
|
||||||
|
id="switch-identity-link"
|
||||||
:to="{ name: 'identity-switcher' }"
|
:to="{ name: 'identity-switcher' }"
|
||||||
class="block text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
class="block text-center text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-2"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -93,6 +93,16 @@
|
|||||||
class="inline-block align-text-bottom border border-slate-300 rounded"
|
class="inline-block align-text-bottom border border-slate-300 rounded"
|
||||||
></EntityIcon>
|
></EntityIcon>
|
||||||
{{ contact.name || "(no name)" }}
|
{{ contact.name || "(no name)" }}
|
||||||
|
<button
|
||||||
|
class="text-sm uppercase bg-slate-500 text-white px-1 rounded-md"
|
||||||
|
@click="
|
||||||
|
contactEdit = contact;
|
||||||
|
contactNewName = contact.name;
|
||||||
|
"
|
||||||
|
title="Edit"
|
||||||
|
>
|
||||||
|
<fa icon="pen" class="fa-fw" />
|
||||||
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div class="text-sm truncate">{{ contact.did }}</div>
|
<div class="text-sm truncate">{{ contact.did }}</div>
|
||||||
<div class="text-sm truncate" v-if="contact.publicKeyBase64">
|
<div class="text-sm truncate" v-if="contact.publicKeyBase64">
|
||||||
@@ -209,6 +219,31 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p v-else>This identity has no contacts.</p>
|
<p v-else>This identity has no contacts.</p>
|
||||||
|
|
||||||
|
<div v-if="contactEdit !== null" class="dialog-overlay">
|
||||||
|
<div class="dialog">
|
||||||
|
<h1 class="text-xl font-bold text-center mb-4">Edit Name</h1>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
|
placeholder="Name"
|
||||||
|
v-model="contactNewName"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="text-sm bg-blue-600 text-white px-2 py-1.5 rounded -ml-1.5 border-l border-blue-400"
|
||||||
|
@click="onClickSaveName(contactEdit, contactNewName)"
|
||||||
|
>
|
||||||
|
<fa icon="save" />
|
||||||
|
</button>
|
||||||
|
<span class="inline-block w-2" />
|
||||||
|
<button
|
||||||
|
class="text-sm bg-blue-600 text-white px-2 py-1.5 rounded -ml-1.5 border-l border-blue-400"
|
||||||
|
@click="onClickCancelName()"
|
||||||
|
>
|
||||||
|
<fa icon="ban" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -251,6 +286,8 @@ export default class ContactsView extends Vue {
|
|||||||
contacts: Array<Contact> = [];
|
contacts: Array<Contact> = [];
|
||||||
contactEndorserUrl = localStorage.getItem("contactEndorserUrl") || "";
|
contactEndorserUrl = localStorage.getItem("contactEndorserUrl") || "";
|
||||||
contactInput = "";
|
contactInput = "";
|
||||||
|
contactEdit: Contact | null = null;
|
||||||
|
contactNewName = "";
|
||||||
// { "did:...": concatenated-descriptions } entry for each contact
|
// { "did:...": concatenated-descriptions } entry for each contact
|
||||||
givenByMeDescriptions: Record<string, string> = {};
|
givenByMeDescriptions: Record<string, string> = {};
|
||||||
// { "did:...": amount } entry for each contact
|
// { "did:...": amount } entry for each contact
|
||||||
@@ -961,6 +998,18 @@ export default class ContactsView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async onClickCancelName() {
|
||||||
|
this.contactEdit = null;
|
||||||
|
this.contactNewName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async onClickSaveName(contact: Contact, newName: string) {
|
||||||
|
contact.name = newName;
|
||||||
|
return db.contacts
|
||||||
|
.update(contact.did, { name: newName })
|
||||||
|
.then(() => (this.contactEdit = null));
|
||||||
|
}
|
||||||
|
|
||||||
public toggleShowGiveTotals() {
|
public toggleShowGiveTotals() {
|
||||||
if (this.showGiveTotals) {
|
if (this.showGiveTotals) {
|
||||||
this.showGiveTotals = false;
|
this.showGiveTotals = false;
|
||||||
@@ -985,6 +1034,26 @@ export default class ContactsView extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
background-color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */
|
/* Tooltip from https://www.w3schools.com/css/css_tooltip.asp */
|
||||||
/* Tooltip container */
|
/* Tooltip container */
|
||||||
.tooltip {
|
.tooltip {
|
||||||
@@ -992,7 +1061,6 @@ export default class ContactsView extends Vue {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
|
border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tooltip text */
|
/* Tooltip text */
|
||||||
.tooltip .tooltiptext {
|
.tooltip .tooltiptext {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|||||||
@@ -41,14 +41,15 @@
|
|||||||
You need someone to register you -- usually the person who told you
|
You need someone to register you -- usually the person who told you
|
||||||
about this app, on the Contacts
|
about this app, on the Contacts
|
||||||
<fa icon="circle-user" class="fa-fw" /> page. After they register you,
|
<fa icon="circle-user" class="fa-fw" /> page. After they register you,
|
||||||
and after you have contacts, you can select any contact on the home page
|
you can select any contact on the home page (or "anonymous") and record
|
||||||
and record your appreciation for... whatever. That is a claim recorded
|
your appreciation for... whatever. The main goal is to record what
|
||||||
|
people have given you, to grow gifting economies. Each claim is recorded
|
||||||
on a custom ledger. The day after being registered, you'll be able to
|
on a custom ledger. The day after being registered, you'll be able to
|
||||||
register others; later, you can create projects, too.
|
able to register others; later, you can create projects, too.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Note that there are limits to how many each person can register, so you
|
Note that there are limits to how many others each person can register,
|
||||||
may have to wait.
|
so you may have to wait.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">How do I backup all my data?</h2>
|
<h2 class="text-xl font-semibold">How do I backup all my data?</h2>
|
||||||
@@ -130,7 +131,9 @@
|
|||||||
|
|
||||||
<h2 class="text-xl font-semibold">How do I create another identity?</h2>
|
<h2 class="text-xl font-semibold">How do I create another identity?</h2>
|
||||||
<p>
|
<p>
|
||||||
Go
|
Before doing this, note that it is an advanced feature that affects
|
||||||
|
functionality (eg. the words "Alt ID" next to results, backup features)
|
||||||
|
so beware if you think that may cause confusion. You can
|
||||||
<router-link to="start" class="text-blue-500">
|
<router-link to="start" class="text-blue-500">
|
||||||
create another identity here.
|
create another identity here.
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|||||||
@@ -294,9 +294,18 @@ export default class HomeView extends Vue {
|
|||||||
this.allMyDids,
|
this.allMyDids,
|
||||||
this.allContacts,
|
this.allContacts,
|
||||||
);
|
);
|
||||||
const gaveAmount = claim.object?.amountOfThisGood
|
let gaveAmount = claim.object?.amountOfThisGood
|
||||||
? this.displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
|
? this.displayAmount(claim.object.unitCode, claim.object.amountOfThisGood)
|
||||||
: claim.description || "something without description";
|
: "";
|
||||||
|
if (claim.description) {
|
||||||
|
if (gaveAmount) {
|
||||||
|
gaveAmount = gaveAmount + ", and also: ";
|
||||||
|
}
|
||||||
|
gaveAmount = gaveAmount + claim.description;
|
||||||
|
}
|
||||||
|
if (!gaveAmount) {
|
||||||
|
gaveAmount = "something not described";
|
||||||
|
}
|
||||||
// recipient.did is for legacy data, before March 2023
|
// recipient.did is for legacy data, before March 2023
|
||||||
const gaveRecipientId =
|
const gaveRecipientId =
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -390,15 +399,18 @@ export default class HomeView extends Vue {
|
|||||||
hours,
|
hours,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.isGiveCreationError(result)) {
|
if (
|
||||||
|
result.type === "error" ||
|
||||||
|
this.isGiveCreationError(result.response)
|
||||||
|
) {
|
||||||
const errorMessage = this.getGiveCreationErrorMessage(result);
|
const errorMessage = this.getGiveCreationErrorMessage(result);
|
||||||
console.log("Error with give result:", result);
|
console.log("Error with give creation result:", result);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
text: errorMessage || "There was an error recording the give.",
|
text: errorMessage || "There was an error creating the give.",
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
@@ -415,7 +427,7 @@ export default class HomeView extends Vue {
|
|||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log("Error with give caught:", error);
|
console.log("Error with give recordation caught:", error);
|
||||||
const message =
|
const message =
|
||||||
error.userMessage ||
|
error.userMessage ||
|
||||||
error.response?.data?.error?.message ||
|
error.response?.data?.error?.message ||
|
||||||
@@ -434,14 +446,26 @@ export default class HomeView extends Vue {
|
|||||||
|
|
||||||
// Helper functions for readability
|
// Helper functions for readability
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param result response "data" from the server
|
||||||
|
* @returns true if the result indicates an error
|
||||||
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
isGiveCreationError(result: any) {
|
isGiveCreationError(result: any) {
|
||||||
return result.status !== 201 || result.data?.error;
|
return result.status !== 201 || result.data?.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param result direct response eg. ErrorResult or SuccessResult (potentially with embedded "data")
|
||||||
|
* @returns best guess at an error message
|
||||||
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
getGiveCreationErrorMessage(result: any) {
|
getGiveCreationErrorMessage(result: any) {
|
||||||
return result.data?.error?.message;
|
return (
|
||||||
|
result.error?.userMessage ||
|
||||||
|
result.error?.error ||
|
||||||
|
result.response?.data?.error?.message
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -49,7 +49,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
|
<!-- id used by puppeteer test script -->
|
||||||
<router-link
|
<router-link
|
||||||
|
id="start-link"
|
||||||
:to="{ name: 'start' }"
|
:to="{ name: 'start' }"
|
||||||
class="block text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
class="block text-center text-lg font-bold uppercase bg-blue-600 text-white px-2 py-3 rounded-md mb-2"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
<p class="text-center text-xl mb-4 font-light">
|
<p class="text-center text-xl mb-4 font-light">
|
||||||
Enter your seed phrase below to import your identity on this device.
|
Enter your seed phrase below to import your identity on this device.
|
||||||
</p>
|
</p>
|
||||||
|
<!-- id used by puppeteer test script -->
|
||||||
<input
|
<input
|
||||||
|
id="seed-input"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Seed Phrase"
|
placeholder="Seed Phrase"
|
||||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
Start Here
|
Start Here
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="mt-8">
|
<!-- id used by puppeteer test script -->
|
||||||
|
<div id="start-question" class="mt-8">
|
||||||
<p class="text-center text-xl mb-4 font-light">
|
<p class="text-center text-xl mb-4 font-light">
|
||||||
Do you have an identity to import?
|
Do you have an identity to import?
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user