From 2eee0e19d88d48dc83d45aac448d8d8305690f24 Mon Sep 17 00:00:00 2001 From: Gamaliel Padillo Date: Thu, 24 Apr 2025 13:15:41 -0700 Subject: [PATCH 1/3] feat: update rate store, apply changes to all pages necessary --- .../extension/src/libs/rate-state/index.ts | 28 +++++++++++++++--- .../extension/src/libs/rate-state/types.ts | 1 + .../verify-transaction/index.vue | 21 ++++++++++++++ .../verify-transaction/index.vue | 21 ++++++++++++++ .../verify-transaction/index.vue | 21 ++++++++++++++ .../verify-transaction/index.vue | 21 ++++++++++++++ .../verify-transaction/index.vue | 29 +++++++++++++++++++ packages/extension/src/ui/action/App.vue | 13 +++++++-- .../src/ui/action/store/rate-store.ts | 17 +++++++++++ .../swap/views/swap-best-offer/index.vue | 21 ++++++++++++++ 10 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 packages/extension/src/ui/action/store/rate-store.ts diff --git a/packages/extension/src/libs/rate-state/index.ts b/packages/extension/src/libs/rate-state/index.ts index 626dc3940..99f5f33a6 100644 --- a/packages/extension/src/libs/rate-state/index.ts +++ b/packages/extension/src/libs/rate-state/index.ts @@ -11,15 +11,35 @@ export default class RateState { this.storage = new BrowserStorage(InternalStorageNamespace.rateState); } - async showPopup(): Promise { + /** + * + * @param immediate + * @returns boolean + * + * allow popup to show immediately + * + */ + async showPopup(immediate: boolean = false): Promise { const state: IState | undefined = await this.storage.get( StorageKeys.rateInfo, ); if (state) { - if (!state.alreadyRated) { + if (!state.askedAfterActivity) { const now = Date.now(); + if (state.popupTime < now) { + const popupTime = Date.now() + POPUP_TIME; + state.popupTime = popupTime; + await this.storage.set(StorageKeys.rateInfo, state); + return true; + } + } + if (immediate) { + return false + } + if (!state.alreadyRated) { + const now = Date.now(); if (state.popupTime < now) { const popupTime = Date.now() + POPUP_TIME; state.popupTime = popupTime; @@ -37,11 +57,11 @@ export default class RateState { const newState: IState = { popupTime, alreadyRated: false, + askedAfterActivity: immediate, }; this.storage.set(StorageKeys.rateInfo, newState); - - return false; + return immediate; } async resetPopupTimer(): Promise { diff --git a/packages/extension/src/libs/rate-state/types.ts b/packages/extension/src/libs/rate-state/types.ts index bc0f8e46b..9369aba0c 100644 --- a/packages/extension/src/libs/rate-state/types.ts +++ b/packages/extension/src/libs/rate-state/types.ts @@ -5,6 +5,7 @@ export enum StorageKeys { export interface Rate { alreadyRated: boolean; popupTime: number; + askedAfterActivity: boolean; } export type IState = Rate; diff --git a/packages/extension/src/providers/bitcoin/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/bitcoin/ui/send-transaction/verify-transaction/index.vue index 6585285f2..5836c43d2 100644 --- a/packages/extension/src/providers/bitcoin/ui/send-transaction/verify-transaction/index.vue +++ b/packages/extension/src/providers/bitcoin/ui/send-transaction/verify-transaction/index.vue @@ -98,10 +98,19 @@ import { BitcoinNetwork } from '@/providers/bitcoin/types/bitcoin-network'; import BitcoinAPI from '@/providers/bitcoin/libs/api'; import { trackSendEvents } from '@/libs/metrics'; import { SendEventType } from '@/libs/metrics/types'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const KeyRing = new PublicKeyRing(); const route = useRoute(); const router = useRouter(); +const rateState = new RateState(); const selectedNetwork: string = route.query.id as string; const txData: VerifyTransactionParams = JSON.parse( Buffer.from(route.query.txData as string, 'base64').toString('utf8'), @@ -185,11 +194,13 @@ const sendAction = async () => { if (getCurrentContext() === 'popup') { setTimeout(() => { isProcessing.value = false; + callToggleRate(); router.go(-2); }, 4500); } else { setTimeout(() => { isProcessing.value = false; + callToggleRate(); window.close(); }, 1500); } @@ -213,6 +224,16 @@ const sendAction = async () => { errorMsg.value = JSON.stringify(error); }); }; + +const callToggleRate = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; const isHasScroll = () => { if (verifyScrollRef.value) { return verifyScrollRef.value.$el.classList.contains('ps--active-y'); diff --git a/packages/extension/src/providers/ethereum/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/ethereum/ui/send-transaction/verify-transaction/index.vue index 413d64e83..4201a009b 100644 --- a/packages/extension/src/providers/ethereum/ui/send-transaction/verify-transaction/index.vue +++ b/packages/extension/src/providers/ethereum/ui/send-transaction/verify-transaction/index.vue @@ -109,10 +109,19 @@ import { toBN } from 'web3-utils'; import { bufferToHex, toBase } from '@enkryptcom/utils'; import { trackSendEvents } from '@/libs/metrics'; import { SendEventType } from '@/libs/metrics/types'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const KeyRing = new PublicKeyRing(); const route = useRoute(); const router = useRouter(); +const rateState = new RateState(); const selectedNetwork: string = route.query.id as string; const txData: VerifyTransactionParams = JSON.parse( Buffer.from(route.query.txData as string, 'base64').toString('utf8'), @@ -141,6 +150,16 @@ const close = () => { } }; +const callToggleRate = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; + const sendAction = async () => { isProcessing.value = true; trackSendEvents(SendEventType.SendApprove, { @@ -198,11 +217,13 @@ const sendAction = async () => { if (getCurrentContext() === 'popup') { setTimeout(() => { isProcessing.value = false; + callToggleRate(); router.go(-2); }, 4500); } else { setTimeout(() => { isProcessing.value = false; + callToggleRate(); window.close(); }, 1500); } diff --git a/packages/extension/src/providers/kadena/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/kadena/ui/send-transaction/verify-transaction/index.vue index e717deb72..46473d3ea 100644 --- a/packages/extension/src/providers/kadena/ui/send-transaction/verify-transaction/index.vue +++ b/packages/extension/src/providers/kadena/ui/send-transaction/verify-transaction/index.vue @@ -104,12 +104,21 @@ import KadenaAPI from '@/providers/kadena/libs/api'; import { KadenaNetwork } from '@/providers/kadena/types/kadena-network'; import { trackSendEvents } from '@/libs/metrics'; import { SendEventType } from '@/libs/metrics/types'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const isSendDone = ref(false); const account = ref(); const chainId = ref(); const kdaToken = ref(); const KeyRing = new PublicKeyRing(); +const rateState = new RateState(); const route = useRoute(); const router = useRouter(); const selectedNetwork: string = route.query.id as string; @@ -197,11 +206,13 @@ const sendAction = async () => { if (getCurrentContext() === 'popup') { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); router.push({ name: 'activity', params: { id: network.value.name } }); }, 2500); } else { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); window.close(); }, 1500); } @@ -218,6 +229,16 @@ const sendAction = async () => { } }; +const callToggleRatePopup = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; + const isHasScroll = () => { if (verifyScrollRef.value) { return verifyScrollRef.value.$el.classList.contains('ps--active-y'); diff --git a/packages/extension/src/providers/polkadot/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/polkadot/ui/send-transaction/verify-transaction/index.vue index 299730846..238393f00 100644 --- a/packages/extension/src/providers/polkadot/ui/send-transaction/verify-transaction/index.vue +++ b/packages/extension/src/providers/polkadot/ui/send-transaction/verify-transaction/index.vue @@ -107,10 +107,19 @@ import CustomScrollbar from '@action/components/custom-scrollbar/index.vue'; import { BaseNetwork } from '@/types/base-network'; import { trackSendEvents } from '@/libs/metrics'; import { SendEventType } from '@/libs/metrics/types'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const isSendDone = ref(false); const account = ref(); const KeyRing = new PublicKeyRing(); +const rateState = new RateState(); const route = useRoute(); const router = useRouter(); const selectedNetwork: string = route.query.id as string; @@ -207,11 +216,13 @@ const sendAction = async () => { if (getCurrentContext() === 'popup') { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); router.go(-2); }, 2500); } else { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); window.close(); }, 1500); } @@ -240,6 +251,16 @@ const sendAction = async () => { } }; +const callToggleRatePopup = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; + const isHasScroll = () => { if (verifyScrollRef.value) { return verifyScrollRef.value.$el.classList.contains('ps--active-y'); diff --git a/packages/extension/src/providers/solana/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/solana/ui/send-transaction/verify-transaction/index.vue index 7daceaa0b..5fd314ba8 100644 --- a/packages/extension/src/providers/solana/ui/send-transaction/verify-transaction/index.vue +++ b/packages/extension/src/providers/solana/ui/send-transaction/verify-transaction/index.vue @@ -115,8 +115,17 @@ import { getSimulationComputeUnits } from '@solana-developers/helpers'; import SolanaAPI from '@/providers/solana/libs/api'; import bs58 from 'bs58'; import { TransactionSigner } from '../../libs/signer'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const KeyRing = new PublicKeyRing(); +const rateState = new RateState(); const route = useRoute(); const router = useRouter(); const selectedNetwork: string = route.query.id as string; @@ -227,14 +236,23 @@ const sendAction = async () => { }, ); isSendDone.value = true; + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); if (getCurrentContext() === 'popup') { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); router.go(-2); }, 4500); } else { setTimeout(() => { isProcessing.value = false; + callToggleRatePopup(); window.close(); }, 1500); } @@ -275,6 +293,17 @@ const sendAction = async () => { console.error('ERROR', errror); }); }; + +const callToggleRatePopup = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; + const isHasScroll = () => { if (verifyScrollRef.value) { return verifyScrollRef.value.$el.classList.contains('ps--active-y'); diff --git a/packages/extension/src/ui/action/App.vue b/packages/extension/src/ui/action/App.vue index 892eb27a5..dd20d3d92 100644 --- a/packages/extension/src/ui/action/App.vue +++ b/packages/extension/src/ui/action/App.vue @@ -71,7 +71,7 @@ @close:popup="settingsShow = !settingsShow" @action:lock="lockAction" /> - + (''); const kr = new PublicKeyRing(); const addNetworkShow = ref(false); const settingsShow = ref(false); -const rateShow = ref(false); const updateShow = ref(false); const isLoading = ref(true); const currentVersion = __PACKAGE_VERSION__; const latestVersion = ref(''); + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { isRatePopupOpen } = storeToRefs(rateStore); +const { toggleRatePopup } = rateStore; /** ------------------- * Exapnded Menu -------------------*/ @@ -276,7 +283,7 @@ onMounted(async () => { setTimeout(() => { rateState.showPopup().then(show => { if (show) { - rateShow.value = true; + toggleRatePopup(true); } else { getLatestEnkryptVersion().then(version => { if ( diff --git a/packages/extension/src/ui/action/store/rate-store.ts b/packages/extension/src/ui/action/store/rate-store.ts new file mode 100644 index 000000000..85a965e74 --- /dev/null +++ b/packages/extension/src/ui/action/store/rate-store.ts @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' +import { ref, } from 'vue'; + +export const useRateStore = defineStore('useRateStore', () => { + + const isRatePopupOpen = ref(false); + + const toggleRatePopup = async (value: boolean) => { + isRatePopupOpen.value = !!value; + } + + return { + isRatePopupOpen, + toggleRatePopup + } +}) + diff --git a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue index 3f4890fbe..edb8ff6d8 100644 --- a/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue +++ b/packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue @@ -157,10 +157,19 @@ import { trackSwapEvents } from '@/libs/metrics'; import { SwapEventType } from '@/libs/metrics/types'; import { getSolanaTransactionFees } from '../../libs/solana-gasvals'; import { SolanaNetwork } from '@/providers/solana/types/sol-network'; +import RateState from '@/libs/rate-state'; +import { useRateStore } from '@action/store/rate-store'; + +/** ------------------- + * Rate + -------------------*/ +const rateStore = useRateStore(); +const { toggleRatePopup } = rateStore; const router = useRouter(); const route = useRoute(); +const rateState = new RateState(); const isInitiated = ref(false); const bestOfferScrollRef = ref>(); const scrollProgress = ref(0); @@ -354,8 +363,10 @@ const close = () => { swapProvider: pickedTrade.value.provider, }); if (!isWindowPopup.value) { + callToggleRatePopup(); router.go(-2); } else { + callToggleRatePopup(); window.close(); } }; @@ -462,6 +473,16 @@ const sendAction = async () => { } }; +const callToggleRatePopup = () => { + /** + * will only show the user if they haven't rated it + * and never been shown before + */ + rateState.showPopup(true).then(show => { + if (show) toggleRatePopup(true); + }); +}; + const handleScroll = (e: any) => { const progress = Number(e.target.lastChild.style.top.replace('px', '')); scrollProgress.value = progress; From 419767b7981d6b63b4d32d6966874eb308687dff Mon Sep 17 00:00:00 2001 From: Gamaliel Padillo Date: Fri, 9 May 2025 12:08:48 -0700 Subject: [PATCH 2/3] chore: add comments --- .../extension/src/libs/rate-state/index.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/extension/src/libs/rate-state/index.ts b/packages/extension/src/libs/rate-state/index.ts index 9db2a0117..62b498389 100644 --- a/packages/extension/src/libs/rate-state/index.ts +++ b/packages/extension/src/libs/rate-state/index.ts @@ -26,17 +26,29 @@ export default class RateState { const now = Date.now(); const popupTime = Date.now() + POPUP_TIME; + /** + * Case 1: if the user has already been asked after activity + * - set askedAfterActivity to true + * - set popupTime to now + 30 days + * Case 2: if the user has not rated (this means that the user got asked after activity) + * - askedAfterActivity is already true + * - set popupTime to now + 30 days + * Case 3: if the user has already rated + * - always return false + * Case 4: if no state exists + * - create a new state with askedAfterActivity = false + * - set popupTime to now + 30 days + * - return immediate + */ + if (state) { if (!state.askedAfterActivity) { - console.log('askedAfterActivity', state); - if (state.popupTime < now) { - state.askedAfterActivity = true; + state.askedAfterActivity = true; - await this.storage.set(StorageKeys.rateInfo, state); - return true; - } + await this.storage.set(StorageKeys.rateInfo, state); + return true; } - if (!state.alreadyRated && !state.askedAfterActivity) { + if (!state.alreadyRated) { if (state.popupTime < now) { state.popupTime = popupTime; @@ -75,6 +87,7 @@ export default class RateState { const newState: IState = { alreadyRated: false, popupTime, + askedAfterActivity: false, }; await this.storage.set(StorageKeys.rateInfo, newState); From d3711dd0075a7ccd71fab9b9130f881388199328 Mon Sep 17 00:00:00 2001 From: Gamaliel Padillo Date: Fri, 9 May 2025 13:48:22 -0700 Subject: [PATCH 3/3] devop: remove unused watch --- .../extension/src/ui/action/views/modal-rate/index.vue | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/extension/src/ui/action/views/modal-rate/index.vue b/packages/extension/src/ui/action/views/modal-rate/index.vue index e314f082d..4afba3d37 100644 --- a/packages/extension/src/ui/action/views/modal-rate/index.vue +++ b/packages/extension/src/ui/action/views/modal-rate/index.vue @@ -21,7 +21,6 @@ import AppDialog from '@action/components/app-dialog/index.vue'; import BaseButton from '@action/components/base-button/index.vue'; import RateState from '@/libs/rate-state'; import { detectBrowser, BROWSER_NAMES, openLink } from '@action/utils/browser'; -import { watch } from 'vue'; const model = defineModel(); const rateState = new RateState(); @@ -31,14 +30,6 @@ const close = async () => { model.value = false; }; -watch( - () => model.value, - async newValue => { - console.log('model.value', newValue); - }, - { immediate: true }, -); - const goToFeedback = async () => { await rateState.resetPopupTimer(); openLink('https://www.enkrypt.com/?ref=enkrypt_help');