forked from trent_larson/crowd-funder-for-time-pwa
add warning if on unexpected server
This commit is contained in:
58
src/components/TopMessage.vue
Normal file
58
src/components/TopMessage.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="text-center text-red-500">{{ message }}</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-facing-decorator";
|
||||
import { db } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||
import { AppString } from "@/constants/app";
|
||||
|
||||
interface Notification {
|
||||
group: string;
|
||||
type: string;
|
||||
title: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
@Component
|
||||
export default class TopMessage extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
@Prop selected = "";
|
||||
|
||||
message = "";
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
await db.open();
|
||||
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
if (
|
||||
settings?.warnIfTestServer &&
|
||||
window.location.hostname !== AppString.PROD_TIME_SAFARI_SERVER
|
||||
) {
|
||||
const didPrefix = settings.activeDid?.slice(11, 14);
|
||||
this.message = "You're linked to a test server, user " + didPrefix;
|
||||
} else if (
|
||||
settings?.warnIfProdServer &&
|
||||
window.location.hostname === AppString.PROD_TIME_SAFARI_SERVER
|
||||
) {
|
||||
const didPrefix = settings.activeDid?.slice(1, 14);
|
||||
this.message =
|
||||
"You're linked to the production server, user " + didPrefix;
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Detecting Server",
|
||||
text: JSON.stringify(err),
|
||||
},
|
||||
10000,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -6,12 +6,15 @@
|
||||
export enum AppString {
|
||||
APP_NAME = "Time Safari",
|
||||
|
||||
PROD_TIME_SAFARI_SERVER = "https://timesafari.app",
|
||||
|
||||
PROD_ENDORSER_API_SERVER = "https://api.endorser.ch",
|
||||
TEST_ENDORSER_API_SERVER = "https://test-api.endorser.ch",
|
||||
LOCAL_ENDORSER_API_SERVER = "http://localhost:3000",
|
||||
|
||||
DEFAULT_ENDORSER_API_SERVER = TEST_ENDORSER_API_SERVER,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
|
||||
PROD_PUSH_SERVER = "https://timesafari.app",
|
||||
TEST1_PUSH_SERVER = "https://test.timesafari.app",
|
||||
TEST2_PUSH_SERVER = "https://timesafari-pwa.anomalistlabs.com",
|
||||
|
||||
@@ -45,8 +45,5 @@ db.on("populate", () => {
|
||||
db.settings.add({
|
||||
id: MASTER_SETTINGS_KEY,
|
||||
apiServer: AppString.DEFAULT_ENDORSER_API_SERVER,
|
||||
|
||||
// remember that things you add from now on aren't automatically in the DB for old users
|
||||
webPushServer: AppString.DEFAULT_PUSH_SERVER,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,15 +12,17 @@ export type BoundingBox = {
|
||||
* Settings type encompasses user-specific configuration details.
|
||||
*/
|
||||
export type Settings = {
|
||||
id: number; // Only one entry using MASTER_SETTINGS_KEY
|
||||
id: number; // Only one entry, keyed with MASTER_SETTINGS_KEY
|
||||
|
||||
activeDid?: string; // Active Decentralized ID
|
||||
apiServer?: string; // API server URL
|
||||
firstName?: string; // User's first name
|
||||
lastName?: string; // deprecated - put all names in firstName
|
||||
lastViewedClaimId?: string; // Last viewed claim ID
|
||||
lastNotifiedClaimId?: string; // Last notified claim ID
|
||||
isRegistered?: boolean;
|
||||
webPushServer?: string; // Web Push server URL
|
||||
lastName?: string; // deprecated - put all names in firstName
|
||||
lastNotifiedClaimId?: string; // Last notified claim ID
|
||||
lastViewedClaimId?: string; // Last viewed claim ID
|
||||
reminderTime?: number; // Time in milliseconds since UNIX epoch for reminders
|
||||
reminderOn?: boolean; // Toggle to enable or disable reminders
|
||||
|
||||
// Array of named search boxes defined by bounding boxes
|
||||
searchBoxes?: Array<{
|
||||
@@ -30,8 +32,9 @@ export type Settings = {
|
||||
|
||||
showContactGivesInline?: boolean; // Display contact inline or not
|
||||
vapid?: string; // VAPID (Voluntary Application Server Identification) field for web push
|
||||
reminderTime?: number; // Time in milliseconds since UNIX epoch for reminders
|
||||
reminderOn?: boolean; // Toggle to enable or disable reminders
|
||||
warnIfProdServer?: boolean; // Warn if using a production server
|
||||
warnIfTestServer?: boolean; // Warn if using a testing server
|
||||
webPushServer?: string; // Web Push server URL
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<QuickNav selected="Profile"></QuickNav>
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
<!-- Heading -->
|
||||
@@ -215,7 +217,7 @@
|
||||
<div v-if="showAdvanced">
|
||||
<p class="text-rose-600 mb-8">
|
||||
Beware: the features here can be confusing and even change data in ways
|
||||
you do not expect. But we support your freedoms!
|
||||
you do not expect. But we support your freedom!
|
||||
</p>
|
||||
|
||||
<!-- Deep Identity Details -->
|
||||
@@ -357,6 +359,46 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label
|
||||
for="toggleProdWarningMessage"
|
||||
class="flex items-center justify-between cursor-pointer my-4"
|
||||
@click="toggleProdWarning"
|
||||
>
|
||||
<!-- label -->
|
||||
<h2>Show warning if on prod server</h2>
|
||||
<!-- toggle -->
|
||||
<div class="relative ml-2">
|
||||
<!-- input -->
|
||||
<input type="checkbox" v-model="warnIfProdServer" class="sr-only" />
|
||||
<!-- line -->
|
||||
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
|
||||
<!-- dot -->
|
||||
<div
|
||||
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
|
||||
></div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="toggleTestWarningMessage"
|
||||
class="flex items-center justify-between cursor-pointer my-4"
|
||||
@click="toggleTestWarning"
|
||||
>
|
||||
<!-- label -->
|
||||
<h2>Show warning if on test server</h2>
|
||||
<!-- toggle -->
|
||||
<div class="relative ml-2">
|
||||
<!-- input -->
|
||||
<input type="checkbox" v-model="warnIfTestServer" class="sr-only" />
|
||||
<!-- line -->
|
||||
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
|
||||
<!-- dot -->
|
||||
<div
|
||||
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
|
||||
></div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="flex py-4">
|
||||
<h2 class="text-slate-500 text-sm font-bold mb-2">
|
||||
Notification Push Server
|
||||
@@ -407,7 +449,8 @@ import "dexie-export-import";
|
||||
import { Component, Vue } from "vue-facing-decorator";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import QuickNav from "@/components/QuickNav";
|
||||
import TopMessage from "@/components/TopMessage";
|
||||
import { AppString } from "@/constants/app";
|
||||
import { db, accountsDB } from "@/db/index";
|
||||
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
||||
@@ -432,7 +475,7 @@ interface IAccount {
|
||||
derivationPath: string;
|
||||
}
|
||||
|
||||
@Component({ components: { QuickNav } })
|
||||
@Component({ components: { QuickNav, TopMessage } })
|
||||
export default class AccountViewView extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
@@ -462,6 +505,8 @@ export default class AccountViewView extends Vue {
|
||||
showAdvanced = false;
|
||||
|
||||
subscription: PushSubscription | null = null;
|
||||
warnIfProdServer = false;
|
||||
warnIfTestServer = false;
|
||||
|
||||
private isSubscribed = false;
|
||||
get toggleNotifications() {
|
||||
@@ -471,6 +516,62 @@ export default class AccountViewView extends Vue {
|
||||
this.isSubscribed = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async function executed when the component is created.
|
||||
* Initializes the component's state with values from the database,
|
||||
* handles identity-related tasks, and checks limitations.
|
||||
*
|
||||
* @throws Will display specific messages to the user based on different errors.
|
||||
*/
|
||||
async created() {
|
||||
try {
|
||||
await db.open();
|
||||
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
|
||||
// Initialize component state with values from the database or defaults
|
||||
this.initializeState(settings);
|
||||
|
||||
// Get and process the identity
|
||||
const identity = await this.getIdentity(this.activeDid);
|
||||
if (identity) {
|
||||
this.processIdentity(identity);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
this.handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
this.subscription = await registration.pushManager.getSubscription();
|
||||
this.toggleNotifications = !!this.subscription;
|
||||
} catch (error) {
|
||||
console.error("Mount error:", error);
|
||||
this.toggleNotifications = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes component state with values from the database or defaults.
|
||||
* @param {SettingsType} settings - Object containing settings from the database.
|
||||
*/
|
||||
initializeState(settings: Settings | undefined) {
|
||||
this.activeDid = (settings?.activeDid as string) || "";
|
||||
this.apiServer = (settings?.apiServer as string) || "";
|
||||
this.apiServerInput = (settings?.apiServer as string) || "";
|
||||
this.givenName =
|
||||
(settings?.firstName || "") +
|
||||
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3
|
||||
this.isRegistered = !!settings?.isRegistered;
|
||||
this.showContactGives = !!settings?.showContactGivesInline;
|
||||
this.warnIfProdServer = !!settings?.warnIfProdServer;
|
||||
this.warnIfTestServer = !!settings?.warnIfTestServer;
|
||||
this.webPushServer = (settings?.webPushServer as string) || "";
|
||||
this.webPushServerInput = (settings?.webPushServer as string) || "";
|
||||
}
|
||||
|
||||
public async getIdentity(activeDid: string): Promise<IIdentifier | null> {
|
||||
try {
|
||||
// Open the accounts database
|
||||
@@ -536,6 +637,16 @@ export default class AccountViewView extends Vue {
|
||||
this.updateShowContactAmounts();
|
||||
}
|
||||
|
||||
toggleProdWarning() {
|
||||
this.warnIfProdServer = !this.warnIfProdServer;
|
||||
this.updateWarnIfProdServer(this.warnIfProdServer);
|
||||
}
|
||||
|
||||
toggleTestWarning() {
|
||||
this.warnIfTestServer = !this.warnIfTestServer;
|
||||
this.updateWarnIfTestServer(this.warnIfTestServer);
|
||||
}
|
||||
|
||||
readableTime(timeStr: string) {
|
||||
return timeStr.substring(0, timeStr.indexOf("T"));
|
||||
}
|
||||
@@ -545,60 +656,6 @@ export default class AccountViewView extends Vue {
|
||||
this.numAccounts = await accountsDB.accounts.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async function executed when the component is created.
|
||||
* Initializes the component's state with values from the database,
|
||||
* handles identity-related tasks, and checks limitations.
|
||||
*
|
||||
* @throws Will display specific messages to the user based on different errors.
|
||||
*/
|
||||
async created() {
|
||||
try {
|
||||
await db.open();
|
||||
|
||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
||||
|
||||
// Initialize component state with values from the database or defaults
|
||||
this.initializeState(settings);
|
||||
|
||||
// Get and process the identity
|
||||
const identity = await this.getIdentity(this.activeDid);
|
||||
if (identity) {
|
||||
this.processIdentity(identity);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
this.handleError(err);
|
||||
}
|
||||
}
|
||||
|
||||
async mounted() {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
this.subscription = await registration.pushManager.getSubscription();
|
||||
this.toggleNotifications = !!this.subscription;
|
||||
} catch (error) {
|
||||
console.error("Mount error:", error);
|
||||
this.toggleNotifications = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes component state with values from the database or defaults.
|
||||
* @param {SettingsType} settings - Object containing settings from the database.
|
||||
*/
|
||||
initializeState(settings: Settings | undefined) {
|
||||
this.activeDid = (settings?.activeDid as string) || "";
|
||||
this.apiServer = (settings?.apiServer as string) || "";
|
||||
this.apiServerInput = (settings?.apiServer as string) || "";
|
||||
this.givenName =
|
||||
(settings?.firstName || "") +
|
||||
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3
|
||||
this.isRegistered = !!settings?.isRegistered;
|
||||
this.webPushServer = (settings?.webPushServer as string) || "";
|
||||
this.webPushServerInput = (settings?.webPushServer as string) || "";
|
||||
this.showContactGives = !!settings?.showContactGivesInline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the identity and updates the component's state.
|
||||
* @param {IdentityType} identity - Object containing identity information.
|
||||
@@ -672,6 +729,52 @@ export default class AccountViewView extends Vue {
|
||||
}
|
||||
}
|
||||
|
||||
public async updateWarnIfProdServer(newSetting) {
|
||||
try {
|
||||
await db.open();
|
||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
warnIfProdServer: newSetting,
|
||||
});
|
||||
} catch (err) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Updating Prod Warning",
|
||||
text: "Clear your cache and start over (after data backup).",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
console.error(
|
||||
"Telling user to clear cache after contact setting update because:",
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateWarnIfTestServer(newSetting) {
|
||||
try {
|
||||
await db.open();
|
||||
db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
warnIfTestServer: newSetting,
|
||||
});
|
||||
} catch (err) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Updating Test Warning",
|
||||
text: "Clear your cache and start over (after data backup).",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
console.error(
|
||||
"Telling user to clear cache after contact setting update because:",
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously exports the database into a downloadable JSON file.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<QuickNav selected="Discover"></QuickNav>
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
@@ -136,6 +137,7 @@ import { didInfo, ProjectData } from "@/libs/endorserServer";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
import TopMessage from "@/components/TopMessage";
|
||||
|
||||
interface Notification {
|
||||
group: string;
|
||||
@@ -149,6 +151,7 @@ interface Notification {
|
||||
QuickNav,
|
||||
InfiniteScroll,
|
||||
EntityIcon,
|
||||
TopMessage,
|
||||
},
|
||||
})
|
||||
export default class DiscoverView extends Vue {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<QuickNav selected="Home"></QuickNav>
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||
@@ -149,6 +151,7 @@ import {
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
import TopMessage from "@/components/TopMessage";
|
||||
import { IIdentifier } from "@veramo/core";
|
||||
import { Account } from "@/db/tables/accounts";
|
||||
|
||||
@@ -160,7 +163,7 @@ interface Notification {
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { GiftedDialog, QuickNav, EntityIcon },
|
||||
components: { GiftedDialog, QuickNav, EntityIcon, TopMessage },
|
||||
})
|
||||
export default class HomeView extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<QuickNav />
|
||||
<TopMessage />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
<!-- Breadcrumb -->
|
||||
@@ -282,6 +284,7 @@ import { Component, Vue } from "vue-facing-decorator";
|
||||
|
||||
import GiftedDialog from "@/components/GiftedDialog.vue";
|
||||
import OfferDialog from "@/components/OfferDialog.vue";
|
||||
import TopMessage from "@/components/TopMessage";
|
||||
import { accountsDB, db } from "@/db/index";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
|
||||
@@ -306,7 +309,7 @@ interface Notification {
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { EntityIcon, GiftedDialog, OfferDialog, QuickNav },
|
||||
components: { EntityIcon, GiftedDialog, OfferDialog, QuickNav, TopMessage },
|
||||
})
|
||||
export default class ProjectViewView extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<QuickNav selected="Projects"></QuickNav>
|
||||
<TopMessage />
|
||||
|
||||
<section id="Content" class="p-6 pb-24">
|
||||
<!-- Heading -->
|
||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||
@@ -79,6 +81,7 @@ import { IIdentifier } from "@veramo/core";
|
||||
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
||||
import QuickNav from "@/components/QuickNav.vue";
|
||||
import EntityIcon from "@/components/EntityIcon.vue";
|
||||
import TopMessage from "@/components/TopMessage";
|
||||
import { ProjectData } from "@/libs/endorserServer";
|
||||
|
||||
interface Notification {
|
||||
@@ -89,7 +92,7 @@ interface Notification {
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: { InfiniteScroll, QuickNav, EntityIcon },
|
||||
components: { InfiniteScroll, QuickNav, EntityIcon, TopMessage },
|
||||
})
|
||||
export default class ProjectsView extends Vue {
|
||||
$notify!: (notification: Notification, timeout?: number) => void;
|
||||
|
||||
Reference in New Issue
Block a user