diff --git a/package-lock.json b/package-lock.json index c534fa78..0a14db68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yours-wallet", - "version": "4.1.4", + "version": "4.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yours-wallet", - "version": "4.1.4", + "version": "4.1.5", "dependencies": { "@bsv/paymail": "^2.0.2", "@bsv/sdk": "^1.1.24", @@ -45,7 +45,7 @@ "react-router-dom": "^6.16.0", "react-scripts": "^5.0.1", "replace-module-webpack-plugin": "^1.0.0", - "spv-store": "^0.1.63", + "spv-store": "^0.1.69", "stream-browserify": "^3.0.0", "styled-components": "^6.0.8", "ts-loader": "^9.5.1", @@ -18634,9 +18634,9 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/spv-store": { - "version": "0.1.63", - "resolved": "https://registry.npmjs.org/spv-store/-/spv-store-0.1.63.tgz", - "integrity": "sha512-UWcD2AaHRSrB2Hnhb/J9KcWWbwMc6HlN/5NUutAZ4LgfjMkLBUbEpu+ySKVoEwOI++cTRmYKRhwkcgIPnLHO0A==", + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/spv-store/-/spv-store-0.1.69.tgz", + "integrity": "sha512-a56XmboamFrsdY3dFmMRnBVfsEAsQ+eINqQs85Mj7+EGGYQbXTAnS/XBFVe5qke/v8G591WluYJoS1RDLH8Xow==", "dependencies": { "@tempfix/idb": "^8.0.3", "buffer": "^6.0.3", diff --git a/package.json b/package.json index 7f73461a..5c8ab82e 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "react-router-dom": "^6.16.0", "react-scripts": "^5.0.1", "replace-module-webpack-plugin": "^1.0.0", - "spv-store": "^0.1.63", + "spv-store": "^0.1.69", "stream-browserify": "^3.0.0", "styled-components": "^6.0.8", "ts-loader": "^9.5.1", diff --git a/src/App.tsx b/src/App.tsx index ce35db4d..2acb256e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,8 +4,7 @@ import { MemoryRouter as Router, Route, Routes } from 'react-router-dom'; import styled from 'styled-components'; import { Show } from './components/Show'; import { UnlockWallet } from './components/UnlockWallet'; -import { BottomMenuContext, BottomMenuProvider } from './contexts/BottomMenuContext'; -import { SnackbarProvider } from './contexts/SnackbarContext'; +import { BottomMenuContext } from './contexts/BottomMenuContext'; import { useActivityDetector } from './hooks/useActivityDetector'; import { useTheme } from './hooks/useTheme'; import { useViewport } from './hooks/useViewport'; @@ -32,12 +31,14 @@ import { WhitelistedApp } from './inject'; import { PageLoader } from './components/PageLoader'; import { useServiceContext } from './hooks/useServiceContext'; import { useWeb3RequestContext } from './hooks/useWeb3RequestContext'; -import { QueueProvider } from './contexts/QueueContext'; import { QueueBanner } from './components/QueueBanner'; -import { BlockHeightProvider } from './contexts/BlockHeightContext'; import { SyncingBlocks } from './components/SyncingBlocks'; import { MasterRestore } from './pages/onboarding/MasterRestore'; import { Bsv20SendRequest } from './pages/requests/Bsv20SendRequest'; +import { BlockHeightProvider } from './contexts/providers/BlockHeightProvider'; +import { QueueProvider } from './contexts/providers/QueueProvider'; +import { BottomMenuProvider } from './contexts/providers/BottomMenuProvider'; +import { SnackbarProvider } from './contexts/providers/SnackbarProvider'; const MainContainer = styled.div` display: flex; diff --git a/src/contexts/BlockHeightContext.ts b/src/contexts/BlockHeightContext.ts new file mode 100644 index 00000000..194668cb --- /dev/null +++ b/src/contexts/BlockHeightContext.ts @@ -0,0 +1,8 @@ +import { createContext } from 'react'; + +type QueueContextType = { + percentCompleted: number; + showSyncPage: boolean; +}; + +export const BlockHeightContext = createContext(null); diff --git a/src/contexts/BottomMenuContext.ts b/src/contexts/BottomMenuContext.ts new file mode 100644 index 00000000..5a6af22b --- /dev/null +++ b/src/contexts/BottomMenuContext.ts @@ -0,0 +1,14 @@ +import { createContext } from 'react'; + +export type MenuItems = 'bsv' | 'ords' | 'tools' | 'settings'; + +type BottomMenuContextType = { + selected: MenuItems | null; + query: string; + handleSelect: (item: MenuItems, query?: string) => void; + showMenu: () => void; + hideMenu: () => void; + isVisible: boolean; +}; + +export const BottomMenuContext = createContext(null); diff --git a/src/contexts/ColorThemeContext.ts b/src/contexts/ColorThemeContext.ts new file mode 100644 index 00000000..a8d24bf6 --- /dev/null +++ b/src/contexts/ColorThemeContext.ts @@ -0,0 +1,8 @@ +import { createContext } from 'react'; +import { Theme } from '../theme.types'; + +export interface ThemeContextProps { + theme: Theme; +} + +export const ThemeContext = createContext(undefined); diff --git a/src/contexts/ColorThemeContext.tsx b/src/contexts/ColorThemeContext.tsx deleted file mode 100644 index cc8f32c0..00000000 --- a/src/contexts/ColorThemeContext.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { ReactNode, createContext } from 'react'; -import { Theme } from '../theme.types'; -import { theme } from '../theme'; - -export interface ThemeContextProps { - theme: Theme; -} - -export const ThemeContext = createContext(undefined); - -interface ThemeProviderProps { - children: ReactNode; -} - -export const ThemeProvider: React.FC = ({ children }) => { - return {children}; -}; diff --git a/src/contexts/QueueContext.ts b/src/contexts/QueueContext.ts new file mode 100644 index 00000000..bfdb8f02 --- /dev/null +++ b/src/contexts/QueueContext.ts @@ -0,0 +1,12 @@ +import { createContext } from 'react'; +import { Theme } from '../theme.types'; + +type QueueContextType = { + queueLength: number; + showQueueBanner: boolean; + updateBalance: boolean; + theme: Theme; + isSyncing: boolean; +}; + +export const QueueContext = createContext(null); diff --git a/src/contexts/QueueContext.tsx b/src/contexts/QueueContext.tsx deleted file mode 100644 index be71fe01..00000000 --- a/src/contexts/QueueContext.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { createContext, ReactNode } from 'react'; -import { useQueueTracker } from '../hooks/useQueueTracker'; -import { Theme } from '../theme.types'; - -type QueueContextType = { - queueLength: number; - showQueueBanner: boolean; - updateBalance: boolean; - theme: Theme; - isSyncing: boolean; -}; - -export const QueueContext = createContext(null); - -export const QueueProvider = ({ children }: { children: ReactNode }) => { - const { queueLength, showQueueBanner, theme, updateBalance, isSyncing } = useQueueTracker(); - - return ( - - {children} - - ); -}; diff --git a/src/contexts/ServiceContext.ts b/src/contexts/ServiceContext.ts new file mode 100644 index 00000000..3e1a2f14 --- /dev/null +++ b/src/contexts/ServiceContext.ts @@ -0,0 +1,26 @@ +import { createContext } from 'react'; +import { ChromeStorageService } from '../services/ChromeStorage.service'; +import { WhatsOnChainService } from '../services/WhatsOnChain.service'; +import { KeysService } from '../services/Keys.service'; +import { ContractService } from '../services/Contract.service'; +import { BsvService } from '../services/Bsv.service'; +import { OrdinalService } from '../services/Ordinal.service'; +import { SPVStore } from 'spv-store'; +import { GorillaPoolService } from '../services/GorillaPool.service'; + +export interface ServiceContextProps { + chromeStorageService: ChromeStorageService; + keysService: KeysService; + bsvService: BsvService; + ordinalService: OrdinalService; + wocService: WhatsOnChainService; + gorillaPoolService: GorillaPoolService; + contractService: ContractService; + isLocked: boolean; + isReady: boolean; + setIsLocked: (isLocked: boolean) => void; + lockWallet: () => Promise; + oneSatSPV: SPVStore; +} + +export const ServiceContext = createContext(undefined); diff --git a/src/contexts/SnackbarContext.ts b/src/contexts/SnackbarContext.ts new file mode 100644 index 00000000..47b25f47 --- /dev/null +++ b/src/contexts/SnackbarContext.ts @@ -0,0 +1,11 @@ +import { createContext } from 'react'; + +export type SnackbarType = 'error' | 'info' | 'success'; + +type SnackbarContextType = { + message: string | null; + snackBarType: SnackbarType | null; + addSnackbar: (message: string, type: SnackbarType, duration?: number) => void; +}; + +export const SnackbarContext = createContext(null); diff --git a/src/contexts/Web3RequestContext.ts b/src/contexts/Web3RequestContext.ts new file mode 100644 index 00000000..e3b67619 --- /dev/null +++ b/src/contexts/Web3RequestContext.ts @@ -0,0 +1,34 @@ +import { createContext } from 'react'; +import { + Broadcast, + DecryptRequest, + EncryptRequest, + GetSignatures, + PurchaseOrdinal, + SendBsv, + SendBsv20, + SignMessage, + TaggedDerivationRequest, + TransferOrdinal, +} from 'yours-wallet-provider'; +import { RequestParams } from '../inject'; +import { ChromeStorageService } from '../services/ChromeStorage.service'; + +export type Web3RequestContextProps = { + connectRequest: RequestParams | undefined; + sendBsvRequest: SendBsv[] | undefined; + sendBsv20Request: SendBsv20 | undefined; + transferOrdinalRequest: TransferOrdinal | undefined; + purchaseOrdinalRequest: PurchaseOrdinal | undefined; + signMessageRequest: SignMessage | undefined; + broadcastRequest: Broadcast | undefined; + getSignaturesRequest: GetSignatures | undefined; + generateTaggedKeysRequest: TaggedDerivationRequest | undefined; + encryptRequest: EncryptRequest | undefined; + decryptRequest: DecryptRequest | undefined; + popupId: number | undefined; + getStorageAndSetRequestState: (chromeStorageService: ChromeStorageService) => void; + clearRequest: (type: keyof Omit) => void; +}; + +export const Web3RequestContext = createContext(undefined); diff --git a/src/contexts/BlockHeightContext.tsx b/src/contexts/providers/BlockHeightProvider.tsx similarity index 50% rename from src/contexts/BlockHeightContext.tsx rename to src/contexts/providers/BlockHeightProvider.tsx index 56762f75..4578a4b9 100644 --- a/src/contexts/BlockHeightContext.tsx +++ b/src/contexts/providers/BlockHeightProvider.tsx @@ -1,12 +1,6 @@ -import { createContext, ReactNode } from 'react'; -import { useBlockHeightTracker } from '../hooks/useBlockHeightTracker'; - -type QueueContextType = { - percentCompleted: number; - showSyncPage: boolean; -}; - -const BlockHeightContext = createContext(null); +import { ReactNode } from 'react'; +import { useBlockHeightTracker } from '../../hooks/useBlockHeightTracker'; +import { BlockHeightContext } from '../BlockHeightContext'; export const BlockHeightProvider = ({ children }: { children: ReactNode }) => { const { percentCompleted, showSyncPage } = useBlockHeightTracker(); diff --git a/src/contexts/BottomMenuContext.tsx b/src/contexts/providers/BottomMenuProvider.tsx similarity index 66% rename from src/contexts/BottomMenuContext.tsx rename to src/contexts/providers/BottomMenuProvider.tsx index a3c7cf30..1ad0f18a 100644 --- a/src/contexts/BottomMenuContext.tsx +++ b/src/contexts/providers/BottomMenuProvider.tsx @@ -1,20 +1,8 @@ -import { ReactNode, createContext, useState } from 'react'; +import { ReactNode, useState } from 'react'; import { NetWork } from 'yours-wallet-provider'; -import { BottomMenu } from '../components/BottomMenu'; -import { useTheme } from '../hooks/useTheme'; - -export type MenuItems = 'bsv' | 'ords' | 'tools' | 'settings'; - -type BottomMenuContextType = { - selected: MenuItems | null; - query: string; - handleSelect: (item: MenuItems, query?: string) => void; - showMenu: () => void; - hideMenu: () => void; - isVisible: boolean; -}; - -export const BottomMenuContext = createContext(null); +import { BottomMenu } from '../../components/BottomMenu'; +import { useTheme } from '../../hooks/useTheme'; +import { BottomMenuContext, MenuItems } from '../BottomMenuContext'; interface BottomMenuProviderProps { network: NetWork; diff --git a/src/contexts/providers/QueueProvider.tsx b/src/contexts/providers/QueueProvider.tsx new file mode 100644 index 00000000..7febef37 --- /dev/null +++ b/src/contexts/providers/QueueProvider.tsx @@ -0,0 +1,13 @@ +import { ReactNode } from 'react'; +import { useQueueTracker } from '../../hooks/useQueueTracker'; +import { QueueContext } from '../QueueContext'; + +export const QueueProvider = ({ children }: { children: ReactNode }) => { + const { queueLength, showQueueBanner, theme, updateBalance, isSyncing } = useQueueTracker(); + + return ( + + {children} + + ); +}; diff --git a/src/contexts/ServiceContext.tsx b/src/contexts/providers/ServiceProvider.tsx similarity index 70% rename from src/contexts/ServiceContext.tsx rename to src/contexts/providers/ServiceProvider.tsx index a4401f99..5e3cfa9b 100644 --- a/src/contexts/ServiceContext.tsx +++ b/src/contexts/providers/ServiceProvider.tsx @@ -1,14 +1,14 @@ -import React, { createContext, ReactNode, useCallback, useEffect, useState } from 'react'; -import { ChromeStorageService } from '../services/ChromeStorage.service'; -import { WhatsOnChainService } from '../services/WhatsOnChain.service'; -import { KeysService } from '../services/Keys.service'; -import { ContractService } from '../services/Contract.service'; -import { BsvService } from '../services/Bsv.service'; -import { OrdinalService } from '../services/Ordinal.service'; -import { INACTIVITY_LIMIT } from '../utils/constants'; -import { SPVStore } from 'spv-store'; -import { oneSatSPVPromise } from '../background'; -import { GorillaPoolService } from '../services/GorillaPool.service'; +import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { oneSatSPVPromise } from '../../background'; +import { BsvService } from '../../services/Bsv.service'; +import { ChromeStorageService } from '../../services/ChromeStorage.service'; +import { ContractService } from '../../services/Contract.service'; +import { GorillaPoolService } from '../../services/GorillaPool.service'; +import { KeysService } from '../../services/Keys.service'; +import { OrdinalService } from '../../services/Ordinal.service'; +import { WhatsOnChainService } from '../../services/WhatsOnChain.service'; +import { INACTIVITY_LIMIT } from '../../utils/constants'; +import { ServiceContext, ServiceContextProps } from '../ServiceContext'; const initializeServices = async () => { const chromeStorageService = new ChromeStorageService(); @@ -41,28 +41,19 @@ const initializeServices = async () => { }; }; -export interface ServiceContextProps { - chromeStorageService: ChromeStorageService; - keysService: KeysService; - bsvService: BsvService; - ordinalService: OrdinalService; - wocService: WhatsOnChainService; - gorillaPoolService: GorillaPoolService; - contractService: ContractService; - isLocked: boolean; - isReady: boolean; - setIsLocked: (isLocked: boolean) => void; - lockWallet: () => Promise; - oneSatSPV: SPVStore; -} - -export const ServiceContext = createContext(undefined); - export const ServiceProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [services, setServices] = useState>({}); const [isLocked, setIsLocked] = useState(false); const [isReady, setIsReady] = useState(false); + useEffect(() => { + if (services?.chromeStorageService) { + const timestamp = Date.now(); + const twentyMinutesAgo = timestamp - 20 * 60 * 1000; + services.chromeStorageService.update({ lastActiveTime: isLocked ? twentyMinutesAgo : timestamp, isLocked }); + } + }, [isLocked, services?.chromeStorageService]); + useEffect(() => { const initServices = async () => { try { @@ -92,10 +83,7 @@ export const ServiceProvider: React.FC<{ children: ReactNode }> = ({ children }) const lockWallet = useCallback(async () => { if (!isReady) return; setIsLocked(true); - const timestamp = Date.now(); - const twentyMinutesAgo = timestamp - 20 * 60 * 1000; - services?.chromeStorageService?.update({ lastActiveTime: twentyMinutesAgo }); - }, [isReady, services]); + }, [isReady]); useEffect(() => { const checkLockState = async () => { diff --git a/src/contexts/SnackbarContext.tsx b/src/contexts/providers/SnackbarProvider.tsx similarity index 64% rename from src/contexts/SnackbarContext.tsx rename to src/contexts/providers/SnackbarProvider.tsx index debe6ee7..846a02c5 100644 --- a/src/contexts/SnackbarContext.tsx +++ b/src/contexts/providers/SnackbarProvider.tsx @@ -1,17 +1,8 @@ -import { ReactNode, createContext, useState } from 'react'; -import { Snackbar } from '../components/Snackbar'; -import { useTheme } from '../hooks/useTheme'; -import { SNACKBAR_TIMEOUT } from '../utils/constants'; - -export type SnackbarType = 'error' | 'info' | 'success'; - -type SnackbarContextType = { - message: string | null; - snackBarType: SnackbarType | null; - addSnackbar: (message: string, type: SnackbarType, duration?: number) => void; -}; - -export const SnackbarContext = createContext(null); +import { ReactNode, useState } from 'react'; +import { Snackbar } from '../../components/Snackbar'; +import { useTheme } from '../../hooks/useTheme'; +import { SNACKBAR_TIMEOUT } from '../../utils/constants'; +import { SnackbarContext, SnackbarType } from '../SnackbarContext'; interface SnackbarProviderProps { children: ReactNode; diff --git a/src/contexts/providers/ThemeProvider.tsx b/src/contexts/providers/ThemeProvider.tsx new file mode 100644 index 00000000..4d5a1618 --- /dev/null +++ b/src/contexts/providers/ThemeProvider.tsx @@ -0,0 +1,11 @@ +import { ReactNode } from 'react'; +import { ThemeContext } from '../ColorThemeContext'; +import { theme } from '../../theme'; + +interface ThemeProviderProps { + children: ReactNode; +} + +export const ThemeProvider: React.FC = ({ children }) => { + return {children}; +}; diff --git a/src/contexts/Web3RequestContext.tsx b/src/contexts/providers/Web3Provider.tsx similarity index 80% rename from src/contexts/Web3RequestContext.tsx rename to src/contexts/providers/Web3Provider.tsx index 6349129c..fa58cf98 100644 --- a/src/contexts/Web3RequestContext.tsx +++ b/src/contexts/providers/Web3Provider.tsx @@ -1,4 +1,4 @@ -import { createContext, useState, ReactNode } from 'react'; +import { ReactNode, useState } from 'react'; import { Broadcast, DecryptRequest, @@ -11,29 +11,11 @@ import { TaggedDerivationRequest, TransferOrdinal, } from 'yours-wallet-provider'; -import { RequestParams } from '../inject'; -import { ChromeStorageService } from '../services/ChromeStorage.service'; -import { ChromeStorageObject } from '../services/types/chromeStorage.types'; -import { sleep } from '../utils/sleep'; - -export type Web3RequestContextProps = { - connectRequest: RequestParams | undefined; - sendBsvRequest: SendBsv[] | undefined; - sendBsv20Request: SendBsv20 | undefined; - transferOrdinalRequest: TransferOrdinal | undefined; - purchaseOrdinalRequest: PurchaseOrdinal | undefined; - signMessageRequest: SignMessage | undefined; - broadcastRequest: Broadcast | undefined; - getSignaturesRequest: GetSignatures | undefined; - generateTaggedKeysRequest: TaggedDerivationRequest | undefined; - encryptRequest: EncryptRequest | undefined; - decryptRequest: DecryptRequest | undefined; - popupId: number | undefined; - getStorageAndSetRequestState: (chromeStorageService: ChromeStorageService) => void; - clearRequest: (type: keyof Omit) => void; -}; - -export const Web3RequestContext = createContext(undefined); +import { RequestParams } from '../../inject'; +import { ChromeStorageService } from '../../services/ChromeStorage.service'; +import { ChromeStorageObject } from '../../services/types/chromeStorage.types'; +import { sleep } from '../../utils/sleep'; +import { Web3RequestContext, Web3RequestContextProps } from '../Web3RequestContext'; export const Web3RequestProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [connectRequest, setConnectRequest] = useState(undefined); diff --git a/src/index.tsx b/src/index.tsx index a3b6985c..4539bc6b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,9 +2,9 @@ import { Buffer } from 'buffer'; import process from 'process'; import ReactDOM from 'react-dom/client'; import { App } from './App'; -import { ThemeProvider } from './contexts/ColorThemeContext'; -import { ServiceProvider } from './contexts/ServiceContext'; -import { Web3RequestProvider } from './contexts/Web3RequestContext'; +import { ServiceProvider } from './contexts/providers/ServiceProvider'; +import { ThemeProvider } from './contexts/providers/ThemeProvider'; +import { Web3RequestProvider } from './contexts/providers/Web3Provider'; import './index.css'; global.Buffer = Buffer; global.process = process; diff --git a/src/initSPVStore.ts b/src/initSPVStore.ts index fc89bd52..9abf4869 100644 --- a/src/initSPVStore.ts +++ b/src/initSPVStore.ts @@ -35,7 +35,7 @@ export const initOneSatSPV = async (chromeStorageService: ChromeStorageService, const lockIndexer = new LockIndexer(owners, IndexMode.TrustAndVerify, network); const bsv20Indexers = [ - new Bsv21Indexer(owners, IndexMode.Trust, network), + new Bsv21Indexer(owners, IndexMode.Trust, [], network), new Bsv20Indexer(owners, IndexMode.Trust, network), ]; diff --git a/src/pages/BsvWallet.tsx b/src/pages/BsvWallet.tsx index 30940749..72dcaa63 100644 --- a/src/pages/BsvWallet.tsx +++ b/src/pages/BsvWallet.tsx @@ -557,9 +557,7 @@ export const BsvWallet = (props: BsvWalletProps) => { title={'Sync Transactions'} style={{ fontSize: '2rem', cursor: 'pointer' }} theme={theme} - onClick={() => { - oneSatSPV.stores.txos?.syncTxLogs(); - }} + onClick={() => oneSatSPV.stores.txos?.syncTxLogs()} > {formatUSD(bsvBalance * exchangeRate)} diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 9901af9f..188cb9c4 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -425,6 +425,16 @@ export const Settings = () => { handleSelect('bsv'); }; + const resyncUTXOs = () => { + oneSatSPV.sync(true); + addSnackbar('Resyncing UTXOs in the background...', 'info'); + }; + + const updateSpends = () => { + oneSatSPV.stores.txos?.refreshSpends(); + addSnackbar('Updating spends in the background...', 'info'); + }; + const main = ( <> { onClick={() => setPage('export-keys-options')} jsxElement={} /> + +