Browse Source

Merge pull request 'New notification system + test' (#48) from notiwind-alert into master

Reviewed-on: https://gitea.anomalistdesign.com/trent_larson/kick-starter-for-time-pwa/pulls/48
notification-request-permission-dialog
anomalist 1 year ago
parent
commit
1767a48a7f
  1. 17
      package-lock.json
  2. 1
      package.json
  3. 127
      src/App.vue
  4. 6
      src/main.ts
  5. 10
      src/router/index.ts
  6. 46
      src/views/AccountViewView.vue
  7. 72
      src/views/ContactAmountsView.vue
  8. 71
      src/views/ContactGiftingView.vue
  9. 10
      src/views/ContactQRScanShowView.vue
  10. 202
      src/views/ContactsView.vue
  11. 44
      src/views/DiscoverView.vue
  12. 172
      src/views/HomeView.vue
  13. 12
      src/views/IdentitySwitcherView.vue
  14. 33
      src/views/NewEditProjectView.vue
  15. 140
      src/views/ProjectViewView.vue
  16. 33
      src/views/ProjectsView.vue
  17. 11
      src/views/SeedBackupView.vue
  18. 11
      src/views/StatisticsView.vue

17
package-lock.json

@ -38,6 +38,7 @@
"luxon": "^3.3.0", "luxon": "^3.3.0",
"merkletreejs": "^0.3.10", "merkletreejs": "^0.3.10",
"moment": "^2.29.4", "moment": "^2.29.4",
"notiwind": "^2.0.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pina": "^0.20.2204228", "pina": "^0.20.2204228",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",
@ -20928,6 +20929,11 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
},
"node_modules/mkdirp": { "node_modules/mkdirp": {
"version": "0.5.6", "version": "0.5.6",
"resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
@ -21275,6 +21281,17 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/notiwind": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/notiwind/-/notiwind-2.0.2.tgz",
"integrity": "sha512-wMCf+07E093d0Q78C5UHroT9GQHm4mIGerhg7dGLJ0GN6zONqKj8nTR3clkq/Y44On9k28/0DtDNwOX7FT5p/A==",
"dependencies": {
"mitt": "^3.0.1"
},
"peerDependencies": {
"vue": "^3.3.4"
}
},
"node_modules/npm-package-arg": { "node_modules/npm-package-arg": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-7.0.0.tgz",

1
package.json

@ -38,6 +38,7 @@
"luxon": "^3.3.0", "luxon": "^3.3.0",
"merkletreejs": "^0.3.10", "merkletreejs": "^0.3.10",
"moment": "^2.29.4", "moment": "^2.29.4",
"notiwind": "^2.0.2",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"pina": "^0.20.2204228", "pina": "^0.20.2204228",
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",

127
src/App.vue

@ -1,5 +1,132 @@
<template> <template>
<router-view /> <router-view />
<NotificationGroup group="alert">
<div
class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end"
>
<Notification
v-slot="{ notifications, close }"
enter="transform ease-out duration-300 transition"
enter-from="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-4"
enter-to="translate-y-0 opacity-100 sm:translate-x-0"
leave="transition ease-in duration-500"
leave-from="opacity-100"
leave-to="opacity-0"
move="transition duration-500"
move-delay="delay-300"
>
<div
v-for="notification in notifications"
:key="notification.id"
class="w-full"
role="alert"
>
<div
v-if="notification.type === 'toast'"
class="w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-900/90 text-white rounded-lg shadow-md"
>
<div class="w-full px-4 py-3">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
</div>
</div>
<div
v-if="notification.type === 'info'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-slate-600 text-slate-100"
>
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-slate-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-slate-200 text-slate-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'success'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-emerald-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-emerald-600 text-emerald-100"
>
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-emerald-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-emerald-200 text-emerald-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'warning'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-amber-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-amber-600 text-amber-100"
>
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-amber-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-amber-200 text-amber-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
<div
v-if="notification.type === 'danger'"
class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-rose-100 rounded-lg shadow-md"
>
<div
class="flex items-center justify-center w-12 bg-rose-600 text-rose-100"
>
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
</div>
<div class="relative w-full pl-4 pr-8 py-2 text-rose-900">
<span class="font-semibold">{{ notification.title }}</span>
<p class="text-sm">{{ notification.text }}</p>
<button
@click="close(notification.id)"
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-rose-200 text-rose-600"
>
<fa icon="xmark" class="fa-fw"></fa>
</button>
</div>
</div>
</div>
</Notification>
</div>
</NotificationGroup>
</template> </template>
<style></style> <style></style>

6
src/main.ts

@ -5,6 +5,7 @@ import "./registerServiceWorker";
import router from "./router"; import router from "./router";
import axios from "axios"; import axios from "axios";
import VueAxios from "vue-axios"; import VueAxios from "vue-axios";
import Notifications from "notiwind";
import "./assets/styles/tailwind.css"; import "./assets/styles/tailwind.css";
@ -16,6 +17,7 @@ import {
faChevronRight, faChevronRight,
faCircle, faCircle,
faCircleCheck, faCircleCheck,
faCircleInfo,
faCircleQuestion, faCircleQuestion,
faCircleUser, faCircleUser,
faClock, faClock,
@ -45,6 +47,7 @@ import {
faSquareCaretDown, faSquareCaretDown,
faSquareCaretUp, faSquareCaretUp,
faTrashCan, faTrashCan,
faTriangleExclamation,
faUser, faUser,
faUsers, faUsers,
faXmark, faXmark,
@ -57,6 +60,7 @@ library.add(
faChevronRight, faChevronRight,
faCircle, faCircle,
faCircleCheck, faCircleCheck,
faCircleInfo,
faCircleQuestion, faCircleQuestion,
faCircleUser, faCircleUser,
faClock, faClock,
@ -86,6 +90,7 @@ library.add(
faSquareCaretDown, faSquareCaretDown,
faSquareCaretUp, faSquareCaretUp,
faTrashCan, faTrashCan,
faTriangleExclamation,
faUser, faUser,
faUsers, faUsers,
faXmark, faXmark,
@ -98,4 +103,5 @@ createApp(App)
.use(createPinia()) .use(createPinia())
.use(VueAxios, axios) .use(VueAxios, axios)
.use(router) .use(router)
.use(Notifications)
.mount("#app"); .mount("#app");

10
src/router/index.ts

@ -182,4 +182,14 @@ const router = createRouter({
routes, routes,
}); });
const errorHandler = (error, to, from) => {
// Handle the error here
console.error(error, to, from);
console.log("XXXXX");
// You can also perform additional actions, such as displaying an error message or redirecting the user to a specific page
};
router.onError(errorHandler); // Assign the error handler to the router instance
export default router; export default router;

46
src/views/AccountViewView.vue

@ -400,13 +400,19 @@ export default class AccountViewView extends Vue {
this.limitsMessage = "No identity."; this.limitsMessage = "No identity.";
this.loadingLimits = false; this.loadingLimits = false;
} else { } else {
this.alertMessage = this.$notify(
"Clear your cache and start over (after data backup)."; {
group: "alert",
type: "danger",
title: "Error Creating Account",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache at page create because:", "Telling user to clear cache at page create because:",
err, err,
); );
this.alertTitle = "Error Creating Account";
} }
} }
} }
@ -418,13 +424,19 @@ export default class AccountViewView extends Vue {
showContactGivesInline: this.showContactGives, showContactGivesInline: this.showContactGives,
}); });
} catch (err) { } catch (err) {
this.alertMessage = this.$notify(
"Clear your cache and start over (after data backup)."; {
group: "alert",
type: "danger",
title: "Error Updating Contact Setting",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache after contact setting update because:", "Telling user to clear cache after contact setting update because:",
err, err,
); );
this.alertTitle = "Error Updating Contact Setting";
} }
} }
@ -440,11 +452,25 @@ export default class AccountViewView extends Vue {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
this.alertTitle = "Download Started"; this.$notify(
this.alertMessage = "See your downloads directory for the backup."; {
group: "alert",
type: "toast",
title: "Download Started",
text: "See your downloads directory for the backup.",
},
5000,
);
} catch (error) { } catch (error) {
this.alertTitle = "Export Error"; this.$notify(
this.alertMessage = "See console logs for more info."; {
group: "alert",
type: "danger",
title: "Export Error",
text: "See console logs for more info.",
},
-1,
);
console.error("Export Error:", error); console.error("Export Error:", error);
} }
} }

72
src/views/ContactAmountsView.vue

@ -146,10 +146,17 @@ export default class ContactsView extends Vue {
this.loadGives(this.activeDid, this.contact); this.loadGives(this.activeDid, this.contact);
} }
} catch (err) { } catch (err) {
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
err.userMessage || group: "alert",
"There was an error retrieving the latest sweet, sweet action."; type: "danger",
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@ -173,9 +180,15 @@ export default class ContactsView extends Vue {
resp.status, resp.status,
resp.data, resp.data,
); );
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = {
"Got an error retrieving your given time from the server."; group: "alert",
type: "danger",
title: "Error With Server",
text: "Got an error retrieving your given time from the server.",
},
-1,
);
} }
const url2 = const url2 =
@ -194,9 +207,15 @@ export default class ContactsView extends Vue {
resp2.status, resp2.status,
resp2.data, resp2.data,
); );
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = {
"Got an error retrieving your given time from the server."; group: "alert",
type: "danger",
title: "Error With Server",
text: "Got an error retrieving your given time from the server.",
},
-1,
);
} }
const sortedResult: Array<GiveServerRecord> = R.sort( const sortedResult: Array<GiveServerRecord> = R.sort(
@ -206,8 +225,15 @@ export default class ContactsView extends Vue {
); );
this.giveRecords = sortedResult; this.giveRecords = sortedResult;
} catch (error) { } catch (error) {
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = error as string; {
group: "alert",
type: "danger",
title: "Error With Server",
text: error as string,
},
-1,
);
} }
} }
@ -276,15 +302,29 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = userMessage; {
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }
cannotConfirmMessage() { cannotConfirmMessage() {
this.alertTitle = "Not Allowed"; this.$notify(
this.alertMessage = "Only the recipient can confirm final receipt."; {
group: "alert",
type: "danger",
title: "Not Allowed",
text: "Only the recipient can confirm final receipt.",
},
-1,
);
} }
} }
</script> </script>

71
src/views/ContactGiftingView.vue

@ -145,10 +145,17 @@ export default class HomeView extends Vue {
this.feedLastViewedId = settings?.lastViewedClaimId; this.feedLastViewedId = settings?.lastViewedClaimId;
this.updateAllFeed(); this.updateAllFeed();
} catch (err) { } catch (err) {
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
err.userMessage || group: "alert",
"There was an error retrieving the latest sweet, sweet action."; type: "danger",
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@ -197,17 +204,27 @@ export default class HomeView extends Vue {
*/ */
public async recordGive(giverDid, description, hours) { public async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.setAlert( this.$notify(
"Error", {
"You must select an identity before you can record a give.", group: "alert",
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
); );
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.setAlert( this.$notify(
"Error", {
"You must enter a description or some number of hours.", group: "alert",
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
); );
return; return;
} }
@ -227,18 +244,38 @@ export default class HomeView extends Vue {
if (isGiveCreationError(result)) { if (isGiveCreationError(result)) {
const errorMessage = getGiveCreationErrorMessage(result); const errorMessage = getGiveCreationErrorMessage(result);
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.setAlert( this.$notify(
"Error", {
errorMessage || "There was an error recording the give.", group: "alert",
type: "danger",
title: "Error",
text: errorMessage || "There was an error recording the give.",
},
-1,
); );
} else { } else {
this.setAlert("Success", "That gift was recorded."); this.$notify(
{
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (error) { } catch (error) {
console.log("Error with give caught:", error); console.log("Error with give caught:", error);
this.setAlert( this.$notify(
"Error", {
getGiveErrorMessage(error) || "There was an error recording the give.", group: "alert",
type: "danger",
title: "Error",
text:
getGiveErrorMessage(error) ||
"There was an error recording the give.",
},
-1,
); );
} }
} }

10
src/views/ContactQRScanShowView.vue

@ -85,7 +85,15 @@ export default class ContactQRScanShow 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);
if (!account) { if (!account) {
this.alertMessage = "You have no identity yet."; this.$notify(
{
group: "alert",
type: "warning",
title: "",
text: "You have no identity yet.",
},
-1,
);
} else { } else {
const identity = await this.getIdentity(this.activeDid); const identity = await this.getIdentity(this.activeDid);
const publicKeyHex = identity.keys[0].publicKeyHex; const publicKeyHex = identity.keys[0].publicKeyHex;

202
src/views/ContactsView.vue

@ -317,12 +317,19 @@ export default class ContactsView extends Vue {
resp.status, resp.status,
resp.data, resp.data,
); );
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = {
"Got an error retrieving your " + group: "alert",
resp.config.url.includes("recipientDid") type: "danger",
? "received" title: "Error With Server",
: "given" + " time from the server."; text:
"Got an error retrieving your " +
resp.config.url.includes("recipientDid")
? "received"
: "given" + " time from the server.",
},
-1,
);
} }
}; };
@ -370,8 +377,15 @@ export default class ContactsView extends Vue {
this.givenToMeConfirmed = givenToMeConfirmed; this.givenToMeConfirmed = givenToMeConfirmed;
this.givenToMeUnconfirmed = givenToMeUnconfirmed; this.givenToMeUnconfirmed = givenToMeUnconfirmed;
} catch (error) { } catch (error) {
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = error as string; {
group: "alert",
type: "danger",
title: "Error With Server",
text: error as string,
},
-1,
);
} }
} }
@ -464,18 +478,32 @@ export default class ContactsView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.embeddedRecordError) { if (resp.data?.success?.embeddedRecordError) {
this.alertTitle = "Registration Still Unknown";
let message = "There was some problem with the registration."; let message = "There was some problem with the registration.";
if (typeof resp.data.success.embeddedRecordError == "string") { if (typeof resp.data.success.embeddedRecordError == "string") {
message += " " + resp.data.success.embeddedRecordError; message += " " + resp.data.success.embeddedRecordError;
} }
this.alertMessage = message; this.$notify(
{
group: "alert",
type: "danger",
title: "Registration Still Unknown",
text: message,
},
-1,
);
} else if (resp.data?.success?.handleId) { } else if (resp.data?.success?.handleId) {
contact.registered = true; contact.registered = true;
db.contacts.update(contact.did, { registered: true }); db.contacts.update(contact.did, { registered: true });
this.alertTitle = "Registration Success"; this.$notify(
this.alertMessage = contact.name + " has been registered."; {
group: "alert",
type: "info",
title: "Registration Success",
text: contact.name + " has been registered.",
},
-1,
);
} }
} catch (error) { } catch (error) {
let userMessage = "There was an error. See logs for more info."; let userMessage = "There was an error. See logs for more info.";
@ -490,8 +518,15 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = userMessage; {
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }
@ -512,17 +547,39 @@ export default class ContactsView extends Vue {
contact.seesMe = visibility; contact.seesMe = visibility;
db.contacts.update(contact.did, { seesMe: visibility }); db.contacts.update(contact.did, { seesMe: visibility });
} else { } else {
this.alertTitle = "Error With Server";
console.error("Bad response setting visibility: ", resp.data); console.error("Bad response setting visibility: ", resp.data);
if (resp.data.error?.message) { if (resp.data.error?.message) {
this.alertMessage = resp.data.error?.message; this.$notify(
{
group: "alert",
type: "danger",
title: "Error With Server",
text: resp.data.error?.message,
},
-1,
);
} else { } else {
this.alertMessage = "Bad server response of " + resp.status; this.$notify(
{
group: "alert",
type: "danger",
title: "Error With Server",
text: "Bad server response of " + resp.status,
},
-1,
);
} }
} }
} catch (err) { } catch (err) {
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = err as string; {
group: "alert",
type: "danger",
title: "Error With Server",
text: err as string,
},
-1,
);
} }
} }
@ -539,23 +596,52 @@ export default class ContactsView extends Vue {
contact.seesMe = visibility; contact.seesMe = visibility;
db.contacts.update(contact.did, { seesMe: visibility }); db.contacts.update(contact.did, { seesMe: visibility });
this.alertTitle = "Refreshed"; this.$notify(
this.alertMessage = {
this.nameForContact(contact, true) + group: "alert",
" can " + type: "toast",
(visibility ? "" : "not ") + title: "Refreshed",
"see your activity."; text:
this.nameForContact(contact, true) +
" can " +
(visibility ? "" : "not ") +
"see your activity.",
},
5000,
);
} else { } else {
this.alertTitle = "Error With Server";
if (resp.data.error?.message) { if (resp.data.error?.message) {
this.alertMessage = resp.data.error?.message; this.$notify(
{
group: "alert",
type: "danger",
title: "Error With Server",
text: resp.data.error?.message,
},
-1,
);
} else { } else {
this.alertMessage = "Bad server response of " + resp.status; this.$notify(
{
group: "alert",
type: "danger",
title: "Error With Server",
text: "Bad server response of " + resp.status,
},
-1,
);
} }
} }
} catch (err) { } catch (err) {
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = err as string; {
group: "alert",
type: "danger",
title: "Error With Server",
text: err as string,
},
-1,
);
} }
} }
@ -594,15 +680,35 @@ export default class ContactsView extends Vue {
} }
} }
if (!this.isNumeric(this.hourInput)) { if (!this.isNumeric(this.hourInput)) {
this.alertTitle = "Input Error"; this.$notify(
this.alertMessage = {
"This is not a valid number of hours: " + this.hourInput; group: "alert",
type: "danger",
title: "Input Error",
text: "This is not a valid number of hours: " + this.hourInput,
},
-1,
);
} else if (!parseFloat(this.hourInput)) { } else if (!parseFloat(this.hourInput)) {
this.alertTitle = "Input Error"; this.$notify(
this.alertMessage = "Giving 0 hours does nothing."; {
group: "alert",
type: "danger",
title: "Input Error",
text: "Giving 0 hours does nothing.",
},
-1,
);
} else if (!identity) { } else if (!identity) {
this.alertTitle = "Status Error"; this.$notify(
this.alertMessage = "No identity is available."; {
group: "alert",
type: "danger",
title: "Status Error",
text: "No identity is available.",
},
-1,
);
} else { } else {
// ask to confirm amount // ask to confirm amount
let toFrom; let toFrom;
@ -686,8 +792,15 @@ export default class ContactsView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.handleId) { if (resp.data?.success?.handleId) {
this.alertTitle = "Done"; this.$notify(
this.alertMessage = "Successfully logged time to the server."; {
group: "alert",
type: "success",
title: "Done",
text: "Successfully logged time to the server.",
},
-1,
);
if (fromDid === identity.did) { if (fromDid === identity.did) {
const newList = R.clone(this.givenByMeUnconfirmed); const newList = R.clone(this.givenByMeUnconfirmed);
@ -712,8 +825,15 @@ export default class ContactsView extends Vue {
userMessage = error as string; userMessage = error as string;
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.alertTitle = "Error With Server"; this.$notify(
this.alertMessage = userMessage; {
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }
} }

44
src/views/DiscoverView.vue

@ -186,6 +186,16 @@ export default class DiscoverView extends Vue {
if (response.status !== 200) { if (response.status !== 200) {
const details = await response.text(); const details = await response.text();
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `There was a problem accessing the server. Please try again later. (${details})`,
},
-1,
);
throw details; throw details;
} }
@ -204,9 +214,15 @@ export default class DiscoverView extends Vue {
} }
} catch (e) { } catch (e) {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.alertMessage = this.$notify(
e.userMessage || "There was an error retrieving projects."; {
this.alertTitle = "Error"; group: "alert",
type: "danger",
title: "Error",
text: e.userMessage || "There was a problem retrieving projects.",
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@ -240,6 +256,16 @@ export default class DiscoverView extends Vue {
); );
if (response.status !== 200) { if (response.status !== 200) {
const details = await response.text();
this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: `There was a problem accessing the server. Please try again later. (${details})`,
},
-1,
);
throw await response.text(); throw await response.text();
} }
@ -263,9 +289,15 @@ export default class DiscoverView extends Vue {
} }
} catch (e) { } catch (e) {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.alertMessage = this.$notify(
e.userMessage || "There was an error retrieving projects."; {
this.alertTitle = "Error"; group: "alert",
type: "danger",
title: "Error",
text: e.userMessage || "There was a problem retrieving projects.",
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }

172
src/views/HomeView.vue

@ -6,6 +6,94 @@
Time Safari Time Safari
</h1> </h1>
<div class="mb-8">
<h2 class="text-xl font-bold mb-4">Notiwind Alert Test Suite</h2>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'toast',
text: 'I\'m a toast. Don\'t mind me.',
},
5000,
)
"
class="font-bold uppercase bg-slate-400 text-white px-3 py-2 rounded-md mr-2"
>
Toast (self-dismiss)
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'info',
title: 'Information Alert',
text: 'Just wanted you to know.',
},
-1,
)
"
class="font-bold uppercase bg-slate-600 text-white px-3 py-2 rounded-md mr-2"
>
Info
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'success',
title: 'Success Alert',
text: 'Congratulations!',
},
-1,
)
"
class="font-bold uppercase bg-emerald-600 text-white px-3 py-2 rounded-md mr-2"
>
Success
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'warning',
title: 'Warning Alert',
text: 'You might wanna look at this.',
},
-1,
)
"
class="font-bold uppercase bg-amber-600 text-white px-3 py-2 rounded-md mr-2"
>
Warning
</button>
<button
@click="
this.$notify(
{
group: 'alert',
type: 'danger',
title: 'Danger Alert',
text: 'Something terrible has happened!',
},
-1,
)
"
class="font-bold uppercase bg-rose-600 text-white px-3 py-2 rounded-md mr-2"
>
Danger
</button>
</div>
<div class="mb-8"> <div class="mb-8">
<h2 class="text-xl font-bold">Quick Action</h2> <h2 class="text-xl font-bold">Quick Action</h2>
<p class="mb-4">Show appreciation to a contact:</p> <p class="mb-4">Show appreciation to a contact:</p>
@ -157,10 +245,17 @@ export default class HomeView extends Vue {
this.feedLastViewedId = settings?.lastViewedClaimId; this.feedLastViewedId = settings?.lastViewedClaimId;
this.updateAllFeed(); this.updateAllFeed();
} catch (err) { } catch (err) {
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
err.userMessage || group: "alert",
"There was an error retrieving the latest sweet, sweet action."; type: "danger",
title: "Error",
text:
err.userMessage ||
"There was an error retrieving the latest sweet, sweet action.",
},
-1,
);
} }
} }
@ -209,9 +304,15 @@ export default class HomeView extends Vue {
}) })
.catch((e) => { .catch((e) => {
console.log("Error with feed load:", e); console.log("Error with feed load:", e);
this.alertMessage = this.$notify(
e.userMessage || "There was an error retrieving feed data."; {
this.alertTitle = "Error"; group: "alert",
type: "danger",
title: "Export Error",
text: e.userMessage || "There was an error retrieving feed data.",
},
-1,
);
}); });
this.isHiddenSpinner = true; this.isHiddenSpinner = true;
@ -302,17 +403,27 @@ export default class HomeView extends Vue {
*/ */
public async recordGive(giverDid, description, hours) { public async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.setAlert( this.$notify(
"Error", {
"You must select an identity before you can record a give.", group: "alert",
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
); );
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.setAlert( this.$notify(
"Error", {
"You must enter a description or some number of hours.", group: "alert",
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
); );
return; return;
} }
@ -332,19 +443,38 @@ export default class HomeView extends Vue {
if (this.isGiveCreationError(result)) { if (this.isGiveCreationError(result)) {
const errorMessage = this.getGiveCreationErrorMessage(result); const errorMessage = this.getGiveCreationErrorMessage(result);
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.setAlert( this.$notify(
"Error", {
errorMessage || "There was an error recording the give.", group: "alert",
type: "danger",
title: "Error",
text: errorMessage || "There was an error recording the give.",
},
-1,
); );
} else { } else {
this.setAlert("Success", "That gift was recorded."); this.$notify(
{
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (error) { } catch (error) {
console.log("Error with give caught:", error); console.log("Error with give caught:", error);
this.setAlert( this.$notify(
"Error", {
this.getGiveErrorMessage(error) || group: "alert",
"There was an error recording the give.", type: "danger",
title: "Error",
text:
this.getGiveErrorMessage(error) ||
"There was an error recording the give.",
},
-1,
); );
} }
} }

12
src/views/IdentitySwitcherView.vue

@ -133,13 +133,19 @@ export default class IdentitySwitcherView extends Vue {
this.limitsMessage = "No identity."; this.limitsMessage = "No identity.";
this.loadingLimits = false; this.loadingLimits = false;
} else { } else {
this.alertMessage = this.$notify(
"Clear your cache and start over (after data backup)."; {
group: "alert",
type: "danger",
title: "Error Creating Account",
text: "Clear your cache and start over (after data backup).",
},
-1,
);
console.error( console.error(
"Telling user to clear cache at page create because:", "Telling user to clear cache at page create because:",
err, err,
); );
this.alertTitle = "Error Creating Account";
} }
} }
} }

33
src/views/NewEditProjectView.vue

@ -245,20 +245,41 @@ export default class NewEditProjectView extends Vue {
if (serverError) { if (serverError) {
if (Object.prototype.hasOwnProperty.call(serverError, "message")) { if (Object.prototype.hasOwnProperty.call(serverError, "message")) {
console.log(serverError); console.log(serverError);
this.alertTitle = "User Message";
userMessage = serverError.response.data.error.message; // This is info for the user. userMessage = serverError.response.data.error.message; // This is info for the user.
this.alertMessage = userMessage; this.$notify(
{
group: "alert",
type: "danger",
title: "User Message",
text: userMessage,
},
-1,
);
} else { } else {
this.alertTitle = "Server Message"; this.$notify(
this.alertMessage = JSON.stringify(serverError.toJSON()); {
group: "alert",
type: "danger",
title: "Server Message",
text: JSON.stringify(serverError.toJSON()),
},
-1,
);
} }
} else { } else {
console.error( console.error(
"Here's the full error trying to save the claim:", "Here's the full error trying to save the claim:",
error, error,
); );
this.alertTitle = "Claim Error"; this.$notify(
this.alertMessage = error as string; {
group: "alert",
type: "danger",
title: "Claim Error",
text: error as string,
},
-1,
);
} }
// Now set that error for the user to see. // Now set that error for the user to see.
this.errorMessage = userMessage; this.errorMessage = userMessage;

140
src/views/ProjectViewView.vue

@ -285,16 +285,38 @@ export default class ProjectViewView extends Vue {
this.truncatedDesc = this.description.slice(0, this.truncateLength); this.truncatedDesc = this.description.slice(0, this.truncateLength);
} else if (resp.status === 404) { } else if (resp.status === 404) {
// actually, axios throws an error so we never get here // actually, axios throws an error so we never get here
this.alertMessage = "That project does not exist."; this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "That project does not exist.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
if (serverError.response?.status === 404) { if (serverError.response?.status === 404) {
this.alertMessage = "That project does not exist."; this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "That project does not exist.",
},
-1,
);
} else { } else {
this.alertMessage = this.$notify(
"Something went wrong retrieving that project." + {
" See logs for more info."; group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving that project. See logs for more info.",
},
-1,
);
console.error("Error retrieving project:", serverError.message); console.error("Error retrieving project:", serverError.message);
} }
} }
@ -308,12 +330,27 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesToThis = resp.data.data; this.givesToThis = resp.data.data;
} else { } else {
this.alertMessage = "Failed to retrieve gives to this project."; this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to retrieve gives to this project.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.alertMessage = this.$notify(
"Something went wrong retrieving gives to this project."; {
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving gives to this project.",
},
-1,
);
console.error( console.error(
"Error retrieving gives to this project:", "Error retrieving gives to this project:",
serverError.message, serverError.message,
@ -329,11 +366,27 @@ export default class ProjectViewView extends Vue {
if (resp.status === 200 && resp.data.data) { if (resp.status === 200 && resp.data.data) {
this.givesByThis = resp.data.data; this.givesByThis = resp.data.data;
} else { } else {
this.alertMessage = "Failed to retrieve gives by this project."; this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Failed to retrieve gives by this project.",
},
-1,
);
} }
} catch (error: unknown) { } catch (error: unknown) {
const serverError = error as AxiosError; const serverError = error as AxiosError;
this.alertMessage = "Something went wrong retrieving gives by project."; this.$notify(
{
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong retrieving gives by project.",
},
-1,
);
console.error( console.error(
"Error retrieving gives by this project:", "Error retrieving gives by this project:",
serverError.message, serverError.message,
@ -364,16 +417,28 @@ export default class ProjectViewView extends Vue {
*/ */
async recordGive(giverDid, description, hours) { async recordGive(giverDid, description, hours) {
if (!this.activeDid) { if (!this.activeDid) {
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
"You must select an identity before you can record a give."; group: "alert",
type: "danger",
title: "Error",
text: "You must select an identity before you can record a give.",
},
-1,
);
return; return;
} }
if (!description && !hours) { if (!description && !hours) {
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
"You must enter a description or some number of hours."; group: "alert",
type: "danger",
title: "Error",
text: "You must enter a description or some number of hours.",
},
-1,
);
return; return;
} }
@ -392,21 +457,42 @@ export default class ProjectViewView extends Vue {
if (result.status !== 201 || result.data?.error) { if (result.status !== 201 || result.data?.error) {
console.log("Error with give result:", result); console.log("Error with give result:", result);
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
result.data?.error?.message || group: "alert",
"There was an error recording the give."; type: "danger",
title: "Error",
text:
result.data?.error?.message ||
"There was an error recording the give.",
},
-1,
);
} else { } else {
this.alertTitle = "Success"; this.$notify(
this.alertMessage = "That gift was recorded."; {
group: "alert",
type: "success",
title: "Success",
text: "That gift was recorded.",
},
-1,
);
} }
} catch (e) { } catch (e) {
console.log("Error with give caught:", e); console.log("Error with give caught:", e);
this.alertTitle = "Error"; this.$notify(
this.alertMessage = {
e.userMessage || group: "alert",
e.response?.data?.error?.message || type: "danger",
"There was an error recording the give."; title: "Error",
text:
e.userMessage ||
e.response?.data?.error?.message ||
"There was an error recording the give.",
},
-1,
);
} }
} }
} }

33
src/views/ProjectsView.vue

@ -126,8 +126,15 @@ export default class ProjectsView extends Vue {
} }
} catch (error) { } catch (error) {
console.error("Got error loading projects:", error.message); console.error("Got error loading projects:", error.message);
this.alertTitle = "Error"; this.$notify(
this.alertMessage = "Got an error loading projects:" + error.message; {
group: "alert",
type: "danger",
title: "Error",
text: "Got an error loading projects: " + error.message,
},
-1,
);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@ -196,8 +203,15 @@ export default class ProjectsView extends Vue {
if (this.numAccounts === 0) { if (this.numAccounts === 0) {
console.error("No accounts found."); console.error("No accounts found.");
this.alertTitle = "Error"; this.$notify(
this.alertMessage = "You need an identity to load your projects."; {
group: "alert",
type: "danger",
title: "Error",
text: "You need an identity to load your projects.",
},
-1,
);
} else { } else {
const identity = await this.getIdentity(activeDid); const identity = await this.getIdentity(activeDid);
this.current = identity; this.current = identity;
@ -205,8 +219,15 @@ export default class ProjectsView extends Vue {
} }
} catch (err) { } catch (err) {
console.log("Error initializing:", err); console.log("Error initializing:", err);
this.alertTitle = "Error"; this.$notify(
this.alertMessage = "Something went wrong loading your projects."; {
group: "alert",
type: "danger",
title: "Error",
text: "Something went wrong loading your projects.",
},
-1,
);
} }
} }

11
src/views/SeedBackupView.vue

@ -72,8 +72,15 @@ export default class SeedBackupView extends Vue {
this.activeAccount = R.find((acc) => acc.did === activeDid, accounts); this.activeAccount = R.find((acc) => acc.did === activeDid, accounts);
} catch (err) { } catch (err) {
console.error("Got an error loading an identity:", err); console.error("Got an error loading an identity:", err);
this.alertTitle = "Error Loading Account"; this.$notify(
this.alertMessage = "Got an error loading your seed data."; {
group: "alert",
type: "danger",
title: "Error Loading Account",
text: "Got an error loading your seed data.",
},
-1,
);
} }
} }

11
src/views/StatisticsView.vue

@ -62,8 +62,15 @@ export default class StatisticsView extends Vue {
this.world = newWorld; this.world = newWorld;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
this.alertTitle = "Mounting error"; this.$notify(
this.alertMessage = err.message; {
group: "alert",
type: "danger",
title: "Mounting Error",
text: err.message,
},
-1,
);
} }
} }

Loading…
Cancel
Save