changes for: `1.2.4`

This commit is contained in:
Andrei O 2023-03-07 19:16:55 +02:00
parent 0d02b1b440
commit a3f92bc3e8
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
5 changed files with 1041 additions and 687 deletions

1
.gitignore vendored
View File

@ -34,3 +34,4 @@ npm-debug.log*
/src/extension/webInject.js /src/extension/webInject.js
releases releases
rules.json rules.json
docs

View File

@ -1,5 +1,11 @@
# Changelog # Changelog
## Manifest Version 1.2.4
- updated showing assets page to use new api
- removed yup score from assets page
- change the info modal in settings
## Manifest Version 1.2.3 ## Manifest Version 1.2.3
- injected stub with chrome feature available in chrome 103 ( register world ) to bypass CSP - injected stub with chrome feature available in chrome 103 ( register world ) to bypass CSP

View File

@ -3,8 +3,8 @@
"name": "__MSG_appName__", "name": "__MSG_appName__",
"description": "__MSG_appDesc__", "description": "__MSG_appDesc__",
"default_locale": "en", "default_locale": "en",
"version": "1.2.2", "version": "1.2.4",
"version_name": "1.2.2", "version_name": "1.2.4",
"icons": { "icons": {
"16": "assets/extension-icon/wallet_16.png", "16": "assets/extension-icon/wallet_16.png",
"32": "assets/extension-icon/wallet_32.png", "32": "assets/extension-icon/wallet_32.png",

View File

@ -6,7 +6,6 @@
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content class="ion-padding"> <ion-content class="ion-padding">
<ion-loading <ion-loading
:is-open="loading" :is-open="loading"
cssClass="my-custom-class" cssClass="my-custom-class"
@ -30,19 +29,12 @@
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon> <ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
</ion-item> </ion-item>
<template v-if="isError"> <template v-if="isError">
Assets info could not be retrieved because of an http error, API down or conectivity issues. Assets info could not be retrieved because of an http error, API down or
</template> conectivity issues.
<template v-else-if="noAssets">
No assets found for this wallet address.
</template> </template>
<template v-else-if="noAssets"> No assets found for this wallet address. </template>
<template v-else> <template v-else>
<ion-item v-if="assets.yupScore"> <template v-if="ethTokens.length || polyTokens.length">
<span style="font-size: 0.9rem">YUP Score:</span> <span style="font-size: 1.1rem; margin-left: 0.5rem">{{ assets.yupScore.toFixed(2) }}</span>
</ion-item>
<ion-item>
<p style="font-size: 0.7rem">YUP score is a score of your wallet based on assets and transactions. </p>
</ion-item>
<template v-if="assets.tokens">
<template v-if="ethTokens.length"> <template v-if="ethTokens.length">
<ion-item>Ethereum Tokens</ion-item> <ion-item>Ethereum Tokens</ion-item>
<ion-list> <ion-list>
@ -57,7 +49,9 @@
@error="token.image = getUrl('assets/randomGrad.svg')" @error="token.image = getUrl('assets/randomGrad.svg')"
/> />
</ion-avatar> </ion-avatar>
<ion-label><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label> <ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item> </ion-item>
<ion-item v-if="hasMore.ethTokens"> <ion-item v-if="hasMore.ethTokens">
<ion-button @click="loadMore('ethTokens')">Load More</ion-button> <ion-button @click="loadMore('ethTokens')">Load More</ion-button>
@ -79,17 +73,17 @@
@error="token.image = getUrl('assets/randomGrad.svg')" @error="token.image = getUrl('assets/randomGrad.svg')"
/> />
</ion-avatar> </ion-avatar>
<ion-label><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label> <ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item> </ion-item>
<ion-item v-if="hasMore.polyTokens"> <ion-item v-if="hasMore.polyTokens">
<ion-button @click="loadMore('polyTokens')">Load More</ion-button> <ion-button @click="loadMore('polyTokens')">Load More</ion-button>
</ion-item> </ion-item>
</ion-list> </ion-list>
</template> </template>
</template> </template>
<template v-if="assets.nfts"> <template v-if="ethNfts.length || polyNfts.length">
<template v-if="ethNfts.length"> <template v-if="ethNfts.length">
<ion-item>Ethereum NFTs</ion-item> <ion-item>Ethereum NFTs</ion-item>
<ion-list> <ion-list>
@ -104,7 +98,9 @@
@error="nft.imageURI = getUrl('assets/randomGrad.svg')" @error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/> />
</ion-avatar> </ion-avatar>
<ion-label><b>{{ nft?.collectionName }}</b></ion-label> <ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item> </ion-item>
<ion-item v-if="hasMore.ethNfts"> <ion-item v-if="hasMore.ethNfts">
<ion-button @click="loadMore('ethNfts')">Load More</ion-button> <ion-button @click="loadMore('ethNfts')">Load More</ion-button>
@ -126,16 +122,16 @@
@error="nft.imageURI = getUrl('assets/randomGrad.svg')" @error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/> />
</ion-avatar> </ion-avatar>
<ion-label><b>{{ nft?.collectionName }}</b></ion-label> <ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item> </ion-item>
<ion-item v-if="hasMore.polyNfts"> <ion-item v-if="hasMore.polyNfts">
<ion-button @click="loadMore('polyNfts')">Load More</ion-button> <ion-button @click="loadMore('polyNfts')">Load More</ion-button>
</ion-item> </ion-item>
</ion-list> </ion-list>
</template> </template>
</template> </template>
<template v-if="assets.poaps">
<template v-if="poaps.length"> <template v-if="poaps.length">
<ion-item>POAPs</ion-item> <ion-item>POAPs</ion-item>
<ion-list> <ion-list>
@ -150,7 +146,9 @@
@error="nft.image = getUrl('assets/randomGrad.svg')" @error="nft.image = getUrl('assets/randomGrad.svg')"
/> />
</ion-avatar> </ion-avatar>
<ion-label><b>{{ nft?.title }}</b></ion-label> <ion-label
><b>{{ nft?.title }}</b></ion-label
>
</ion-item> </ion-item>
<ion-item v-if="hasMore.poaps"> <ion-item v-if="hasMore.poaps">
<ion-button @click="loadMore('poaps')">Load More</ion-button> <ion-button @click="loadMore('poaps')">Load More</ion-button>
@ -158,163 +156,407 @@
</ion-list> </ion-list>
</template> </template>
</template> </template>
</template>
</ion-content> </ion-content>
</ion-page> </ion-page>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, Ref, ref, reactive } from "vue"; import { defineComponent, Ref, ref, reactive } from "vue";
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, onIonViewWillEnter, IonItem, IonLabel, IonAvatar, IonList, IonButton, IonToast, IonLoading, IonIcon } from "@ionic/vue"; import {
import { getSelectedAccount, copyAddress, getUrl } from "@/utils/platform" IonContent,
import type { Account } from "@/extension/types" IonHeader,
IonPage,
IonTitle,
IonToolbar,
onIonViewWillEnter,
IonItem,
IonLabel,
IonAvatar,
IonList,
IonButton,
IonToast,
IonLoading,
IonIcon,
} from "@ionic/vue";
import { getSelectedAccount, copyAddress, getUrl } from "@/utils/platform";
import type { Account } from "@/extension/types";
import { copyOutline } from "ionicons/icons"; import { copyOutline } from "ionicons/icons";
const yupAssetsApi = 'https://api.yup.io/profile'
interface IProfileToken { interface IProfileToken {
address: string address: string;
balance: number balance: number;
image: string image: string;
name: string name: string;
symbol: string symbol: string;
} }
interface IProfileNFT { interface IProfileNFT {
address: string address: string;
collectionImageURI: string collectionImageURI: string;
collectionName: string collectionName: string;
imageURI: string imageURI: string;
link: string link: string;
tokenId: number tokenId: number;
} }
interface IProfilePOAP { interface IProfilePOAP {
description: string description: string;
eventId: string eventId: string;
image: string image: string;
link: string link: string;
title: string title: string;
} }
export default defineComponent({ export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonAvatar, IonList, IonButton, IonToast, IonLoading, IonIcon }, components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonAvatar,
IonList,
IonButton,
IonToast,
IonLoading,
IonIcon,
},
setup: () => { setup: () => {
const selectedAccount = ref({}) as Ref<Account> const selectedAccount = ref({}) as Ref<Account>;
const assets = ref({}) as Ref< { const loading = ref(true);
yupScore?: number const isError = ref(false);
tokens?: any[] const noAssets = ref(false);
nfts?: any[]
poaps?: any[]
}>
const loading = ref(true)
const isError = ref(false)
const noAssets = ref(false)
const toastState = ref(false); const toastState = ref(false);
const ethTokens = ref([]) as Ref< IProfileToken[]> const ethTokens = ref([]) as Ref<IProfileToken[]>;
const polyTokens = ref([]) as Ref< IProfileToken[]> const polyTokens = ref([]) as Ref<IProfileToken[]>;
const ethNfts = ref([]) as Ref< IProfileNFT[]> const ethNfts = ref([]) as Ref<IProfileNFT[]>;
const polyNfts = ref([]) as Ref< IProfileNFT[]> const polyNfts = ref([]) as Ref<IProfileNFT[]>;
const poaps = ref([]) as Ref< IProfilePOAP[]> const poaps = ref([]) as Ref<IProfilePOAP[]>;
const hasMore = reactive({ const hasMore = reactive({
poaps: true, poaps: true,
ethTokens: true, ethTokens: true,
polyTokens: true, polyTokens: true,
ethNfts: true, ethNfts: true,
polyNfts: true, polyNfts: true,
}) });
const getToastRef = () => toastState; const getToastRef = () => toastState;
onIonViewWillEnter(async () => { const resources = ["nfts", "poaps", "tokens"];
selectedAccount.value = await getSelectedAccount() const chains = ["ethereum", "polygon"];
const req = await fetch(`${yupAssetsApi}/${selectedAccount.value.address}`)
if(req.ok) { const fetchFromWallet = async ({
assets.value = (await req.json()) ?? {} apiBase = "https://api.yup.io",
if(!('poaps' in assets.value) && !('tokens' in assets.value) && !('nfts' in assets.value)) { address,
noAssets.value = true resource = resources[0],
} chain = chains[0],
if ('poaps' in assets.value) { start = 0,
poaps.value = assets.value?.poaps?.slice(0, 10) ?? [] limit = 10,
if(poaps.value.length >= (assets.value?.poaps?.length ?? 0)) { }: {
hasMore.poaps = false apiBase?: string;
} address: string;
} resource?: string;
if ('nfts' in assets.value) { chain?: string;
ethNfts.value = assets.value?.nfts?.filter(n => n.chain === 'ethereum').slice(0, 10) ?? [] start?: number;
if(ethNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'ethereum').length ?? 0)) { limit?: number;
hasMore.ethNfts = false }) => {
} try {
polyNfts.value = assets.value?.nfts?.filter(n => n.chain === 'polygon').slice(0, 10) ?? [] const res = await fetch(
if(polyNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'polygon').length ?? 0)) { `${apiBase}/web3-profiles/${resource}/${address}?chain=${chain}&start=${start}&limit=${limit}`
hasMore.polyNfts = false );
} const req = await res.json();
} if (res.ok) {
if ('tokens' in assets.value) { return req;
ethTokens.value = assets.value?.tokens?.filter(n => n.chain === 'ethereum').slice(0, 10) ?? []
if(ethTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'ethereum').length ?? 0)) {
hasMore.ethTokens = false
}
polyTokens.value = assets.value?.tokens?.filter(n => n.chain === 'polygon').slice(0, 10) ?? []
if(polyTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'polygon').length ?? 0)) {
hasMore.polyTokens = false
}
}
} else { } else {
isError.value = true return null;
} }
loading .value = false } catch (error) {
console.error("Failed to fetch web3 profiles", error);
return null;
}
};
const walletLoadArgs = {
address: "",
start: 0,
limit: 11,
res: resources,
ch: chains,
apiBase: "https://api.yup.io",
};
const getProfileWallet = async ({
address,
start,
limit,
res,
ch,
apiBase,
}: {
address: string;
start: number;
limit: number;
res: string[];
ch: string[];
apiBase: string;
}) => {
const r = {
poaps: [] as IProfilePOAP[],
ethNfts: [] as IProfileNFT[],
polyNfts: [] as IProfileNFT[],
ethTokens: [] as IProfileToken[],
polyTokens: [] as IProfileToken[],
};
try {
const promises = [];
if (res.includes("poaps")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "poaps",
chain: "ethereum",
}).then((rz) => {
r.poaps = rz ?? [];
}) })
);
}
if (res.includes("nfts")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "ethereum",
}).then((rz) => {
r.ethNfts = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "polygon",
}).then((rz) => {
r.polyNfts = rz ?? [];
})
);
}
}
if (res.includes("tokens")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "ethereum",
}).then((rz) => {
r.ethTokens = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "polygon",
}).then((rz) => {
r.polyTokens = rz ?? [];
})
);
}
}
const loadMore = (type: string) => { await Promise.all(promises);
return r;
} catch {
return r;
}
};
onIonViewWillEnter(async () => {
selectedAccount.value = await getSelectedAccount();
walletLoadArgs.address = selectedAccount.value.address;
const r = await getProfileWallet(walletLoadArgs);
ethNfts.value = r.ethNfts.slice(0, 10);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
}
polyNfts.value = r.polyNfts.slice(0, 10);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
}
ethTokens.value = r.ethTokens.slice(0, 10);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
}
polyTokens.value = r.polyTokens.slice(0, 10);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
}
poaps.value = r.poaps.slice(0, -1);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
}
noAssets.value =
poaps.value.length &&
ethNfts.value.length &&
polyNfts.value.length &&
ethTokens.value.length &&
polyTokens.value.length
? false
: true;
loading.value = false;
// const req = await fetch(`${yupAssetsApi}/${selectedAccount.value.address}`);
// if (req.ok) {
// assets.value = (await req.json()) ?? {};
// if (
// !("poaps" in assets.value) &&
// !("tokens" in assets.value) &&
// !("nfts" in assets.value)
// ) {
// noAssets.value = true;
// }
// if ("poaps" in assets.value) {
// poaps.value = assets.value?.poaps?.slice(0, 10) ?? [];
// if (poaps.value.length >= (assets.value?.poaps?.length ?? 0)) {
// hasMore.poaps = false;
// }
// }
// if ("nfts" in assets.value) {
// ethNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "ethereum").slice(0, 10) ?? [];
// if (
// ethNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethNfts = false;
// }
// polyNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyNfts = false;
// }
// }
// if ("tokens" in assets.value) {
// ethTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "ethereum").slice(0, 10) ??
// [];
// if (
// ethTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethTokens = false;
// }
// polyTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyTokens = false;
// }
// }
// } else {
// isError.value = true;
// }
// loading.value = false;
});
const loadMore = async (type: string) => {
switch (type) { switch (type) {
case 'ethTokens': { case "ethTokens": {
ethTokens.value = assets.value?.tokens?.filter(n => n.chain === 'ethereum').slice(0, ethTokens.value.length + 10) ?? [] walletLoadArgs.start = ethTokens.value.length;
if(ethTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'ethereum').length ?? 0)) { walletLoadArgs.res = ["tokens"];
hasMore.ethTokens = false walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
return;
} }
break ethTokens.value = [...ethTokens.value, ...r.ethTokens.slice(0, 10)];
break;
} }
case 'polyTokens': { case "polyTokens": {
polyTokens.value = assets.value?.tokens?.filter(n => n.chain === 'polygon').slice(0, polyTokens.value.length + 10) ?? [] walletLoadArgs.start = polyTokens.value.length;
if(polyTokens.value.length >= (assets.value?.tokens?.filter(n => n.chain === 'polygon').length ?? 0)) { walletLoadArgs.res = ["tokens"];
hasMore.polyTokens = false walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
return;
} }
break polyTokens.value = [...polyTokens.value, ...r.polyTokens.slice(0, 10)];
break;
} }
case 'ethNfts': { case "ethNfts": {
ethNfts.value = assets.value?.nfts?.filter(n => n.chain === 'ethereum').slice(0, ethNfts.value.length + 10) ?? [] walletLoadArgs.start = ethNfts.value.length;
if(ethNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'ethereum').length ?? 0)) { walletLoadArgs.res = ["nfts"];
hasMore.ethNfts = false walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
return;
} }
break ethNfts.value = [...ethNfts.value, ...r.ethNfts.slice(0, 10)];
break;
} }
case 'polyNfts': { case "polyNfts": {
polyNfts.value = assets.value?.nfts?.filter(n => n.chain === 'polygon').slice(0, polyNfts.value.length + 10) ?? [] walletLoadArgs.start = polyNfts.value.length;
if(polyNfts.value.length >= (assets.value?.nfts?.filter(n => n.chain === 'polygon').length ?? 0)) { walletLoadArgs.res = ["nfts"];
hasMore.polyNfts = false walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
return;
} }
break polyNfts.value = [...polyNfts.value, ...r.polyNfts.slice(0, 10)];
break;
} }
case 'poaps': { case "poaps": {
poaps.value = assets.value?.poaps?.slice(0, poaps.value.length + 10) ?? [] walletLoadArgs.start = poaps.value.length;
if(poaps.value.length >= (assets.value?.poaps?.length ?? 0)) { walletLoadArgs.res = ["poaps"];
hasMore.poaps = false walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
return;
} }
break poaps.value = [...poaps.value, ...r.poaps.slice(0, 10)];
break;
} }
} }
};
}
return { return {
selectedAccount, selectedAccount,
loading, loading,
isError, isError,
noAssets, noAssets,
assets,
getToastRef, getToastRef,
copyAddress, copyAddress,
copyOutline, copyOutline,
@ -326,9 +568,8 @@ export default defineComponent({
polyNfts, polyNfts,
loadMore, loadMore,
toastState, toastState,
getUrl getUrl,
} };
} },
}); });
</script> </script>

View File

@ -13,11 +13,18 @@
</ion-item> </ion-item>
<div class="ion-padding" slot="content"> <div class="ion-padding" slot="content">
<ion-list> <ion-list>
<ion-item v-if="noAccounts">You need at least one account to touch this settings</ion-item> <ion-item v-if="noAccounts"
>You need at least one account to touch this settings</ion-item
>
<ion-list :disabled="noAccounts"> <ion-list :disabled="noAccounts">
<ion-item> <ion-item>
<ion-label>Enable Storage Encryption</ion-label> <ion-label>Enable Storage Encryption</ion-label>
<ion-toggle :key="updateKey" @ion-change="changeEncryption" slot="end" :checked="settings.s.enableStorageEnctyption"></ion-toggle> <ion-toggle
:key="updateKey"
@ion-change="changeEncryption"
slot="end"
:checked="settings.s.enableStorageEnctyption"
></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
This will require to input an encrypto key when storage is locked. This will require to input an encrypto key when storage is locked.
@ -25,25 +32,54 @@
</ion-list> </ion-list>
<ion-item :disabled="!settings.s.enableStorageEnctyption"> <ion-item :disabled="!settings.s.enableStorageEnctyption">
<ion-label>Enable Auto Lock</ion-label> <ion-label>Enable Auto Lock</ion-label>
<ion-toggle :key="updateKey" @ion-change="changeAutoLock" slot="end" :checked="settings.s.lockOutEnabled"></ion-toggle> <ion-toggle
:key="updateKey"
@ion-change="changeAutoLock"
slot="end"
:checked="settings.s.lockOutEnabled"
></ion-toggle>
</ion-item> </ion-item>
<ion-list> <ion-list>
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled"> <ion-item
:disabled="
!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled
"
>
<ion-label>Auto-lock Period: (2-120) minutes</ion-label> <ion-label>Auto-lock Period: (2-120) minutes</ion-label>
</ion-item> </ion-item>
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled"> <ion-item
<ion-input :key="updateKey" v-model="settings.s.lockOutPeriod" type="number"></ion-input> :disabled="
!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled
"
>
<ion-input
:key="updateKey"
v-model="settings.s.lockOutPeriod"
type="number"
></ion-input>
</ion-item> </ion-item>
<ion-item :disabled="!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled"> <ion-item
:disabled="
!settings.s.enableStorageEnctyption || !settings.s.lockOutEnabled
"
>
<ion-button @click="setTime">Set Auto-lock</ion-button> <ion-button @click="setTime">Set Auto-lock</ion-button>
</ion-item> </ion-item>
</ion-list> </ion-list>
<ion-list> <ion-list>
<ion-item> <ion-item>
<ion-label>Permanent Lock</ion-label> <ion-label>Permanent Lock</ion-label>
<ion-toggle @ion-change="changePermaLock" :key="updateKey" slot="end" :disabled="!settings.s.enableStorageEnctyption" :checked="settings.s.encryptAfterEveryTx"></ion-toggle> <ion-toggle
@ion-change="changePermaLock"
:key="updateKey"
slot="end"
:disabled="!settings.s.enableStorageEnctyption"
:checked="settings.s.encryptAfterEveryTx"
></ion-toggle>
</ion-item> </ion-item>
<ion-item>Will require decrypt pass before any sign or transaction</ion-item> <ion-item
>Will require decrypt pass before any sign or transaction</ion-item
>
</ion-list> </ion-list>
</ion-list> </ion-list>
</div> </div>
@ -56,27 +92,15 @@
<ion-list> <ion-list>
<ion-radio-group :value="radioTheme"> <ion-radio-group :value="radioTheme">
<ion-item> <ion-item>
<ion-radio <ion-radio slot="start" value="system" @click="changeTheme('system')" />
slot="start"
value="system"
@click="changeTheme('system')"
/>
<ion-label>System Default</ion-label> <ion-label>System Default</ion-label>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-radio <ion-radio slot="start" value="dark" @click="changeTheme('dark')" />
slot="start"
value="dark"
@click="changeTheme('dark')"
/>
<ion-label>Dark</ion-label> <ion-label>Dark</ion-label>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-radio <ion-radio slot="start" value="light" @click="changeTheme('light')" />
slot="start"
value="light"
@click="changeTheme('light')"
/>
<ion-label>Light</ion-label> <ion-label>Light</ion-label>
</ion-item> </ion-item>
</ion-radio-group> </ion-radio-group>
@ -88,16 +112,43 @@
<ion-label>About</ion-label> <ion-label>About</ion-label>
</ion-item> </ion-item>
<div class="ion-padding" slot="content"> <div class="ion-padding" slot="content">
<p>Clear EVM Wallet (CLW) is a fully open-source wallet built with Vue, Ionic, and Ethers.</p> <p>
<p>It emulates Metamask Wallet and can be used as a drop-in replacement, right now if you have both extensions, CLW will overwrite Metamask.</p> Clear EVM Wallet (CLW) is a fully open-source wallet built with Vue, Ionic,
<p>Main philosophy of the wallet is: no trackers, full control, export/import JSONs with accounts, fast generate new accounts, and wipe everything with one click.</p> and Ethers.
<p>Github Repo: <a href="#" @click="openTab('https://github.com/andrei0x309/clear-wallet')">LINK</a></p> </p>
<p>
It emulates Metamask Wallet and can be used as a drop-in replacement, right
now if you have both extensions, CLW will overwrite Metamask.
</p>
<p>
Main philosophy of the wallet is: no trackers, full control, export/import
JSONs with accounts, fast generate new accounts, and wipe everything with
one click.
</p>
<p>
Github Repo:
<a href="#" @click="openTab('https://github.com/andrei0x309/clear-wallet')"
>LINK</a
>
</p>
<br /> <br />
<p style="margin-bottom: 0.2rem">Some Web3 Projects I personally appreciate:</p> <p style="margin-bottom: 0.2rem">Places you can check me out:</p>
<p>YUP - web3 social platform <a href="#" @click="openTab('https://app.yup.io')">LINK</a></p> <p>
<p>Crypto-Leftists: web3 left-wing crypto community <a href="#" @click="openTab('https://discord.gg/gzA4bTCdhb')">LINK</a></p> Github andrei0x309 -
<p>Idena: web3 fully private identity provider blockchain <a href="#" @click="openTab('https://www.idena.io/')">LINK</a></p> <a href="#" @click="openTab('https://github.com/andrei0x309')">LINK</a>
<p>Mirror: web3 publishing platform <a href="#" @click="openTab('https://mirror.xyz')">LINK</a></p> </p>
<p>
Mirror Profile
<a href="#" @click="openTab('https://mirror.xyz/andrei0x309.eth')">LINK</a>
</p>
<p>
Blog Flashsoft
<a href="#" @click="openTab('https://blog.flashsoft.eu')">LINK</a>
</p>
<p>
Crypto-Leftists Discord
<a href="#" @click="openTab('https://discord.gg/gzA4bTCdhb')">LINK</a>
</p>
</div> </div>
</ion-accordion> </ion-accordion>
<ion-accordion value="4"> <ion-accordion value="4">
@ -144,14 +195,29 @@
</ion-loading> </ion-loading>
<ion-modal <ion-modal
:is-open="mpModal" :is-open="mpModal"
@did-dismiss="mpModal=false;modalDismiss()" @did-dismiss="
mpModal = false;
modalDismiss();
"
> >
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-buttons slot="start"> <ion-buttons slot="start">
<ion-button @click="modalGetPassword?.reject ? (() => { modalGetPassword.reject(); modalGetPassword = null })() : mpModal=false">Close</ion-button> <ion-button
@click="
modalGetPassword?.reject
? (() => {
modalGetPassword.reject();
modalGetPassword = null;
})()
: (mpModal = false)
"
>Close</ion-button
>
</ion-buttons> </ion-buttons>
<ion-title v-if="!settings.s.enableStorageEnctyption">Create Encryption Password</ion-title> <ion-title v-if="!settings.s.enableStorageEnctyption"
>Create Encryption Password</ion-title
>
<ion-title v-else>Enter Encryption Password</ion-title> <ion-title v-else>Enter Encryption Password</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
@ -159,7 +225,8 @@
<ion-list v-if="settings.s.enableStorageEnctyption"> <ion-list v-if="settings.s.enableStorageEnctyption">
<ion-item> <ion-item>
<ion-label>Old Password</ion-label> <ion-label>Old Password</ion-label>
</ion-item> <ion-item> </ion-item>
<ion-item>
<ion-input v-model="mpPass" type="password"></ion-input> <ion-input v-model="mpPass" type="password"></ion-input>
</ion-item> </ion-item>
</ion-list> </ion-list>
@ -167,20 +234,32 @@
<ion-list> <ion-list>
<ion-item> <ion-item>
<ion-label>New Password</ion-label> <ion-label>New Password</ion-label>
</ion-item> <ion-item> </ion-item>
<ion-item>
<ion-input v-model="mpPass" type="password"></ion-input> <ion-input v-model="mpPass" type="password"></ion-input>
</ion-item> </ion-item>
</ion-list> </ion-list>
<ion-list> <ion-list>
<ion-item> <ion-item>
<ion-label>Confirm</ion-label> <ion-label>Confirm</ion-label>
</ion-item> <ion-item> </ion-item>
<ion-item>
<ion-input v-model="mpConfirm" type="password"></ion-input> <ion-input v-model="mpConfirm" type="password"></ion-input>
</ion-item> </ion-item>
</ion-list> </ion-list>
</div> </div>
<ion-item> <ion-item>
<ion-button @click="modalGetPassword?.resolve ? (() => { modalGetPassword.resolve(); modalGetPassword = null })() : confirmModal()">Confirm</ion-button> <ion-button
@click="
modalGetPassword?.resolve
? (() => {
modalGetPassword.resolve();
modalGetPassword = null;
})()
: confirmModal()
"
>Confirm</ion-button
>
</ion-item> </ion-item>
</ion-content> </ion-content>
</ion-modal> </ion-modal>
@ -197,11 +276,19 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, reactive, Ref } from "vue"; import { defineComponent, ref, reactive, Ref } from "vue";
import { storageWipe, getSettings, setSettings, getAccounts, saveSelectedAccount, replaceAccounts, openTab } from "@/utils/platform"; import {
import { decrypt, encrypt, getCryptoParams } from "@/utils/webCrypto" storageWipe,
import { Account } from '@/extension/types' getSettings,
import { exportFile } from '@/utils/misc' setSettings,
import type { Settings } from "@/extension/types" getAccounts,
saveSelectedAccount,
replaceAccounts,
openTab,
} from "@/utils/platform";
import { decrypt, encrypt, getCryptoParams } from "@/utils/webCrypto";
import { Account } from "@/extension/types";
import { exportFile } from "@/utils/misc";
import type { Settings } from "@/extension/types";
import { import {
IonContent, IonContent,
IonHeader, IonHeader,
@ -223,7 +310,7 @@ import {
IonRadio, IonRadio,
IonButtons, IonButtons,
IonAlert, IonAlert,
IonToast IonToast,
} from "@ionic/vue"; } from "@ionic/vue";
export default defineComponent({ export default defineComponent({
@ -247,25 +334,28 @@ export default defineComponent({
IonRadio, IonRadio,
IonButtons, IonButtons,
IonAlert, IonAlert,
IonToast IonToast,
}, },
setup() { setup() {
const loading = ref(true); const loading = ref(true);
const mpModal = ref(false); const mpModal = ref(false);
const mpPass = ref(''); const mpPass = ref("");
const mpConfirm = ref(''); const mpConfirm = ref("");
const updateKey = ref(0); const updateKey = ref(0);
const alertOpen = ref(false); const alertOpen = ref(false);
const alertMsg = ref(''); const alertMsg = ref("");
const toastState = ref(false); const toastState = ref(false);
const toastMsg = ref(''); const toastMsg = ref("");
const alertHeader = ref('Error') const alertHeader = ref("Error");
const importFile = ref(null) as unknown as Ref<HTMLInputElement> const importFile = (ref(null) as unknown) as Ref<HTMLInputElement>;
type ModalPromisePassword = null | { resolve: ((p?: unknown) => void), reject: ((p?: unknown) => void)} type ModalPromisePassword = null | {
const modalGetPassword = ref(null) as Ref<ModalPromisePassword> resolve: (p?: unknown) => void;
const noAccounts = ref(true) reject: (p?: unknown) => void;
const defaultAccordionOpen = ref("0") };
const radioTheme = ref('system') as Ref<'system' | 'light' | 'dark'> const modalGetPassword = ref(null) as Ref<ModalPromisePassword>;
const noAccounts = ref(true);
const defaultAccordionOpen = ref("0");
const radioTheme = ref("system") as Ref<"system" | "light" | "dark">;
const wipeStorage = async () => { const wipeStorage = async () => {
loading.value = true; loading.value = true;
@ -273,280 +363,296 @@ export default defineComponent({
loading.value = false; loading.value = false;
}; };
const settings = reactive({ const settings = reactive({
s: null as unknown as Settings s: (null as unknown) as Settings,
}) as { s: Settings} }) as { s: Settings };
const saveSettings = async () => { const saveSettings = async () => {
loading.value = true loading.value = true;
await setSettings(settings.s) await setSettings(settings.s);
loading.value = false loading.value = false;
} };
const setEncryptToggle = (state: boolean) => { const setEncryptToggle = (state: boolean) => {
settings.s.enableStorageEnctyption = state settings.s.enableStorageEnctyption = state;
updateKey.value++ updateKey.value++;
defaultAccordionOpen.value = "1" defaultAccordionOpen.value = "1";
} };
const changeAutoLock = async () => { const changeAutoLock = async () => {
settings.s.lockOutEnabled = !settings.s.lockOutEnabled settings.s.lockOutEnabled = !settings.s.lockOutEnabled;
updateKey.value++ updateKey.value++;
await saveSettings() await saveSettings();
defaultAccordionOpen.value = "1" defaultAccordionOpen.value = "1";
} };
const changePermaLock = async () => { const changePermaLock = async () => {
settings.s.encryptAfterEveryTx = !settings.s.encryptAfterEveryTx settings.s.encryptAfterEveryTx = !settings.s.encryptAfterEveryTx;
updateKey.value++ updateKey.value++;
await saveSettings() await saveSettings();
defaultAccordionOpen.value = "1" defaultAccordionOpen.value = "1";
} };
const changeTheme = async (theme: 'system' | 'light' | 'dark') => { const changeTheme = async (theme: "system" | "light" | "dark") => {
document.body.classList.remove(radioTheme.value) document.body.classList.remove(radioTheme.value);
document.body.classList.add(theme) document.body.classList.add(theme);
radioTheme.value = theme radioTheme.value = theme;
settings.s.theme = theme settings.s.theme = theme;
await saveSettings() await saveSettings();
defaultAccordionOpen.value = "2" defaultAccordionOpen.value = "2";
} };
const changeEncryption = async () => { const changeEncryption = async () => {
loading.value = true loading.value = true;
mpModal.value = true mpModal.value = true;
loading.value = false loading.value = false;
} };
const confirmModal = async () => { const confirmModal = async () => {
loading.value = true loading.value = true;
if (mpPass.value.length < 3) { if (mpPass.value.length < 3) {
loading.value = false loading.value = false;
alertHeader.value = 'Error' alertHeader.value = "Error";
alertMsg.value = 'Password is too short. More than 3 characters are required.'; alertMsg.value = "Password is too short. More than 3 characters are required.";
alertOpen.value = true alertOpen.value = true;
setEncryptToggle(settings.s.enableStorageEnctyption) setEncryptToggle(settings.s.enableStorageEnctyption);
return return;
} }
if (!settings.s.enableStorageEnctyption) { if (!settings.s.enableStorageEnctyption) {
if (mpPass.value !== mpConfirm.value) { if (mpPass.value !== mpConfirm.value) {
loading.value = false loading.value = false;
alertHeader.value = 'Error' alertHeader.value = "Error";
alertMsg.value = 'Password and confirm password do not match'; alertMsg.value = "Password and confirm password do not match";
alertOpen.value = true alertOpen.value = true;
setEncryptToggle(settings.s.enableStorageEnctyption) setEncryptToggle(settings.s.enableStorageEnctyption);
return return;
} }
let accounts = await getAccounts() let accounts = await getAccounts();
const cryptoParams = await getCryptoParams(mpPass.value) const cryptoParams = await getCryptoParams(mpPass.value);
const accProm = accounts.map(async a => { const accProm = accounts.map(async (a) => {
a.encPk = await encrypt(a.pk, cryptoParams) a.encPk = await encrypt(a.pk, cryptoParams);
a.pk = '' a.pk = "";
return a return a;
}) });
accounts = await Promise.all(accProm) accounts = await Promise.all(accProm);
await replaceAccounts(accounts) await replaceAccounts(accounts);
await saveSelectedAccount(accounts[0]) await saveSelectedAccount(accounts[0]);
setEncryptToggle(true) setEncryptToggle(true);
await setSettings(settings.s) await setSettings(settings.s);
mpPass.value = '' mpPass.value = "";
mpConfirm.value = '' mpConfirm.value = "";
mpModal.value = false mpModal.value = false;
} else { } else {
try { try {
let accounts = await getAccounts() let accounts = await getAccounts();
const cryptoParams = await getCryptoParams(mpPass.value) const cryptoParams = await getCryptoParams(mpPass.value);
const accProm = accounts.map(async a => { const accProm = accounts.map(async (a) => {
if (a.encPk) { if (a.encPk) {
a.pk = await decrypt(a.encPk, cryptoParams) a.pk = await decrypt(a.encPk, cryptoParams);
} }
return a return a;
}) });
accProm.forEach( a => a.catch(e => console.log(e)) ) accProm.forEach((a) => a.catch((e) => console.log(e)));
accounts = await Promise.all(accProm) accounts = await Promise.all(accProm);
await replaceAccounts(accounts) await replaceAccounts(accounts);
await saveSelectedAccount(accounts[0]) await saveSelectedAccount(accounts[0]);
setEncryptToggle(false) setEncryptToggle(false);
settings.s.lockOutEnabled = false settings.s.lockOutEnabled = false;
settings.s.encryptAfterEveryTx = false settings.s.encryptAfterEveryTx = false;
await setSettings(settings.s) await setSettings(settings.s);
mpPass.value = '' mpPass.value = "";
mpConfirm.value = '' mpConfirm.value = "";
mpModal.value = false mpModal.value = false;
} catch (error) { } catch (error) {
console.log(error) console.log(error);
loading.value = false loading.value = false;
alertHeader.value = 'Error' alertHeader.value = "Error";
alertMsg.value = 'Decryption failed, password is not correct.'; alertMsg.value = "Decryption failed, password is not correct.";
alertOpen.value = true alertOpen.value = true;
setEncryptToggle(settings.s.enableStorageEnctyption) setEncryptToggle(settings.s.enableStorageEnctyption);
return return;
} }
} }
loading.value = false loading.value = false;
} };
const validateFile = () => { const validateFile = () => {
return new Promise((resolve) => { return new Promise((resolve) => {
try { try {
if (!importFile.value?.value?.length) { if (!importFile.value?.value?.length) {
return resolve({ return resolve({
error: 'Import json file is missing' error: "Import json file is missing",
}) });
} }
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (event) => { reader.onload = (event) => {
const json = JSON.parse(event?.target?.result as string) const json = JSON.parse(event?.target?.result as string);
if (!json.length) { if (!json.length) {
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}]' }) return resolve({
error:
'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}]',
});
} }
const test = json.some((e:any) => ( !('pk' in e ) || !('name' in e) || !('address' in e) || !(e.pk.length === 66 || e.pk.length === 64))) const test = json.some(
(e: any) =>
!("pk" in e) ||
!("name" in e) ||
!("address" in e) ||
!(e.pk.length === 66 || e.pk.length === 64)
);
if (test) { if (test) {
return resolve({ error: 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}], Also PK must be valid (66 || 64 length) !' }) return resolve({
} error:
return resolve({ error: false, json }) 'JSON format is wrong. Corrrect JSON format is: [{ "name": "Account Name", "pk": "Private Key", "address": "0x..." },{...}], Also PK must be valid (66 || 64 length) !',
});
} }
return resolve({ error: false, json });
};
reader.readAsText(importFile.value?.files?.[0] as File); reader.readAsText(importFile.value?.files?.[0] as File);
} catch { } catch {
return resolve( return resolve({
{ error: "Parsing JSON file",
error: 'Parsing JSON file' });
})
}
})
} }
});
};
const getPassword = () => { const getPassword = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
modalGetPassword.value = { resolve, reject } modalGetPassword.value = { resolve, reject };
mpModal.value = true mpModal.value = true;
}) });
} };
const promptForPassword = async (accounts: Account[]) => { const promptForPassword = async (accounts: Account[]) => {
let isCorectPass = false let isCorectPass = false;
do { do {
try { try {
await getPassword() await getPassword();
modalGetPassword.value = null modalGetPassword.value = null;
} catch { } catch {
alertHeader.value = 'Error' alertHeader.value = "Error";
alertMsg.value = "Password is required!" alertMsg.value = "Password is required!";
alertOpen.value = true alertOpen.value = true;
mpModal.value = false mpModal.value = false;
return false return false;
} }
try { try {
const cryptoParams = await getCryptoParams(mpPass.value) const cryptoParams = await getCryptoParams(mpPass.value);
if (accounts?.[0]?.encPk) { if (accounts?.[0]?.encPk) {
await decrypt(accounts[0].encPk, cryptoParams) await decrypt(accounts[0].encPk, cryptoParams);
} }
isCorectPass = true isCorectPass = true;
} catch { } catch {
isCorectPass = false isCorectPass = false;
alertHeader.value = 'Error' alertHeader.value = "Error";
alertMsg.value = "Password is wrong!" alertMsg.value = "Password is wrong!";
alertOpen.value = true alertOpen.value = true;
} }
} while (!isCorectPass); } while (!isCorectPass);
return true return true;
} };
const importAcc = async () => { const importAcc = async () => {
const validation = await validateFile() as { error: any } const validation = (await validateFile()) as { error: any };
if (validation.error) { if (validation.error) {
alertMsg.value = validation.error alertMsg.value = validation.error;
alertOpen.value = true alertOpen.value = true;
return return;
} }
const accounts = await getAccounts() const accounts = await getAccounts();
const newAccounts = (validation as unknown as { json: Account[] }).json const newAccounts = ((validation as unknown) as { json: Account[] }).json;
if (settings.s.enableStorageEnctyption) { if (settings.s.enableStorageEnctyption) {
const hasPass = await promptForPassword(accounts) const hasPass = await promptForPassword(accounts);
if (hasPass) { if (hasPass) {
const cryptoParams = await getCryptoParams(mpPass.value) const cryptoParams = await getCryptoParams(mpPass.value);
const accProm = newAccounts.map(async a => { const accProm = newAccounts.map(async (a) => {
if (a.pk.length === 64) { if (a.pk.length === 64) {
a.pk = `0x${a.pk}` a.pk = `0x${a.pk}`;
} }
a.encPk = await encrypt(a.pk, cryptoParams) a.encPk = await encrypt(a.pk, cryptoParams);
return a return a;
}) });
const encNewAccounts = await Promise.all(accProm) const encNewAccounts = await Promise.all(accProm);
await replaceAccounts([...accounts, ...encNewAccounts]) await replaceAccounts([...accounts, ...encNewAccounts]);
alertHeader.value = 'Success' alertHeader.value = "Success";
alertMsg.value = "Successfully imported new accounts." alertMsg.value = "Successfully imported new accounts.";
alertOpen.value = true alertOpen.value = true;
noAccounts.value = false noAccounts.value = false;
} }
return false return false;
} else { } else {
await replaceAccounts([...accounts, ...newAccounts.map( a => { a.encPk = ''; return a })]) await replaceAccounts([
alertHeader.value = 'Success' ...accounts,
alertMsg.value = "Successfully imported new accounts." ...newAccounts.map((a) => {
alertOpen.value = true a.encPk = "";
noAccounts.value = false return a;
} }),
]);
alertHeader.value = "Success";
alertMsg.value = "Successfully imported new accounts.";
alertOpen.value = true;
noAccounts.value = false;
} }
};
const exportAcc = async () => { const exportAcc = async () => {
const accounts = await getAccounts() const accounts = await getAccounts();
if (!accounts.length) { if (!accounts.length) {
alertMsg.value = "You need at least one account to export." alertMsg.value = "You need at least one account to export.";
alertOpen.value = true alertOpen.value = true;
} }
if (settings.s.enableStorageEnctyption) { if (settings.s.enableStorageEnctyption) {
const hasPass = await promptForPassword(accounts) const hasPass = await promptForPassword(accounts);
if (hasPass) { if (hasPass) {
const cryptoParams = await getCryptoParams(mpPass.value) const cryptoParams = await getCryptoParams(mpPass.value);
const accProm = accounts.map(async a => { const accProm = accounts.map(async (a) => {
a.pk = await decrypt(a.encPk, cryptoParams) a.pk = await decrypt(a.encPk, cryptoParams);
return a return a;
}) });
const encNewAccounts = await Promise.all(accProm) const encNewAccounts = await Promise.all(accProm);
exportFile('wallet_export.json', JSON.stringify(encNewAccounts, null, 2)) exportFile("wallet_export.json", JSON.stringify(encNewAccounts, null, 2));
} }
return false return false;
} else { } else {
exportFile('wallet_export.json', JSON.stringify(accounts, null, 2)) exportFile("wallet_export.json", JSON.stringify(accounts, null, 2));
} }
} };
onIonViewWillEnter(async () => { onIonViewWillEnter(async () => {
await Promise.all([getSettings().then((storeSettings) => await Promise.all([
{ getSettings().then((storeSettings) => {
settings.s = storeSettings settings.s = storeSettings;
radioTheme.value = settings.s.theme radioTheme.value = settings.s.theme;
}), }),
getAccounts().then((accounts) => { getAccounts().then((accounts) => {
if (accounts.length) { if (accounts.length) {
noAccounts.value = false noAccounts.value = false;
} }
})]) }),
loading.value = false ]);
}) loading.value = false;
});
const setTime = async () => { const setTime = async () => {
loading.value = true loading.value = true;
if (settings.s.lockOutPeriod < 2 || settings.s.lockOutPeriod > 120) { if (settings.s.lockOutPeriod < 2 || settings.s.lockOutPeriod > 120) {
loading.value = false loading.value = false;
alertMsg.value = 'Auto-lock period must be between 2 and 120'; alertMsg.value = "Auto-lock period must be between 2 and 120";
alertOpen.value = true alertOpen.value = true;
return return;
}
settings.s.lockOutPeriod = Math.trunc(settings.s.lockOutPeriod)
await saveSettings()
loading.value = false
toastMsg.value = 'Auto-lock period was set';
toastState.value = true
} }
settings.s.lockOutPeriod = Math.trunc(settings.s.lockOutPeriod);
await saveSettings();
loading.value = false;
toastMsg.value = "Auto-lock period was set";
toastState.value = true;
};
const modalDismiss = () => { const modalDismiss = () => {
setEncryptToggle(settings.s.enableStorageEnctyption) setEncryptToggle(settings.s.enableStorageEnctyption);
} };
return { return {
wipeStorage, wipeStorage,
@ -576,7 +682,7 @@ export default defineComponent({
changeTheme, changeTheme,
openTab, openTab,
radioTheme, radioTheme,
changePermaLock changePermaLock,
}; };
}, },
}); });