diff --git a/.changeset/cold-donuts-film.md b/.changeset/cold-donuts-film.md new file mode 100644 index 0000000000..539b2569e8 --- /dev/null +++ b/.changeset/cold-donuts-film.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Fix display of dApp messages diff --git a/.changeset/curvy-timers-jump.md b/.changeset/curvy-timers-jump.md new file mode 100644 index 0000000000..6bf478dac9 --- /dev/null +++ b/.changeset/curvy-timers-jump.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Group NFTs per collection diff --git a/.changeset/eighty-squids-drive.md b/.changeset/eighty-squids-drive.md new file mode 100644 index 0000000000..ba157ebc21 --- /dev/null +++ b/.changeset/eighty-squids-drive.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Enable Thai Baht (THB) and Hong Kong Dollar (HKD) fiat currencies diff --git a/.changeset/green-tools-beg.md b/.changeset/green-tools-beg.md new file mode 100644 index 0000000000..19a380480c --- /dev/null +++ b/.changeset/green-tools-beg.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Display transaction and fee worth before sending diff --git a/.changeset/grumpy-cycles-pay.md b/.changeset/grumpy-cycles-pay.md new file mode 100644 index 0000000000..dfa230f4ca --- /dev/null +++ b/.changeset/grumpy-cycles-pay.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Support video NFTs diff --git a/.changeset/large-parrots-remain.md b/.changeset/large-parrots-remain.md new file mode 100644 index 0000000000..ebeb9aa031 --- /dev/null +++ b/.changeset/large-parrots-remain.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Hide unwanted assets diff --git a/.changeset/long-buckets-act.md b/.changeset/long-buckets-act.md new file mode 100644 index 0000000000..49e454d161 --- /dev/null +++ b/.changeset/long-buckets-act.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Improve battery consumption diff --git a/.changeset/nervous-olives-provide.md b/.changeset/nervous-olives-provide.md new file mode 100644 index 0000000000..e5d02a3528 --- /dev/null +++ b/.changeset/nervous-olives-provide.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Display address group diff --git a/.changeset/perfect-chairs-peel.md b/.changeset/perfect-chairs-peel.md new file mode 100644 index 0000000000..f9a14ff46d --- /dev/null +++ b/.changeset/perfect-chairs-peel.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Improve performance diff --git a/.changeset/proud-ears-judge.md b/.changeset/proud-ears-judge.md new file mode 100644 index 0000000000..73b4eb01a5 --- /dev/null +++ b/.changeset/proud-ears-judge.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Unknown tokens modal diff --git a/.changeset/quiet-ducks-grin.md b/.changeset/quiet-ducks-grin.md new file mode 100644 index 0000000000..c788cf7477 --- /dev/null +++ b/.changeset/quiet-ducks-grin.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Searchable regions list diff --git a/.changeset/silly-books-rule.md b/.changeset/silly-books-rule.md new file mode 100644 index 0000000000..6402e65c32 --- /dev/null +++ b/.changeset/silly-books-rule.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Close open modals when sending token diff --git a/.changeset/six-mangos-grab.md b/.changeset/six-mangos-grab.md new file mode 100644 index 0000000000..b9ceed4ac1 --- /dev/null +++ b/.changeset/six-mangos-grab.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Add address sorting options diff --git a/.changeset/tall-frogs-tickle.md b/.changeset/tall-frogs-tickle.md new file mode 100644 index 0000000000..bea04b9998 --- /dev/null +++ b/.changeset/tall-frogs-tickle.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Hide imported secret recovery words diff --git a/.changeset/three-files-cross.md b/.changeset/three-files-cross.md new file mode 100644 index 0000000000..b9ad636f43 --- /dev/null +++ b/.changeset/three-files-cross.md @@ -0,0 +1,5 @@ +--- +"alephium-desktop-wallet": patch +--- + +Fix password wrongly required for Ledger wallet diff --git a/.changeset/warm-schools-jog.md b/.changeset/warm-schools-jog.md new file mode 100644 index 0000000000..ea67e68a76 --- /dev/null +++ b/.changeset/warm-schools-jog.md @@ -0,0 +1,5 @@ +--- +"@alephium/mobile-wallet": patch +--- + +Display fiat worth of entered amount diff --git a/.gitignore b/.gitignore index fec3b0f136..51940027ec 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ node_modules # turbo .turbo + +# IDE +*.idea \ No newline at end of file diff --git a/apps/desktop-wallet/.afterAllArtifactBuild.js b/apps/desktop-wallet/.afterAllArtifactBuild.js index 1f92a0d23a..b164bc8295 100644 --- a/apps/desktop-wallet/.afterAllArtifactBuild.js +++ b/apps/desktop-wallet/.afterAllArtifactBuild.js @@ -1,19 +1,3 @@ -// Copyright 2018 - 2024 The Alephium Authors -// This file is part of the alephium project. -// -// The library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the library. If not, see . - const crypto = require('crypto') const fs = require('fs') const path = require('path') diff --git a/apps/desktop-wallet/.afterPack.js b/apps/desktop-wallet/.afterPack.js index af5b0b6424..5a5019dc25 100644 --- a/apps/desktop-wallet/.afterPack.js +++ b/apps/desktop-wallet/.afterPack.js @@ -1,19 +1,3 @@ -// Copyright 2018 - 2024 The Alephium Authors -// This file is part of the alephium project. -// -// The library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the library. If not, see . - // The hook is a temporary fix to set the right icon size for the deb archive. // The hook only applies to the `deb` target. // The hook will set the icon size to 512 for all icons with a size of 0. diff --git a/apps/desktop-wallet/.eslintrc.js b/apps/desktop-wallet/.eslintrc.js index f5c80fc989..29590ee7e8 100644 --- a/apps/desktop-wallet/.eslintrc.js +++ b/apps/desktop-wallet/.eslintrc.js @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - module.exports = { extends: [ '@alephium/eslint-config/base', diff --git a/apps/desktop-wallet/.nvmrc b/apps/desktop-wallet/.nvmrc new file mode 100644 index 0000000000..9a2a0e219c --- /dev/null +++ b/apps/desktop-wallet/.nvmrc @@ -0,0 +1 @@ +v20 diff --git a/apps/desktop-wallet/.signWindows.js b/apps/desktop-wallet/.signWindows.js index 11defb646f..6e430427da 100644 --- a/apps/desktop-wallet/.signWindows.js +++ b/apps/desktop-wallet/.signWindows.js @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - const path = require('path') const fs = require('fs') const childProcess = require('child_process') diff --git a/apps/desktop-wallet/CHANGELOG.md b/apps/desktop-wallet/CHANGELOG.md index c36308edcf..3443094dcf 100644 --- a/apps/desktop-wallet/CHANGELOG.md +++ b/apps/desktop-wallet/CHANGELOG.md @@ -1,5 +1,18 @@ # alephium-desktop-wallet +## 2.5.1 + +### Patch Changes + +- b3124a0: Fix auto update +- 8abda40: Fix removal of active WalletConnect sessions +- 8abda40: Frameless window on Windows +- 8abda40: Optimize transactions loading +- 8abda40: Fix Linux app title bar +- 8abda40: Enable Thai translations +- 8abda40: Support alph_signAndSubmitUnsignedTx WalletConnect method +- 1c6dcd7: Improve performance of address picker + ## 2.5.0 ### Minor Changes diff --git a/apps/desktop-wallet/electron/appProtocol.ts b/apps/desktop-wallet/electron/appProtocol.ts new file mode 100644 index 0000000000..16a33b8c53 --- /dev/null +++ b/apps/desktop-wallet/electron/appProtocol.ts @@ -0,0 +1,45 @@ +import path from 'node:path' +import { pathToFileURL } from 'node:url' + +import { app, net, protocol } from 'electron' + +import { APP_ROOT_PATH } from './paths' + +export const APP_PROTOCOL = 'alephium' + +// See: https://www.electronjs.org/docs/latest/tutorial/security#18-avoid-usage-of-the-file-protocol-and-prefer-usage-of-custom-protocols +export const registerAppProtocol = () => { + protocol.registerSchemesAsPrivileged([ + { + scheme: APP_PROTOCOL, + privileges: { + secure: true, + standard: true, + supportFetchAPI: true + } + } + ]) + + if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient(APP_PROTOCOL, process.execPath, [path.resolve(process.argv[1])]) + } + } else { + app.setAsDefaultProtocolClient(APP_PROTOCOL) + } +} + +export const handleAppProtocolRequests = () => { + protocol.handle(APP_PROTOCOL, (request) => { + const filePath = request.url.slice(`${APP_PROTOCOL}://`.length) + + // Validate the path to prevent directory traversal attacks + const pathToServe = path.resolve(APP_ROOT_PATH, filePath) + const relativePath = path.relative(APP_ROOT_PATH, pathToServe) + const isSafe = relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath) + + return isSafe + ? net.fetch(pathToFileURL(pathToServe).toString()) + : new Response('Invalid path', { status: 400, headers: { 'content-type': 'text/plain' } }) + }) +} diff --git a/apps/desktop-wallet/electron/autoUpdater.ts b/apps/desktop-wallet/electron/autoUpdater.ts index 4ede50c116..1a3fb7df52 100644 --- a/apps/desktop-wallet/electron/autoUpdater.ts +++ b/apps/desktop-wallet/electron/autoUpdater.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { BrowserWindow, ipcMain } from 'electron' import { autoUpdater } from 'electron-updater' diff --git a/apps/desktop-wallet/electron/electron-env.ts b/apps/desktop-wallet/electron/electron-env.ts index 90ca212e4a..1c58993065 100644 --- a/apps/desktop-wallet/electron/electron-env.ts +++ b/apps/desktop-wallet/electron/electron-env.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ProxySettings } from '@alephium/shared' import { NativeTheme } from 'electron/main' import { ProgressInfo, UpdateDownloadedEvent } from 'electron-updater' @@ -50,8 +32,16 @@ declare global { getSystemLanguage: () => Promise getSystemRegion: () => Promise setProxySettings: (proxySettings: ProxySettings) => Promise + openOnRampServiceWindow: ({ url, targetLocation }: { url: string; targetLocation: string }) => void + onOnRampTargetLocationReached: (callback: () => void) => () => void restart: () => void } + window: { + minimize: () => Promise + maximize: () => Promise + close: () => Promise + onMaximizedChange: (callback: (maximized: boolean) => void) => () => void + } } } } diff --git a/apps/desktop-wallet/electron/ledger.ts b/apps/desktop-wallet/electron/ledger.ts index 2ed002bf23..9a31032155 100644 --- a/apps/desktop-wallet/electron/ledger.ts +++ b/apps/desktop-wallet/electron/ledger.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ledgerUSBVendorId } from '@ledgerhq/devices' import { BrowserWindow } from 'electron' diff --git a/apps/desktop-wallet/electron/main.ts b/apps/desktop-wallet/electron/main.ts index 30dad7ef2a..b045400a88 100644 --- a/apps/desktop-wallet/electron/main.ts +++ b/apps/desktop-wallet/electron/main.ts @@ -1,48 +1,26 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import path from 'node:path' -import { fileURLToPath } from 'node:url' -import { app, BrowserWindow, ipcMain, nativeImage, protocol, shell } from 'electron' +import { app, BrowserWindow, ipcMain, nativeImage, shell } from 'electron' import contextMenu from 'electron-context-menu' import isDev from 'electron-is-dev' +import { APP_PROTOCOL, handleAppProtocolRequests, registerAppProtocol } from './appProtocol' import { configureAutoUpdater, handleAutoUpdaterUserActions, setupAutoUpdaterListeners } from './autoUpdater' import { setupLedgerDevicePermissions } from './ledger' import { setupAppMenu } from './menu' import { handleNativeThemeUserActions, setupNativeThemeListeners } from './nativeTheme' +import { handleOnRampWindows } from './onRamp' +import { ICON_PATH, RENDERER_PATH } from './paths' import { IS_RC, isIpcSenderValid, isMac, isWindows } from './utils' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL'] -const APP_ROOT = path.join(__dirname, '..') -const RENDERER_DIST = path.join(APP_ROOT, 'build') -const VITE_PUBLIC = isDev ? path.join(APP_ROOT, 'public') : RENDERER_DIST -const ICON_PATH = path.join(VITE_PUBLIC, 'icons', 'logo-48.png') +import { + handleWalletConnectDeepLink, + handleWindowsWalletConnectDeepLink, + initializeWalletConnectDeepLinkUri, + setupWalletConnectDeepLinkIPCHandlers +} from './walletConnect' configureAutoUpdater() -// Handle deep linking for alephium:// -const ALEPHIUM = 'alephium' -const ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX = `${ALEPHIUM}://wc` -const ALEPHIUM_WALLET_CONNECT_URI_PREFIX = '?uri=' - // See https://github.com/alephium/alephium-frontend/issues/176 const OLD_APP_NAME = 'alephium-wallet' app.setName(OLD_APP_NAME) @@ -50,14 +28,7 @@ app.setName(OLD_APP_NAME) // Expose Garbage Collector flag for manual trigger app.commandLine.appendSwitch('js-flags', '--expose-gc') -protocol.registerSchemesAsPrivileged([{ scheme: ALEPHIUM, privileges: { secure: true, standard: true } }]) -if (process.defaultApp) { - if (process.argv.length >= 2) { - app.setAsDefaultProtocolClient(ALEPHIUM, process.execPath, [path.resolve(process.argv[1])]) - } -} else { - app.setAsDefaultProtocolClient(ALEPHIUM) -} +registerAppProtocol() contextMenu() @@ -65,16 +36,16 @@ contextMenu() // be closed automatically when the JavaScript object is garbage collected. let mainWindow: BrowserWindow | null -let deepLinkUri: string | null = null - function createWindow() { mainWindow = new BrowserWindow({ icon: ICON_PATH, width: 1200, height: 800, - minWidth: 1200, + minWidth: 900, minHeight: 700, - titleBarStyle: isWindows ? 'default' : 'hidden', + frame: isMac, + titleBarStyle: 'hidden', + trafficLightPosition: { x: 8, y: 8 }, webPreferences: { preload: path.join(__dirname, 'preload.js'), spellcheck: true @@ -87,10 +58,10 @@ function createWindow() { mainWindow.setIcon(nativeImage.createFromPath(ICON_PATH)) } - if (VITE_DEV_SERVER_URL) { - mainWindow.loadURL(VITE_DEV_SERVER_URL) + if (process.env['VITE_DEV_SERVER_URL']) { + mainWindow.loadURL(process.env['VITE_DEV_SERVER_URL']) } else { - mainWindow.loadFile(path.join(RENDERER_DIST, 'index.html')) + mainWindow.loadURL(`${APP_PROTOCOL}://${RENDERER_PATH}/index.html`) } if (isDev || IS_RC) { @@ -114,15 +85,7 @@ function createWindow() { setupLedgerDevicePermissions(mainWindow) - if (!isMac) { - if (process.argv.length > 1) { - const url = process.argv.find((arg) => arg.startsWith(ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX)) - - if (url) { - deepLinkUri = extractWalletConnectUri(url) - } - } - } + initializeWalletConnectDeepLinkUri() } if (!app.requestSingleInstanceLock()) { @@ -136,15 +99,7 @@ app.on('second-instance', (_event, args) => { if (mainWindow.isMinimized()) mainWindow.restore() mainWindow.focus() - // Handle deep-link for Windows - if (args.length > 1) { - const url = args.find((arg) => arg.startsWith(ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX)) - - if (url) { - deepLinkUri = extractWalletConnectUri(url) - mainWindow?.webContents.send('wc:connect', deepLinkUri) - } - } + handleWindowsWalletConnectDeepLink(mainWindow, args) } }) @@ -158,14 +113,22 @@ app.on('ready', async function () { REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS } = await import('electron-devtools-installer') - await installExtension(REACT_DEVELOPER_TOOLS) - await installExtension(REDUX_DEVTOOLS) + + try { + await installExtension([REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS]) + } catch (e) { + console.error('Failed to install devtools:', e) + } } + handleAppProtocolRequests() + handleNativeThemeUserActions() handleAutoUpdaterUserActions() + handleOnRampWindows(mainWindow) + ipcMain.handle('app:hide', ({ senderFrame }) => { if (!isIpcSenderValid(senderFrame)) return null @@ -221,16 +184,30 @@ app.on('ready', async function () { app.exit() }) - ipcMain.handle('wc:getDeepLinkUri', ({ senderFrame }) => { - if (!isIpcSenderValid(senderFrame)) return null + setupWalletConnectDeepLinkIPCHandlers() - return deepLinkUri + ipcMain.handle('window:minimize', () => { + mainWindow?.minimize() }) - ipcMain.handle('wc:resetDeepLinkUri', ({ senderFrame }) => { - if (!isIpcSenderValid(senderFrame)) return null + ipcMain.handle('window:maximize', () => { + if (mainWindow?.isMaximized()) { + mainWindow.unmaximize() + } else { + mainWindow?.maximize() + } + }) + + ipcMain.handle('window:close', () => { + mainWindow?.close() + }) + + mainWindow?.on('maximize', () => { + mainWindow?.webContents.send('window:maximized', true) + }) - deepLinkUri = null + mainWindow?.on('unmaximize', () => { + mainWindow?.webContents.send('window:maximized', false) }) createWindow() @@ -249,15 +226,7 @@ app.on('activate', function () { if (mainWindow === null) createWindow() }) -app.on('open-url', (_, url) => { - if (url.startsWith(ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX)) { - deepLinkUri = extractWalletConnectUri(url) - if (mainWindow) mainWindow?.webContents.send('wc:connect', deepLinkUri) - } -}) - -const extractWalletConnectUri = (url: string) => - url.substring(url.indexOf(ALEPHIUM_WALLET_CONNECT_URI_PREFIX) + ALEPHIUM_WALLET_CONNECT_URI_PREFIX.length) +app.on('open-url', (_, url) => handleWalletConnectDeepLink(url, mainWindow)) // Handle window controls via IPC ipcMain.on('shell:open', () => { diff --git a/apps/desktop-wallet/electron/menu.ts b/apps/desktop-wallet/electron/menu.ts index 8ef948523d..0b2e51b8a7 100644 --- a/apps/desktop-wallet/electron/menu.ts +++ b/apps/desktop-wallet/electron/menu.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { app, BrowserWindow, dialog, Menu, MenuItemConstructorOptions, shell } from 'electron' import { CURRENT_VERSION, isMac, isWindows } from './utils' diff --git a/apps/desktop-wallet/electron/nativeTheme.ts b/apps/desktop-wallet/electron/nativeTheme.ts index f5368868d5..b8655c491d 100644 --- a/apps/desktop-wallet/electron/nativeTheme.ts +++ b/apps/desktop-wallet/electron/nativeTheme.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { BrowserWindow, ipcMain, nativeTheme } from 'electron' import { isIpcSenderValid } from './utils' diff --git a/apps/desktop-wallet/electron/onRamp.ts b/apps/desktop-wallet/electron/onRamp.ts new file mode 100644 index 0000000000..fc331a7871 --- /dev/null +++ b/apps/desktop-wallet/electron/onRamp.ts @@ -0,0 +1,63 @@ +import { BrowserWindow, ipcMain } from 'electron' + +export const handleOnRampWindows = (mainWindow: BrowserWindow | null) => { + let onRampWindow: BrowserWindow | null + + ipcMain.handle('app:openOnRampServiceWindow', (event, { url, targetLocation }) => { + if (onRampWindow) { + onRampWindow.show() + return + } + + onRampWindow = new BrowserWindow({ + width: 1000, + height: 800, + webPreferences: { + contextIsolation: true, + webSecurity: true + } + }) + + // Ensure window reference is cleaned up when closed + onRampWindow.on('closed', () => { + onRampWindow = null + }) + + onRampWindow.loadURL(url) + + onRampWindow.webContents.on('did-navigate', (event, currentUrl) => { + if (currentUrl.includes(targetLocation)) { + onRampWindow?.close() + + mainWindow?.webContents.send('target-location-reached') + } + }) + + // Handle child windows opening (onramper opens provider in a new window) + onRampWindow.webContents.setWindowOpenHandler(({ url }) => { + const childWindow = new BrowserWindow({ + parent: onRampWindow!, + width: 800, + height: 600, + webPreferences: { + contextIsolation: true, + webSecurity: true + } + }) + childWindow.loadURL(url) + + // Listen for navigation events on the new window + childWindow.webContents.on('did-navigate', (event, currentUrl) => { + if (currentUrl.includes(targetLocation)) { + childWindow?.close() + onRampWindow?.close() + + mainWindow?.webContents.send('target-location-reached') + } + }) + + // Prevent the default action (which would be Electron creating a default window) + return { action: 'deny' } + }) + }) +} diff --git a/apps/desktop-wallet/electron/paths.ts b/apps/desktop-wallet/electron/paths.ts new file mode 100644 index 0000000000..59b7a06845 --- /dev/null +++ b/apps/desktop-wallet/electron/paths.ts @@ -0,0 +1,14 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import isDev from 'electron-is-dev' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export const APP_ROOT_PATH = path.join(__dirname, '..') + +export const RENDERER_PATH = 'build' + +const VITE_PUBLIC_PATH = path.join(APP_ROOT_PATH, isDev ? 'public' : RENDERER_PATH) + +export const ICON_PATH = path.join(VITE_PUBLIC_PATH, 'icons', 'logo-48.png') diff --git a/apps/desktop-wallet/electron/preload.ts b/apps/desktop-wallet/electron/preload.ts index cfb832d979..04c93293f2 100644 --- a/apps/desktop-wallet/electron/preload.ts +++ b/apps/desktop-wallet/electron/preload.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ProxySettings } from '@alephium/shared' import { IpcRendererEvent, NativeTheme } from 'electron/main' import { ProgressInfo, UpdateDownloadedEvent } from 'electron-updater' @@ -80,7 +62,24 @@ contextBridge.exposeInMainWorld('electron', { show: () => ipcRenderer.invoke('app:show'), getSystemLanguage: () => ipcRenderer.invoke('app:getSystemLanguage'), getSystemRegion: () => ipcRenderer.invoke('app:getSystemRegion'), + openOnRampServiceWindow: ({ url, targetLocation }: { url: string; targetLocation: string }) => + ipcRenderer.invoke('app:openOnRampServiceWindow', { url, targetLocation }), + onOnRampTargetLocationReached: (callback: () => void) => { + const sanitizedCallback = (_event: IpcRendererEvent) => callback() + ipcRenderer.on('target-location-reached', sanitizedCallback) + return () => ipcRenderer.removeListener('target-location-reached', sanitizedCallback) + }, setProxySettings: (proxySettings: ProxySettings) => ipcRenderer.invoke('app:setProxySettings', proxySettings), restart: () => ipcRenderer.invoke('app:restart') + }, + window: { + minimize: () => ipcRenderer.invoke('window:minimize'), + maximize: () => ipcRenderer.invoke('window:maximize'), + close: () => ipcRenderer.invoke('window:close'), + onMaximizedChange: (callback: (maximized: boolean) => void) => { + const subscription = (_event: IpcRendererEvent, maximized: boolean) => callback(maximized) + ipcRenderer.on('window:maximized', subscription) + return () => ipcRenderer.removeListener('window:maximized', subscription) + } } }) diff --git a/apps/desktop-wallet/electron/utils.ts b/apps/desktop-wallet/electron/utils.ts index 6dfe1c72fe..9fc71ecbf9 100644 --- a/apps/desktop-wallet/electron/utils.ts +++ b/apps/desktop-wallet/electron/utils.ts @@ -1,23 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { app, WebFrameMain } from 'electron' +import { APP_PROTOCOL } from './appProtocol' + export const isMac = process.platform === 'darwin' export const isWindows = process.platform === 'win32' @@ -27,4 +11,15 @@ export const CURRENT_VERSION = app.getVersion() export const IS_RC = CURRENT_VERSION.includes('-rc.') // See: https://www.electronjs.org/docs/latest/tutorial/security#17-validate-the-sender-of-all-ipc-messages -export const isIpcSenderValid = (frame: WebFrameMain | null) => frame && new URL(frame.url).host === 'localhost:3000' +export const isIpcSenderValid = (frame: WebFrameMain | null) => { + if (frame) { + const url = new URL(frame.url) + + return ( + url.protocol === `${APP_PROTOCOL}:` || // for production builds + url.host === 'localhost:3000' // for dev environment + ) + } else { + return false + } +} diff --git a/apps/desktop-wallet/electron/walletConnect.ts b/apps/desktop-wallet/electron/walletConnect.ts new file mode 100644 index 0000000000..5ffd00202f --- /dev/null +++ b/apps/desktop-wallet/electron/walletConnect.ts @@ -0,0 +1,63 @@ +import { BrowserWindow, ipcMain } from 'electron' + +import { APP_PROTOCOL } from './appProtocol' +import { isIpcSenderValid, isMac } from './utils' + +const ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX = `${APP_PROTOCOL}://wc` +const ALEPHIUM_WALLET_CONNECT_URI_PREFIX = '?uri=' + +let deepLinkUri: string | null = null + +export const initializeWalletConnectDeepLinkUri = () => { + if (!isMac) { + if (process.argv.length > 1) { + const url = findWalletConnectUrlInArgs(process.argv) + + if (url) { + deepLinkUri = extractWalletConnectUri(url) + } + } + } + + return deepLinkUri +} + +export const handleWalletConnectDeepLink = (url: string, mainWindow: BrowserWindow | null) => { + if (startsWithWalletConnectPrefix(url)) { + deepLinkUri = extractWalletConnectUri(url) + + if (mainWindow) mainWindow?.webContents.send('wc:connect', deepLinkUri) + } +} + +export const handleWindowsWalletConnectDeepLink = (mainWindow: BrowserWindow, args: string[]) => { + if (args.length > 1) { + const url = findWalletConnectUrlInArgs(args) + + if (url) { + deepLinkUri = extractWalletConnectUri(url) + mainWindow.webContents.send('wc:connect', deepLinkUri) + } + } +} + +export const setupWalletConnectDeepLinkIPCHandlers = () => { + ipcMain.handle('wc:getDeepLinkUri', ({ senderFrame }) => { + if (!isIpcSenderValid(senderFrame)) return null + + return deepLinkUri + }) + + ipcMain.handle('wc:resetDeepLinkUri', ({ senderFrame }) => { + if (!isIpcSenderValid(senderFrame)) return null + + deepLinkUri = null + }) +} + +const extractWalletConnectUri = (url: string) => + url.substring(url.indexOf(ALEPHIUM_WALLET_CONNECT_URI_PREFIX) + ALEPHIUM_WALLET_CONNECT_URI_PREFIX.length) + +const findWalletConnectUrlInArgs = (args: string[]) => args.find(startsWithWalletConnectPrefix) + +const startsWithWalletConnectPrefix = (str: string) => str.startsWith(ALEPHIUM_WALLET_CONNECT_DEEP_LINK_PREFIX) diff --git a/apps/desktop-wallet/locales/bg-BG/translation.json b/apps/desktop-wallet/locales/bg-BG/translation.json index ea781d8875..8dc74fb42e 100644 --- a/apps/desktop-wallet/locales/bg-BG/translation.json +++ b/apps/desktop-wallet/locales/bg-BG/translation.json @@ -179,7 +179,6 @@ "To address": "Към адрес", "To remove this address from being the default address, you must set another one as default first.": "За да премахнете този адрес от списъка на адресите по подразбиране, първо трябва да зададете друг адрес по подразбиране.", "Toggle discreet mode (hide amounts).": "Превключване на дискретен режим (скриване на суми).", - "Total value": "Обща стойност", "Transaction details": "Подробности за транзакцията", "Transaction sent!": "Транзакцията е изпратена!", "Transactions": "Транзакции", @@ -203,7 +202,7 @@ "Wallets": "Портфейли", "Welcome to Alephium.": "Добре дошли в Alephium.", "welcomeScreenOneAddressPerGroupMessage": "Напреднал потребител: искате да започнете с <1>един адрес на група за добив или DeFi?", - "You have great ideas you want to share?": "Имате страхотни идеи, които искате да споделите?", + "Do you have great ideas you want to share?": "Имате страхотни идеи, които искате да споделите?", "Your address": "Вашият адрес", "Your Wallet": "Вашият портфейл", "{{ amount }} words entered": "{{ amount }} думи въведени" diff --git a/apps/desktop-wallet/locales/de-DE/translation.json b/apps/desktop-wallet/locales/de-DE/translation.json index 22769c629f..b1bb95e0b3 100644 --- a/apps/desktop-wallet/locales/de-DE/translation.json +++ b/apps/desktop-wallet/locales/de-DE/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "Adresse NFTs", "Address options": "Adressoptionen", "Address tokens": "Adress-Tokens", - "Address transactions": "Adresstransaktionen", "Addresses": "Adressen", "Addresses & contacts": "Adressen & Kontakte", "Advanced feature": "Erweiterte Funktion", @@ -22,6 +21,9 @@ "all": "alle", "All good? Let's continue!": "Alles in Ordnung? Machen wir weiter!", "All selected": "Alle ausgewählten", + "All transactions were loaded!": "Alle Transaktionen wurden geladen!", + "All the transactions of this address were loaded!": "Alle Transaktionen von dieser Adresse wurden geladen!", + "All the transactions that match the filtering criteria were loaded!": "Alle Transaktionen, die den Filterkriterien entsprechen, wurden geladen!", "ALPH price: {{ price }}": "ALPH Preis: {{ price }}", "Alright! Time to check if you got your words right!": "Alles klar! Prüfen Sie, ob Sie die Wörter richtig aufgeschrieben haben!", "Amount": "Betrag", @@ -31,6 +33,7 @@ "and": "und", "Anyone with your private keys can steal any assets held in your addresses.": "Jeder mit Ihren privaten Schlüsseln kann Vermögenswerte stehlen, die auf Ihren Adressen gehalten werden.", "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "Sind Sie sicher, dass Sie \"{{ contactName }}\" aus Ihrer Kontaktliste entfernen möchten?", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "Sind Sie sicher, dass Sie \"{{ address }}\" aus Ihrer Kontaktliste entfernen möchten?", "Asset": "Vermögenswert", "Assets": "Vermögenswerte", "Available": "Verfügbar", @@ -289,7 +292,6 @@ "Set gas and lock time": "Zeit für Gas und Sperre festlegen", "Set gas settings": "Gas Einstellungen festlegen", "Settings": "Einstellungen", - "Shortcuts": "Verknüpfungen", "Show": "Anzeigen", "Show advanced options": "Erweiterte Optionen anzeigen", "Show in explorer": "Im Explorer anzeigen", @@ -337,18 +339,18 @@ "Tokens": "Token", "Tokens to issue (optional)": "Auszugebende Token (optional)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Zu viele Chains im WalletConnect-Vorschlag, erwartet 1, erhalten {{ num }}", - "Total value": "Gesamtwert", "Transaction details": "Transaktionsdetails", "Transaction sent!": "Transaktion gesendet!", "Transactions": "Transaktionen", "Transactions sent!": "Transaktionen gesendet!", - "Transfers": "Transfers", "Type your password to change this setting.": "Geben Sie Ihr Passwort ein, um diese Einstellung zu ändern.", "Type your password to export your wallet.": "Geben Sie Ihr Passwort ein, um Ihr Wallet zu exportieren.", "Type your password to show the phrase.": "Geben Sie Ihr Passwort ein, um die Phrase anzuzeigen.", "Type your recovery phrase": "Geben Sie Ihre Wiederherstellungsphrase ein", "No metadata": "Keine Metadaten", "Unknown": "Unbekannt", + "unknownTokensKey_one": "{{ count }} unbekannter Token", + "unknownTokensKey_other": "{{ count }} unbekannte Tokens", "Unknown tokens": "Unbekannte Tokens", "Unknown wallet name": "Unbekannter Walletname", "Unlock": "Entsperren", @@ -362,7 +364,6 @@ "Use max amount": "Maximalbetrag verwenden", "Use optional passphrase": "Optionale Passphrase verwenden", "Useful for miners or DeFi use.": "Nützlich für Miner oder DeFi.", - "Value today": "Wert heute", "Version": "Version", "Version {{ newVersion }} is available": "Version {{ newVersion }} ist verfügbar", "Please, download it and install it to avoid any issues with the wallet.": "Bitte laden Sie sie herunter und installieren Sie sie, um Probleme mit der Wallet zu vermeiden.", @@ -383,7 +384,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Sie sind derzeit mit dem {{ currentNetwork }} Netzwerk verbunden. Stellen Sie sicher, dass Sie sich mit dem Testnet- oder Devnet-Netzwerk verbinden, um Ihre Token zu sehen.", "You can disable this confirmation step from the wallet settings.": "Sie können diesen Bestätigungsschritt in den Wallet-Einstellungen deaktivieren.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Sie können den Transaktionsverlauf für einen ausgewählten Zeitraum herunterladen. Dies kann für die Steuererklärung nützlich sein.", - "You have great ideas you want to share?": "Sie haben tolle Ideen, die Sie mit uns teilen möchten?", + "Do you have great ideas you want to share?": "Sie haben tolle Ideen, die Sie mit uns teilen möchten?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Sie haben das Aufruflimit erreicht. Bitte versuchen Sie es in ein paar Minuten erneut.", "You will not be able to recover your account with the private key!": "Sie werden nicht in der Lage sein, Ihr Konto mit dem privaten Schlüssel wiederherzustellen!", "Your address": "Ihre Adresse", @@ -421,8 +422,11 @@ "Select the address to send assets to.": "Wählen Sie die Adresse aus, an die das Guthaben überwiesen werden soll.", "Destination": "Ziel", "Origin": "Herkunft", - "One of your addresses to send the assets from.": "Eine Ihrer Adressen, von der die Guthaben gesendet werden sollen.", + "Clear cache": "Cache leeren", "Clear": "Löschen", + "Deletes cached wallet and WalletConnect data.": "Löscht zwischengespeichertes Wallet- und WalletConnect-Daten.", + "App data cleared successfully.": "App-Daten erfolgreich gelöscht.", + "Could not clear app data.": "App-Daten konnten nicht gelöscht werden.", "WalletConnect cache cleared successfully.": "WalletConnect Cache erfolgreich geleert.", "Could not clear WalletConnect cache.": "WalletConnect-Cache konnte nicht geleert werden.", "Sign Unsigned Transaction": "Unterzeichne unsignierte Transaktion", @@ -446,5 +450,62 @@ "To delete this wallet use it without a passphrase": "Um diese Wallet zu löschen, verwenden Sie sie ohne Passphrase", "To export this wallet use it without a passphrase": "Um diese Wallet zu exportieren, verwenden Sie sie ohne Passphrase", "The proposal does not include a list of required chains": "Der Vorschlag enthält keine Liste der erforderlichen Blockchains", - "All UTXOs are already consolidated for this address. No consolidation is needed.": "Alle UTXOs sind für diese Adresse bereits konsolidiert. Es ist keine Konsolidierung erforderlich." + "All UTXOs are already consolidated for this address. No consolidation is needed.": "Alle UTXOs sind für diese Adresse bereits konsolidiert. Es ist keine Konsolidierung erforderlich.", + "No addresses match the search criteria.": "Keine Adresse stimmt mit den Suchkriterien überein.", + "walletConnectSwitchNetwork": "Sie sind derzeit mit <1>{{ currentNetworkName }} verbunden, aber die dApp benötigt eine Verbindung zu <3>{{ network }}.", + "walletConnectNewAddress": "Die dApp bittet um eine Adresse in der Gruppe <1>{{ currentNetworkName }}. Klicken Sie unten, um eine zu generieren!", + "ALPH only": "Nur ALPH", + "This address doesn't have any tokens yet.": "Diese Adresse hat noch keine Tokens.", + "This address doesn't have any NFTs yet.": "Diese Adresse besitzt noch keine NFTs.", + "This address doesn't have any transactions yet.": "Diese Adresse hat noch keine Transaktionen.", + "This wallet doesn't have any transactions yet.": "Dieses Wallet hat noch keine Transaktionen.", + "The wallet doesn't have any tokens. Tokens of all your addresses will appear here.": "Das Wallet besitzt keine Tokens. Tokens aller Ihrer Adressen werden hier angezeigt.", + "The wallet doesn't have any NFTs. NFTs of all your addresses will appear here.": "Das Wallet besitzt keine NFTs. NFTs aller Ihrer Adressen werden hier angezeigt.", + "There is something wrong in the received WalletConnect data.": "Es gibt ein Problem mit den empfangenen WalletConnect-Daten.", + "To forget this address set another one as the default one first.": "Um diese Adresse zu vergessen, legen Sie zuerst eine andere als Standardadresse fest.", + "Click to display new transactions": "Klicken, um neue Transaktionen anzuzeigen", + "Transaction was sent...": "Transaktion gesendet...", + "Transaction is about to be included in the blockchain...": "Die Transaktion wird in die Blockchain aufgenommen...", + "Transaction is now part of the blockchain": "Die Transaktion ist jetzt Teil der Blockchain", + "Forget": "Vergessen", + "forgetAddress_one": "Adresse vergessen", + "forgetAddress_other": "Adressen vergessen", + "Declutter your wallet by removing this address from your lists.": "Räumen Sie Ihr Wallet auf, indem Sie diese Adresse aus Ihren Listen entfernen.", + "Declutter your wallet by removing addresses you don't need.": "Bereinigen Sie Ihr Wallet, indem Sie nicht benötigte Adressen entfernen.", + "Forgetting addresses does not delete your assets in them.": "Das Vergessen von Adressen löscht nicht die Vermögenswerte, die sich darauf befinden.", + "If you choose to forget an address with assets, you can always re-add it to your wallet using the \"Discover active addresses\" feature.": "Wenn Sie sich entscheiden, eine Adresse mit Vermögenswerten zu vergessen, können Sie sie jederzeit über die Funktion „Aktive Adressen entdecken“ wieder zu Ihrem Wallet hinzufügen.", + "This will remove {{ num }} address(es) from your address list.": "Dies wird {{ num }} Adresse(n) aus Ihrer Adressliste entfernen.", + "Forget selected addresses": "Ausgewählte Adressen vergessen", + "You only have one address. You cannot forget it.": "Sie haben nur eine Adresse. Sie können diese nicht vergessen.", + "Total worth": "Gesamtwert", + "Could not load NFT metadata": "NFT-Metadaten konnten nicht geladen werden", + "Region": "Region", + "Choose your region to update formats of dates, time, and currencies.": "Wählen Sie Ihre Region aus, um Formate für Datum, Uhrzeit und Währungen zu aktualisieren.", + "Video thumbnail": "Video-Miniaturansicht", + "Unsupported media type": "Nicht unterstützter Medientyp", + "You need to be on testnet or devnet in order to use the faucet.": "Sie müssen sich im Testnet oder Devnet befinden, um den Faucet nutzen zu können.", + "Could not get latest data": "Aktuelle Daten konnten nicht abgerufen werden", + "Updating...": "Aktualisiere...", + "To send funds you first need to load your wallet with some.": "Um Guthaben zu senden, müssen Sie Ihr Wallet zuerst mit Guthaben aufladen.", + "The wallet is empty. Use the faucet in the developer tools in the app settings.": "Das Wallet ist leer. Verwenden Sie den Faucet in den Entwickler-Tools in den App-Einstellungen.", + "Connect with Ledger": "Mit Ledger verbinden", + "Connect your Ledger": "Verbinden Sie Ihren Ledger", + "Could not generate address": "Adresse konnte nicht generiert werden", + "Could not generate one address per group": "Konnte keine Adresse pro Gruppe generieren", + "could_not_save_new_address_one": "Neue Adresse konnte nicht gespeichert werden", + "could_not_save_new_address_other": "Neue Adressen konnten nicht gespeichert werden", + "Could not connect to Alephium Ledger app": "Keine Verbindung zur Alephium Ledger App möglich", + "Not supported.": "Nicht unterstützt.", + "Signing messages with Ledger is not supported.": "Das Signieren von Nachrichten mit Ledger wird nicht unterstützt.", + "Plug in and unlock your Ledger device.": "Schliessen Sie Ihr Ledger-Gerät an und entsperren Sie es.", + "ledgerInstructionsOpenApp": "Öffnen Sie die Alephium Ledger-App. Die Alephium-App kann über <1>Ledger Live installiert werden.", + "Is your device plugged in and the Alephium app open?": "Ist Ihr Gerät angeschlossen und die Alephium-App geöffnet?", + "Please, confirm the transaction on your Ledger.": "Bitte bestätigen Sie die Transaktion auf Ihrem Ledger.", + "Welcome to your Ledger!": "Willkommen bei Ihrem Ledger!", + "Would you like to scan for active addresses?": "Möchten Sie nach aktiven Adressen suchen?", + "Scan": "Scannen", + "Active address discovery completed. Addresses added: {{ count }}": "Suche nach aktiven Adressen abgeschlossen. Hinzugefügte Adressen: {{ count }}", + "Loading new transactions...": "Lade neue Transaktionen...", + "Sign and Send Unsigned Transaction": "Unterschreiben und unsignierte Transaktion senden", + "Sign and Send": "Unterschreiben und senden" } diff --git a/apps/desktop-wallet/locales/el-GR/translation.json b/apps/desktop-wallet/locales/el-GR/translation.json index 470e4fe143..3d3e19d89d 100644 --- a/apps/desktop-wallet/locales/el-GR/translation.json +++ b/apps/desktop-wallet/locales/el-GR/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "NFTs διεύθυνσης", "Address options": "Επιλογές διεύθυνσης", "Address tokens": "Διακριτικά διεύθυνσης", - "Address transactions": "Συναλλαγές διεύθυνσης", "Addresses": "Διευθύνσεις", "Addresses & contacts": "Διευθύνσεις & επαφές", "Advanced feature": "Προηγμένες λειτουργίες", @@ -288,7 +287,6 @@ "Set gas and lock time": "Ορισμός gas και χρόνου κλειδώματος", "Set gas settings": "Ορισμός ρυθμίσεων gas", "Settings": "Ρυθμίσεις", - "Shortcuts": "Συντομεύσεις", "Show": "Εμφάνιση", "Show advanced options": "Εμφάνιση προηγμένων επιλογών", "Show in explorer": "Προβολή στον εξερευνητή", @@ -336,12 +334,10 @@ "Tokens": "Διακριτικά", "Tokens to issue (optional)": "Διακριτικά προς έκδοση (προαιρετικά)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Πάρα πολλές αλυσίδες στην πρόταση WalletConnect, αναμενόταν 1, πήρε {{ num }}", - "Total value": "Συνολική αξία", "Transaction details": "Λεπτομέρειες συναλλαγής", "Transaction sent!": "Η συναλλαγή στάλθηκε!", "Transactions": "Συναλλαγές", "Transactions sent!": "Οι συναλλαγές στάλθηκαν!", - "Transfers": "Μεταφορές", "Type your password to change this setting.": "Πληκτρολογήστε τον κωδικό πρόσβασής σας για να αλλάξετε αυτή τη ρύθμιση.", "Type your password to export your wallet.": "Πληκτρολογήστε τον κωδικό πρόσβασης σας για να εξάγετε το πορτοφόλι σας.", "Type your password to show the phrase.": "Πληκτρολογήστε τον κωδικό πρόσβασης σας για να εμφανίσετε τη φράση.", @@ -361,7 +357,6 @@ "Use max amount": "Χρησιμοποιήστε μέγιστο ποσό", "Use optional passphrase": "Χρησιμοποιήστε προαιρετική φράση πρόσβασης", "Useful for miners or DeFi use.": "Χρήσιμο για τους εξορυκτές ή για χρήση DeFi.", - "Value today": "Η αξία σήμερα", "Version": "Έκδοση", "Version {{ newVersion }} is available": "Η έκδοση {{ newVersion }} είναι διαθέσιμη", "Please, download it and install it to avoid any issues with the wallet.": "Παρακαλώ, κατεβάστε την και εγκαταστήστε την για να αποφύγετε τυχόν προβλήματα με το πορτοφόλι.", @@ -381,7 +376,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Αυτή τη στιγμή είστε συνδεδεμένοι στο δίκτυο {{ currentNetwork }}. Φροντίστε να συνδεθείτε στο δίκτυο δοκιμής ή ανάπτυξης για να δείτε τα νομίσματα σας.", "You can disable this confirmation step from the wallet settings.": "Μπορείτε να απενεργοποιήσετε αυτό το βήμα επιβεβαίωσης από τις ρυθμίσεις του πορτοφολιού.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Μπορείτε να κατεβάσετε το ιστορικό των συναλλαγών διευθύνσεων για μια επιλεγμένη χρονική περίοδο. Αυτό μπορεί να είναι χρήσιμο για τη φορολογική αναφορά.", - "You have great ideas you want to share?": "Έχετε υπέροχες ιδέες που θα θέλατε να μοιραστείτε;", + "Do you have great ideas you want to share?": "Έχετε υπέροχες ιδέες που θα θέλατε να μοιραστείτε;", "You have reached the maximum calls limit. Please try again in a few minutes.": "Έχετε φτάσει το μέγιστο όριο κλήσεων. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά.", "You will not be able to recover your account with the private key!": "Δε θα είστε σε θέση να ανακτήσετε τον λογαριασμό σας με το ιδιωτικό κλειδί!", "Your address": "Η διεύθυνση σας", @@ -419,7 +414,6 @@ "Select the address to send assets to.": "Επιλέξτε τη διεύθυνση στην οποία θα στείλετε τα περιουσιακά στοιχεία.", "Destination": "Προορισμός", "Origin": "Προέλευση", - "One of your addresses to send the assets from.": "Μία από τις διευθύνσεις σας για να στείλουμε τα περιουσιακά στοιχεία από εκεί.", "Clear": "Εκκαθάριση", "WalletConnect cache cleared successfully.": "Η προσωρινή μνήμη WalletConnect καθαρίστηκε επιτυχώς.", "Could not clear WalletConnect cache.": "Αδυναμία εκκαθάρισης προσωρινής μνήμης WalletConnect.", diff --git a/apps/desktop-wallet/locales/en-US/translation.json b/apps/desktop-wallet/locales/en-US/translation.json index 3ee2b8175d..4eb472314e 100644 --- a/apps/desktop-wallet/locales/en-US/translation.json +++ b/apps/desktop-wallet/locales/en-US/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "Address NFTs", "Address options": "Address options", "Address tokens": "Address tokens", - "Address transactions": "Address transactions", "Addresses": "Addresses", "Addresses & contacts": "Addresses & contacts", "Advanced feature": "Advanced feature", @@ -293,7 +292,6 @@ "Set gas and lock time": "Set gas and lock time", "Set gas settings": "Set gas settings", "Settings": "Settings", - "Shortcuts": "Shortcuts", "Show": "Show", "Show advanced options": "Show advanced options", "Show in explorer": "Show in explorer", @@ -341,12 +339,10 @@ "Tokens": "Tokens", "Tokens to issue (optional)": "Tokens to issue (optional)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}", - "Total value": "Total value", "Transaction details": "Transaction details", "Transaction sent!": "Transaction sent!", "Transactions": "Transactions", "Transactions sent!": "Transactions sent!", - "Transfers": "Transfers", "Type your password to change this setting.": "Type your password to change this setting.", "Type your password to export your wallet.": "Type your password to export your wallet.", "Type your password to show the phrase.": "Type your password to show the phrase.", @@ -368,7 +364,6 @@ "Use max amount": "Use max amount", "Use optional passphrase": "Use optional passphrase", "Useful for miners or DeFi use.": "Useful for miners or DeFi use.", - "Value today": "Value today", "Version": "Version", "Version {{ newVersion }} is available": "Version {{ newVersion }} is available", "Please, download it and install it to avoid any issues with the wallet.": "Please, download it and install it to avoid any issues with the wallet.", @@ -383,13 +378,13 @@ "Welcome back.": "Welcome back.", "Welcome to Alephium.": "Welcome to Alephium.", "Welcome.": "Welcome.", - "welcomeScreenOneAddressPerGroupMessage": "Advanced user: want to start with <1>one address per group for mining or DeFi?", + "welcomeScreenOneAddressPerGroupMessage": "Advanced user: do you want to start with <1>one address per group for mining or DeFi?", "welcomeScreenPassphraseMessage": "If you want to use a <1>passphrase, lock your newly created wallet.", "with": "with", "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.", "You can disable this confirmation step from the wallet settings.": "You can disable this confirmation step from the wallet settings.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "You can download the address transaction history for a selected time period. This can be useful for tax reporting.", - "You have great ideas you want to share?": "You have great ideas you want to share?", + "Do you have great ideas you want to share?": "Do you have great ideas you want to share?", "You have reached the maximum calls limit. Please try again in a few minutes.": "You have reached the maximum calls limit. Please try again in a few minutes.", "You will not be able to recover your account with the private key!": "You will not be able to recover your account with the private key!", "Your address": "Your address", @@ -427,7 +422,6 @@ "Select the address to send assets to.": "Select the address to send assets to.", "Destination": "Destination", "Origin": "Origin", - "One of your addresses to send the assets from.": "One of your addresses to send the assets from.", "Clear cache": "Clear cache", "Clear": "Clear", "Deletes cached wallet and WalletConnect data.": "Deletes cached wallet and WalletConnect data.", @@ -473,7 +467,6 @@ "Transaction was sent...": "Transaction was sent...", "Transaction is about to be included in the blockchain...": "Transaction is about to be included in the blockchain...", "Transaction is now part of the blockchain": "Transaction is now part of the blockchain", - "Your wallet has too many addresses. These are the transactions of the {{ maxNumber }} most frequently used addresses. Please, consider removing some of them.": "Your wallet has too many addresses. These are the transactions of the {{ maxNumber }} most frequently used addresses. Please, consider removing some of them.", "Forget": "Forget", "forgetAddress_one": "Forget address", "forgetAddress_other": "Forget addresses", @@ -495,6 +488,17 @@ "Updating...": "Updating...", "To send funds you first need to load your wallet with some.": "To send funds you first need to load your wallet with some.", "The wallet is empty. Use the faucet in the developer tools in the app settings.": "The wallet is empty. Use the faucet in the developer tools in the app settings.", + "Buy": "Buy", + "onramperDisclaimer": "You are about to access 3rd party services provided by <1>onramper.com through an in-app browser. Alephium does not control Onramper's services. Onramper's terms and conditions will apply, so please read and understand them before proceeding.", + "You can now complete your purchase in the dedicated window!": "You can now complete your purchase in the dedicated window!", + "Token": "Token", + "Price": "Price", + "Worth": "Worth", + "Invalid mnemonic. Please double check your words.": "Invalid mnemonic. Please double check your words.", + "Incorrect word. Please try again.": "Incorrect word. Please try again.", + "Select the correct word for word number <1>{{ number }} of 24.": "Select the correct word for word number <1>{{ number }} of 24.", + "Allocation": "Allocation", + "Start by selecting the origin and destination addresses.": "Start by selecting the origin and destination addresses.", "Connect with Ledger": "Connect with Ledger", "Connect your Ledger": "Connect your Ledger", "Could not generate address": "Could not generate address", @@ -511,5 +515,25 @@ "Welcome to your Ledger!": "Welcome to your Ledger!", "Would you like to scan for active addresses?": "Would you like to scan for active addresses?", "Scan": "Scan", - "Active address discovery completed. Addresses added: {{ count }}": "Active address discovery completed. Addresses added: {{ count }}" + "Active address discovery completed. Addresses added: {{ count }}": "Active address discovery completed. Addresses added: {{ count }}", + "Purchase done!": "Purchase done!", + "Wallet worth": "Wallet worth", + "Address worth": "Address worth", + "Loading new transactions...": "Loading new transactions...", + "Sign and Send Unsigned Transaction": "Sign and Send Unsigned Transaction", + "Sign and Send": "Sign and Send", + "Unlock a wallet to connect to the dApp.": "Unlock a wallet to connect to the dApp.", + "Activity": "Activity", + "Passphrase warning": "Passphrase warning", + "Don't use passphrase": "Don't use passphrase", + "Token balance": "Token balance", + "Hide asset": "Hide asset", + "nb_of_hidden_assets_one": "{{ count }} hidden asset", + "nb_of_hidden_assets_other": "{{ count }} hidden assets", + "Close hidden assets list": "Close hidden assets list", + "Unhide asset": "Unhide asset", + "Asset to send": "Asset to send", + "No transactions before {{ date }}": "No transactions before {{ date }}", + "Sort by": "Sort by", + "ALPH balance": "ALPH balance" } diff --git a/apps/desktop-wallet/locales/es-ES/translation.json b/apps/desktop-wallet/locales/es-ES/translation.json index b07e681768..7a9321113f 100644 --- a/apps/desktop-wallet/locales/es-ES/translation.json +++ b/apps/desktop-wallet/locales/es-ES/translation.json @@ -176,7 +176,6 @@ "To remove this address from being the default address, you must set another one as default first.": "Para quitar esta billetera como predeterminada, primero debe establecer otra como predeterminada.", "Toggle discreet mode (hide amounts).": "Alternar el modo discreto (ocultar montos)", "Tokens to issue (optional)": "Fichas por emitir (opcional)", - "Total value": "Valor total", "Transaction details": "Detalles de la transacción", "Transaction sent!": "¡Transacción enviada!", "Transactions": "Transacciones", @@ -201,7 +200,7 @@ "Welcome to Alephium.": "Bienvenido a Alephium.", "welcomeScreenOneAddressPerGroupMessage": "Usuario avanzado: ¿desea empezar con <1> una dirección por grupo para minería o DeFi?", "welcomeScreenPassphraseMessage": "Si desea utilizar una <1>frase, cierre la billetera recién creada.", - "You have great ideas you want to share?": "¿Tienes buenas ideas que quieres compartir?", + "Do you have great ideas you want to share?": "¿Tienes buenas ideas que quieres compartir?", "Your address": "Su dirección", "Your Wallet": "Su billetera", "{{ amount }} words entered": "{{ amount }} palabras introducidas" diff --git a/apps/desktop-wallet/locales/fr-FR/translation.json b/apps/desktop-wallet/locales/fr-FR/translation.json index bd6e9e0c8e..ec70888c11 100644 --- a/apps/desktop-wallet/locales/fr-FR/translation.json +++ b/apps/desktop-wallet/locales/fr-FR/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "NFTs de l'adresse", "Address options": "Paramètres de l'adresse", "Address tokens": "Jetons de l'adresse", - "Address transactions": "Transactions de l'adresse", "Addresses": "Adresses", "Addresses & contacts": "Adresses & contacts", "Advanced feature": "Fonctionnalité avancée", @@ -289,7 +288,6 @@ "Set gas and lock time": "Définir le gaz et le temps de verrouillage", "Set gas settings": "Définir les paramètres de gaz", "Settings": "Paramètres", - "Shortcuts": "Raccourcis", "Show": "Afficher", "Show advanced options": "Afficher les options avancées", "Show in explorer": "Montrer dans l'explorer", @@ -337,12 +335,10 @@ "Tokens": "Jetons", "Tokens to issue (optional)": "Jetons à créer (optionnel)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Trop de chaines dans la proposition WalletConnect. Une attendue, reçues {{ num }}", - "Total value": "Valeur totale", "Transaction details": "Détails de la transaction", "Transaction sent!": "Transaction envoyée !", "Transactions": "Transactions", "Transactions sent!": "Transactions envoyées !", - "Transfers": "Transferts", "Type your password to change this setting.": "Taper votre mot de passe pour changer ce paramètre.", "Type your password to export your wallet.": "Taper votre mot de passe pour exporter votre portefeuille.", "Type your password to show the phrase.": "Entrez votre mot de passe pour afficher la phrase.", @@ -364,7 +360,6 @@ "Use max amount": "Utiliser le montant max", "Use optional passphrase": "Utiliser la phrase de passe optionnelle", "Useful for miners or DeFi use.": "Utile pour les mineurs ou l'usage DeFi.", - "Value today": "Valeur aujourd'hui", "Version": "Version", "Version {{ newVersion }} is available": "La version {{ newVersion }} est disponible", "Please, download it and install it to avoid any issues with the wallet.": "Téléchargez et installez la afin d'éviter tout problème de fonctionnement.", @@ -385,7 +380,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Vous êtes actuellement connecté au réseau {{ currentNetwork }}. Assurez-vous de vous connecter au réseau testnet ou devnet pour voir vos jetons.", "You can disable this confirmation step from the wallet settings.": "Vous pouvez désactiver cette étape de confirmation dans les paramètres du portefeuille.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Vous pouvez télécharger l'historique des transactions pour cette adresse sur une période de temps donnée. Cela peut être utile pour vos impôts.", - "You have great ideas you want to share?": "Vous avez de supers idées à partager ?", + "Do you have great ideas you want to share?": "Vous avez de supers idées à partager ?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Vous avez atteint la limite maximale d'appels. Veuillez réessayer dans quelques minutes.", "You will not be able to recover your account with the private key!": "Vous ne serez pas en mesure de récupérer votre portefeuille avec la clé privée !", "Your address": "Votre adresse", @@ -423,7 +418,6 @@ "Select the address to send assets to.": "Sélectionner l'adresse vers laquelle envoyer les fonds.", "Destination": "Destination", "Origin": "Origine", - "One of your addresses to send the assets from.": "Une de vos adresses à partir de laquelle envoyer les fonds.", "Clear": "Effacer", "WalletConnect cache cleared successfully.": "Le cache de WalletConnect a été vidé avec succès.", "Could not clear WalletConnect cache.": "Le cache WalletConnect n'a pas pu être vidé.", diff --git a/apps/desktop-wallet/locales/id-ID/translation.json b/apps/desktop-wallet/locales/id-ID/translation.json index 1b9047332b..c2243d82d1 100644 --- a/apps/desktop-wallet/locales/id-ID/translation.json +++ b/apps/desktop-wallet/locales/id-ID/translation.json @@ -9,9 +9,9 @@ "Address NFTs": "Alamat NFT", "Address options": "Pilihan alamat", "Address tokens": "Alamat token", - "Address transactions": "Alamat transaksi", "Addresses": "Alamat-alamat", "Addresses & contacts": "Alamat & kontak", + "Advanced feature": "Tampilan yang lebih baik", "Advanced operations": "Operasi lanjutan", "Advanced options": "Opsi lanjutan", "Advanced settings": "Pengaturan lanjutan", @@ -21,14 +21,19 @@ "all": "semua", "All good? Let's continue!": "Semua baik? Ayo lanjutkan!", "All selected": "Semua telah dipilih", + "All transactions were loaded!": "Semua transaksi telah ditampilkan!", + "All the transactions of this address were loaded!": "Semua transaksi pada alamat ini telah ditampilkan!", + "All the transactions that match the filtering criteria were loaded!": "Semua transaksi yang sesuai dengan kriteria yang disaring telah ditampilkan!", "ALPH price: {{ price }}": "Harga ALPH: {{ price }}", "Alright! Time to check if you got your words right!": "Baiklah! Saatnya memeriksa apakah kata-kata Anda benar!", "Amount": "Jumlah", "Amount exceeds available balance": "Jumlah melebihi saldo yang tersedia", "Amount must be greater than {{ minAmountInAlph }}": "Jumlah harus lebih besar dari {{ minAmountInAlph }}", + "Analytics": "Analisis", "and": "dan", "Anyone with your private keys can steal any assets held in your addresses.": "Siapa pun dengan kunci pribadi Anda dapat mencuri aset apa pun yang disimpan di alamat Anda.", "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "Anda yakin ingin menghapus \"{{ contactName }}\" dari daftar kontak Anda?", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "Apakah Anda yakin ingin menghapus \"{{ address }}\" dari daftar alamat Anda?", "Asset": "Aset", "Assets": "Aset", "Available": "Tersedia", @@ -36,6 +41,7 @@ "between": "antara", "Browse your transaction history. Execute new transfers easily.": "Jelajahi riwayat transaksi Anda. Jalankan transfer baru dengan mudah.", "By default, the address is generated in a random group. You can select the group you want the address to be generated in using the Advanced options.": "Secara bawaan, alamat dihasilkan dalam grup acak. Anda dapat memilih grup tempat Anda ingin alamat dibuat menggunakan opsi Lanjutan.", + "Bytecode": "Bytecode", "Call contract": "Kontrak panggilan", "Cancel": "Batalkan", "Carefully note down the words! They are your wallet's secret recovery phrase.": "Catat kata-kata dengan hati-hati! Itu adalah frasa pemulihan rahasia dompet Anda.", @@ -44,10 +50,12 @@ "Change wallet name": "Ubah nama dompet", "Check": "Periksa", "Choose a contact": "Pilih kontak", + "Click to deactivate discreet mode": "Ketuk untuk menonaktifkan mode rahasia", "Click to see less": "Klik untuk melihat lebih sedikit", "Click to see more": "Klik untuk melihat lebih banyak", "Close": "Tutup", "Confirm": "Konfirmasi", + "Confirm lock time": "Konfirmasi waktu penguncian ", "Confirm passphrase": "Konfirmasi frasa sandi", "CONFIRM REMOVAL": "KONFIRMASI PENGHAPUSAN", "Confirmed": "Dikonfirmasi", @@ -55,6 +63,9 @@ "Connect to a dApp": "Hubungkan ke dApp", "Connect to dApp": "Hubungkan ke dApp", "Connect wallet to dApp": "Hubungkan dompet ke dApp", + "Consolidate": "Mengkonsolidasikan", + "Consolidate (merge) your UTXOs into one.": "Konsolidasikan (merge) UTXOs Anda menjadi satu.", + "Consolidate UTXOs": "Mengkonsolidasi UTXOs", "Contact deleted": "Kontak telah dihapus", "Contact saved": "Kontak disimpan", "Contacts": "Kontak", @@ -65,12 +76,14 @@ "Copy contact address": "Salin alamat kontak", "Copy private key": "Salin kunci pribadi", "Copy to clipboard": "Salin ke papan klip", + "Copy to clipboard failed": "Penyalinan ke Papan-klip gagal", "Copy token hash": "Salin token hash", "Could not connect to the {{ currentNetwork }} network.": "Tidak dapat tersambung ke jaringan {{ currentNetwork }}.", "Could not copy private key.": "Tidak dapat menyalin kunci pribadi.", "Could not delete contact.": "Tidak dapat menghapus kontak.", "Could not find a contact with this ID": "Tidak dapat menemukan kontak dengan ID ini", "Could not find a contact with this name": "Tidak dapat menemukan kontak dengan nama ini", + "Could not pair with WalletConnect": "Tidak dapat memasangkan dengan WalletConnect ", "Could not save contact.": "Tidak dapat menyimpan kontak.", "Could not save new wallet name.": "Tidak dapat menyimpan nama dompet yang baru.", "Create contacts to avoid mistakes when sending transactions!": "Buat kontak untuk menghindari kesalahan saat mengirim transaksi!", @@ -79,6 +92,7 @@ "Current wallet": "Dompet saat ini", "Custom": "Khusus", "Custom network settings saved.": "Pengaturan khusus jaringan telah disimpan.", + "Custom proxy (SOCKS5)": "Proxy khusus (SOCKS5)", "Dark": "Gelap", "Default address": "Alamat bawaan", "Default address for sending transactions.": "Alamat bawaan untuk mengirim transaksi.", @@ -89,7 +103,12 @@ "Direction": "Arah", "Directions": "Arah", "Disconnect": "Putuskan", + "Discover active addresses": "Temukan Alamat-alamat yang aktif", "Discreet mode": "Mode rahasia", + "Download": "Unduh ", + "Download failed": "Gagal mengunduh", + "Try again later.": "Coba lagi nanti.", + "Downloading version {{ newVersion }}...": "Sedang mengunduh versi {{ newVersion }}...", "Duration in minutes after which an idle wallet will lock automatically.": "Durasi dalam menit setelah dompet yang menganggur akan terkunci secara otomatis.", "Easily organize your addresses and your contacts for a more serene transfer experience.": "Atur alamat dan kontak Anda dengan mudah untuk pengalaman transfer yang lebih tenang.", "Edit": "Sunting", @@ -97,6 +116,7 @@ "Enable developer tools": "Aktifkan alat pengembang", "Encountered error while calling the faucet.": "Menemukan kesalahan saat memanggil faucet.", "Encountered error while exporting your transactions.": "Mengalami kesalahan saat mengekspor transaksi Anda.", + "Encountered error while syncing your addresses' data.": "Terjadi kesalahan saat menyingkronkan data alamat - alamat anda.", "Enter": "Masuk", "Enter password": "Ketikan kata sandi", "Enter password for \"{{ walletName }}\"": "Masukkan kata sandi untuk \"{{ walletName }}\"", @@ -107,12 +127,15 @@ "Error while sending the transaction": "Kesalahan saat mengirim transaksi", "Error while sweeping address {{ from }}": "Kesalahan saat menyapu alamat {{ from }}", "Everything is ready!": "Semuanya sudah siap!", + "Expand": "Memperluas", "Expected fee": "Biaya yang diharapkan", + "Explorer API host": "Kumpulan penjelajah API", "Explorer URL": "Penjelajah URL", "Export": "Ekspor", "Export address transactions": "Ekspor alamat transaksi", "Export current wallet": "Ekspor dompet sekarang", "Export wallet": "Ekspor dompet", + "Features for developers only": "Fitur hanya untuk para pengembang saja", "Fee": "Biaya", "For mining or DeFi use.": "Untuk menambang atau menggunakan DeFi.", "from": "dari", @@ -144,8 +167,10 @@ "Info": "kabar", "Inputs": "Masukan", "Insecure password": "Kata sandi tidak aman", + "into": "ke dalam", "Invalid password": "Kata sandi salah", "Issue token amount": "Terbitkan jumlah token", + "It appears that your wallet has too many UTXOs to be able to send this transaction. Please, consolidate (merge) your UTXOs first. This will cost a small fee.": "Tampaknya dompet Anda memiliki terlalu banyak UTXOs untuk dapat mengirimkan transaksi ini. Harap, menggabungkan (merge) UTXOs anda terlebih dahulu. Ini akan memakan sedikit biaya.", "It seems like you made a mistake in the words' order. But don't worry, you can reorder the words by dragging them around.": "Sepertinya Anda membuat kesalahan dalam urutan kata. Tapi jangan khawatir, Anda dapat menyusun ulang kata dengan menyeretnya.", "Label": "Keterangan barang", "Language": "Bahasa", @@ -162,6 +187,8 @@ "Devnet": "Devnet", "Lock": "Kunci", "Lock current wallet": "Kunci dompet saat ini", + "Lock time": "Waktu penguncian", + "Lock time must be in the future.": "Waktu penguncian harus ada di masa mendatang.", "Lock wallet": "Kunci dompet", "Locked": "Terkunci", "Locked ALPH balance": "Saldo ALPH terkunci", @@ -192,12 +219,15 @@ "NFTs": "NFT", "No options match the search criteria.": "Tidak ada opsi yang cocok dengan kriteria pencarian.", "No transactions to display": "Tidak ada transaksi untuk ditampilkan", + "Node host": "Kumpulan node", + "Not your keys, not your coins.": "Bukan kunci-kunci milik anda, bukan koin-koin milik anda.", "Note that if activated, \"{{ address }}\" will not be the default address anymore.": "Perhatikan bahwa jika diaktifkan, \"{{ address }}\" tidak akan menjadi alamat bawaan lagi.", "Off": "Mati", "Optional passphrase": "Frasa sandi opsional", "Outputs": "Keluaran", "Overview": "Ikhtisar", "Passphrases don't match": "frasa sandi tidak cocok", + "passphraseWarningMessage": "<0>Ini adalah fitur lanjutan!
Gunakan hanya jika anda tahu apa yang anda lakukan.
Silakan baca <5>dokumentasi kami untuk mempelajarinya.", "Password": "Kata Sandi", "Password is too weak": "Kata sandi terlalu lemah", "Password requirement": "Persyaratan kata sandi", @@ -208,24 +238,35 @@ "Pick a color": "Pilih warna", "Please choose whether you want to create a new wallet or import an existing one.": "Silakan pilih apakah Anda ingin membuat dompet baru atau mengimpor yang sudah ada.", "Please make sure to have your recovery phrase saved and stored somewhere secure to restore your wallet in the future. Without the recovery phrase, your wallet will be unrecoverable and permanently lost.": "Harap pastikan frasa pemulihan Anda telah disimpan dan sudah disimpan di tempat yang aman untuk memulihkan dompet Anda di masa mendatang. Tanpa frasa pemulihan, dompet Anda tidak akan dapat dipulihkan dan hilang secara permanen.", + "Please, backup your secret phrase instead.": "Tolong, cadangkan frasa rahasia Anda sebagai gantinya.", "Previous year": "Tahun sebelumnya", "Private key copied.": "Kunci pribadi telah disalin.", + "Proxy address": "Alamat Proxy", + "Proxy port": "Port proxy", + "Raw amount": "Jumlah mentah", "Ready!": "Siap!", "Receive": "Terima", "Receive test tokens": "Terima token uji coba", "Receive test tokens in your default address.": "Terima token uji coba di alamat bawaan Anda.", "Received": "Diterima", + "Refresh": "Perbarui", + "Refresh data": "Perbarui data", "Reject": "Tolak", "Remove current wallet": "Hapus dompet saat ini", "Remove wallet \"{{ walletName }}\"": "Hapus dompet \"{{ walletName }}\"", "Require password confirmation before sending each transaction.": "Minta konfirmasi kata sandi sebelum mengirim setiap transaksi.", + "Reset filters": "Atur ulang penyaringannya", "Restart": "Mulai Ulang", + "Restart the application to install the new update.": "Atur ulang aplikasi untuk memasang Pembaruan yang baru.", "Retype password": "Ketik ulang kata sandi", "Review": "Ulasan", "Save": "Simpan", + "Scan the blockchain for addresses you used in the past.": "Pindai blockchain untuk alamat - alamat yang telah anda gunakan sebelumnya.", + "Scan this animated QR code with your mobile wallet.": "Pindai animasi kode QR ini menggunakan dompet seluler Anda.", "Search": "Cari", "Search for label, a hash or an asset...": "Telusuri untuk label, hash, atau aset...", "Search for name or a hash...": "Telusuri untuk nama atau hash...", + "Secret recovery phrase": "Frasa pemulihan rahasia", "Security Check": "Pemeriksaan Keamanan", "See more": "Lihat lebih banyak", "Select": "Pilih", @@ -246,48 +287,67 @@ "Ready?": "Siap?", "Send": "Kirim", "Send locked assets": "Kirim aset yang terkunci", + "Sent": "Berhasil dikirim", + "Set custom network URLs": "Tetapkan URLs jaringan khusus", "Set gas and lock time": "Tetapkan bahan bakar dan kunci waktu", "Set gas settings": "Tetapkan pengaturan bahan bakar", "Settings": "Pengaturan", - "Shortcuts": "Jalan pintas", "Show": "Tampilkan", "Show advanced options": "Tampilkan opsi lanjutan", "Show in explorer": "Tampilkan di penjelajah", "Show more": "Tampilkan lebih banyak", "Show QR code": "Tampilkan kode QR", + "Show your secret recovery phrase": "Tampilkan frasa pemulihan rahasia Anda", "Sign": "Tanda tangan", + "Signer address": "Alamat penandatangan", "Smart contract": "Kontrak pintar", "Smart contracts": "Kontrak pintar", + "Something went wrong when creating encrypted wallet.": "Terjadi kesalahan saat membuat dompet yang terenkripsi.", "Start": "Mulai", + "Status": "Status", "Submit": "Kirim", "Swapped": "Ditukar", "Sweep": "Sapu", "Sweep address": "Sapu alamat", "Sweep all the unlocked funds of this address to another address.": "Sapu semua dana yang tidak dikunci dari alamat ini ke alamat lain.", + "sweepOperationFromTo": "Operasi ini akan mehapus semua dana simpanan dari <1>{{ from }} dan memindahkannya ke <3>{{ to }}.", "System": "Sistem", + "Tab navigation": "Navigasi Tab", "Tell us!": "Beri tahu kami!", + "Testnet": "Testnet", "Token faucet": "Faucet Token Uji", "Test tokens incoming.": "Token uji masuk.", "The current network ({{ currentNetwork }}) does not match the network requested by WalletConnect ({{ walletConnectNetwork }})": "Jaringan saat ini ({{ currentNetwork }}) tidak cocok dengan jaringan yang diminta oleh WalletConnect ({{ walletConnectNetwork }})", "The group number will be automatically be appended to the addresses’ label.": "Nomor grup akan ditambahkan secara otomatis ke label alamat.", "The group of the selected address ({{ addressGroup }}) does not match the group required by WalletConnect ({{ walletConnectGroup }})": "Grup alamat yang telah dipilih ({{ addressGroup }}) tidak cocok dengan grup yang diperlukan oleh WalletConnect ({{ walletConnectGroup }})", + "The wallet is offline.": "Dompet sedang luring.", "Theme": "Tema", "There are no addresses with available balance. Please, send some funds to one of your addresses, and try again.": "Tidak ada alamat dengan saldo yang tersedia. Tolong, kirimkan sejumlah dana ke salah satu alamat Anda, dan coba lagi.", "There are no available addresses.": "Tidak ada alamat yang tersedia.", + "This address is not valid": "Alamat ini tidak sah", "This asset cannot have more than {{ decimals }} decimals": "Aset ini tidak boleh memiliki lebih dari {{ decimals }} desimal", "This field is required": "Kolom ini wajib diisi", + "This is a feature for developers only.": "Ini adalah sebuah fitur untuk para pengembang saja.", "This year": "Tahun ini", "Time period": "Jangka waktu", "Timestamp": "Catatan Waktu", "to": "kepada", "To": "Kepada", "To address": "ke alamat", + "To remove this address from being the default address, you must set another one as default first.": "Untuk menghapus alamat ini dari alamat utama, Anda harus menetapkan alamat lain sebagai alamat utama terlebih dahulu.", + "Toggle discreet mode (hide amounts).": "Beralih ke mode rahasia (sembunyikan jumlah nominal).", "Tokens": "Token", - "Total value": "Nilai Total", + "Tokens to issue (optional)": "Token - token untuk diterbitkan (yang dapat dipilih)", + "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Terlalu banyak rantai-rantai di dalam pengajuan WalletConnect, diharapkan 1, mendapat {{ num }}", "Transaction details": "Detail transaksi", + "Transaction sent!": "Transaksi telah terkirim!", "Transactions": "Transaksi", - "Transfers": "Transfer", + "Transactions sent!": "Transaksi-transaksi telah terkirim!", "Type your password to change this setting.": "Ketik kata sandi Anda untuk mengubah pengaturan ini.", + "Type your password to export your wallet.": "Ketik kata sandi Anda untuk memindahkan dompet Anda.", + "Type your password to show the phrase.": "Ketik kata sandi Anda untuk menampilkan frasa.", + "Type your recovery phrase": "Ketik frasa pemulihan Anda", + "No metadata": "Tidak terdapat metadata", "Unknown": "Tidak dikenal", "unknownTokensKey_one": "{{ count }} Token tidak dikenal", "unknownTokensKey_other": "{{ count }} Token tidak dikenal", @@ -299,11 +359,15 @@ "Unlocked at": "Tidak terkunci pada", "Unlocks at": "Buka kunci di", "Unselect all": "Batalkan semua pilihan", + "Update": "Pembaruan", "Use an existing wallet": "Gunakan dompet yang sudah ada", "Use max amount": "Gunakan jumlah maksimum", + "Use optional passphrase": "Gunakan pilihan frasa sandi", "Useful for miners or DeFi use.": "Berguna untuk penambang atau penggunaan DeFi.", - "Value today": "Nilai hari ini", "Version": "Versi", + "Version {{ newVersion }} is available": "Versi {{ newVersion }} tersedia", + "Please, download it and install it to avoid any issues with the wallet.": "Silakan unduh dan pasang untuk menghindari kendala-kendala apa pun dengan dompet.", + "Download finished": "Selesai mengunduh", "Wallet": "Dompet", "Wallet list": "Daftar dompet", "Wallet name": "Nama dompet", @@ -318,15 +382,22 @@ "welcomeScreenPassphraseMessage": "Jika Anda ingin menggunakan <1>frasa sandi, kunci dompet yang baru Anda buat.", "with": "dengan", "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Anda saat ini terhubung ke jaringan {{ currentNetwork }}. Pastikan untuk terhubung ke jaringan testnet atau devnet untuk melihat token Anda.", - "You have great ideas you want to share?": "Anda memiliki ide hebat yang ingin Anda bagikan?", + "You can disable this confirmation step from the wallet settings.": "Anda dapat menonaktifkan langkah konfirmasi ini dari pengaturan dompet anda.", + "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Anda dapat mengunduh alamat pada riwayat transaksi untuk jangka waktu yang dipilih. Ini akan berguna untuk pelaporan pajak.", + "Do you have great ideas you want to share?": "Anda memiliki ide hebat yang ingin Anda bagikan?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Anda telah mencapai batas panggilan maksimum. Silakan coba lagi dalam beberapa menit nanti.", "You will not be able to recover your account with the private key!": "Anda tidak akan dapat memulihkan akun Anda dengan kunci pribadi!", "Your address": "Alamat Anda", "Your addresses": "Alamat Milik Anda", + "Your CSV file has been generated successfully.": "Berkas CSV Anda telah berhasil dibuat.", + "Your CSV file is being compiled in the background.": "Berkas CSV Anda sedang dikompilasi di latar belakang.", "Your Wallet": "Dompet Anda", "{{ amount }} words entered": "{{ amount }} kata-kata yang dimasukkan", "{{ number }} selected": "{{ number }} telah dipilih", + "Disclaimer": "Penyanggahan", "The wallet is developed in English.
Translations in other languages are provided by the Alephium community.
In case of doubt, always refer to the English version.": "Dompet dikembangkan dalam bahasa Inggris.
Terjemahan dalam bahasa lain disediakan oleh komunitas Alephium.
Jika ragu, selalu merujuk ke dalam versi bahasa Inggris.", + "{{ amount }} ALPH are added for UTXO spam prevention. Click here to know more.": "{{ amount }} ALPH telah ditambahkan untuk mencegah spam UTXO. Klik di sini untuk mengetahui lebih lanjut.", + "Address unknown tokens": "Alamat token-token yang tidak diketahui", "Experimental": "Eksperimental", "Public key": "Kunci publik", "Private key": "Kunci pribadi", @@ -336,17 +407,26 @@ "Copy the keys of an address.": "Salin kunci alamat.", "Transaction hash": "Hash transaksi", "Copy hash": "Salin hash", + "Could not find chain requirements in WalletConnect proposal": "Tidak dapat menemukan persyaratan - persyaratan \n rantai didalam usulan WalletConnect", "Decline": "Tolak", "Switch network": "Ganti jaringan", "Generate new address": "Hasilkan alamat baru", + "Connect with address": "Hubungkan dengan alamat", + "Select an address to connect with.": "Pilih alamat untuk dihubungkan dengan.", "Accept": "Terima", + "walletConnectProposalMessage": "Hubungkan ke <1>{{ dAppUrl }} dengan salah satu Alamat-alamat Anda:", + "walletConnectProposalMessageWithGroup": "Hubungkan ke <1>{{ dAppUrl }} dengan salah satu alamat -alamat Anda di grup <1>{{ group }}:", "Active dApp connections": "Koneksi dApp yang aktif", + "Connect to {{ dAppUrl }}": "Hubungkan ke {{ dAppUrl }}", "The address which will receive the assets.": "Alamat yang akan menerima aset.", "Select the address to send assets to.": "Pilih alamat tujuan untuk pengiriman aset.", "Destination": "Tujuan", "Origin": "Asal", - "One of your addresses to send the assets from.": "Salah satu alamat yang Anda gunakan untuk mengirim aset.", + "Clear cache": "Hapus cache", "Clear": "Hapus", + "Deletes cached wallet and WalletConnect data.": "Hapus cache dompet dan data WalletConnect.", + "App data cleared successfully.": "Data aplikasi telah berhasil dihapus.", + "Could not clear app data.": "Tidak dapat menghapus data aplikasi.", "WalletConnect cache cleared successfully.": "Cache WalletConnect berhasil dihapus.", "Could not clear WalletConnect cache.": "Tidak dapat menghapus cache WalletConnect.", "Sign Unsigned Transaction": "Menandatangani Transaksi yang Belum Ditandatangani", @@ -362,10 +442,70 @@ "Could not sign unsigned tx": "Tidak dapat menandatangani tx yang tidak ditandatangani", "Could not sign message": "Tidak dapat menandatangani pesan", "NFT details": "Rincian NFT", + "Image URL": "URL Gambar", "Attributes": "Atribut", "Collection": "Koleksi", "ID": "Pengenal", "Copy ID": "salin ID", "To delete this wallet use it without a passphrase": "Untuk menghapus dompet ini, gunakan tanpa kata sandi", - "To export this wallet use it without a passphrase": "Untuk mengekspor dompet ini, gunakan tanpa kata sandi" + "To export this wallet use it without a passphrase": "Untuk mengekspor dompet ini, gunakan tanpa kata sandi", + "The proposal does not include a list of required chains": "Permohonan tidak termasuk dalam daftar rantai-rantai yang diperlukan", + "All UTXOs are already consolidated for this address. No consolidation is needed.": "Semua UTXOs sudah terkonsolidasi untuk alamat ini. Konsolidasi tidak diperlukan.", + "No addresses match the search criteria.": "Tidak ada alamat - alamat yang cocok dengan kriteria pencarian.", + "walletConnectSwitchNetwork": "Saat ini Anda telah terhubung ke <1>{{ currentNetworkName }}, namun dApp memerlukan koneksi ke <3>{{ network }}.", + "walletConnectNewAddress": "Dapp meminta sebuah alamat di grup <1>{{ currentNetworkName }}. Klik di bawah untuk membuatnya!", + "ALPH only": "Hanya ALPH", + "This address doesn't have any tokens yet.": "Alamat ini belum memiliki token - token apa pun.", + "This address doesn't have any NFTs yet.": "Alamat ini belum memiliki NFTs apa pun.", + "This address doesn't have any transactions yet.": "Alamat ini belum memiliki transaksi apa pun.", + "This wallet doesn't have any transactions yet.": "Dompet ini belum memiliki transaksi apa pun.", + "The wallet doesn't have any tokens. Tokens of all your addresses will appear here.": "Dompet belum memiliki token apa pun. Token - token dari semua alamat - alamat anda akan muncul di sini.", + "The wallet doesn't have any NFTs. NFTs of all your addresses will appear here.": "Dompet belum memiliki NFTs apa pun. NFTs dari semua alamat - alamat anda akan muncul di sini.", + "There is something wrong in the received WalletConnect data.": "Terdapat kesalahan pada data WalletConnect yang diterima.", + "To forget this address set another one as the default one first.": "Untuk mengabaikan alamat ini, setel alamat lain sebagai alamat utama terlebih dahulu.", + "Click to display new transactions": "Ketuk untuk menampilkan transaksi - transaksi terbaru", + "Transaction was sent...": "Transaksi telah terkirim...", + "Transaction is about to be included in the blockchain...": "Transaksi akan dimasukkan ke dalam blockchain...", + "Transaction is now part of the blockchain": "Transaksi saat ini telah menjadi bagian dari blockchain", + "Forget": "Lupakan", + "forgetAddress_one": "Lupakan alamat", + "forgetAddress_other": "Lupakan alamat-alamat", + "Declutter your wallet by removing this address from your lists.": "Berikan ruang pada dompet anda dengan menghapus alamat ini dari daftar anda.", + "Declutter your wallet by removing addresses you don't need.": "Berikan ruang pada dompet anda dengan menghapus alamat - alamat yang tidak anda perlukan.", + "Forgetting addresses does not delete your assets in them.": "Mengabaikan alamat - alamat tidak akan menghapus aset anda di dalamnya.", + "If you choose to forget an address with assets, you can always re-add it to your wallet using the \"Discover active addresses\" feature.": "Jika anda memilih untuk mengabaikan alamat bersama aset - aset didalamnya, anda selalu dapat menambahkannya kembali ke dompet anda menggunakan fitur \"Temukan alamat - alamat aktif\".", + "This will remove {{ num }} address(es) from your address list.": "Ini akan menghapus {{ num }} alamat(es) dari daftar alamat Anda.", + "Forget selected addresses": "Abaikan alamat - alamat yang dipilih", + "You only have one address. You cannot forget it.": "Anda hanya memiliki satu alamat. Anda tidak bisa mengabaikannya.", + "Total worth": "Total harta", + "Could not load NFT metadata": "Gagal untuk memuat metadata NFT", + "Region": "Kawasan daerah", + "Choose your region to update formats of dates, time, and currencies.": "Pilih kawasan daerah anda untuk memperbarui format - format pada tanggal, waktu, dan jenis mata uang.", + "Video thumbnail": "Pratinjau video", + "Unsupported media type": "Jenis media tidak mendukung", + "You need to be on testnet or devnet in order to use the faucet.": "Anda harus berada di testnet atau devnet untuk menggunakan faucet.", + "Could not get latest data": "Gagal memperoleh data terbaru", + "Updating...": "Memperbarui...", + "To send funds you first need to load your wallet with some.": "Untuk melakukan pengiriman dana, pertama-tama anda perlu mengisi dompet anda dengan sejumlah dana.", + "The wallet is empty. Use the faucet in the developer tools in the app settings.": "Dompet anda tidak memiliki dana. Gunakan faucet yang terdapat pada alat para pengembang di pengaturan aplikasi.", + "Connect with Ledger": "Terhubung dengan Ledger", + "Connect your Ledger": "Hubungkan Ledger anda", + "Could not generate address": "Gagal membuat alamat", + "Could not generate one address per group": "Gagal menghasilkan satu alamat per grup", + "could_not_save_new_address_one": "Gagal menyimpan alamat baru", + "could_not_save_new_address_other": "Gagal menyimpan alamat - alamat baru", + "Could not connect to Alephium Ledger app": "Gagal terhubung ke aplikasi Alephium Ledger", + "Not supported.": "Tidak mendukung.", + "Signing messages with Ledger is not supported.": "Tidak mendukung penandatanganan pesan - pesan dengan Ledger.", + "Plug in and unlock your Ledger device.": "Menyambungkan dan membuka kunci perangkat Ledger anda.", + "ledgerInstructionsOpenApp": "Buka aplikasi Alephium Ledger. Aplikasi Alephium dapat dipasang melalui <1>Ledger Live.", + "Is your device plugged in and the Alephium app open?": "Apakah perangkat anda telah tersambung dan aplikasi Alephium terbuka?", + "Please, confirm the transaction on your Ledger.": "Harap konfirmasikan transaksi di Buku Besar Anda.", + "Welcome to your Ledger!": "Selamat datang di Ledger Anda!", + "Would you like to scan for active addresses?": "Apakah Anda ingin memindai alamat yang aktif?", + "Scan": "Memindai", + "Active address discovery completed. Addresses added: {{ count }}": "Penemuan alamat aktif selesai. Alamat yang ditambahkan: {{ count }}", + "Loading new transactions...": "Memuat transaksi baru...", + "Sign and Send Unsigned Transaction": "Tandatangani dan Kirim Transaksi yang Belum Ditandatangani", + "Sign and Send": "Tanda tangani dan Kirim" } diff --git a/apps/desktop-wallet/locales/it-IT/translation.json b/apps/desktop-wallet/locales/it-IT/translation.json index 7430330aca..256080f363 100644 --- a/apps/desktop-wallet/locales/it-IT/translation.json +++ b/apps/desktop-wallet/locales/it-IT/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "Indirizzo degli NFT", "Address options": "Opzioni indirizzo", "Address tokens": "Indirizzo dei token", - "Address transactions": "Indirizzo transazioni", "Addresses": "Indirizzi", "Addresses & contacts": "Indirizzi e contatti", "Advanced feature": "Funzionalità avanzate", @@ -281,7 +280,6 @@ "Set gas and lock time": "Impostare il gas e il tempo di blocco", "Set gas settings": "Configurare le impostazioni del gas", "Settings": "Impostazioni", - "Shortcuts": "Scorciatoie", "Show": "Mostra", "Show advanced options": "Mostra opzioni avanzate", "Show in explorer": "Mostra nell'esploratore", @@ -328,12 +326,10 @@ "Tokens": "Gettoni", "Tokens to issue (optional)": "Gettoni da emettere (opzionale)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Troppe catene nella proposta WalletConnect, previsto 1, ottenuto {{ num }}", - "Total value": "Valore totale", "Transaction details": "Dettagli di transazione", "Transaction sent!": "Transazione inviata!", "Transactions": "Transazioni", "Transactions sent!": "Transazioni inviate!", - "Transfers": "Trasferimenti", "Type your password to change this setting.": "Digita la password per modificare questa impostazione.", "Type your password to export your wallet.": "Digita la tua password per esportare il tuo portafoglio.", "Type your password to show the phrase.": "Digita la tua password per mostrare la frase.", @@ -354,7 +350,6 @@ "Use an existing wallet": "Usa un portafoglio esistente", "Use max amount": "Usa l'importo massimo", "Useful for miners or DeFi use.": "Utile per minatori o uso DeFi.", - "Value today": "Valore oggi", "Version": "Versione", "Version {{ newVersion }} is available": "Versione {{ newVersion }} è disponibile", "Please, download it and install it to avoid any issues with the wallet.": "Per favore, scaricalo e installalo per evitare qualsiasi problema con il portafoglio.", @@ -373,7 +368,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Sei attualmente connesso alla rete {{ currentNetwork }}. Assicurati di connetterti alla rete testnet o devnet per vedere i tuoi token.", "You can disable this confirmation step from the wallet settings.": "Puoi disabilitare questo passaggio di conferma dalle impostazioni del portafoglio.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "È possibile scaricare la cronologia delle transazioni degli indirizzi per un periodo di tempo selezionato. Questo può essere utile per la dichiarazione dei redditi.", - "You have great ideas you want to share?": "Hai grandi idee che vuoi condividere?", + "Do you have great ideas you want to share?": "Hai grandi idee che vuoi condividere?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Hai raggiunto il limite massimo di chiamate. Riprova tra qualche minuto.", "You will not be able to recover your account with the private key!": "Non sarai in grado di recuperare il tuo account con la chiave privata!", "Your address": "Il tuo indirizzo", diff --git a/apps/desktop-wallet/locales/nl-NL/translation.json b/apps/desktop-wallet/locales/nl-NL/translation.json index 0967ef424b..fd3b7813b4 100644 --- a/apps/desktop-wallet/locales/nl-NL/translation.json +++ b/apps/desktop-wallet/locales/nl-NL/translation.json @@ -1 +1,291 @@ -{} +{ + "A contact with this address already exists": "Er bestaat al een contactpersoon met dit adres", + "A contact with this name already exists": "Een contactpersoon met deze naam bestaat al", + "Add asset": "Asset toevoegen", + "Address": "Adres", + "Address details": "Adresgegevens", + "Address group": "Adresgroep", + "Address label": "Adresetiket", + "Address NFTs": "Adres NFTs", + "Address options": "Adresopties", + "Address tokens": "Adrestokens", + "Addresses": "Adressen", + "Addresses & contacts": "Adressen & contacten", + "Advanced feature": "Geavanceerde functies", + "Advanced operations": "Geavanceerde acties", + "Advanced options": "Geavanceerde opties", + "Advanced settings": "Geavanceerde instellingen", + "Alephium": "Alephium", + "Alephium doesn't have access to your wallet.\nYou are the only owner.": "Alephium heeft geen toegang tot uw portemonnee. Jij bent de enige eigenaar.", + "Alephium's services": "Alephiums diensten", + "all": "alles", + "All good? Let's continue!": "Alles in orde? Laten we doorgaan!", + "All selected": "Alles geselecteerd", + "All transactions were loaded!": "Alle transacties zijn geladen!", + "All the transactions of this address were loaded!": "Alle transacties van dit adres zijn geladen!", + "All the transactions that match the filtering criteria were loaded!": "Alle transacties die overeenkomen met de filtercriteria zijn geladen!", + "ALPH price: {{ price }}": "ALPH-prijs \"{{ price }}\"", + "Alright! Time to check if you got your words right!": "Oke! Tijd om te kijken of je de juiste woorden hebt gebruikt!", + "Amount": "Bedrag", + "Amount exceeds available balance": "Bedrag overschrijdt het beschikbare saldo", + "Amount must be greater than {{ minAmountInAlph }}": "Het bedrag moet groter zijn dan {{ minAmountInAlph }}", + "Analytics": "Analyses", + "and": "en", + "Anyone with your private keys can steal any assets held in your addresses.": "Iedereen die uw private keys heeft, kan uw bezittingen uit uw wallet stelen.", + "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "Weet u zeker dat u {{ contactName }} wilt verwijderen uit uw contactlijst?", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "Weet u zeker dat u {{ address }} wilt verwijderen uit uw adreslijst?", + "Asset": "Asset", + "Assets": "Assets", + "Available": "Beschikbaar", + "Back": "Terug", + "between": "tussen", + "Browse your transaction history. Execute new transfers easily.": "Blader door uw transactiegeschiedenis. Voer gemakkelijk nieuwe overschrijvingen uit.", + "By default, the address is generated in a random group. You can select the group you want the address to be generated in using the Advanced options.": "Het adres wordt standaard gegenereerd in een willekeurige groep. Via de geavanceerde opties kunt u de groep selecteren waarin u wilt dat het adres gegenereerd wordt.", + "Bytecode": "Bytecode", + "Call contract": "Bel contract", + "Cancel": "Annuleren", + "Carefully note down the words! They are your wallet's secret recovery phrase.": "Noteer de woorden zorgvuldig! Dit is de geheime herstelzin van uw wallet.", + "Change the currency to use to display amounts.": "Verander de valuta om de bedragen weer te geven.", + "Change the wallet language.": "Wijzig de taal van uw wallet.", + "Change wallet name": "Wijzig de naam van uw wallet", + "Check": "Controleer", + "Choose a contact": "Kies een contact", + "Click to deactivate discreet mode": "Klik om de discrete modus uit te schakelen", + "Click to see less": "Klik om minder te zien", + "Click to see more": "Klik om meer te zien", + "Close": "Sluiten", + "Confirm": "Bevestigen", + "Confirm lock time": "Bevestig vergrendeltijd", + "Confirm passphrase": "Bevestig wachtwoordzin", + "CONFIRM REMOVAL": "Bevestig verwijdering", + "Confirmed": "Bevestigd", + "Connect": "Verbinding maken", + "Connect to a dApp": "Verbinding maken met een dApp", + "Connect to dApp": "Verbinding maken met dApp", + "Connect wallet to dApp": "Verbind wallet met dApp", + "Consolidate": "Consolideren", + "Consolidate (merge) your UTXOs into one.": "Consolideren (samenvoegen) van je UTXO's in één.", + "Consolidate UTXOs": "UTXO's consolideren", + "Contact deleted": "Contact verwijderd", + "Contact saved": "Contact opgeslagen", + "Contacts": "Contacten", + "Continue": "Ga verder", + "Copied": "Gekopieerd", + "Copied to clipboard!": "Gekopieerd naar klembord!", + "Copy address": "Adres kopiëren", + "Copy contact address": "Kopieer contactadres", + "Copy private key": "Kopieer persoonlijke sleutel", + "Copy to clipboard": "Kopieer naar klembord", + "Copy to clipboard failed": "Kopiëren naar klembord mislukt", + "Copy token hash": "Kopieer token hash", + "Could not connect to the {{ currentNetwork }} network.": "Kan geen verbinding maken met het {{ currentNetwork }} netwerk.", + "Could not copy private key.": "Kon private key niet kopiëren.", + "Could not delete contact.": "Kon contact niet verwijderen.", + "Could not find a contact with this ID": "Kon geen contact vinden met deze ID", + "Could not find a contact with this name": "Kon geen contact vinden met deze naam", + "Could not pair with WalletConnect": "Kon niet koppelen met WalletConnect", + "Could not save contact.": "Kon contact niet opslaan.", + "Could not save new wallet name.": "Kon nieuwe naam van wallet niet opslaan.", + "Create contacts to avoid mistakes when sending transactions!": "Maak contacten aan om fouten te voorkomen bij het verzenden van transacties!", + "Currency": "Valuta", + "Current network": "Huidig netwerk", + "Current wallet": "Huidige wallet", + "Custom": "Aangepast", + "Custom network settings saved.": "Aangepaste netwerkinstellingen opgeslagen.", + "Custom proxy (SOCKS5)": "Aangepaste proxy (SOCKS5)", + "Dark": "Donker", + "Default address": "Standaard adres", + "Default address for sending transactions.": "Standaard adres voor het verzenden van transacties.", + "Delete": "Verwijderen", + "Deploy contract": "Contract toepassen", + "Description": "Beschrijving", + "Developer tools": "Tools voor ontwikkelaars", + "Direction": "Richtlijn", + "Directions": "Richtlijnen", + "Disconnect": "Loskoppelen", + "Discover active addresses": "Actieve adressen ontdekken", + "Discreet mode": "Discrete modus", + "Download": "Download", + "Download failed": "Download mislukt", + "Try again later.": "Probeer het later opnieuw.", + "Downloading version {{ newVersion }}...": "Download versie {{ newVersion }}...", + "Duration in minutes after which an idle wallet will lock automatically.": "Duur in minuten waarna een inactieve wallet automatisch wordt vergrendeld.", + "Easily organize your addresses and your contacts for a more serene transfer experience.": "Organiseer eenvoudig uw adressen en uw contacten voor een prettigere overdrachtservaring.", + "Edit": "Bewerken", + "Edit contact": "Contact bewerken", + "Enable developer tools": "Ontwikkelaarstools inschakelen", + "Encountered error while calling the faucet.": "Fout opgetreden tijdens het aanroepen van de faucet.", + "Encountered error while exporting your transactions.": "Fout opgetreden bij het exporteren van uw transacties.", + "Encountered error while syncing your addresses' data.": "Fout opgetreden bij het synchroniseren van de gegevens van uw adressen.", + "Enter": "Invoeren", + "Enter password": "Wachtwoord invoeren", + "Enter password for \"{{ walletName }}\"": "Voer wachtwoord in voor \"{{ walletName }}\"", + "Enter your password to copy the private key.": "Voer uw wachtwoord in om de private key te kopiëren.", + "Enter your password to send the transaction.": "Voer uw wachtwoord in om de transactie te verzenden.", + "Error while building transaction": "Fout bij het opbouwen van de transactie", + "Error while importing wallet": "Fout bij het importeren van de wallet", + "Error while sending the transaction": "Fout tijdens het verzenden van de transactie", + "Error while sweeping address {{ from }}": "Fout tijdens het sweepen van adres {{ from }}", + "Everything is ready!": "Alles is gereed!", + "Expand": "Uitbreiden", + "Expected fee": "Verwachte fee", + "Explorer API host": "Explorer API host", + "Explorer URL": "Explorer URL", + "Export": "Exporteer", + "Export address transactions": "Exporteer adrestransacties", + "Export current wallet": "Exporteer huidige wallet", + "Export wallet": "Exporteer wallet", + "Features for developers only": "Functies alleen voor ontwikkelaars", + "Fee": "Fee", + "For mining or DeFi use.": "Voor mining of DeFi-gebruik.", + "from": "van", + "From": "Van", + "From address": "Van adres", + "Gas": "Gas", + "Gas amount": "Gas hoeveelheid", + "Gas amount must be greater than {{ MINIMAL_GAS_AMOUNT }}.": "De hoeveelheid gas moet groter zijn dan {{ MINIMAL_GAS_AMOUNT }}.", + "Gas price": "Gasprijs", + "Gas price must be greater than {{ amount }}": "Gasprijs moet hoger zijn dan {{ amount }}", + "General": "Algemeen", + "Generate": "Genereren", + "Generate one address per group": "Genereer één adres per groep", + "Genesis TX": "Genesis TX", + "Good afternoon.": "Goedemiddag.", + "Good evening.": "Goedenavond.", + "Good morning.": "Goedemorgen.", + "Good night.": "Goedenacht.", + "Great job! Remember to keep those words safe.": "Goed gedaan! Vergeet niet om deze woorden veilig te bewaren.", + "Group": "Groep", + "Help us improve your experience!": "Help ons uw ervaring te verbeteren!", + "Hide advanced options": "Verberg geavanceerde opties", + "Hide empty": "Lege verbergen", + "I have read and understood the documentation": "Ik heb de documentatie gelezen en begrepen", + "I've copied the words, continue": "Ik heb de woorden gekopieerd, ga verder", + "Import or create a wallet": "Importeer of maak een wallet aan", + "Import wallet": "Importeer wallet", + "in": "in", + "Info": "Info", + "Inputs": "Inputs", + "Insecure password": "Onveilig wachtwoord", + "into": "naar", + "Invalid password": "Ongeldig wachtwoord", + "Issue token amount": "Uitgifte tokenbedrag", + "It appears that your wallet has too many UTXOs to be able to send this transaction. Please, consolidate (merge) your UTXOs first. This will cost a small fee.": "Het lijkt erop dat uw wallet te veel UTXO's heeft om deze transactie te kunnen versturen. Consolideer (samenvoegen) alstublieft eerst uw UTXO's. Dit kost een kleine fee.", + "It seems like you made a mistake in the words' order. But don't worry, you can reorder the words by dragging them around.": "Het lijkt erop dat u een fout heeft gemaakt in de volgorde van de woorden. Maar geen zorgen, u kunt de volgorde van de woorden veranderen door ze te verslepen.", + "Label": "Label", + "Language": "Taal", + "Last 12 months": "Afgelopen 12 maanden", + "Last 24h": "Afgelopen 24 uur", + "Last 6 months": "Afgelopen 6 maanden", + "Last activity": "Laatste activiteit", + "Last month": "Afgelopen maand", + "Last used": "Laatst gebruikt", + "Last week": "Vorige week", + "Latest transactions": "Laatste transacties", + "Let's go!": "Daar gaan we!", + "Light": "Licht", + "Devnet": "Devnet", + "Lock": "Vergrendel", + "Lock current wallet": "Vergrendel huidige wallet", + "Lock time": "Vergrendeltijd", + "Lock time must be in the future.": "Vergrendeltijd moet in de toekomst zijn.", + "Lock wallet": "Vergrendel wallet", + "Locked": "Vergrendeld", + "Locked ALPH balance": "ALPH-saldo vergrendeld", + "lockTimeConfirmation": "U hebt ervoor gekozen om de assets te vergrendelen tot <1>{{ datetime }}. Dat is ongeveer <3>{{ inTimeFromNow }} vanaf nu. Weet u zeker dat u de assets tot dan wilt vergrendelen?", + "Mainnet": "Hoofdnet", + "Make sure to always check what is the selected network before sending transactions.": "Zorg ervoor dat u altijd controleert wat het geselecteerde netwerk is voordat u transacties verstuurt.", + "Make sure to keep your password secured as it cannot be changed in the future.": "Zorg ervoor dat uw wachtwoord beveiligd blijft, want het kan in de toekomst niet meer worden gewijzigd.", + "Make sure to store the words in a secure location! They are your wallet's secret recovery phrase.": "Zorg ervoor dat u de woorden op een veilige locatie bewaart! Dit is de geheime herstelzin van uw wallet.", + "Make this your default address": "Maak dit uw standaardadres", + "Mining Rewards": "Beloningen voor mining", + "Minutes": "Minuten", + "More info": "Meer informatie", + "More options": "Meer opties", + "More to come...": "Er volgt meer...", + "Moved": "Verplaatst", + "Name": "Naam", + "Network": "Netwerk", + "Network ID": "Netwerk ID", + "Networks": "Netwerken", + "Never disclose this key.": "Deze sleutel nooit openbaar maken.", + "Never used": "Nooit gebruikt", + "New address": "Nieuw adres", + "New contact": "Nieuw contact", + "New password": "Nieuw wachtwoord", + "New version": "Nieuwe versie", + "New wallet": "Nieuwe wallet", + "New wallet name": "Nieuwe walletnaam", + "NFTs": "NFT's", + "No options match the search criteria.": "Er zijn geen opties die overeenkomen met de zoekcriteria.", + "No transactions to display": "Geen transacties om weer te geven", + "Node host": "Node host", + "Not your keys, not your coins.": "Niet uw sleutels, niet uw munten.", + "Note that if activated, \"{{ address }}\" will not be the default address anymore.": "Houd er rekening mee dat indien geactiveerd, \"{{ address }}\" niet meer het standaardadres is.", + "Off": "Uit", + "Optional passphrase": "Optionele wachtwoordzin", + "Outputs": "Outputs", + "Overview": "Overzicht", + "Passphrases don't match": "Wachtwoordzinnen komen niet overeen", + "passphraseWarningMessage": "<0>Dit is een geavanceerde functie!
Gebruik het alleen als u weet wat u doet.
Lees onze <5>documentatie voor meer informatie.", + "Password": "Wachtwoord", + "Password is too weak": "Wachtwoord is te zwak", + "Password requirement": "Wachtwoordvereisten", + "Passwords are different": "Wachtwoorden verschillen", + "Paste WalletConnect URI copied from the dApp": "Plak WalletConnect URI gekopieerd van de dApp", + "Pending": "In afwachting", + "Period": "Periode", + "Pick a color": "Kies een kleur", + "Please choose whether you want to create a new wallet or import an existing one.": "Kies of u een nieuwe wallet wilt aanmaken of een bestaande wilt importeren.", + "Please make sure to have your recovery phrase saved and stored somewhere secure to restore your wallet in the future. Without the recovery phrase, your wallet will be unrecoverable and permanently lost.": "Zorg ervoor dat uw herstelzin veilig wordt bewaard om uw wallet in de toekomst te kunnen herstellen. Zonder de herstelzin zal uw wallet onherstelbaar en permanent verloren gaan.", + "Please, backup your secret phrase instead.": "Maak alstublieft een back-up van uw geheime zin.", + "Previous year": "Vorig jaar", + "Private key copied.": "Private key gekopieerd.", + "Proxy address": "Proxy-adres", + "Proxy port": "Proxypoort", + "Raw amount": "Ruw bedrag", + "Ready!": "Klaar!", + "Receive": "Ontvang", + "Receive test tokens": "Ontvang test tokens", + "Receive test tokens in your default address.": "Ontvang test tokens in uw standaardadres", + "Received": "Ontvangen", + "Refresh": "Herladen", + "Refresh data": "Gegevens herladen", + "Reject": "Afwijzen", + "Remove current wallet": "Huidige wallet verwijderen", + "Remove wallet \"{{ walletName }}\"": "Verwijder wallet \"{{ walletName }}\"", + "Require password confirmation before sending each transaction.": "Wachtwoordbevestiging vereisen voor het verzenden van elke transactie.", + "Reset filters": "Filters resetten", + "Restart": "Herstarten", + "Restart the application to install the new update.": "Herstart de applicatie om de nieuwe update te installeren.", + "Retype password": "Wachtwoord opnieuw invoeren", + "Review": "Herzien", + "Save": "Opslaan", + "Scan the blockchain for addresses you used in the past.": "Scan de blockchain voor adressen die u in het verleden heeft gebruikt.", + "Scan this animated QR code with your mobile wallet.": "Scan deze geanimeerde QR-code met uw mobiele wallet.", + "Search": "Zoek", + "Search for label, a hash or an asset...": "Zoek naar label, een hash of een asset...", + "Search for name or a hash...": "Zoek naar naam of een hash...", + "Secret recovery phrase": "Geheime herstelzin", + "Security Check": "Beveiligingscontrole", + "See more": "Meer weergeven", + "Select": "Selecteren", + "Select a wallet": "Selecteer een wallet", + "Select address group": "Adresgroep selecteren", + "Select addresses": "Adressen selecteren", + "Select all": "Alles selecteren", + "Select an asset": "Selecteer een asset", + "Select assets": "Selecteer assets", + "Select directions": "Selecteer richtingen", + "Select group": "Selecteer groep", + "Select the address to receive funds to.": "Selecteer het adres om geld naartoe te verplaatsen.", + "Select the address to send funds from.": "Selecteer het adres waarvandaan u geld wilt sturen.", + "Select the address to sweep the funds from.": "Selecteer het adres om de tegoeden vandaan te verplaatsen.", + "Select the address to sweep the funds to.": "Selecteer het adres om de tegoeden naartoe te verplaatsen.", + "Select the theme and please your eyes.": "Selecteer het thema en doe uw ogen een plezier.", + "Select the words in the right order.": "Selecteer de woorden in de juiste volgorde.", + "Ready?": "Klaar?", + "Send": "Verzenden", + "Send locked assets": "Vergrendelde assets verzenden", + "Sent": "Verzonden" +} diff --git a/apps/desktop-wallet/locales/pt-PT/translation.json b/apps/desktop-wallet/locales/pt-PT/translation.json index ff9a19b017..94cb8c2943 100644 --- a/apps/desktop-wallet/locales/pt-PT/translation.json +++ b/apps/desktop-wallet/locales/pt-PT/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "NFTs do endereço", "Address options": "Opções de endereço", "Address tokens": "Tokens do endereço", - "Address transactions": "Transações do endereço", "Addresses": "Endereços", "Addresses & contacts": "Endereços e contatos", "Advanced feature": "Recursos avançados", @@ -22,6 +21,9 @@ "all": "todas", "All good? Let's continue!": "Tudo bem? Vamos continuar!", "All selected": "Todos selecionados", + "All transactions were loaded!": "Todas as transações foram carregadas!", + "All the transactions of this address were loaded!": "Todas as transações deste endereço foram carregadas!", + "All the transactions that match the filtering criteria were loaded!": "Todas as transações que correspondem aos critérios de filtragem foram carregadas!", "ALPH price: {{ price }}": "Preço ALPH {{ price }}", "Alright! Time to check if you got your words right!": "Boa! Está na hora de verificar se as suas palavras estão correctas!", "Amount": "Montante", @@ -31,6 +33,7 @@ "and": "e", "Anyone with your private keys can steal any assets held in your addresses.": "Qualquer pessoa com suas chaves privadas pode roubar quaisquer ativos mantidos em seus endereços.", "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "Tem certeza de que deseja remover \"{{ contactName }}\" da sua lista de contatos?", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "Tem certeza de que deseja remover \"{{ address }}\" da sua lista de contatos?", "Asset": "Ativo", "Assets": "Ativos", "Available": "Disponível", @@ -289,7 +292,6 @@ "Set gas and lock time": "Definir valor de gás e tempo de bloqueio", "Set gas settings": "Definir configurações de gás", "Settings": "Definições", - "Shortcuts": "Atalhos", "Show": "Mostrar", "Show advanced options": "Mostrar opções avançadas", "Show in explorer": "Mostrar no explorador", @@ -337,12 +339,10 @@ "Tokens": "Tokens", "Tokens to issue (optional)": "Tokens a emitir (opcional)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Muitas redes na proposta WalletConnect, esperada 1, obteve {{ num }}", - "Total value": "Valor total", "Transaction details": "Detalhes da transação", "Transaction sent!": "Transação enviada!", "Transactions": "Transações", "Transactions sent!": "Transações enviadas!", - "Transfers": "Transferências", "Type your password to change this setting.": "Digite a sua palavra-passe para mudar esta definição.", "Type your password to export your wallet.": "Digite sua senha para exportar sua carteira.", "Type your password to show the phrase.": "Digite a sua palavra-passe para mostrar a frase.", @@ -364,7 +364,6 @@ "Use max amount": "Usar o montante máximo", "Use optional passphrase": "Utilizar senha opcional", "Useful for miners or DeFi use.": "Útil para os mineradores ou uso DeFi.", - "Value today": "Valor de hoje", "Version": "Versão", "Version {{ newVersion }} is available": "A versão {{ newVersion }} está disponível", "Please, download it and install it to avoid any issues with the wallet.": "Clique em \"Atualizar\" para evitar qualquer problema com a carteira digital.", @@ -385,7 +384,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Você está atualmente conectado à rede {{ currentNetwork }}. Certifique-se de se conectar à rede de testes ou devnet para ver seus tokens.", "You can disable this confirmation step from the wallet settings.": "Você pode desativar esta etapa de confirmação nas configurações da carteira.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Você pode baixar o histórico de transações de endereço para um período de tempo selecionado. Isso pode ser útil para o relatório de impostos.", - "You have great ideas you want to share?": "Tem boas ideias que quer partilhar?", + "Do you have great ideas you want to share?": "Tem boas ideias que quer partilhar?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Você atingiu o limite máximo de chamadas. Por favor, tente novamente em alguns minutos.", "You will not be able to recover your account with the private key!": "Você não poderá recuperar a sua conta com a chave privada!", "Your address": "O seu endereço", @@ -423,8 +422,11 @@ "Select the address to send assets to.": "Selecione o endereço para enviar os ativos.", "Destination": "Destino", "Origin": "Origem", - "One of your addresses to send the assets from.": "Um de seus endereços de onde enviar os ativos.", + "Clear cache": "Limpar cache", "Clear": "Limpar", + "Deletes cached wallet and WalletConnect data.": "Exclui dados da carteira e da WalletConnect.", + "App data cleared successfully.": "Dados do aplicativo apagados com sucesso.", + "Could not clear app data.": "Não foi possível limpar os dados da aplicação.", "WalletConnect cache cleared successfully.": "Cache do WalletConnect foi limpado com sucesso.", "Could not clear WalletConnect cache.": "Não foi possível limpar o cache do WalletConnect.", "Sign Unsigned Transaction": "Assinar transação não assinada", @@ -448,5 +450,59 @@ "To delete this wallet use it without a passphrase": "Para excluir essa carteira, use-a sem uma senha", "To export this wallet use it without a passphrase": "Para exportar esta carteira use-a sem uma frase-senha", "The proposal does not include a list of required chains": "A proposta não inclui uma lista de cadeias necessárias", - "All UTXOs are already consolidated for this address. No consolidation is needed.": "Todas as UTXOs já estão consolidadas para este endereço. Nenhuma consolidação é necessária." + "All UTXOs are already consolidated for this address. No consolidation is needed.": "Todas as UTXOs já estão consolidadas para este endereço. Nenhuma consolidação é necessária.", + "No addresses match the search criteria.": "Nenhum endereço corresponde aos critérios de pesquisa.", + "walletConnectSwitchNetwork": "Você está conectado a <1>{{ currentNetworkName }}, mas o dApp requer uma conexão com <3>{{ network }}.", + "walletConnectNewAddress": "O dApp pede um endereço no grupo <1>{{ currentNetworkName }}. Clique abaixo para gerar um!", + "ALPH only": "Apenas ALPH", + "This address doesn't have any tokens yet.": "Ainda não há tokens neste endereço.", + "This address doesn't have any NFTs yet.": "Este endereço ainda não tem NFTs.", + "This address doesn't have any transactions yet.": "Este endereço ainda não tem nenhuma transação.", + "This wallet doesn't have any transactions yet.": "Esta carteira ainda não tem quaisquer transações.", + "The wallet doesn't have any tokens. Tokens of all your addresses will appear here.": "A carteira não tem tokens. Tokens de todos os seus endereços aparecerão aqui.", + "The wallet doesn't have any NFTs. NFTs of all your addresses will appear here.": "A carteira não tem NFTs. As NFTs de todos os seus endereços aparecerão aqui.", + "There is something wrong in the received WalletConnect data.": "Há algo errado nos dados recebidos do WalletConnect.", + "To forget this address set another one as the default one first.": "Para esquecer esse endereço defina outro como padrão primeiro.", + "Click to display new transactions": "Clique para mostrar novas transações", + "Transaction was sent...": "Transação enviada...", + "Transaction is about to be included in the blockchain...": "A transação está prestes a ser incluída na blockchain...", + "Transaction is now part of the blockchain": "A transação agora faz parte da blockchain", + "Forget": "Esquecer", + "forgetAddress_one": "Esquecer endereço", + "forgetAddress_other": "Esquecer endereços", + "Declutter your wallet by removing this address from your lists.": "Organize sua carteira removendo este endereço de suas listas.", + "Declutter your wallet by removing addresses you don't need.": "Organize sua carteira removendo endereços que você não precisa.", + "Forgetting addresses does not delete your assets in them.": "Esquecer endereços não exclui seus ativos neles.", + "If you choose to forget an address with assets, you can always re-add it to your wallet using the \"Discover active addresses\" feature.": "Se você optar por esquecer um endereço com ativos, você sempre pode readicioná-lo à sua carteira usando o recurso \"Descubra endereços ativos\".", + "This will remove {{ num }} address(es) from your address list.": "Isto irá remover {{ num }} endereço(s) da sua lista de endereços.", + "Forget selected addresses": "Esquecer os endereços selecionados", + "You only have one address. You cannot forget it.": "Você tem apenas um endereço, você não pode esquecê-lo.", + "Total worth": "Valor total", + "Could not load NFT metadata": "Não foi possível carregar os metadados NFT", + "Region": "Região", + "Choose your region to update formats of dates, time, and currencies.": "Escolha a sua região para atualizar os formatos de datas, hora e moedas.", + "Video thumbnail": "Miniatura de vídeo", + "Unsupported media type": "Tipo de mídia não suportado", + "You need to be on testnet or devnet in order to use the faucet.": "Você precisa estar na rede de testes ou devnet para receber tokens.", + "Could not get latest data": "Não foi possível obter os últimos dados", + "Updating...": "Atualizando...", + "To send funds you first need to load your wallet with some.": "Para enviar fundos, você primeiro precisa carregar sua carteira com alguns.", + "The wallet is empty. Use the faucet in the developer tools in the app settings.": "A carteira está vazia. Use o \"faucet\" nas ferramentas de desenvolvedor nas configurações do aplicativo.", + "Connect with Ledger": "Conectar um novo Ledger", + "Connect your Ledger": "Conecte o seu Ledger", + "Could not generate address": "Não foi possível esquecer o endereço", + "Could not generate one address per group": "Não foi possível gerar um endereço por grupo", + "could_not_save_new_address_one": "Não foi possível salvar o novo endereço", + "could_not_save_new_address_other": "Não foi possível salvar novos endereços", + "Could not connect to Alephium Ledger app": "Não foi possível conectar-se ao app Alephium Ledger", + "Not supported.": "Não é suportado.", + "Signing messages with Ledger is not supported.": "A assinatura de mensagens com Ledger não é suportada.", + "Plug in and unlock your Ledger device.": "Conecte e desbloqueie seu dispositivo Ledger.", + "ledgerInstructionsOpenApp": "Abra o app Alephium Ledger. O app Alephium pode ser instalado através de <1>Ledger Live.", + "Is your device plugged in and the Alephium app open?": "Seu dispositivo está conectado e o aplicativo Alephium está aberto?", + "Please, confirm the transaction on your Ledger.": "Por favor, confirme a transação no seu Ledger.", + "Welcome to your Ledger!": "Bem-vindo ao seu Ledger!", + "Would you like to scan for active addresses?": "Você gostaria de verificar endereços ativos?", + "Scan": "Escanear", + "Active address discovery completed. Addresses added: {{ count }}": "Descoberta de endereço ativa concluída. Endereços adicionados: {{ count }}" } diff --git a/apps/desktop-wallet/locales/ru-RU/translation.json b/apps/desktop-wallet/locales/ru-RU/translation.json index 2c65ec3a6c..ea9175bdf1 100644 --- a/apps/desktop-wallet/locales/ru-RU/translation.json +++ b/apps/desktop-wallet/locales/ru-RU/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "Адрес NFTs", "Address options": "Параметры адреса", "Address tokens": "Адрес токенов", - "Address transactions": "Адрес транзакции", "Addresses": "Адреса", "Addresses & contacts": "Адреса и контакты", "Advanced feature": "Расширенная функция", @@ -273,7 +272,6 @@ "Set gas and lock time": "Задать газ и время блокировки", "Set gas settings": "Задать параметры газа", "Settings": "Настройки", - "Shortcuts": "Ярлыки", "Show": "Показать", "Show advanced options": "Показать расширенные параметры", "Show in explorer": "Показать в проводнике", @@ -316,12 +314,10 @@ "Tokens": "Токены", "Tokens to issue (optional)": "Токены для выпуска (необязательно)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Слишком много цепочек в предложении WalletConnect, ожидалось 1, получено {{ num }}", - "Total value": "Итоговое значение", "Transaction details": "Детали транзакции", "Transaction sent!": "Транзакция отправлена!", "Transactions": "Транзакции", "Transactions sent!": "Транзакции отправлены!", - "Transfers": "Переводы", "Type your password to change this setting.": "Введите пароль для изменения этого параметра.", "Type your password to export your wallet.": "Введите пароль для экспорта кошелька.", "Type your password to show the phrase.": "Введите свой пароль для отображения фразы.", @@ -341,7 +337,6 @@ "Use max amount": "Использовать максимальную сумму", "Use optional passphrase": "Использовать дополнительную парольную фразу", "Useful for miners or DeFi use.": "Пригодится для майнеров или для использования в DeFi.", - "Value today": "Стоимость на сегодня", "Version": "Версия", "Version {{ newVersion }} is available": "Доступна версия {{ newVersion }}", "Please, download it and install it to avoid any issues with the wallet.": "Пожалуйста, скачайте и установите ее, чтобы избежать проблем с кошельком.", @@ -361,7 +356,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "В настоящее время вы подключены к сети {{ currentNetwork }}. Обязательно подключитесь к тестовой или devnet сети, чтобы увидеть свои токены.", "You can disable this confirmation step from the wallet settings.": "Вы можете отключить этот этап подтверждения в настройках кошелька.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Вы можете загрузить историю транзакций по адресу за выбранный период времени. Это может быть полезно для налоговой отчетности.", - "You have great ideas you want to share?": "У вас есть отличные идеи, которыми вы хотите поделиться?", + "Do you have great ideas you want to share?": "У вас есть отличные идеи, которыми вы хотите поделиться?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Вы достигли максимального лимита вызовов. Пожалуйста, повторите попытку через несколько минут.", "You will not be able to recover your account with the private key!": "Вы не сможете восстановить свою учетную запись с помощью закрытого ключа!", "Your address": "Ваш адрес", diff --git a/apps/desktop-wallet/locales/th-TH/translation.json b/apps/desktop-wallet/locales/th-TH/translation.json index 0967ef424b..e7dc0dfb3a 100644 --- a/apps/desktop-wallet/locales/th-TH/translation.json +++ b/apps/desktop-wallet/locales/th-TH/translation.json @@ -1 +1,538 @@ -{} +{ + "A contact with this address already exists": "มีผู้ติดต่อที่ใช้ที่อยู่นี้อยู่แล้ว\n", + "A contact with this name already exists": "มีผู้ติดต่อที่ใช้ชื่อนี้อยู่แล้ว", + "Add asset": "เพิ่มสินทรัพย์", + "Address": "ที่อยู่", + "Address details": "รายละเอียดที่อยู่", + "Address group": "กลุ่มที่อยู่", + "Address label": "ป้ายกำกับที่อยู่", + "Address NFTs": "ที่อยู่ NTFs", + "Address options": "ตัวเลือกเกี่ยวกับที่อยู่", + "Address tokens": "โทเค็นที่อยู่", + "Addresses": "ที่อยู่ทั้งหมด", + "Addresses & contacts": "ที่อยู่และผู้ติดต่อ", + "Advanced feature": "คุณสมบัติขั้นสูง", + "Advanced operations": "การดำเนินงานขั้นสูง", + "Advanced options": "ตัวเลือกขั้นสูง", + "Advanced settings": "ตั้งค่าขั้นสูง", + "Alephium": "Alephium", + "Alephium doesn't have access to your wallet.\nYou are the only owner.": "Alephium ไม่สามารถเข้าถึงกระเป๋าเงินของคุณได้ คุณเป็นเจ้าของเพียงคนเดียว", + "Alephium's services": "บริการจาก Alephium", + "all": "ทั้งหมด", + "All good? Let's continue!": "โอเคไหม มาต่อกัน", + "All selected": "เลือกทั้งหมด", + "All transactions were loaded!": "โหลดทุกธุรกรรมแล้ว", + "All the transactions of this address were loaded!": "โหลดทุกธุรกรรมของที่อยู่นี้แล้ว", + "All the transactions that match the filtering criteria were loaded!": "ธุรกรรมทั้งหมดที่ตรงตามเกณฑ์การกรองถูกโหลดแล้ว", + "ALPH price: {{ price }}": "ราคา ALPH: {{ price }}", + "Alright! Time to check if you got your words right!": "ถึงเวลาตรวจสอบว่าคุณเลือกคำถูกหรือไม่", + "Amount": "จำนวน", + "Amount exceeds available balance": "จำนวนเกินยอดคงเหลือที่มีอยู่", + "Amount must be greater than {{ minAmountInAlph }}": "จำนวนต้องมากกว่า {{ minAmountInAlph }}", + "Analytics": "การวิเคราะห์", + "and": "และ", + "Anyone with your private keys can steal any assets held in your addresses.": "ใครก็ตามที่มีคีย์ส่วนตัวของคุณสามารถขโมยสินทรัพย์ใดๆ ที่เก็บไว้ในที่อยู่ได้", + "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "คุณแน่ใจหรือไม่ว่าต้องการลบ \"{{ contactName }}\" ออกจากรายชื่อผู้ติดต่อ", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "คุณแน่ใจหรือไม่ว่าต้องการลบ \"{{ address }}\" ออกจากรายชื่อผู้ติดต่อ", + "Asset": "สินทรัพย์", + "Assets": "สินทรัพย์ทั้งหมด", + "Available": "ว่าง", + "Back": "กลับ", + "between": "ระหว่าง", + "Browse your transaction history. Execute new transfers easily.": "เรียกดูประวัติการทำธุรกรรม ดำเนินการถ่ายโอนใหม่ได้อย่างง่าย ๆ", + "By default, the address is generated in a random group. You can select the group you want the address to be generated in using the Advanced options.": "ตามค่าเริ่มต้น ที่อยู่จะถูกสร้างขึ้นในกลุ่มสุ่ม คุณสามารถเลือกกลุ่มที่คุณต้องการให้สร้างที่อยู่ได้โดยใช้ตัวเลือกขั้นสูง", + "Bytecode": "Bytecode", + "Call contract": "สัญญา \"คอล\" (Call contract)", + "Cancel": "ยกเลิก", + "Carefully note down the words! They are your wallet's secret recovery phrase.": "จดคำศัพท์อย่างระมัดระวัง นั่นเป็นคำวลีกู้คืนความลับ (secret recovery phrase) ของกระเป๋าเงิน", + "Change the currency to use to display amounts.": "เปลี่ยนสกุลเงินที่ใช้แสดงจำนวนเงิน", + "Change the wallet language.": "เปลี่ยนภาษากระเป๋าเงิน", + "Change wallet name": "เปลี่ยนชื่อกระเป๋าเงิน", + "Check": "ตรวจสอบ", + "Choose a contact": "เลือกรายชื่อติดต่อ", + "Click to deactivate discreet mode": "คลิกเพื่อปิดใช้งานโหมดไม่ระบุตัวตน", + "Click to see less": "คลิกเพื่อดูน้อยลง", + "Click to see more": "คลิกเพื่อดูเพิ่มเติม", + "Close": "ปิด", + "Confirm": "ยืนยัน", + "Confirm lock time": "ยืนยันเวลาล็อค", + "Confirm passphrase": "ยืนยันรหัสผ่านพาสเฟรซ", + "CONFIRM REMOVAL": "ยืนยันการลบ", + "Confirmed": "ยืนยันแล้ว", + "Connect": "เชื่อมต่อ", + "Connect to a dApp": "เชื่อมต่อกับ dApp เลือกได้", + "Connect to dApp": "เชื่อมต่อกับ dApp", + "Connect wallet to dApp": "เชื่อมต่อกระเป๋าเงินกับ dApp", + "Consolidate": "รวมข้อมูล", + "Consolidate (merge) your UTXOs into one.": "รวม UTXO ให้เป็นหนึ่งเดียว", + "Consolidate UTXOs": "รวม UTXOs", + "Contact deleted": "ลบผู้ติดต่อแล้ว", + "Contact saved": "บันทึกผู้ติดต่อแล้ว", + "Contacts": "ผู้ติดต่อ", + "Continue": "ไปต่อ", + "Copied": "คัดลอกแล้ว", + "Copied to clipboard!": "คัดลอกไปยังคลิปบอร์ดแล้ว", + "Copy address": "คัดลอกที่อยู่", + "Copy contact address": "คัดลอกที่อยู่ผู้ติดต่อ", + "Copy private key": "คัดลอกคีย์ส่วนตัว", + "Copy to clipboard": "คัดลอกไปยังคลิปบอร์ด", + "Copy to clipboard failed": "คัดลอกไปยังคลิปบอร์ดล้มเหลว", + "Copy token hash": "คัดลอกแฮชโทเค็น (token hash)", + "Could not connect to the {{ currentNetwork }} network.": "ไม่สามารถเชื่อมต่อกับเครือข่าย {{ currentNetwork }} ได้", + "Could not copy private key.": "ไม่สามารถคัดลอกคีย์ส่วนตัวได้ (private key)", + "Could not delete contact.": "ไม่สามารถลบผู้ติดต่อได้", + "Could not find a contact with this ID": "ไม่พบผู้ติดต่อรหัสนี้", + "Could not find a contact with this name": "ไม่พบผู้ติดต่อชื่อนี้", + "Could not pair with WalletConnect": "ไม่สามารถจับคู่กับ WalletConnect ได้", + "Could not save contact.": "ไม่สามารถบันทึกผู้ติดต่อได้", + "Could not save new wallet name.": "ไม่สามารถบันทึกชื่อกระเป๋าเงินได้", + "Create contacts to avoid mistakes when sending transactions!": "สร้างผู้ติดต่อเพื่อหลีกเลี่ยงข้อผิดพลาดในการส่งธุรกรรม", + "Currency": "สกุลเงิน", + "Current network": "เครือข่ายปัจจุบัน", + "Current wallet": "กระเป๋าเงินปัจจุบัน", + "Custom": "กำหนดเอง", + "Custom network settings saved.": "บันทึกการตั้งค่าเครือข่ายแบบกำหนดเองแล้ว", + "Custom proxy (SOCKS5)": "พร็อกซีแบบกำหนดเอง (SOCKS5)", + "Dark": "มืด", + "Default address": "ที่อยู่เริ่มต้น", + "Default address for sending transactions.": "ที่อยู่เริ่มต้นสำหรับการส่งธุรกรรม", + "Delete": "ลบ", + "Deploy contract": "ปรับใช้สัญญา", + "Description": "คำอธิบาย", + "Developer tools": "เครื่องมือสำหรับนักพัฒนา", + "Direction": "ทิศทาง", + "Directions": "ทิศทาง", + "Disconnect": "ยกเลิกการเชื่อมต่อ", + "Discover active addresses": "ค้นหาที่อยู่ที่ใช้งานอยู่", + "Discreet mode": "โหมดไม่ระบุตัวตน", + "Download": "ดาวน์โหลด", + "Download failed": "ดาวน์โหลดล้มเหลว", + "Try again later.": "ลองอีกครั้งในภายหลัง", + "Downloading version {{ newVersion }}...": "กำลังดาวน์โหลดเวอร์ชัน {{ newVersion }}...", + "Duration in minutes after which an idle wallet will lock automatically.": "ระยะเวลาเป็นนาทีหลังจากนั้นกระเป๋าเงินที่ไม่ได้ใช้งานจะถูกล็อคโดยอัตโนมัติ", + "Easily organize your addresses and your contacts for a more serene transfer experience.": "จัดระเบียบที่อยู่และผู้ติดต่อได้อย่างง่ายดายเพื่อประสบการณ์การถ่ายโอนที่ง่ายขึ้น", + "Edit": "แก้ไข", + "Edit contact": "แก้ไขผู้ติดต่อ", + "Enable developer tools": "เปิดใช้งานเครื่องมือสำหรับนักพัฒนา", + "Encountered error while calling the faucet.": "พบข้อผิดพลาดขณะเรียก faucet.", + "Encountered error while exporting your transactions.": "พบข้อผิดพลาดขณะส่งออกธุรกรรม", + "Encountered error while syncing your addresses' data.": "พบข้อผิดพลาดขณะซิงค์ข้อมูลที่อยู่", + "Enter": "เข้าไป", + "Enter password": "ป้อนรหัสผ่าน", + "Enter password for \"{{ walletName }}\"": "ป้อนรหัสผ่านสำหรับ \"{{ walletName }}\"", + "Enter your password to copy the private key.": "ป้อนรหัสผ่านเพื่อคัดลอกคีย์ส่วนตัว", + "Enter your password to send the transaction.": "ป้อนรหัสผ่านเพื่อส่งธุรกรรม", + "Error while building transaction": "เกิดข้อผิดพลาดขณะสร้างธุรกรรม", + "Error while importing wallet": "เกิดข้อผิดพลาดขณะนำเข้ากระเป๋าเงิน", + "Error while sending the transaction": "เกิดข้อผิดพลาดขณะส่งธุรกรรม", + "Error while sweeping address {{ from }}": "เกิดข้อผิดพลาดขณะใช้ยอดเงินคงเหลือ (sweeping) จากที่อยู่ {{ from }}", + "Everything is ready!": "ทุกอย่างพร้อมแล้ว", + "Expand": "ขยาย", + "Expected fee": "ค่าธรรมเนียมที่คาดหวัง", + "Explorer API host": "Explorer API host", + "Explorer URL": "Explorer URL", + "Export": "ส่งออก", + "Export address transactions": "ส่งออกธุรกรรมที่อยู่", + "Export current wallet": "ส่งออกกระเป๋าเงินปัจจุบัน", + "Export wallet": "ส่งออกกระเป๋าเงิน", + "Features for developers only": "คุณสมบัติสำหรับนักพัฒนาเท่านั้น", + "Fee": "ค่าธรรมเนียม", + "For mining or DeFi use.": "สำหรับการขุด หรือ DeFi", + "from": "จาก", + "From": "จาก", + "From address": "จากที่อยู่", + "Gas": "แก๊ส", + "Gas amount": "จำนวนแก๊ส", + "Gas amount must be greater than {{ MINIMAL_GAS_AMOUNT }}.": "จำนวนแก๊สจะต้องมากกว่า {{ MINIMAL_GAS_AMOUNT }}", + "Gas price": "ค่าแก๊ส", + "Gas price must be greater than {{ amount }}": "ค่าแก๊สจะต้องมากกว่า {{ amount }}", + "General": "ทั่วไป", + "Generate": "สร้าง", + "Generate one address per group": "สร้างหนึ่งที่อยู่ต่อกลุ่ม", + "Genesis TX": "Genesis TX", + "Good afternoon.": "สวัสดีตอนบ่าย", + "Good evening.": "สวัสดีตอนเย็น", + "Good morning.": "สวัสดีตอนเช้า", + "Good night.": "ราตรีสวัสดิ์", + "Great job! Remember to keep those words safe.": "เยี่ยมมาก! อย่าลืมเก็บคำศัพท์เหล่านั้นไว้อย่างปลอดภัย", + "Group": "กลุ่ม", + "Help us improve your experience!": "ช่วยเราปรับปรุงประสบการณ์ของคุณให้ดีขึ้น", + "Hide advanced options": "ซ่อนตัวเลือกขั้นสูง", + "Hide empty": "ซ่อนว่างเปล่า", + "I have read and understood the documentation": "ฉันได้อ่านและเข้าใจเอกสารแล้ว", + "I've copied the words, continue": "ฉันคัดลอกคำศัพท์แล้ว ต่อไป", + "Import or create a wallet": "นำเข้าหรือสร้างกระเป๋าเงิน", + "Import wallet": "นำเข้ากระเป๋าเงิน", + "in": "ใน", + "Info": "ข้อมูล", + "Inputs": "หน่วยเข้า", + "Insecure password": "รหัสผ่านนี้ไม่ปลอดภัย", + "into": "เข้าไปใน", + "Invalid password": "รหัสผ่านไม่ถูกต้อง", + "Issue token amount": "จำนวนโทเค็นออก", + "It appears that your wallet has too many UTXOs to be able to send this transaction. Please, consolidate (merge) your UTXOs first. This will cost a small fee.": "ดูเหมือนว่ากระเป๋าเงินมี UTXO มากเกินไปที่จะส่งธุรกรรมนี้ได้ กรุณารวม UTXO ก่อนใช้ ซึ่งจะมีค่าธรรมเนียมเล็กน้อย", + "It seems like you made a mistake in the words' order. But don't worry, you can reorder the words by dragging them around.": "ดูเหมือนว่าคุณเรียงลำดับคำศัพท์ผิด แต่อย่ากังวล คุณสามารถเรียงลำดับคำใหม่ได้โดยการลากไปรอบๆ", + "Label": "ป้ายกำกับ", + "Language": "ภาษา", + "Last 12 months": "12 เดือนที่ผ่านมา", + "Last 24h": "24 ชม. ที่ผ่านมา", + "Last 6 months": "6 เดือนที่ผ่านมา", + "Last activity": "กิจกรรมล่าสุด", + "Last month": "เดือนที่แล้ว", + "Last used": "ใช้งานล่าสุด", + "Last week": "อาทิตย์ที่แล้ว", + "Latest transactions": "การทำธุรกรรมล่าสุด", + "Let's go!": "ไปกันเลย!", + "Light": "สว่าง", + "Devnet": "Devnet", + "Lock": "ล็อก", + "Lock current wallet": "ล็อคกระเป๋าเงินปัจจุบัน", + "Lock time": "เวลาล็อค", + "Lock time must be in the future.": "เวลาล็อคจะต้องเป็นเวลาในอนาคต", + "Lock wallet": "ล็อคกระเป๋าเงิน", + "Locked": "ถูกล็อก", + "Locked ALPH balance": "ล็อคยอดคงเหลือ ALPH", + "lockTimeConfirmation": "คุณเลือกที่จะล็อคสินทรัพย์จนถึง <1>{{ datetime }} นั่นก็คือประมาณ <3>{{ inTimeFromNow }} จากตอนนี้ คุณแน่ใจหรือไม่ว่าต้องการล็อคสินทรัพย์จนกว่าจะถึงเวลานั้น?", + "Mainnet": "Mainnet", + "Make sure to always check what is the selected network before sending transactions.": "ตรวจสอบให้แน่ใจว่าได้ตรวจสอบเครือข่ายที่เลือกไว้เสมอก่อนที่จะส่งธุรกรรม", + "Make sure to keep your password secured as it cannot be changed in the future.": "ตรวจสอบให้แน่ใจว่ารหัสผ่านปลอดภัยเนื่องจากไม่สามารถเปลี่ยนแปลงได้ในอนาคต", + "Make sure to store the words in a secure location! They are your wallet's secret recovery phrase.": "อย่าลืมเก็บคำศัพท์ไว้ในที่ปลอดภัย! คำเหล่านี้เป็นวลีกู้คืนความลับ (secret recovery phrase) ของกระเป๋าเงิน", + "Make this your default address": "ทำให้ที่อยู่นี่เป็นที่อยู่เริ่มต้น", + "Mining Rewards": "รางวัลการขุด", + "Minutes": "นาที", + "More info": "ข้อมูลเพิ่มเติม", + "More options": "ตัวเลือกเพิ่มเติม", + "More to come...": "มากขึ้นที่จะมา ...", + "Moved": "ย้ายแล้ว", + "Name": "ชื่อ", + "Network": "เครือข่าย", + "Network ID": "รหัสเครือข่าย", + "Networks": "เครือข่ายทั้งหมด", + "Never disclose this key.": "อย่าเปิดเผยคีย์นี้", + "Never used": "ไม่เคยใช้", + "New address": "ที่อยู่ใหม่", + "New contact": "เพิ่มชื่อใหม่", + "New password": "เพิ่มรหัสผ่านใหม่", + "New version": "รุ่นใหม่", + "New wallet": "สร้างกระเป๋าเงินใหม่", + "New wallet name": "ชื่อกระเป๋าเงินใหม่", + "NFTs": "NFTs", + "No options match the search criteria.": "ไม่มีตัวเลือกที่ตรงกับเกณฑ์การค้นหา", + "No transactions to display": "ไม่มีธุรกรรมที่จะแสดง", + "Node host": "Node Host", + "Not your keys, not your coins.": "\" ไม่ใช่คีย์ของคุณ ไม่ใช่เหรียญของคุณ \"", + "Note that if activated, \"{{ address }}\" will not be the default address anymore.": "โปรดทราบว่าเปิดใช้งานแล้ว \"{{ address }}\" จะไม่ใช่ที่อยู่เริ่มต้นอีกต่อไป", + "Off": "ปิด", + "Optional passphrase": "รหัสผ่านพาสเฟรซไม่จำเป็น", + "Outputs": "หน่วยออก", + "Overview": "ภาพรวม", + "Passphrases don't match": "รหัสผ่านพาสเฟรซไม่ถูกต้อง", + "passphraseWarningMessage": "<0>นี่เป็นคุณสมบัติขั้นสูง!
ใช้เฉพาะเมื่อคุณรู้ว่าคุณกำลังทำอะไรอยู่
กรุณาอ่าน<5>เอกสารของเรา<5>เพื่อเรียนรู้เกี่ยวกับเรื่องนี้", + "Password": "รหัสผ่าน", + "Password is too weak": "รหัสผ่านอ่อนเกินไป", + "Password requirement": "ข้อกำหนดรหัสผ่าน", + "Passwords are different": "รหัสผ่านแตกต่างกัน", + "Paste WalletConnect URI copied from the dApp": "วาง WalletConnect URI ที่คัดลอกจาก dApp", + "Pending": "รอดำเนินการ", + "Period": "ระยะเวลา", + "Pick a color": "เลือกสี", + "Please choose whether you want to create a new wallet or import an existing one.": "กรุณาเลือกสร้างกระเป๋าเงินหรือนำเข้ากระเป๋าเงินที่มีอยู่", + "Please make sure to have your recovery phrase saved and stored somewhere secure to restore your wallet in the future. Without the recovery phrase, your wallet will be unrecoverable and permanently lost.": "โปรดตรวจสอบให้แน่ใจว่าคุณได้บันทึกวลีกู้คืนความลับ (secret recovery phrase) และจัดเก็บไว้ที่ปลอดภัยเพื่อกู้คืนกระเป๋าเงินในอนาคต หากไม่มีวลีกู้คืนความลับ กระเป๋าเงินของคุณจะไม่สามารถกู้คืนได้และสูญหายไปอย่างถาวร", + "Please, backup your secret phrase instead.": "โปรดเก็บวลีกู้คืนความลับ (secret recovery phrase) ไว้เป็นความลับ", + "Previous year": "ปีที่แล้ว", + "Private key copied.": "คัดลอกคีย์ส่วนตัวแล้ว", + "Proxy address": "ที่อยู่พร็อกซี", + "Proxy port": "พอร์ตพร็อกซี", + "Raw amount": "จำนวนดิบ", + "Ready!": "พร้อม!", + "Receive": "รับ", + "Receive test tokens": "รับโทเค็นทดสอบ", + "Receive test tokens in your default address.": "รับโทเค็นทดสอบในที่อยู่เริ่มต้น", + "Received": "ได้​รับ​แล้ว", + "Refresh": "รีเฟรช", + "Refresh data": "รีเฟรชข้อมูล", + "Reject": "ปฏิเสธ", + "Remove current wallet": "ลบกระเป๋าเงินปัจจุบัน", + "Remove wallet \"{{ walletName }}\"": "ลบกระเป๋าเงิน \"{{ walletName }}\"", + "Require password confirmation before sending each transaction.": "ต้องมีการยืนยันรหัสผ่านก่อนส่งธุรกรรมแต่ละครั้ง", + "Reset filters": "รีเซ็ตตัวกรอง", + "Restart": "เริ่มใหม่", + "Restart the application to install the new update.": "รีสตาร์ทแอปเพื่อติดตั้งการอัปเดตใหม่", + "Retype password": "พิมพ์รหัสผ่านอีกครั้ง", + "Review": "ตรวจสอบ", + "Save": "บันทึก", + "Scan the blockchain for addresses you used in the past.": "สแกนบล็อคเชนเพื่อหาที่อยู่ที่เคยใช้", + "Scan this animated QR code with your mobile wallet.": "สแกนรหัส QR แบบเคลื่อนไหวนี้ด้วยกระเป๋าเงินมือถือ", + "Search": "ค้นหา", + "Search for label, a hash or an asset...": "ค้นหาป้ายกำกับ แฮช หรือสินทรัพย์...", + "Search for name or a hash...": "ค้นหาชื่อหรือแฮช", + "Secret recovery phrase": "วลีกู้คืนความลับ (secret recovery phrase) ", + "Security Check": "ตรวจสอบการป้องกัน", + "See more": "ดูเพิ่มเติม", + "Select": "เลือก", + "Select a wallet": "เลือกกระเป๋าเงิน", + "Select address group": "เลือกกลุ่มที่อยู่", + "Select addresses": "เลือกที่อยู่", + "Select all": "เลือกทั้งหมด", + "Select an asset": "เลือกสินทรัพย์", + "Select assets": "เลือกสินทรัพย์", + "Select directions": "เลือกเส้นทางธุรกรรม", + "Select group": "เลือก​กลุ่ม", + "Select the address to receive funds to.": "เลือกที่อยู่เพื่อรับเงิน", + "Select the address to send funds from.": "เลือกที่อยู่สำหรับส่งเงิน", + "Select the address to sweep the funds from.": "เลือกที่อยู่สำหรับใช้ยอดเงินคงเหลือ (sweep) จาก", + "Select the address to sweep the funds to.": "เลือกที่อยู่เพื่อส่งใช้ยอดเงินคงเหลือ (sweep)", + "Select the theme and please your eyes.": "เลือกธีมและทำให้ตาพอใจ", + "Select the words in the right order.": "เลือกคำตามลำดับที่ถูกต้อง", + "Ready?": "พร้อมหรือยัง", + "Send": "ส่ง", + "Send locked assets": "ส่งสินทรัพย์ที่ถูกล็อค", + "Sent": "ส่งแล้ว", + "Set custom network URLs": "ตั้งค่าเครือข่าย URLs เอง", + "Set gas and lock time": "ตั้งค่าแก๊สและเวลาล็อค", + "Set gas settings": "ตั้งค่าแก๊ส", + "Settings": "การตั้งค่า", + "Show": "แสดง", + "Show advanced options": "แสดงตัวเลือกขั้นสูง", + "Show in explorer": "ดูในการสำรวจ (Explorer)", + "Show more": "ดูเพิ่มเติม", + "Show QR code": "แสดงรหัส QR", + "Show your secret recovery phrase": "แสดงวลีกู้คืนความลับ (secret recovery phrase) ", + "Sign": "ลงชื่อ", + "Signer address": "ที่อยู่ของผู้ลงชื่อ", + "Smart contract": "สัญญาอัจฉริยะ", + "Smart contracts": "สัญญาอัจฉริยะ", + "Something went wrong when creating encrypted wallet.": "เกิดข้อผิดพลาดขณะสร้างกระเป๋าเงินที่เข้ารหัส", + "Start": "เริ่ม", + "Status": "สถานะ", + "Submit": "ส่ง", + "Swapped": "สลับแล้ว", + "Sweep": "ใช้ยอดเงินคงเหลือ (sweep)", + "Sweep address": "ที่อยู่ใช้ยอดเงินคงเหลือ (sweep)", + "Sweep all the unlocked funds of this address to another address.": "ใช้ยอดเงินคงเหลือ (sweep) ปลดล็อคเงินจากที่อยู่นี้ไปยังที่อยู่อื่น", + "sweepOperationFromTo": "การดำเนินการนี้จะใช้ยอดเงินคงเหลือ (sweep) เงินทุนทั้งหมดจาก<1>{{ from }} และส่งออกไปยัง<3>{{ to }}.", + "System": "ระบบ", + "Tab navigation": "แถบทิศทาง", + "Tell us!": "บอกพวกเรา!", + "Testnet": "เครือข่าย Testnet", + "Token faucet": "โทเค็นทดลอง (faucet)", + "Test tokens incoming.": "โทเค็น Test กำลังเข้ามา", + "The current network ({{ currentNetwork }}) does not match the network requested by WalletConnect ({{ walletConnectNetwork }})": "เครือข่ายปัจจุบัน ({{ currentNetwork }}) ไม่ตรงกับเครือข่ายที่ร้องขอโดย WalletConnect ({{ walletConnectNetwork }})", + "The group number will be automatically be appended to the addresses’ label.": "หมายเลขกลุ่มจะถูกเพิ่มเข้ากับป้ายกำกับที่อยู่โดยอัตโนมัติ", + "The group of the selected address ({{ addressGroup }}) does not match the group required by WalletConnect ({{ walletConnectGroup }})": "กลุ่มของที่อยู่ที่เลือก ({{ addressGroup }}) ไม่ตรงกับกลุ่มที่ WalletConnect ต้องการ ({{ walletConnectGroup }})", + "The wallet is offline.": "กระเป๋าเงินออฟไลน์อยู่", + "Theme": "ธีม", + "There are no addresses with available balance. Please, send some funds to one of your addresses, and try again.": "ไม่มีที่อยู่ที่มียอดคงเหลือเพียงพอ กรุณาส่งเงินไปยังที่อยู่และลองอีกครั้ง", + "There are no available addresses.": "ไม่มีที่อยู่ที่ใช้ได้", + "This address is not valid": "ที่อยู่นี้ไม่ถูกต้อง", + "This asset cannot have more than {{ decimals }} decimals": "สินทรัพย์นี้ไม่สามารถมีทศนิยมมากกว่า {{ decimals }} หลักได้", + "This field is required": "ต้องระบุข้อมูลในช่องนี้", + "This is a feature for developers only.": "นี่เป็นคุณสมบัติสำหรับนักพัฒนาเท่านั้น", + "This year": "ปีนี้", + "Time period": "ระยะเวลา", + "Timestamp": "ประทับเวลา", + "to": "ถึง", + "To": "จนถึง", + "To address": "ไปยังที่อยู่", + "To remove this address from being the default address, you must set another one as default first.": "หากต้องการลบที่อยู่นี้ออกจากการเป็นที่อยู่เริ่มต้น คุณต้องตั้งค่าที่อยู่อื่นเป็นค่าเริ่มต้นก่อน", + "Toggle discreet mode (hide amounts).": "สลับโหมดไม่ระบุตัวตน (ซ่อนจำนวน)", + "Tokens": "โทเค็น", + "Tokens to issue (optional)": "โทเค็นที่จะออก (ทางเลือก)", + "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "มีเชนมากเกินไปในข้อเสนอ WalletConnect คาดว่า 1 ได้รับ {{ num }}", + "Transaction details": "รายละเอียดของธุรกรรม", + "Transaction sent!": "ธุรกรรมส่งแล้ว", + "Transactions": "ธุรกรรม", + "Transactions sent!": "ธุรกรรมส่งแล้ว", + "Type your password to change this setting.": "พิมพ์รหัสผ่านเพื่อเปลี่ยนการตั้งค่านี้", + "Type your password to export your wallet.": "พิมพ์รหัสผ่านเพื่อส่งกระเป๋าเงินออก", + "Type your password to show the phrase.": "พิมพ์รหัสผ่านเพื่อแสดงวลี", + "Type your recovery phrase": "พิมพ์วลีกู้คืนความลับ (secret recovery phrase) ", + "No metadata": "ไม่มีข้อมูลเมตา", + "Unknown": "ไม่รู้จัก", + "unknownTokensKey_one": "{{ count }} โทเค็นที่ไม่รู้จัก", + "unknownTokensKey_other": "{{ count }} โทเค็นที่ไม่รู้จัก", + "Unknown tokens": "ไม่รู้จักโทเค็นนี้", + "Unknown wallet name": "ไม่รู้จักชื่อกระเป๋าเงินนี้", + "Unlock": "ปลดล็อค", + "Unlock a wallet to continue.": "ปลดล็อกกระเป๋าเงินเพื่อดำเนินการต่อ", + "Unlock your wallet to continue.": "ปลดล็อคกระเป๋าเงินเพื่อดำเนินการต่อ", + "Unlocked at": "ปลดล็อคแล้ว เวลา", + "Unlocks at": "จะปลดล็อคเวลา", + "Unselect all": "ไม่เลือกทั้งหมด", + "Update": "อัปเดต", + "Use an existing wallet": "ใช้กระเป๋าเงินที่มีอยู่", + "Use max amount": "ใช้ทั้งหมด", + "Use optional passphrase": "ใช้รหัสผ่านพาสเฟรซ ไม่จำเป็น", + "Useful for miners or DeFi use.": "ประโยชน์สำหรับนักขุดหรือนักพัฒนา DeFi", + "Version": "รุ่น (เวอร์ชั่น)", + "Version {{ newVersion }} is available": "รุ่น {{ newVersion }} พร้อมแล้ว", + "Please, download it and install it to avoid any issues with the wallet.": "กรุณาดาวน์โหลดและติดตั้งเพื่อหลีกเลี่ยงปัญหาใดๆ กับกระเป๋าเงิน", + "Download finished": "ดาวน์โหลดเสร็จแล้ว", + "Wallet": "กระเป๋าเงิน", + "Wallet list": "รายการกระเป๋าเงิน", + "Wallet name": "ชื่อกระเป๋าเงิน", + "Wallet name already taken": "ชื่อกระเป๋าเงินถูกใช้ไปแล้ว", + "Wallet name is too short": "ชื่อกระเป๋าเงินสั้นเกินไป", + "Wallet name updated to: {{ newWalletName }}": "ชื่อกระเป๋าเงินถูกเปลี่ยนชื่อเป็น {{ newWalletName }}", + "Wallets": "กระเป๋าเงิน", + "Welcome back.": "ยินดีต้อนรับอีกครั้ง", + "Welcome to Alephium.": "ยินดีต้อนรับสู่ Alephium ( อาเลเฟียม )", + "Welcome.": "ยินดีต้อนรับ", + "welcomeScreenOneAddressPerGroupMessage": "ผู้ใช้ขั้นสูง: คุณต้องการเริ่มต้นด้วย <1> หนึ่งที่อยู่ต่อกลุ่มสำหรับการขุดหรือ DeFi หรือไม่ ", + "welcomeScreenPassphraseMessage": "หากคุณต้องการใช้ <1>รหัสผ่านพาสเฟรซ ให้ล็อคกระเป๋าเงินที่คุณเพิ่งสร้างขึ้น", + "with": "กับ", + "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "ขณะนี้คุณเชื่อมต่อกับเครือข่าย {{ currentNetwork }} ตรวจสอบให้แน่ใจว่าได้เชื่อมต่อกับเครือข่าย testnet หรือ devnet เพื่อดูโทเค็น", + "You can disable this confirmation step from the wallet settings.": "คุณสามารถปิดการใช้งานขั้นตอนการยืนยันนี้ได้จากการตั้งค่ากระเป๋าเงิน", + "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "คุณสามารถดาวน์โหลดประวัติการทำธุรกรรมที่อยู่สำหรับช่วงเวลาที่เลือกได้ ซึ่งอาจมีประโยชน์สำหรับการรายงานภาษี", + "Do you have great ideas you want to share?": "คุณมีความคิดดีๆ ที่ต้องการแบ่งปันบ้างไหม", + "You have reached the maximum calls limit. Please try again in a few minutes.": "คุณถึงขีดจำกัดคอลสูงสุดแล้ว โปรดลองอีกครั้งในอีกไม่กี่นาที", + "You will not be able to recover your account with the private key!": "คุณจะไม่สามารถกู้คืนบัญชีด้วยคีย์ส่วนตัวได้", + "Your address": "ที่อยู่ของคุณ", + "Your addresses": "ที่อยู่ของคุณทั้งหมด", + "Your CSV file has been generated successfully.": "สร้างไฟล์ CSV สำเร็จแล้ว", + "Your CSV file is being compiled in the background.": "ไฟล์ CSV กำลังถูกรวบรวมในเบื้องหลัง", + "Your Wallet": "กระเป๋าเงินของคุณ", + "{{ amount }} words entered": "ใส่ {{ amount }} คำแล้ว", + "{{ number }} selected": "{{ number }} เลือก", + "Disclaimer": "การปฏิเสธความรับผิดชอบ", + "The wallet is developed in English.
Translations in other languages are provided by the Alephium community.
In case of doubt, always refer to the English version.": "กระเป๋าเงินได้รับการพัฒนาเป็นภาษาอังกฤษ
ชุมชน Alephium เป็นผู้จัดหาคำแปลเป็นภาษาอื่น
ในกรณีที่มีข้อสงสัย โปรดดูเวอร์ชันภาษาอังกฤษเสมอ", + "{{ amount }} ALPH are added for UTXO spam prevention. Click here to know more.": "เพิ่ม ALPH {{ amount }} เพื่อป้องกันสแปม UTXO คลิกที่นี่เพื่อดูข้อมูลเพิ่มเติม", + "Address unknown tokens": "ที่อยู่โทเค็นที่ไม่รู้จัก", + "Experimental": "อยู่ในช่วงทดลอง", + "Public key": "คีย์สาธารณะ", + "Private key": "คีย์ส่วนตัว", + "Address keys export": "ส่งออกคีย์ที่อยู่", + "Public key copied.": "คัดลอกคีย์สาธารณะแล้ว", + "Could not copy public key.": "ไม่สามารถคัดลอกคีย์สาธารณะได้", + "Copy the keys of an address.": "คัดลอกคีย์ของที่อยู่", + "Transaction hash": "ธุรกรรมแฮช", + "Copy hash": "คัดลอกแฮช", + "Could not find chain requirements in WalletConnect proposal": "ไม่พบข้อกำหนดของเชนในข้อเสนอ WalletConnect", + "Decline": "ปฏิเสธ", + "Switch network": "เปลี่ยนเครือข่าย", + "Generate new address": "สร้างที่อยู่ใหม่", + "Connect with address": "เชื่อมต่อกับที่อยู่", + "Select an address to connect with.": "เลือกที่อยู่สำหรับเชื่อมต่อ", + "Accept": "ยอมรับ", + "walletConnectProposalMessage": "เชื่อมต่อกับ <1>{{ dAppUrl }} ด้วยที่อยู่หนึ่งในหลายที่อยู่:", + "walletConnectProposalMessageWithGroup": "เชื่อมต่อกับ <1>{{ dAppUrl }} ด้วยที่อยู่หนึ่งในกลุ่ม <1>{{ group }}:", + "Active dApp connections": "การเชื่อมต่อ dApp ที่ใช้งานอยู่", + "Connect to {{ dAppUrl }}": "เชื่อมต่อถึง {{ dAppUrl }}", + "The address which will receive the assets.": "ที่อยู่ซึ่งจะรับสินทรัพย์", + "Select the address to send assets to.": "เลือกที่อยู่ที่ต้องการส่งสินทรัพย์", + "Destination": "ปลายทาง", + "Origin": "ต้นทาง", + "Clear cache": "ล้างแคช", + "Clear": "ล้าง", + "Deletes cached wallet and WalletConnect data.": "ลบแคชในกระเป๋าเงินและข้อมูล WalletConnect", + "App data cleared successfully.": "ล้างข้อมูลแอปเรียบร้อยแล้ว", + "Could not clear app data.": "ไม่สามารถล้างข้อมูลแอปได้", + "WalletConnect cache cleared successfully.": "ล้างแคชใน WalletConnect สำเร็จ", + "Could not clear WalletConnect cache.": "ไม่สามารถล้างแคชใน WalletConnect ได้", + "Sign Unsigned Transaction": "ลงชื่อธุรกรรมที่ยังไม่ได้ลงชื่", + "Transaction ID": "รหัสธุรกรรม", + "Unsigned transaction": "ธุรกรรมที่ยังไม่ได้ลงชื่", + "Sign Message": "ลงชื่อข้อความ", + "Message": "ข้อความ", + "Received dApp request": "ได้รับคำขอ dApp แล้ว", + "dApp operation": "การดำเนินการ dApp", + "Script execution failed": "การดำเนินการสคริปต์ล้มเหลว", + "Hide": "ซ่อน", + "Could not decode unsigned tx": "ไม่สามารถถอดรหัส tx ที่ยังไม่ได้ลงชื่อ", + "Could not sign unsigned tx": "ไม่สามารถลงชื่ใน tx ที่ยังไม่ได้ลลงชื่", + "Could not sign message": "ไม่สามารถลงชื่อข้อความได้", + "NFT details": "รายละเอียด NFT", + "Image URL": "URL รูปภาพ", + "Attributes": "คุณสมบัติ", + "Collection": "คอลเลคชั่น", + "ID": "ID", + "Copy ID": "คัดลอก ID", + "To delete this wallet use it without a passphrase": "หากต้องการลบกระเป๋าเงินนี้ ให้ใช้โดยไม่ต้องมีรหัสผ่านพาสเฟรซ", + "To export this wallet use it without a passphrase": "หากต้องการส่งออกกระเป๋าเงินนี้ ให้ใช้โดยไม่ต้องมีรหัสผ่านพาสเฟรซ", + "The proposal does not include a list of required chains": "ข้อเสนอนี้ไม่ได้รวมรายการเชนที่จำเป็น", + "All UTXOs are already consolidated for this address. No consolidation is needed.": "UTXO ทั้งหมดได้รับการรวมไว้สำหรับที่อยู่นี้แล้ว ไม่จำเป็นต้องรวมไว้", + "No addresses match the search criteria.": "ไม่มีที่อยู่ตรงตามเกณฑ์การค้นหา", + "walletConnectSwitchNetwork": "ขณะนี้คุณเชื่อมต่อกับ <1>{{ currentNetworkName }} แต่ dApp ต้องการการเชื่อมต่อกับ <3>{{ network }}", + "walletConnectNewAddress": "dApp จะขอที่อยู่ในกลุ่ม <1>{{ currentNetworkName }} คลิกด้านล่างเพื่อสร้างที่อยู่", + "ALPH only": "เฉพาะ ALPH", + "This address doesn't have any tokens yet.": "ที่อยู่นี้ยังไม่มีโทเค็น", + "This address doesn't have any NFTs yet.": "ที่อยู่นี้ยังไม่มี NFTs", + "This address doesn't have any transactions yet.": "ที่อยู่นี้ยังไม่มีการธุรกรรม", + "This wallet doesn't have any transactions yet.": "กระเป๋าเงินนี้ยังไม่มีธุรกรรมใดๆ", + "The wallet doesn't have any tokens. Tokens of all your addresses will appear here.": "กระเป๋าเงินไม่มีโทเค็นใดๆ โทเค็นของที่อยู่ทั้งหมดจะปรากฏที่นี่", + "The wallet doesn't have any NFTs. NFTs of all your addresses will appear here.": "กระเป๋าเงินไม่มี NFT ใดๆ NFT ของที่อยู่ทั้งหมดจะปรากฏที่นี่", + "There is something wrong in the received WalletConnect data.": "มีบางอย่างผิดปกติในข้อมูลที่ได้รับจาก WalletConnect", + "To forget this address set another one as the default one first.": "หากต้องการลืมที่อยู่นี้ ให้ตั้งค่าที่อยู่อื่นเป็นค่าเริ่มต้นก่อน", + "Click to display new transactions": "คลิกเพื่อแสดงธุรกรรมใหม่", + "Transaction was sent...": "ธุรกรรมได้รับการส่งแล้ว", + "Transaction is about to be included in the blockchain...": "ธุรกรรมกำลังจะถูกรวมอยู่ในบล็อคเชน...", + "Transaction is now part of the blockchain": "ธุรกรรมตอนนี้เป็นส่วนหนึ่งของบล็อคเชนแล้ว", + "Forget": "ลืม", + "forgetAddress_one": "ลืมที่อยู่", + "forgetAddress_other": "ลืมที่อยู่", + "Declutter your wallet by removing this address from your lists.": "จัดระเบียบกระเป๋าเงินโดยลบที่อยู่นี้ออกจากรายการ", + "Declutter your wallet by removing addresses you don't need.": "จัดระเบียบกระเป๋าเงินโดยลบที่อยู่ที่คุณไม่ต้องการออกไป", + "Forgetting addresses does not delete your assets in them.": "การลืมที่อยู่ไม่ได้ลบสินทรัพย์ในนั้น", + "If you choose to forget an address with assets, you can always re-add it to your wallet using the \"Discover active addresses\" feature.": "หากคุณเลือกที่จะลืมที่อยู่พร้อมสินทรัพย์ คุณสามารถเพิ่มที่อยู่นั้นกลับลงในกระเป๋าเงินได้เสมอโดยใช้ฟีเจอร์ \"ค้นพบที่อยู่ที่ใช้งานอยู่\"", + "This will remove {{ num }} address(es) from your address list.": "การดำเนินการนี้จะลบที่อยู่ {{ num }} รายการออกจากรายการที่อยู่", + "Forget selected addresses": "ลืมที่อยู่ที่เลือก", + "You only have one address. You cannot forget it.": "คุณมีที่อยู่เพียงแห่งเดียว คุณไม่สามารถลืมได้", + "Total worth": "มูลค่ารวม", + "Could not load NFT metadata": "ไม่สามารถโหลดข้อมูลเมตา NFT ได้", + "Region": "ภูมิภาค", + "Choose your region to update formats of dates, time, and currencies.": "เลือกภูมิภาคเพื่ออัปเดตรูปแบบของวันที่ เวลา และสกุลเงิน", + "Video thumbnail": "ขนาดย่อวิดีโอ", + "Unsupported media type": "ชนิดสื่อที่ไม่รองรับ", + "You need to be on testnet or devnet in order to use the faucet.": "คุณต้องอยู่ในเครือข่าย testnet รือเครือข่าย devnet เพื่อใช้งาน faucet ได้", + "Could not get latest data": "ไม่สามารถรับข้อมูลล่าสุดได้", + "Updating...": "กำลังอัปเดต...", + "To send funds you first need to load your wallet with some.": "หากต้องการส่งเงิน คุณต้องโหลดเงินบางส่วนลงในกระเป๋าเงินเสียก่อน", + "The wallet is empty. Use the faucet in the developer tools in the app settings.": "กระเป๋าเงินว่างเปล่า ใช้ faucet ในเครื่องมือสำหรับนักพัฒนาในการตั้งค่าแอป", + "Buy": "ซื้อ", + "onramperDisclaimer": "คุณกำลังเข้าถึงบริการของบุคคลที่ให้บริการโดย <1>Onramper.com ผ่านเบราว์เซอร์ภายในแอป Alephium ไม่ได้ควบคุมบริการของ Onramper ข้อกำหนดและเงื่อนไขของ Onramper จะมีผลบังคับใช้ โปรดอ่านและทำความเข้าใจก่อนดำเนินการต่อ", + "You can now complete your purchase in the dedicated window!": "คุณสามารถดำเนินการสั่งซื้อให้เสร็จสิ้นได้ในหน้าต่างที่กำหนด", + "Token": "โทเค็น", + "Price": "ราคา", + "Worth": "คุณค่า", + "Invalid mnemonic. Please double check your words.": "คำช่วยจำไม่ถูกต้อง กรุณาตรวจสอบคำอีกครั้ง", + "Incorrect word. Please try again.": "คำไม่ถูกต้อง กรุณาลองใหม่อีกครั้ง", + "Select the correct word for word number <1>{{ number }} of 24.": "เลือกคำที่ถูกต้องสำหรับหมายเลขคำ <1>{{ number }} จาก 24", + "Allocation": "การจัดสรร", + "Start by selecting the origin and destination addresses.": "เริ่มต้นด้วยการเลือกที่อยู่ต้นทางและปลายทาง", + "Connect with Ledger": "เชื่อมต่อกับบัญชีแยกประเภท", + "Connect your Ledger": "เชื่อมต่อกับบัญชีแยกประเภท", + "Could not generate address": "ไม่สามารถสร้างที่อยู่ได้", + "Could not generate one address per group": "ไม่สามารถสร้างที่อยู่หนึ่งแห่งต่อกลุ่มได้", + "could_not_save_new_address_one": "ไม่สามารถบันทึกที่อยู่ได้", + "could_not_save_new_address_other": "ไม่สามารถบันทึกที่อยู่ได้", + "Could not connect to Alephium Ledger app": "ไม่สามารถเชื่อมต่อกับแอป Alephium Ledger ได้", + "Not supported.": "ไม่รองรับ", + "Signing messages with Ledger is not supported.": "ไม่รองรับการลงชื่อข้อความด้วย Ledger", + "Plug in and unlock your Ledger device.": "เสียบและปลดล็อคอุปกรณ์ Ledger ของคุณ", + "ledgerInstructionsOpenApp": "เปิดแอป Alephium Ledger สามารถติดตั้งแอป Alephium ได้ผ่าน <1>Ledger Live", + "Is your device plugged in and the Alephium app open?": "อุปกรณ์เสียบอยู่และแอป Alephium เปิดอยู่หรือไม่", + "Please, confirm the transaction on your Ledger.": "โปรดยืนยันการธุรกรรมในบัญชีแยกประเภท", + "Welcome to your Ledger!": "ยินดีต้อนรับสู่บัญชีแยกประเภทของคุณ", + "Would you like to scan for active addresses?": "คุณต้องการสแกนหาที่อยู่ หรือไม่", + "Scan": "ค้นหา", + "Active address discovery completed. Addresses added: {{ count }}": "การค้นหาที่อยู่ใช้งานเสร็จสมบูรณ์แล้ว เพิ่มที่อยู่: {{ count }}", + "Purchase done!": "การซื้อเสร็จแล้ว", + "Wallet worth": "มูลค่ากระเป๋าเงิน", + "Address worth": "มูลค่าที่อยู่", + "Loading new transactions...": "กำลังโหลดธุรกรรมใหม่", + "Sign and Send Unsigned Transaction": "ลงชื่อและส่งธุรกรรมที่ไม่ได้ลงชื่อ", + "Sign and Send": "ลงชื่อและส่ง", + "Unlock a wallet to connect to the dApp.": "ปลดล็อกกระเป๋าเงินเพื่อเชื่อมต่อกับ dApp", + "Activity": "กิจกรรม", + "Passphrase warning": "คำเตือนรหัสผ่านพาสเฟรซ", + "Don't use passphrase": "อย่าใช้รหัสผ่านพาสเฟรซ", + "Token balance": "ยอดคงเหลือโทเค็น", + "Hide asset": "ซ่อนสินทรัพย์", + "nb_of_hidden_assets_one": "สินทรัพย์ที่ซ่อนอยู่ {{ count }}", + "nb_of_hidden_assets_other": "สินทรัพย์ที่ซ่อนอยู่ {{ count }}", + "Close hidden assets list": "ปิดรายการสินทรัพย์ที่ซ่อนอยู่", + "Unhide asset": "ยกเลิกการซ่อนสินทรัพย์", + "No transactions before {{ date }}": "ไม่มีการทำธุรกรรมก่อนวันที่ {{ date }}", + "Sort by": "จัดเรียงตาม", + "ALPH balance": "ยอดคงเหลือ ALPH" +} diff --git a/apps/desktop-wallet/locales/tr-TR/translation.json b/apps/desktop-wallet/locales/tr-TR/translation.json index c85bc987a8..b237a96a35 100644 --- a/apps/desktop-wallet/locales/tr-TR/translation.json +++ b/apps/desktop-wallet/locales/tr-TR/translation.json @@ -180,7 +180,6 @@ "To remove this address from being the default address, you must set another one as default first.": "Bu adresi varsayılan adresten kaldırmak için öncelikle bir başka varsayılan adres belirlemelisiniz.", "Toggle discreet mode (hide amounts).": "Gizli moda geç (miktarları gizle).", "Tokens to issue (optional)": "Yayınlanacak varlıklar (tercihe bağlı)", - "Total value": "Toplam değer", "Transaction details": "İşlem detayları", "Transaction sent!": "İşlem gönderildi!", "Transactions": "İşlemler", @@ -204,7 +203,7 @@ "Welcome to Alephium.": "Alephium'a hoş geldiniz.", "welcomeScreenOneAddressPerGroupMessage": "Gelişmiş kullanıcı: <1>her bir gruba madencilik ya da defi için bir adres ile başlamak ister misiniz?", "welcomeScreenPassphraseMessage": "Eğer <1>kurtarma sözcüklerini kullanmak istiyorsanız, yeni oluşturduğunuz cüzdanı kilitleyin.", - "You have great ideas you want to share?": "Harika fikirleriniz mi var, paylaşmak ister misiniz?", + "Do you have great ideas you want to share?": "Harika fikirleriniz mi var, paylaşmak ister misiniz?", "Your address": "Adresiniz", "Your Wallet": "Cüzdanınız", "{{ amount }} words entered": "{{ amount }} kelimeler girildi" diff --git a/apps/desktop-wallet/locales/vi-VN/translation.json b/apps/desktop-wallet/locales/vi-VN/translation.json index d35a999206..6cde723b05 100644 --- a/apps/desktop-wallet/locales/vi-VN/translation.json +++ b/apps/desktop-wallet/locales/vi-VN/translation.json @@ -4,12 +4,11 @@ "Add asset": "Thêm tài sản", "Address": "Địa chỉ", "Address details": "Thông tin địa chỉ ví", - "Address group": "Địa chỉ nhóm", + "Address group": "Nhóm địa chỉ", "Address label": "Nhãn ví", - "Address NFTs": "Địa chỉ các NFT", + "Address NFTs": "NFT của địa chỉ", "Address options": "Tuỳ chọn địa chỉ", - "Address tokens": "Địa chỉ các token", - "Address transactions": "Địa chỉ các giao dịch", + "Address tokens": "Token của địa chỉ", "Addresses": "Địa chỉ ví", "Addresses & contacts": "Địa chỉ ví & danh bạ", "Advanced feature": "Tính năng nâng cao", @@ -20,10 +19,13 @@ "Alephium doesn't have access to your wallet.\nYou are the only owner.": "Alephium hoàn toàn không có quyền truy cập vào ví của bạn.\nBạn là chủ sở hữu duy nhất.", "Alephium's services": "Các dịch vụ của Alephium", "all": "tất cả", - "All good? Let's continue!": "Mọi thứ đã sẳn sàng? Hãy tiếp tục!", + "All good? Let's continue!": "Mọi thứ đã sẵn sàng? Hãy tiếp tục!", "All selected": "Tất cả đã được chọn", + "All transactions were loaded!": "Tất cả giao dịch đã được cập nhật!", + "All the transactions of this address were loaded!": "Tất cả giao dịch của địa chỉ này đã được cập nhật!", + "All the transactions that match the filtering criteria were loaded!": "Tất cả giao dịch khớp với bộ lọc đã được cập nhật!", "ALPH price: {{ price }}": "Giá ALPH: {{ price }}", - "Alright! Time to check if you got your words right!": "Tuyệt vời! Bây giờ hãy kiểm tra nếu bạn ghi chính xác các cụm từ nhé!", + "Alright! Time to check if you got your words right!": "Được rồi! Đã đến lúc kiểm tra xem bạn có ghi đúng các từ chưa!", "Amount": "Số lượng", "Amount exceeds available balance": "Số lượng vượt quá số dư khả dụng", "Amount must be greater than {{ minAmountInAlph }}": "Số lượng phải lớn hơn {{ minAmountInAlph }}", @@ -31,13 +33,14 @@ "and": "và", "Anyone with your private keys can steal any assets held in your addresses.": "Bất kỳ ai có khoá cá nhân này đều có thể đánh cắp tài sản được lưu trữ trên ví của bạn.", "Are you sure you want to remove \"{{ contactName }}\" from your contact list?": "Bạn có chắc muốn gỡ bỏ \"{{ contactName }}\" từ danh sách danh bạ của bạn?", + "Are you sure you want to remove \"{{ address }}\" from your address list?": "Bạn có chắc muốn gỡ bỏ \"{{ address }}\" từ danh sách danh bạ?", "Asset": "Tài sản", - "Assets": "Các tài sản", + "Assets": "Tài sản", "Available": "Khả dụng", "Back": "Quay lại", "between": "giữa", - "Browse your transaction history. Execute new transfers easily.": "Duyệt qua các lịch sử giao dịch của bạn. Thực hiện các giao dịch một cách dễ dàng.", - "By default, the address is generated in a random group. You can select the group you want the address to be generated in using the Advanced options.": "Địa chỉ ví sẽ được mặc định tạo trong một nhóm ngẫu nhiên. Bạn có thể chọn nhóm mong muốn cho địa chỉ ví ở trong phần Tuỳ chọn nâng cao.", + "Browse your transaction history. Execute new transfers easily.": "Duyệt qua lịch sử giao dịch của bạn. Thực hiện các giao dịch mới một cách dễ dàng.", + "By default, the address is generated in a random group. You can select the group you want the address to be generated in using the Advanced options.": "Địa chỉ ví sẽ được mặc định tạo trong một nhóm ngẫu nhiên. Bạn có thể chọn một nhóm mong muốn cho địa chỉ ví ở trong phần Tuỳ chọn nâng cao.", "Bytecode": "Bytecode", "Call contract": "Gọi hợp đồng", "Cancel": "Huỷ bỏ", @@ -53,7 +56,7 @@ "Close": "Đóng", "Confirm": "Xác nhận", "Confirm lock time": "Xác nhận thời gian khoá", - "Confirm passphrase": "Xác nhận cụm từ khôi phục", + "Confirm passphrase": "Xác nhận passphrase", "CONFIRM REMOVAL": "XÁC NHẬN GỠ BỎ", "Confirmed": "Đã xác nhận", "Connect": "Kết nối", @@ -61,7 +64,7 @@ "Connect to dApp": "Kết nối đến dApp", "Connect wallet to dApp": "Kết nối ví đến dApp", "Consolidate": "Hợp nhất", - "Consolidate (merge) your UTXOs into one.": "Hợp nhất (trộn) các UTXOs của bạn.", + "Consolidate (merge) your UTXOs into one.": "Hợp nhất (trộn) các UTXO của bạn thành một.", "Consolidate UTXOs": "Hợp nhất các UTXO", "Contact deleted": "Danh bạ đã xoá", "Contact saved": "Đã lưu danh bạ", @@ -88,7 +91,7 @@ "Current network": "Mạng hiện tại", "Current wallet": "Ví hiện tại", "Custom": "Tuỳ chỉnh", - "Custom network settings saved.": "Tuỳ chỉnh mạng đã lưu.", + "Custom network settings saved.": "Đã lưu cài đặt tuỳ chỉnh mạng.", "Custom proxy (SOCKS5)": "Tùy chỉnh proxy (SOCKS5)", "Dark": "Tối", "Default address": "Địa chỉ mặc định", @@ -96,7 +99,7 @@ "Delete": "Xoá", "Deploy contract": "Triển khai hợp đồng", "Description": "Mô tả", - "Developer tools": "Công cụ dành cho Nhà phát triển", + "Developer tools": "Công cụ phát triển", "Direction": "Bộ lọc", "Directions": "Bộ lọc", "Disconnect": "Ngắt kết nối", @@ -105,30 +108,31 @@ "Download": "Tải về", "Download failed": "Tải về không thành công", "Try again later.": "Vui lòng thử lại.", - "Duration in minutes after which an idle wallet will lock automatically.": "Thời gian tự động khoá bảo vệ Ví được kích hoạt nếu không có bất kỳ hoạt động nào.", + "Downloading version {{ newVersion }}...": "Đang tải phiên bản {{ newVersion }}...", + "Duration in minutes after which an idle wallet will lock automatically.": "Thời gian tính bằng phút để ví tự động khoá sau khi không sử dụng.", "Easily organize your addresses and your contacts for a more serene transfer experience.": "Dễ dàng sắp xếp các địa chỉ ví và danh bạ của bạn để có trải nghiệm giao dịch thuận tiện hơn.", "Edit": "Sửa", "Edit contact": "Sửa danh bạ", - "Enable developer tools": "Kích hoạt công cụ dành cho Nhà phát triển", + "Enable developer tools": "Kích hoạt công cụ phát triển", "Encountered error while calling the faucet.": "Đã xảy ra lỗi khi yêu cầu faucet.", "Encountered error while exporting your transactions.": "Đã gặp lỗi khi xuất các giao dịch của bạn.", "Encountered error while syncing your addresses' data.": "Đã xảy ra lỗi khi đang đồng bộ dữ liệu địa chỉ ví của bạn.", "Enter": "Đồng ý", "Enter password": "Nhập mật khẩu", "Enter password for \"{{ walletName }}\"": "Nhập mật khẩu cho \"{{ walletName }}\"", - "Enter your password to copy the private key.": "Gõ mật khẩu để sao chép khoá cá nhân.", + "Enter your password to copy the private key.": "Nhập mật khẩu để sao chép khoá cá nhân.", "Enter your password to send the transaction.": "Nhập mật khẩu để thực hiện giao dịch.", - "Error while building transaction": "Đã xãy ra lỗi trong quá trình thực hiện giao dịch", + "Error while building transaction": "Đã xảy ra lỗi trong quá trình tạo giao dịch", "Error while importing wallet": "Đã xảy ra lỗi trong quá trình thêm ví", "Error while sending the transaction": "Đã xảy ra lỗi trong quá trình gửi giao dịch", "Error while sweeping address {{ from }}": "Lỗi khi đang sweep địa chỉ {{ from }}", - "Everything is ready!": "Tất cả đã sẳn sàng!", + "Everything is ready!": "Mọi thứ đã sẵn sàng!", "Expand": "Mở rộng", - "Expected fee": "Phí tuỳ chỉnh", + "Expected fee": "Phí dự kiến", "Explorer API host": "Explorer API host", "Explorer URL": "Explorer URL", "Export": "Xuất", - "Export address transactions": "Xuất các địa chỉ giao dịch", + "Export address transactions": "Xuất các giao dịch của địa chỉ", "Export current wallet": "Xuất ví hiện tại", "Export wallet": "Xuất ví", "Features for developers only": "Các tính năng chỉ dành cho Nhà phát triển", @@ -139,9 +143,9 @@ "From address": "Từ địa chỉ", "Gas": "Gas", "Gas amount": "Số lượng gas", - "Gas amount must be greater than {{ MINIMAL_GAS_AMOUNT }}.": "Số lượng gas phải lớn hơn hoặc bằng {{ MINIMAL_GAS_AMOUNT }}.", + "Gas amount must be greater than {{ MINIMAL_GAS_AMOUNT }}.": "Số lượng gas phải lớn hơn {{ MINIMAL_GAS_AMOUNT }}.", "Gas price": "Giá gas", - "Gas price must be greater than {{ amount }}": "Giá gas phải lớn hơn hoặc bằng {{ amount }}", + "Gas price must be greater than {{ amount }}": "Giá gas phải lớn hơn {{ amount }}", "General": "Tổng quan", "Generate": "Tạo", "Generate one address per group": "Tạo một địa chỉ trên mỗi nhóm", @@ -156,9 +160,9 @@ "Hide advanced options": "Ẩn các tuỳ chọn nâng cao", "Hide empty": "Ẩn địa chỉ trống", "I have read and understood the documentation": "Tôi đã đọc và hiểu về tài liệu", - "I've copied the words, continue": "Tôi đã sao chép các cụm từ khôi phục. Tiếp tục", - "Import or create a wallet": "Thêm hoặc tạo ví", - "Import wallet": "Thêm ví đã có sẳn", + "I've copied the words, continue": "Tôi đã sao chép các từ khôi phục, tiếp tục", + "Import or create a wallet": "Thêm hoặc tạo một ví", + "Import wallet": "Thêm ví", "in": "trong", "Info": "Thông tin", "Inputs": "Nguồn vào", @@ -166,19 +170,19 @@ "into": "vào trong", "Invalid password": "Mật khẩu không chính xác", "Issue token amount": "Phát hành số lượng token", - "It appears that your wallet has too many UTXOs to be able to send this transaction. Please, consolidate (merge) your UTXOs first. This will cost a small fee.": "Ví của bạn có quá nhiều các UTXOs. Để có thể thực hiện được giao dịch, vui lòng hợp nhất (trộn) các UTXO. Thao tác này sẽ cần một ít phí.", - "It seems like you made a mistake in the words' order. But don't worry, you can reorder the words by dragging them around.": "Rất tiếc, thứ tự của các cụm từ khôi phục không chính xác. Hãy thử thay đổi thứ tự các từ bằng cách kéo thả đến vị trí mong muốn.", + "It appears that your wallet has too many UTXOs to be able to send this transaction. Please, consolidate (merge) your UTXOs first. This will cost a small fee.": "Ví của bạn có quá nhiều các UTXO. Để có thể thực hiện được giao dịch, vui lòng hợp nhất (trộn) các UTXO. Thao tác này sẽ cần một ít phí.", + "It seems like you made a mistake in the words' order. But don't worry, you can reorder the words by dragging them around.": "Có vẻ như bạn đã sắp xếp sai thứ tự của các từ. Nhưng đừng lo, bạn có thể kéo và thả các từ để sắp xếp lại chúng.", "Label": "Nhãn", "Language": "Ngôn ngữ", - "Last 12 months": "12 tháng gần nhất", - "Last 24h": "24 giờ gần nhất", - "Last 6 months": "6 tháng gần nhất", + "Last 12 months": "12 tháng trước", + "Last 24h": "24 giờ trước", + "Last 6 months": "6 tháng trước", "Last activity": "Hoạt động gần nhất", "Last month": "Tháng trước", "Last used": "Cập nhật gần nhất", "Last week": "Tuần trước", "Latest transactions": "Giao dịch gần nhất", - "Let's go!": "Bắt đầu!", + "Let's go!": "Bắt đầu thôi!", "Light": "Sáng", "Devnet": "Devnet", "Lock": "Khoá", @@ -186,7 +190,7 @@ "Lock time": "Thời gian khoá", "Lock time must be in the future.": "Thời gian khoá trong tương lai.", "Lock wallet": "Khoá ví", - "Locked": "Khoá", + "Locked": "Đã khóa", "Locked ALPH balance": "Số dư ALPH đã khoá", "lockTimeConfirmation": "Bạn đã chọn khoá các tài sản cho đến <1>{{ datetime }}. Ước tính trong khoảng <3>{{ inTimeFromNow }} bắt đầu từ bây giờ. Bạn có chắc chắn muốn khoá các tài sản đến khi ấy?", "Mainnet": "Mainnet", @@ -199,7 +203,7 @@ "More info": "Thông tin thêm", "More options": "Thêm các tuỳ chỉnh", "More to come...": "Xem thêm...", - "Moved": "Đã di chuyển", + "Moved": "Di chuyển", "Name": "Tên", "Network": "Mạng", "Network ID": "ID mạng", @@ -212,8 +216,8 @@ "New version": "Phiên bản mới", "New wallet": "Tạo ví mới", "New wallet name": "Tên ví mới", - "NFTs": "Những NFT", - "No options match the search criteria.": "Không có tuỳ chọn nào phù hợp với tìm kiếm.", + "NFTs": "NFT", + "No options match the search criteria.": "Không có kết quả nào trùng khớp với tìm kiếm.", "No transactions to display": "Không có giao dịch nào để hiển thị", "Node host": "Node host", "Not your keys, not your coins.": "Not your keys, not your coins.", @@ -222,20 +226,20 @@ "Optional passphrase": "Passphrase tùy chọn", "Outputs": "Nguồn ra", "Overview": "Tổng quan", - "Passphrases don't match": "Cụm từ khôi phục không trùng khớp", + "Passphrases don't match": "Passphrase không trùng khớp", "passphraseWarningMessage": "<0>Đây là một tính năng nâng cao!
Chỉ sử dụng tính năng này nếu bạn hiểu về nó.
Vui lòng tham khảo <5>tài liệu để biết thêm thông tin chi tiết.", "Password": "Mật khẩu", "Password is too weak": "Mật khẩu quá yếu", "Password requirement": "Yêu cầu mật khẩu", - "Passwords are different": "Các mật khẩu phải khác nhau", + "Passwords are different": "Các mật khẩu không trùng khớp", "Paste WalletConnect URI copied from the dApp": "Dán WalletConnect URI đã được sao chép từ dApp", - "Pending": "Đang chờ", + "Pending": "Đang thực hiện", "Period": "Giai đoạn", "Pick a color": "Chọn màu", - "Please choose whether you want to create a new wallet or import an existing one.": "Vui lòng chọn tạo một ví mới hoặc thêm ví đã có sẳn.", + "Please choose whether you want to create a new wallet or import an existing one.": "Vui lòng chọn tạo một ví mới hoặc thêm ví đã có sẵn.", "Please make sure to have your recovery phrase saved and stored somewhere secure to restore your wallet in the future. Without the recovery phrase, your wallet will be unrecoverable and permanently lost.": "Hãy chắc chắn rằng bạn đã lưu trữ cụm từ khôi phục bí mật ở nơi an toàn. Bạn sẽ cần đến các cụm từ khôi phục này trong tương lai. Nếu bạn đánh mất các cụm từ khôi phục này, ví của bạn sẽ không có cách nào khôi phục lại.", "Please, backup your secret phrase instead.": "Xin hãy sao lưu cụm từ khôi phục bí mật.", - "Previous year": "Năm ngoái", + "Previous year": "Năm trước", "Private key copied.": "Đã sao chép khóa cá nhân.", "Proxy address": "Địa chỉ proxy", "Proxy port": "Cổng proxy", @@ -258,7 +262,7 @@ "Review": "Xem lại", "Save": "Lưu lại", "Scan the blockchain for addresses you used in the past.": "Quét trên blockchain để tìm ra các địa chỉ ví mà bạn đã từng sử dụng trước đó.", - "Scan this animated QR code with your mobile wallet.": "Quét mã QR này bằng mobile wallet.", + "Scan this animated QR code with your mobile wallet.": "Quét mã QR này bằng ví mobile.", "Search": "Tìm kiếm", "Search for label, a hash or an asset...": "Tìm theo nhãn, hash hoặc tài sản...", "Search for name or a hash...": "Tìm theo tên hoặc hash...", @@ -267,7 +271,7 @@ "See more": "Xem thêm", "Select": "Chọn", "Select a wallet": "Chọn một ví", - "Select address group": "Chọn địa chỉ nhóm", + "Select address group": "Chọn nhóm cho địa chỉ", "Select addresses": "Chọn các địa chỉ", "Select all": "Chọn tất cả", "Select an asset": "Chọn một tài sản", @@ -276,11 +280,11 @@ "Select group": "Chọn nhóm", "Select the address to receive funds to.": "Chọn địa chỉ ví để nhận giao dịch.", "Select the address to send funds from.": "Chọn địa chỉ ví nguồn để gửi tài sản.", - "Select the address to sweep the funds from.": "Chọn địa chỉ ví cần được sweep tài sản.", - "Select the address to sweep the funds to.": "Chọn địa chỉ ví để nhận tài sản.", + "Select the address to sweep the funds from.": "Chọn địa chỉ cần được dọn tài sản.", + "Select the address to sweep the funds to.": "Chọn địa chỉ để nhận tài sản.", "Select the theme and please your eyes.": "Chọn giao diện hiển thị.", - "Select the words in the right order.": "Chọn các cụm từ khôi phục với thứ tự chính xác.", - "Ready?": "Sẵn sàng?", + "Select the words in the right order.": "Chọn các từ theo trình tự chính xác.", + "Ready?": "Sẵn sàng chứ?", "Send": "Gửi", "Send locked assets": "Gửi các tài sản bị khoá", "Sent": "Đã gửi", @@ -288,7 +292,6 @@ "Set gas and lock time": "Đặt gas và thời gian khoá", "Set gas settings": "Đặt tuỳ chỉnh gas", "Settings": "Cài đặt", - "Shortcuts": "Lối tắt", "Show": "Hiện", "Show advanced options": "Hiện các tuỳ chọn nâng cao", "Show in explorer": "Xem trên explorer", @@ -299,20 +302,20 @@ "Signer address": "Địa chỉ người ký", "Smart contract": "Hợp đồng thông minh", "Smart contracts": "Hợp đồng thông minh", - "Something went wrong when creating encrypted wallet.": "Đã xãy ra lỗi trong quá trình tạo ví mã hoá.", + "Something went wrong when creating encrypted wallet.": "Đã xảy ra lỗi trong quá trình tạo ví mã hoá.", "Start": "Bắt đầu", "Status": "Trạng thái", "Submit": "Gửi", "Swapped": "Đã hoán đổi", "Sweep": "Dọn", - "Sweep address": "Sweep địa chỉ ví", - "Sweep all the unlocked funds of this address to another address.": "Sweep tất cả tài sản khả dụng từ địa chỉ ví này sang địa chỉ ví khác.", - "sweepOperationFromTo": "Thao tác này sẽ sweep toàn bộ tài sản từ <1>{{ from }} và di chuyển đến <3>{{ to }}.", + "Sweep address": "Dọn địa chỉ", + "Sweep all the unlocked funds of this address to another address.": "Dọn tất cả tài sản khả dụng từ địa chỉ này sang địa chỉ khác.", + "sweepOperationFromTo": "Thao tác này sẽ dọn toàn bộ tài sản từ <1>{{ from }} và di chuyển đến <3>{{ to }}.", "System": "Hệ thống", "Tab navigation": "Thẻ điều hướng", "Tell us!": "Hãy liên hệ với chúng tôi!", "Testnet": "Testnet", - "Token faucet": "Vòi token", + "Token faucet": "Token faucet", "Test tokens incoming.": "Token thử nghiệm đang được gửi.", "The current network ({{ currentNetwork }}) does not match the network requested by WalletConnect ({{ walletConnectNetwork }})": "Mạng hiện tại ({{ currentNetwork }}) không trùng khớp với mạng được yêu cầu bởi WalletConnect ({{ walletConnectNetwork }})", "The group number will be automatically be appended to the addresses’ label.": "Số nhóm sẽ tự động được thêm vào nhãn của địa ví.", @@ -333,40 +336,38 @@ "To address": "Đến địa chỉ", "To remove this address from being the default address, you must set another one as default first.": "Bạn cần phải đặt một địa chỉ ví mặc định mới trước khi gỡ nhãn mặc định của địa chỉ ví này.", "Toggle discreet mode (hide amounts).": "Bật chế độ ẩn số dư tài sản.", - "Tokens": "Các token", + "Tokens": "Token", "Tokens to issue (optional)": "Các token để phát hành (tùy chọn)", - "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Có quá nhiều chain trong WalletConnect proposal, lý tưởng là 1, đã nhận {{ num }}", - "Total value": "Tổng giá trị", + "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "Có quá nhiều chain trong WalletConnect proposal, đề xuất 1, đã nhận {{ num }}", "Transaction details": "Chi tiết giao dịch", "Transaction sent!": "Giao dịch đã gửi!", - "Transactions": "Số lượng giao dịch", + "Transactions": "Giao dịch", "Transactions sent!": "Các giao dịch đã gửi!", - "Transfers": "Các giao dịch", "Type your password to change this setting.": "Gõ mật khẩu để thay đổi các tuỳ chọn.", "Type your password to export your wallet.": "Gõ mật khẩu để xuất ví của bạn.", "Type your password to show the phrase.": "Gõ mật khẩu để hiện cụm từ khôi phục.", "Type your recovery phrase": "Gõ cụm từ khôi phục bí mật", - "No metadata": "Không có metadata", + "No metadata": "Không có dữ liệu", "Unknown": "Không xác định", "unknownTokensKey_one": "{{ count }} Token không xác định", "unknownTokensKey_other": "{{ count }} Các token không xác định", - "Unknown tokens": "Các token không xác định", + "Unknown tokens": "Token không xác định", "Unknown wallet name": "Tên ví không xác định", "Unlock": "Mở khoá", - "Unlock a wallet to continue.": "Đăng nhập vào một ví để tiếp tục.", - "Unlock your wallet to continue.": "Đăng nhập vào ví của bạn để tiếp tục.", + "Unlock a wallet to continue.": "Mở khoá một ví để tiếp tục.", + "Unlock your wallet to continue.": "Mở khoá ví của bạn để tiếp tục.", "Unlocked at": "Đã mở khoá vào", "Unlocks at": "Mở khoá vào", "Unselect all": "Bỏ chọn tất cả", "Update": "Cập nhật", - "Use an existing wallet": "Sử dụng ví đã có sẳn", + "Use an existing wallet": "Sử dụng một ví đã có sẵn", "Use max amount": "Sử dụng tất cả số dư", "Use optional passphrase": "Sử dụng passphrase tùy chỉnh", "Useful for miners or DeFi use.": "Hữu ích cho khai thác (đào) hoặc sử dụng DeFi.", - "Value today": "Giá trị hôm nay", "Version": "Phiên bản", "Version {{ newVersion }} is available": "Phiên bản {{ newVersion }} vừa mới được phát hành", "Please, download it and install it to avoid any issues with the wallet.": "Vui lòng tải về và cài đặt để khắc phục các lỗi hiện đang có.", + "Download finished": "Tải xuống hoàn tất", "Wallet": "Ví", "Wallet list": "Danh sách ví", "Wallet name": "Tên ví", @@ -374,20 +375,20 @@ "Wallet name is too short": "Tên ví quá ngắn", "Wallet name updated to: {{ newWalletName }}": "Tên ví đã cập nhật sang: {{ newWalletName }}", "Wallets": "Ví", - "Welcome back.": "Chào mừng bạn quay trở lại.", - "Welcome to Alephium.": "Chào mừng bạn đến với Alephium.", - "Welcome.": "Chào mừng.", - "welcomeScreenOneAddressPerGroupMessage": "Người dùng nâng cao: Bạn muốn bắt đầu với <1>một địa chỉ ví trên nhóm dành cho thợ đào hoặc DeFi?", + "Welcome back.": "Chào mừng bạn trở lại.", + "Welcome to Alephium.": "Chào mừng đến với Alephium.", + "Welcome.": "Xin chào.", + "welcomeScreenOneAddressPerGroupMessage": "Người dùng nâng cao: Bạn muốn bắt đầu với <1>một địa chỉ ví trên mỗi nhóm dành cho thợ đào hoặc DeFi?", "welcomeScreenPassphraseMessage": "Nếu bạn muốn sử dụng một <1>passphrase, khoá ví vừa mới tạo.", - "with": "cùng", + "with": "với", "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "Bạn hiện đang kết nối vào mạng {{ currentNetwork }}. Hãy chắc chắn rằng bạn đã kết nối vào mạng testnet hoặc devnet để nhìn thấy các token.", "You can disable this confirmation step from the wallet settings.": "Bạn có thể tắt bước xác nhận này trong cài đặt ví.", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "Bạn có thể tải về lịch sử các giao dịch của địa chỉ ví trong một khoảng thời gian đã chọn. Tính năng này có thể hữu ích trong báo cáo thuế.", - "You have great ideas you want to share?": "Bạn muốn đóng góp ý tưởng cho chúng tôi?", + "Do you have great ideas you want to share?": "Bạn muốn đóng góp ý tưởng cho chúng tôi?", "You have reached the maximum calls limit. Please try again in a few minutes.": "Bạn đã đạt giới hạn call tối đa. Xin vui lòng thử lại sau ít phút.", "You will not be able to recover your account with the private key!": "Bạn không thể khôi phục tài khoản với khoá cá nhân!", - "Your address": "Địa chỉ ví của bạn", - "Your addresses": "Các địa chỉ ví của bạn", + "Your address": "Địa chỉ của bạn", + "Your addresses": "Địa chỉ của bạn", "Your CSV file has been generated successfully.": "Tệp tin CSV của bạn đã được tạo thành công.", "Your CSV file is being compiled in the background.": "Tệp tin CSV của bạn đang được soạn trong nền.", "Your Wallet": "Ví của bạn", @@ -396,7 +397,7 @@ "Disclaimer": "Lưu ý quan trọng", "The wallet is developed in English.
Translations in other languages are provided by the Alephium community.
In case of doubt, always refer to the English version.": "Ví được phát triển trên ngôn ngữ Tiếng Anh.
Các ngôn ngữ khác được dịch bởi cộng đồng Alephium.
Trong trường hợp bạn không chắc chắn, hãy luôn chọn English.", "{{ amount }} ALPH are added for UTXO spam prevention. Click here to know more.": "{{ amount }} ALPH đã được thêm vào để tránh spam trên UTXO. Nhấn vào đây để tìm hiểu thêm.", - "Address unknown tokens": "Không xác định địa chỉ các token", + "Address unknown tokens": "Token của địa chỉ không xác định", "Experimental": "Thử nghiệm", "Public key": "Khóa công khai", "Private key": "Khóa cá nhân", @@ -406,32 +407,35 @@ "Copy the keys of an address.": "Sao chép các khóa của một địa chỉ ví.", "Transaction hash": "Transaction hash", "Copy hash": "Sao chép hash", - "Could not find chain requirements in WalletConnect proposal": "Không tìm thấy chain trong WalletConnect proposal", + "Could not find chain requirements in WalletConnect proposal": "Không tìm thấy chain trong đề xuất WalletConnect", "Decline": "Từ chối", "Switch network": "Đổi network", - "Generate new address": "Tạo địa chỉ ví mới", - "Connect with address": "Kết nối đến địa chỉ ví", - "Select an address to connect with.": "Chọn một địa chỉ ví để kết nối đến.", + "Generate new address": "Tạo địa chỉ mới", + "Connect with address": "Kết nối với địa chỉ", + "Select an address to connect with.": "Chọn một địa chỉ để kết nối đến.", "Accept": "Chấp nhận", "walletConnectProposalMessage": "Kết nối đến <1>{{ dAppUrl }} với một trong các địa chỉ ví của bạn:", "walletConnectProposalMessageWithGroup": "Kết nối đến <1>{{ dAppUrl }} với một trong các địa chỉ ví của bạn tại nhóm <1>{{ group }}:", "Active dApp connections": "Các kết nối đến dApp đang hoạt động", "Connect to {{ dAppUrl }}": "Kết nối đến {{ dAppUrl }}", "The address which will receive the assets.": "Địa chỉ ví sẽ nhận tài sản.", - "Select the address to send assets to.": "Chọn địa chỉ ví để gửi tài sản đến.", - "Destination": "Người nhận", - "Origin": "Nguồn", - "One of your addresses to send the assets from.": "Một trong các địa chỉ ví của bạn để gửi tài sản đi.", + "Select the address to send assets to.": "Chọn địa chỉ ví để gửi tài sản đi.", + "Destination": "Nơi nhận", + "Origin": "Nơi gửi", + "Clear cache": "Xóa bộ nhớ đệm", "Clear": "Dọn dẹp", - "WalletConnect cache cleared successfully.": "WalletConnect cache đã được dọn dẹp thành công.", - "Could not clear WalletConnect cache.": "Không thể dọn dẹp WalletConnect cache.", - "Sign Unsigned Transaction": "Ký vào Giao dịch Chưa thực thi", + "Deletes cached wallet and WalletConnect data.": "Xoá bộ nhớ đệm ví và dữ liệu WalletConnect.", + "App data cleared successfully.": "Dữ liệu ứng dụng đã được xóa thành công.", + "Could not clear app data.": "Không thể xóa dữ liệu ứng dụng.", + "WalletConnect cache cleared successfully.": "Đã dọn bộ nhớ tạm của WalletConnect.", + "Could not clear WalletConnect cache.": "Không thể dọn bộ nhớ tạm của WalletConnet.", + "Sign Unsigned Transaction": "Ký Giao dịch Chưa thực thi", "Transaction ID": "Transaction ID", "Unsigned transaction": "Giao dịch chưa thực thi", "Sign Message": "Ký Thông điệp", "Message": "Thông điệp", "Received dApp request": "Đã nhận yêu cầu của dApp", - "dApp operation": "Hoạt động của dApp", + "dApp operation": "Hoạt động dApp", "Script execution failed": "Lỗi thực thi script", "Hide": "Ẩn", "Could not decode unsigned tx": "Không thể giải mã giao dịch chưa thực thi", @@ -445,5 +449,90 @@ "Copy ID": "Sao chép ID", "To delete this wallet use it without a passphrase": "Để xóa ví này, sử dụng nó mà không cần một passphrase", "To export this wallet use it without a passphrase": "Để xuất ví này, sử dụng nó mà không cần một passphrase", - "The proposal does not include a list of required chains": "Proposal không bao gồm danh sách các chain được yêu cầu" + "The proposal does not include a list of required chains": "Đề xuất không bao gồm danh sách các chain được yêu cầu", + "All UTXOs are already consolidated for this address. No consolidation is needed.": "Tất cả UTXO của địa chỉ này đã được hợp nhất. Không cần phải hợp nhất thêm.", + "No addresses match the search criteria.": "Không có địa chỉ nào khớp với tiêu chí tìm kiếm.", + "walletConnectSwitchNetwork": "Bạn hiện đang kết nối đến <1>{{ currentNetworkName }}, nhưng dApp yêu cầu kết nối đến <3>{{ network }}.", + "walletConnectNewAddress": "dApp yêu cầu một địa chỉ ở nhóm <1>{{ currentNetworkName }}. Nhấn vào bên dưới để tạo!", + "ALPH only": "chỉ ALPH", + "This address doesn't have any tokens yet.": "Địa chỉ này hiện chưa có token nào.", + "This address doesn't have any NFTs yet.": "Địa chỉ này hiện chưa có NFT nào.", + "This address doesn't have any transactions yet.": "Địa chỉ này hiện chưa có giao dịch nào.", + "This wallet doesn't have any transactions yet.": "Ví này hiện chưa có giao dịch nào.", + "The wallet doesn't have any tokens. Tokens of all your addresses will appear here.": "Ví hiện chưa có token nào. Các token của tất cả các địa chỉ sẽ xuất hiện tại đây.", + "The wallet doesn't have any NFTs. NFTs of all your addresses will appear here.": "Ví hiện chưa có NFT nào. Các NFT của tất cả các địa chỉ sẽ xuất hiện tại đây.", + "There is something wrong in the received WalletConnect data.": "Đã xảy ra lỗi khi nhận dữ liệu WalletConnect.", + "To forget this address set another one as the default one first.": "Để quên địa chỉ này, hãy đặt một địa chỉ khác làm địa chỉ mặc định.", + "Click to display new transactions": "Nhấn để hiển thị các giao dịch mới", + "Transaction was sent...": "Giao dịch đã được gửi...", + "Transaction is about to be included in the blockchain...": "Giao dịch sắp được đưa vào trong blockchain...", + "Transaction is now part of the blockchain": "Giao dịch đã được thêm vào trong blockchain", + "Forget": "Quên", + "forgetAddress_one": "Quên địa chỉ", + "forgetAddress_other": "Quên các địa chỉ", + "Declutter your wallet by removing this address from your lists.": "Dọn dẹp ví của bạn bằng cách xóa địa chỉ này khỏi danh sách.", + "Declutter your wallet by removing addresses you don't need.": "Dọn dẹp ví của bạn bằng cách xóa những địa chỉ không cần thiết.", + "Forgetting addresses does not delete your assets in them.": "Việc quên địa chỉ sẽ không xóa đi các tài sản trong đó.", + "If you choose to forget an address with assets, you can always re-add it to your wallet using the \"Discover active addresses\" feature.": "Nếu bạn chọn quên một địa chỉ có tài sản, bạn luôn có thể thêm lại địa chỉ đó vào ví của mình bằng cách sử dụng tính năng \"Khám phá địa chỉ đang hoạt động\".", + "This will remove {{ num }} address(es) from your address list.": "Điều này sẽ xóa bỏ {{ num }} (các) địa chỉ từ danh sách địa chỉ của bạn.", + "Forget selected addresses": "Quên các địa chỉ đã chọn", + "You only have one address. You cannot forget it.": "Bạn chỉ có một địa chỉ. Bạn không thể quên nó được.", + "Total worth": "Tổng giá trị", + "Could not load NFT metadata": "Không thể tải thông tin NFT", + "Region": "Vùng", + "Choose your region to update formats of dates, time, and currencies.": "Chọn khu vực của bạn để cập nhật định dạng ngày, giờ và tiền tệ.", + "Video thumbnail": "Video thu nhỏ", + "Unsupported media type": "Định dạng media không hỗ trợ", + "You need to be on testnet or devnet in order to use the faucet.": "Bạn cần phải sử dụng testnet hoặc devnet để sử dụng faucet.", + "Could not get latest data": "Không thể tải dữ liệu mới nhất", + "Updating...": "Đang cập nhật...", + "To send funds you first need to load your wallet with some.": "Để gửi tài sản, trước tiên bạn cần nạp một số tiền vào ví.", + "The wallet is empty. Use the faucet in the developer tools in the app settings.": "Ví hiện đang trống. Hãy sử dụng faucet trong công cụ dành cho nhà phát triển tại phần cài đặt ứng dụng.", + "Buy": "Mua", + "onramperDisclaimer": "Bạn sẽ truy cập các dịch vụ của bên thứ ba do <1>onramper.com cung cấp thông qua trình duyệt trong ứng dụng. Alephium không kiểm soát các dịch vụ của Onramper. Các điều khoản và điều kiện của Onramper sẽ được áp dụng, vui lòng đọc kỹ và hiểu về họ trước khi tiếp tục.", + "You can now complete your purchase in the dedicated window!": "Bạn có thể hoàn tất giao dịch trong cửa sổ thanh toán!", + "Token": "Token", + "Price": "Giá", + "Worth": "Giá trị", + "Invalid mnemonic. Please double check your words.": "Mnemonic không hợp lệ. Vui lòng kiểm tra lại các từ của bạn.", + "Incorrect word. Please try again.": "Từ không chính xác. Vui lòng thử lại.", + "Select the correct word for word number <1>{{ number }} of 24.": "Chọn từ đúng cho từ có số thứ tự <1>{{ number }} trong 24.", + "Allocation": "Phân bổ", + "Start by selecting the origin and destination addresses.": "Bắt đầu bằng cách chọn địa chỉ gửi và nhận.", + "Connect with Ledger": "Kết nối với Ledger", + "Connect your Ledger": "Kết nối Ledger của bạn", + "Could not generate address": "Không thể tạo địa chỉ", + "Could not generate one address per group": "Không thể tạo một địa chỉ trên trên mỗi nhóm", + "could_not_save_new_address_one": "Không thể lưu địa chỉ mới", + "could_not_save_new_address_other": "Không thể lưu các địa chỉ mới", + "Could not connect to Alephium Ledger app": "Không thể kết nối đến ứng dụng Alephium Ledger", + "Not supported.": "Chưa hỗ trợ.", + "Signing messages with Ledger is not supported.": "Ký xác nhận thông điệp với Ledger chưa được hỗ trợ.", + "Plug in and unlock your Ledger device.": "Cắm vào và mở khoá thiết bị Ledger của bạn.", + "ledgerInstructionsOpenApp": "Mở ứng dụng Alephium Ledger. Ứng dụng Alephium có thể được cài đặt thông qua <1>Ledger Live.", + "Is your device plugged in and the Alephium app open?": "Có phải thiết bị của bạn đã được cắm vào và ứng dụng Alephium đã được mở?", + "Please, confirm the transaction on your Ledger.": "Vui lòng xác nhận giao dịch trên Ledger của bạn.", + "Welcome to your Ledger!": "Chào mừng đến với Ledger của bạn!", + "Would you like to scan for active addresses?": "Bạn có muốn quét để tìm các địa chỉ đang hoạt động không?", + "Scan": "Quét", + "Active address discovery completed. Addresses added: {{ count }}": "Quá trình tìm kiếm các địa chỉ đang hoạt động đã hoàn tất. Các địa chỉ được thêm vào: {{ count }}", + "Purchase done!": "Mua hoàn tất!", + "Wallet worth": "Giá trị ví", + "Address worth": "Giá trị địa chỉ", + "Loading new transactions...": "Đang tải giao dịch mới...", + "Sign and Send Unsigned Transaction": "Ký và Gửi Giao Dịch Chưa Thực Thi", + "Sign and Send": "Ký và Gửi", + "Unlock a wallet to connect to the dApp.": "Mở khóa một ví để kết nối đến dApp.", + "Activity": "Hoạt động", + "Passphrase warning": "Cảnh báo passphrase", + "Don't use passphrase": "Không sử dụng passphrase", + "Token balance": "Số dư token", + "Hide asset": "Ẩn tài sản", + "nb_of_hidden_assets_one": "{{ count }} tài sản đã ẩn", + "nb_of_hidden_assets_other": "{{ count }} tài sản đã ẩn", + "Close hidden assets list": "Đóng danh sách các tài sản ẩn", + "Unhide asset": "Hiện tài sản", + "No transactions before {{ date }}": "Không có giao dịch nào trước {{ date }}", + "Sort by": "Sắp xếp theo", + "ALPH balance": "Số dư ALPH" } diff --git a/apps/desktop-wallet/locales/zh-CN/translation.json b/apps/desktop-wallet/locales/zh-CN/translation.json index c97b68f9ee..f72545a6b5 100644 --- a/apps/desktop-wallet/locales/zh-CN/translation.json +++ b/apps/desktop-wallet/locales/zh-CN/translation.json @@ -9,7 +9,6 @@ "Address NFTs": "地址 NFT", "Address options": "地址选项", "Address tokens": "地址代币", - "Address transactions": "地址交易", "Addresses": "地址", "Addresses & contacts": "地址&联系人", "Advanced feature": "高级特征", @@ -293,7 +292,6 @@ "Set gas and lock time": "设置Gas和锁定时间", "Set gas settings": "设定Gas设置", "Settings": "设置", - "Shortcuts": "快捷键", "Show": "显示", "Show advanced options": "显示高级选项", "Show in explorer": "在区块浏览器中显示", @@ -341,12 +339,10 @@ "Tokens": "代币", "Tokens to issue (optional)": "发行代币(可选)", "Too many chains in the WalletConnect proposal, expected 1, got {{ num }}": "WalletConnect 提议中的链太多,预期为 1,结果为 {{ num }}", - "Total value": "总额", "Transaction details": "交易详情", "Transaction sent!": "交易已发送!", "Transactions": "交易记录", "Transactions sent!": "交易已发送!", - "Transfers": "转账", "Type your password to change this setting.": "输入您的密码以更改此设置。", "Type your password to export your wallet.": "输入您的密码以导出您的钱包。", "Type your password to show the phrase.": "输入您的密码以显示短语。", @@ -368,7 +364,6 @@ "Use max amount": "使用最大金额", "Use optional passphrase": "使用可选密码", "Useful for miners or DeFi use.": "适用于矿工或使用 DeFi", - "Value today": "今日价值", "Version": "版本", "Version {{ newVersion }} is available": "版本 {{ newVersion }} 已发布", "Please, download it and install it to avoid any issues with the wallet.": "请下载并安装它,以避免钱包出现任何问题。", @@ -389,7 +384,7 @@ "You are currently connected to the {{ currentNetwork }} network. Make sure to connect to the testnet or devnet network to see your tokens.": "您当前连接的是 {{ currentNetwork }} 网络。请确保连接到 testnet 或 devnet 网络,以查看您的代币。", "You can disable this confirmation step from the wallet settings.": "您可以从钱包设置中禁用此确认步骤。", "You can download the address transaction history for a selected time period. This can be useful for tax reporting.": "您可以在选定的时间段下载地址交易历史记录。这可以用于税务报告。", - "You have great ideas you want to share?": "您有很好的想法要分享吗?", + "Do you have great ideas you want to share?": "您有很好的想法要分享吗?", "You have reached the maximum calls limit. Please try again in a few minutes.": "您已达到最大API服务请求限制。请几分钟后再试。", "You will not be able to recover your account with the private key!": "您将无法用私钥恢复您的帐户!", "Your address": "您的地址", @@ -427,7 +422,6 @@ "Select the address to send assets to.": "选择发送资金的地址。", "Destination": "目标地址", "Origin": "来源", - "One of your addresses to send the assets from.": "发送资产的地址。", "Clear cache": "清除缓存", "Clear": "清除", "Deletes cached wallet and WalletConnect data.": "删除缓存的钱包和 WalletConnect 数据。", @@ -473,7 +467,6 @@ "Transaction was sent...": "交易已发送...", "Transaction is about to be included in the blockchain...": "交易将被包含在区块链中...", "Transaction is now part of the blockchain": "交易现在是区块链的一部分", - "Your wallet has too many addresses. These are the transactions of the {{ maxNumber }} most frequently used addresses. Please, consider removing some of them.": "您的钱包有太多地址。这些是 {{ maxNumber }} 个最常用地址的交易。请考虑删除其中一些。", "Forget": "忘记", "forgetAddress_one": "忘记地址", "forgetAddress_other": "忘记地址", diff --git a/apps/desktop-wallet/package.json b/apps/desktop-wallet/package.json index 5062acd335..a4ae807b3d 100644 --- a/apps/desktop-wallet/package.json +++ b/apps/desktop-wallet/package.json @@ -1,7 +1,7 @@ { "name": "alephium-desktop-wallet", "description": "The official Alephium wallet", - "version": "2.5.0", + "version": "2.5.1", "author": "Alephium dev ", "main": "dist-electron/main.js", "homepage": "./", @@ -14,7 +14,7 @@ "build": "tsc && cross-env INLINE_RUNTIME_CHUNK=false VITE_VERSION=$npm_package_version vite build", "start": "turbo start:electron", "start:electron": "cross-env VITE_VERSION=$npm_package_version vite", - "start:debug": "concurrently \"pnpm start:web\" \"npx react-scan@latest localhost:3000\"", + "start:debug": "concurrently \"pnpm start\" \"npx react-scan@0.0.47 localhost:3000\"", "build:electron:windows": "electron-builder -w", "build:electron:macos": "electron-builder --mac --universal", "build:electron:ubuntu": "electron-builder -l", @@ -43,7 +43,6 @@ "dependencies": { "@tanstack/react-query": "5.45.0", "@tanstack/react-query-devtools": "^5.50.1", - "axios": "^1.7.4", "electron-context-menu": "^3.1.2", "electron-is-dev": "^2.0.0", "electron-updater": "^6.3.9" @@ -63,9 +62,9 @@ "@alephium/shared-react": "workspace:*", "@alephium/token-list": "^0.0.19", "@alephium/typescript-config": "workspace:*", - "@alephium/walletconnect-provider": "1.5.1", - "@alephium/web3": "1.5.1", - "@alephium/web3-wallet": "1.5.1", + "@alephium/walletconnect-provider": "1.11.3", + "@alephium/web3": "1.11.3", + "@alephium/web3-wallet": "1.11.3", "@electron/notarize": "^1.2.3", "@json-rpc-tools/utils": "^1.7.6", "@ledgerhq/devices": "^8.4.4", @@ -82,7 +81,6 @@ "@types/react-color": "^3.0.6", "@types/react-dom": "^18.2.4", "@types/semver-compare": "^1.0.1", - "@types/yaireo__tagify": "^4.18.0", "@types/zxcvbn": "^4.4.1", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", @@ -95,9 +93,9 @@ "@walletconnect/sign-client": "2.17.2", "@walletconnect/types": "2.17.2", "@walletconnect/utils": "2.17.2", - "@yaireo/tagify": "^4.18.3", "@yornaath/batshit": "^0.10.1", "ajv": "^8.12.0", + "axios": "^1.7.4", "better-sqlite3": "^8.3.0", "bip39": "^3.0.4", "classnames": "^2.3.1", @@ -108,21 +106,21 @@ "dayjs": "^1.10.7", "electron": "33.2.0", "electron-builder": "^24.13.3", - "electron-devtools-installer": "^3.2.0", + "electron-devtools-installer": "^4.0.0", "eslint": "^8.48.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-header": "^3.1.1", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-unused-imports": "^3.0.0", "events": "^3.3.0", - "framer-motion": "^11.2.12", + "framer-motion": "^11.12.0", "i18next": "^23.4.6", "jsdom": "^21.1.1", "lodash": "^4.17.21", "lucide-react": "^0.287.0", + "msw": "^2.7.0", "nanoid": "^3.3.8", "posthog-js": "^1.52.0", "qrloop": "^1.4.1", @@ -130,8 +128,10 @@ "react-apexcharts": "^1.4.0", "react-color": "^2.19.3", "react-confetti": "^6.0.1", + "react-custom-scroll": "^7.0.0", "react-detect-click-outside": "^1.1.2", "react-dom": "^18.2.0", + "react-freeze": "^1.0.4", "react-hook-form": "^7.42.1", "react-i18next": "^13.2.1", "react-idle-timer": "^5.7.2", @@ -140,7 +140,6 @@ "react-qr-code": "^2.0.7", "react-redux": "^8.0.5", "react-router-dom": "^6.3.0", - "react-scrollbars-custom": "^4.1.1", "react-tooltip": "^5.25.1", "redux": "^4.2.0", "semver-compare": "^1.0.0", diff --git a/apps/desktop-wallet/scripts/syncTranslationFiles.js b/apps/desktop-wallet/scripts/syncTranslationFiles.js index f12ac6e267..2d5b32a710 100755 --- a/apps/desktop-wallet/scripts/syncTranslationFiles.js +++ b/apps/desktop-wallet/scripts/syncTranslationFiles.js @@ -1,23 +1,5 @@ #!/usr/bin/env node -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - const glob = require('glob') const fs = require('fs') diff --git a/apps/desktop-wallet/src/App.tsx b/apps/desktop-wallet/src/App.tsx index ac2c618676..9aed6d83fd 100644 --- a/apps/desktop-wallet/src/App.tsx +++ b/apps/desktop-wallet/src/App.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { localStorageNetworkSettingsMigrated } from '@alephium/shared' import { useInitializeThrottledClient } from '@alephium/shared-react' import { memo, ReactNode, useCallback, useEffect } from 'react' @@ -23,6 +5,7 @@ import styled, { css, ThemeProvider } from 'styled-components' import PersistedQueryCacheVersionStorage from '@/api/persistedCacheVersionStorage' import { usePersistQueryClientContext } from '@/api/persistQueryClientContext' +import AnimatedBackground from '@/components/AnimatedBackground' import AppSpinner from '@/components/AppSpinner' import { CenteredSection } from '@/components/PageComponents/PageContainers' import SnackbarManager from '@/components/SnackbarManager' @@ -30,19 +13,19 @@ import SplashScreen from '@/components/SplashScreen' import useAnalytics from '@/features/analytics/useAnalytics' import useTrackUserSettings from '@/features/analytics/useTrackUserSettings' import AutoUpdateSnackbar from '@/features/autoUpdate/AutoUpdateSnackbar' +import { languageOptions } from '@/features/localization/languages' +import { systemLanguageMatchFailed, systemLanguageMatchSucceeded } from '@/features/localization/localizationActions' +import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions' import { localStorageGeneralSettingsMigrated, - systemLanguageMatchFailed, - systemLanguageMatchSucceeded, systemRegionMatchFailed, systemRegionMatchSucceeded } from '@/features/settings/settingsActions' -import { languageOptions } from '@/features/settings/settingsConstants' -import useRegionOptions from '@/features/settings/useRegionOptions' import { darkTheme, lightTheme } from '@/features/theme/themes' import { WalletConnectContextProvider } from '@/features/walletConnect/walletConnectContext' import { useAppDispatch, useAppSelector } from '@/hooks/redux' import useAutoLock from '@/hooks/useAutoLock' +import useWalletLock from '@/hooks/useWalletLock' import AppModals from '@/modals/AppModals' import Router from '@/routes' import { @@ -73,16 +56,14 @@ const App = memo(() => { return ( - - + - @@ -264,3 +245,11 @@ const AppContainerStyled = styled.div<{ showDevIndication: boolean }>` border: 5px solid ${theme.global.valid}; `}; ` + +const LoginAnimatedBackground = () => { + const { isWalletUnlocked } = useWalletLock() + + if (isWalletUnlocked) return null + + return +} diff --git a/apps/desktop-wallet/src/animations/index.ts b/apps/desktop-wallet/src/animations/index.ts index 18aa864e6b..4ad7ded2b8 100644 --- a/apps/desktop-wallet/src/animations/index.ts +++ b/apps/desktop-wallet/src/animations/index.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - const transition = { duration: 0.3 } export const fadeIn = { @@ -35,21 +17,37 @@ export const fadeInOut = { } export const fadeInBottom = { - initial: { opacity: 0, y: 10 }, + initial: { opacity: 0, y: 5 }, + animate: { opacity: 1, y: 0 }, + transition +} + +export const fadeOutBottom = { + exit: { opacity: 0, y: 5 }, + transition +} + +export const fadeInTop = { + initial: { opacity: 0, y: -5 }, animate: { opacity: 1, y: 0 }, transition } +export const fadeOutTop = { + exit: { opacity: 0, y: -5 }, + transition +} + export const slowTransition = { transition: { duration: 0.8 } } export const normalTransition = { - transition: { type: 'spring', damping: 50, stiffness: 400 } + transition: { type: 'spring', damping: 50, stiffness: 600 } } export const fastTransition = { - transition: { type: 'spring', damping: 40, stiffness: 500 } + transition: { type: 'spring', damping: 40, stiffness: 900 } } export const fadeInSlowly = { @@ -67,9 +65,9 @@ export const fadeInOutFast = { ...fastTransition } -export const fadeInOutScaleFast = { - initial: { opacity: 0, scale: 0.9 }, - animate: { opacity: 1, scale: 1 }, - exit: { opacity: 0, scale: 0.95 }, +export const fadeInOutBottomFast = { + initial: { opacity: 0, y: 5 }, + animate: { opacity: 1, y: 1 }, + exit: { opacity: 0, y: 1 }, ...fastTransition } diff --git a/apps/desktop-wallet/src/api/addresses.ts b/apps/desktop-wallet/src/api/addresses.ts index c4040a71bc..d5ade26282 100644 --- a/apps/desktop-wallet/src/api/addresses.ts +++ b/apps/desktop-wallet/src/api/addresses.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { NonSensitiveAddressData } from '@alephium/keyring' import { throttledClient } from '@alephium/shared' import { TOTAL_NUMBER_OF_GROUPS } from '@alephium/web3' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/addressApiDataHooksTypes.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/addressApiDataHooksTypes.ts index ba5cfd7c03..8dd15bb77f 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/addressApiDataHooksTypes.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/addressApiDataHooksTypes.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalances.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalances.ts index caca30caf7..0e6c1103be 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalances.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalances.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import useFetchAddressBalancesAlph from '@/api/apiDataHooks/address/useFetchAddressBalancesAlph' import useFetchAddressBalancesTokens from '@/api/apiDataHooks/address/useFetchAddressBalancesTokens' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesAlph.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesAlph.ts index 4c378bef24..268adb9282 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesAlph.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesAlph.ts @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import { addressAlphBalancesQuery } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchAddressBalancesAlph = ({ addressHash, skip }: UseFetchAddressProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data, isLoading } = useQuery(addressAlphBalancesQuery({ addressHash, networkId, skip })) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesTokens.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesTokens.ts index ba81561655..670892b703 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesTokens.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressBalancesTokens.ts @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import { addressTokensBalancesQuery } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchAddressBalancesTokens = ({ addressHash, skip }: UseFetchAddressProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data, isLoading } = useQuery(addressTokensBalancesQuery({ addressHash, networkId, skip })) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressFts.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressFts.ts index da8449793e..631e5d8ac3 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressFts.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressFts.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' import useFetchSortedFts from '@/api/apiDataHooks/utils/useFetchSortedFts' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressHiddenTokens.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressHiddenTokens.ts new file mode 100644 index 0000000000..561cdd8339 --- /dev/null +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressHiddenTokens.ts @@ -0,0 +1,22 @@ +import { useMemo } from 'react' + +import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' +import useFetchAddressBalancesTokens from '@/api/apiDataHooks/address/useFetchAddressBalancesTokens' +import { useAppSelector } from '@/hooks/redux' + +const useFetchAddressHiddenTokens = ({ addressHash }: UseFetchAddressProps) => { + const hiddenTokensIds = useAppSelector((s) => s.hiddenTokens.hiddenTokensIds) + const { data: tokensBalances, isLoading: isLoadingTokensBalances } = useFetchAddressBalancesTokens({ addressHash }) + + const addressHiddenTokens = useMemo( + () => tokensBalances?.filter(({ id }) => hiddenTokensIds.includes(id)).map(({ id }) => id), + [tokensBalances, hiddenTokensIds] + ) + + return { + data: addressHiddenTokens, + isLoading: isLoadingTokensBalances + } +} + +export default useFetchAddressHiddenTokens diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressInfiniteTransactions.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressInfiniteTransactions.ts index 121a8d4b38..7403848789 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressInfiniteTransactions.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressInfiniteTransactions.ts @@ -1,37 +1,18 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useInfiniteQuery } from '@tanstack/react-query' import { useCallback, useMemo } from 'react' import useFetchAddressLatestTransaction from '@/api/apiDataHooks/address/useFetchAddressLatestTransaction' import { addressTransactionsInfiniteQuery } from '@/api/queries/transactionQueries' import queryClient from '@/api/queryClient' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' interface UseFetchAddressInfiniteTransactionsProps { addressHash: AddressHash } const useFetchAddressInfiniteTransactions = ({ addressHash }: UseFetchAddressInfiniteTransactionsProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data: addressLatestTx, isLoading: isLoadingLatestTx } = useFetchAddressLatestTransaction({ addressHash }) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressLatestTransaction.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressLatestTransaction.ts index f0c74da0d1..d17b540bdb 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressLatestTransaction.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressLatestTransaction.ts @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import { addressLatestTransactionQuery } from '@/api/queries/transactionQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchAddressLatestTransaction = ({ addressHash, skip }: UseFetchAddressProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data, isLoading } = useQuery(addressLatestTransactionQuery({ addressHash, networkId, skip })) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressSingleTokenBalances.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressSingleTokenBalances.ts index 95f7f58c65..01e5f72318 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressSingleTokenBalances.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressSingleTokenBalances.ts @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { ALPH } from '@alephium/token-list' import { useQuery } from '@tanstack/react-query' import useFetchAddressBalancesAlph from '@/api/apiDataHooks/address/useFetchAddressBalancesAlph' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import { addressTokensBalancesQuery } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' import { TokenId } from '@/types/tokens' interface UseFetchAddressSingleTokenBalancesProps extends SkipProp { @@ -37,7 +18,7 @@ const useFetchAddressSingleTokenBalances = ({ tokenId, skip }: UseFetchAddressSingleTokenBalancesProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const isALPH = tokenId === ALPH.id const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchAddressBalancesAlph({ diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressTokensByType.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressTokensByType.ts index 29e88c0e78..ada9c8ea87 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressTokensByType.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressTokensByType.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseFetchAddressProps } from '@/api/apiDataHooks/address/addressApiDataHooksTypes' import useFetchAddressBalances from '@/api/apiDataHooks/address/useFetchAddressBalances' import useFetchTokensSeparatedByType from '@/api/apiDataHooks/utils/useFetchTokensSeparatedByType' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressWorth.ts b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressWorth.ts index c75fb4f69e..89e2b5f6f6 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressWorth.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/address/useFetchAddressWorth.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import useFetchAddressBalances from '@/api/apiDataHooks/address/useFetchAddressBalances' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksTypes.ts b/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksTypes.ts index 832b20ba97..b709d70bd1 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksTypes.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksTypes.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - export type DataHook = { data: T isLoading: boolean diff --git a/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksUtils.ts b/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksUtils.ts index 1644762426..d7485ea835 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksUtils.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/apiDataHooksUtils.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseQueryResult } from '@tanstack/react-query' import { isDefined } from '@/utils/misc' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/market/useFetchTokenPrices.ts b/apps/desktop-wallet/src/api/apiDataHooks/market/useFetchTokenPrices.ts index 77603283fe..d8eb0771c4 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/market/useFetchTokenPrices.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/market/useFetchTokenPrices.ts @@ -1,21 +1,4 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' @@ -23,11 +6,10 @@ import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' import { tokensPriceQuery } from '@/api/queries/priceQueries' import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchTokenPrices = (props?: SkipProp) => { const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency) - const networkIsOffline = useAppSelector(selectCurrentlyOnlineNetworkId) === undefined + const networkIsOffline = useCurrentlyOnlineNetworkId() === undefined const { data: symbols, isLoading: isLoadingFtSymbols } = useFetchWalletFtsSymbols() @@ -49,7 +31,7 @@ export default useFetchTokenPrices export const useFetchTokenPrice = (symbol: string) => { const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency) - const networkIsOffline = useAppSelector(selectCurrentlyOnlineNetworkId) === undefined + const networkIsOffline = useCurrentlyOnlineNetworkId() === undefined const { data: symbols, isLoading: isLoadingFtSymbols } = useFetchWalletFtsSymbols() @@ -68,7 +50,7 @@ const useFetchWalletFtsSymbols = () => { const { data: { listedFts }, isLoading: isLoadingTokensByType - } = useFetchWalletTokensByType({ includeAlph: true }) + } = useFetchWalletTokensByType({ includeHidden: true }) const symbols = useMemo(() => listedFts.map((ft) => ft.symbol), [listedFts]) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchNft.ts b/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchNft.ts index 0372422278..6a52134cce 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchNft.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchNft.ts @@ -1,28 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import { nftDataQuery, nftMetadataQuery } from '@/api/queries/tokenQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' import { TokenId } from '@/types/tokens' interface UseNFTProps extends SkipProp { @@ -30,7 +11,7 @@ interface UseNFTProps extends SkipProp { } const useFetchNft = ({ id, skip }: UseNFTProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data: nftMetadata, isLoading: isLoadingNftMetadata } = useQuery(nftMetadataQuery({ id, networkId, skip })) const { diff --git a/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchToken.ts b/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchToken.ts index 07002fe732..a27543caee 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchToken.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/token/useFetchToken.ts @@ -1,72 +1,18 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { NFT } from '@alephium/shared' -import { explorer as e } from '@alephium/web3' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' -import { DataHook } from '@/api/apiDataHooks/apiDataHooksTypes' -import useFetchNft from '@/api/apiDataHooks/token/useFetchNft' -import useFetchFtList from '@/api/apiDataHooks/utils/useFetchFtList' -import { fungibleTokenMetadataQuery, tokenTypeQuery } from '@/api/queries/tokenQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' -import { ListedFT, NonStandardToken, TokenId, UnlistedFT } from '@/types/tokens' - -type UseFetchTokenResponse = DataHook - -const useFetchToken = (id: TokenId): UseFetchTokenResponse => { - const { data: fTList, isLoading: isLoadingFtList } = useFetchFtList() - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) +import { tokenQuery } from '@/api/queries/tokenQueries' +import { TokenId } from '@/types/tokens' - const listedFT = fTList?.find((t) => t.id === id) +const useFetchToken = (id: TokenId) => { + const networkId = useCurrentlyOnlineNetworkId() - const { data: tokenType, isLoading: isLoadingTokenType } = useQuery( - tokenTypeQuery({ id, networkId, skip: isLoadingFtList || !!listedFT }) - ) - - const { data: unlistedFT, isLoading: isLoadingUnlistedFT } = useQuery( - fungibleTokenMetadataQuery({ - id, - networkId, - skip: isLoadingTokenType || tokenType?.stdInterfaceId !== e.TokenStdInterfaceId.Fungible - }) - ) - - const { data: nft, isLoading: isLoadingNft } = useFetchNft({ - id, - skip: isLoadingTokenType || tokenType?.stdInterfaceId !== e.TokenStdInterfaceId.NonFungible - }) + const { data, isLoading } = useQuery(tokenQuery({ id, networkId })) return { - data: listedFT ?? unlistedFT ?? nft ?? { id }, - isLoading: isLoadingFtList || isLoadingTokenType || isLoadingUnlistedFT || isLoadingNft + data, + isLoading } } export default useFetchToken - -export const isFT = (token: UseFetchTokenResponse['data']): token is ListedFT | UnlistedFT => - (token as ListedFT | UnlistedFT).symbol !== undefined - -export const isListedFT = (token: UseFetchTokenResponse['data']): token is ListedFT => - (token as ListedFT).logoURI !== undefined - -export const isUnlistedFT = (token: UseFetchTokenResponse['data']) => isFT(token) && !isListedFT(token) - -export const isNFT = (token: UseFetchTokenResponse['data']): token is NFT => (token as NFT).nftIndex !== undefined diff --git a/apps/desktop-wallet/src/api/apiDataHooks/transaction/transactionTypes.ts b/apps/desktop-wallet/src/api/apiDataHooks/transaction/transactionTypes.ts index bed2b28cf5..8e2d051cdc 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/transaction/transactionTypes.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/transaction/transactionTypes.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' export interface UseFetchTransactionProps extends SkipProp { diff --git a/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchPendingTransaction.ts b/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchPendingTransaction.ts index 20a00c3155..812924738b 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchPendingTransaction.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchPendingTransaction.ts @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { UseFetchTransactionProps } from '@/api/apiDataHooks/transaction/transactionTypes' import { pendingTransactionQuery } from '@/api/queries/transactionQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchPendingTransaction = (props: UseFetchTransactionProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data, isLoading } = useQuery(pendingTransactionQuery({ ...props, networkId })) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchTransaction.ts b/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchTransaction.ts index b67a7a74a4..8b714f9b6f 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchTransaction.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/transaction/useFetchTransaction.ts @@ -1,21 +1,4 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQuery } from '@tanstack/react-query' import { UseFetchTransactionProps } from '@/api/apiDataHooks/transaction/transactionTypes' @@ -23,11 +6,10 @@ import useFetchPendingTransaction from '@/api/apiDataHooks/transaction/useFetchP import { confirmedTransactionQuery } from '@/api/queries/transactionQueries' import { selectSentTransactionByHash } from '@/features/send/sentTransactions/sentTransactionsSelectors' import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchTransaction = ({ txHash, skip }: UseFetchTransactionProps) => { const sentTx = useAppSelector((s) => selectSentTransactionByHash(s, txHash)) - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const isPendingTx = sentTx && sentTx.status !== 'confirmed' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/getQueryConfig.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/getQueryConfig.ts deleted file mode 100644 index 4cdcd3cb49..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/getQueryConfig.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -interface GetQueryConfigProps { - gcTime: number - staleTime?: number - networkId?: number -} - -export const getQueryConfig = ({ staleTime, gcTime, networkId }: GetQueryConfigProps) => ({ - staleTime, - gcTime, - meta: { isMainnet: networkId === 0 } -}) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchFtList.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchFtList.ts index 09468deae0..6062a7c395 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchFtList.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchFtList.ts @@ -1,30 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' +import { TokenList } from '@alephium/token-list' +import { useQuery } from '@tanstack/react-query' -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ONE_DAY_MS } from '@alephium/shared' -import { ALPH, getTokensURL, mainnet, testnet, TokenList } from '@alephium/token-list' -import { skipToken, useQuery } from '@tanstack/react-query' -import axios from 'axios' - -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' - -import { getQueryConfig } from './getQueryConfig' +import { ftListQuery } from '@/api/queries/tokenQueries' export interface FTList { data: TokenList['tokens'] | undefined @@ -36,23 +14,9 @@ type FTListProps = { } const useFetchFtList = (props?: FTListProps): FTList => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - const network = networkId === 0 ? 'mainnet' : networkId === 1 ? 'testnet' : networkId === 4 ? 'devnet' : undefined + const networkId = useCurrentlyOnlineNetworkId() - const { data, isLoading } = useQuery({ - queryKey: ['tokenList', { networkId }], - // The token list is essential for the whole app so we don't want to ever delete it. Even if we set a lower gcTime, - // it will never become inactive (since it's always used by a mount component). - ...getQueryConfig({ staleTime: ONE_DAY_MS, gcTime: Infinity, networkId }), - queryFn: - !network || props?.skip - ? skipToken - : () => - network === 'devnet' - ? [ALPH] - : axios.get(getTokensURL(network)).then(({ data }) => (data as TokenList)?.tokens), - placeholderData: network === 'mainnet' ? mainnet.tokens : network === 'testnet' ? testnet.tokens : [] - }) + const { data, isLoading } = useQuery(ftListQuery({ networkId, skip: props?.skip })) // TODO: Maybe return an object instead of an array for faster search? return { diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchListedFtsWorth.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchListedFtsWorth.ts index b5c6c45cbc..67637d37b2 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchListedFtsWorth.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchListedFtsWorth.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { calculateAmountWorth } from '@alephium/shared' import { isNumber } from 'lodash' import { useMemo } from 'react' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchSortedFts.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchSortedFts.ts index cff506c56b..a372441495 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchSortedFts.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchSortedFts.ts @@ -1,22 +1,5 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { calculateAmountWorth } from '@alephium/shared' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries } from '@tanstack/react-query' import { isNumber, orderBy } from 'lodash' import { useMemo } from 'react' @@ -25,8 +8,6 @@ import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import { combineDefined } from '@/api/apiDataHooks/apiDataHooksUtils' import useFetchTokenPrices from '@/api/apiDataHooks/market/useFetchTokenPrices' import { fungibleTokenMetadataQuery } from '@/api/queries/tokenQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' import { ApiBalances, ListedFT, TokenId } from '@/types/tokens' interface UseSortFTsProps extends SkipProp { @@ -35,7 +16,7 @@ interface UseSortFTsProps extends SkipProp { } const useFetchSortedFts = ({ listedFts, unlistedFtIds, skip }: UseSortFTsProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data: unlistedFts, isLoading: isLoadingUnlistedFTs } = useQueries({ queries: unlistedFtIds.map((id) => fungibleTokenMetadataQuery({ id, networkId })), diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByListing.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByListing.ts index b90006e45d..707032a7eb 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByListing.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByListing.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useMemo } from 'react' import useFetchFtList from '@/api/apiDataHooks/utils/useFetchFtList' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByType.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByType.ts index bc27e8bd7e..e2a524a706 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByType.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchTokensSeparatedByType.ts @@ -1,27 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries } from '@tanstack/react-query' import useFetchTokensSeparatedByListing from '@/api/apiDataHooks/utils/useFetchTokensSeparatedByListing' import { combineTokenTypeQueryResults, tokenTypeQuery } from '@/api/queries/tokenQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' import { ListedFT, TokenId, UnlistedToken } from '@/types/tokens' interface TokensByType { @@ -36,7 +17,7 @@ interface TokensByType { } const useFetchTokensSeparatedByType = (tokens: T[] = []): TokensByType => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data: { listedFts, unlistedTokens }, diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlph.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlph.ts deleted file mode 100644 index 1fd6862459..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlph.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { useQueries, UseQueryResult } from '@tanstack/react-query' - -import { addressAlphBalancesQuery, AddressAlphBalancesQueryFnData } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' -import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' - -const useFetchWalletBalancesAlph = ( - combine: (results: UseQueryResult[]) => { - data: T - isLoading: boolean - isFetching?: boolean - error?: boolean - } -) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - const allAddressHashes = useUnsortedAddressesHashes() - - const { data, isLoading, isFetching, error } = useQueries({ - queries: allAddressHashes.map((addressHash) => addressAlphBalancesQuery({ addressHash, networkId })), - combine - }) - - return { - data, - isLoading, - isFetching, - error - } -} - -export default useFetchWalletBalancesAlph diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlphBase.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlphBase.ts new file mode 100644 index 0000000000..e3835bfce4 --- /dev/null +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesAlphBase.ts @@ -0,0 +1,31 @@ +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' +import { useQueries, UseQueryResult } from '@tanstack/react-query' + +import { addressAlphBalancesQuery, AddressAlphBalancesQueryFnData } from '@/api/queries/addressQueries' +import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' + +const useFetchWalletBalancesAlphBase = ( + combine: (results: UseQueryResult[]) => { + data: T + isLoading: boolean + isFetching?: boolean + error?: boolean + } +) => { + const networkId = useCurrentlyOnlineNetworkId() + const allAddressHashes = useUnsortedAddressesHashes() + + const { data, isLoading, isFetching, error } = useQueries({ + queries: allAddressHashes.map((addressHash) => addressAlphBalancesQuery({ addressHash, networkId })), + combine + }) + + return { + data, + isLoading, + isFetching, + error + } +} + +export default useFetchWalletBalancesAlphBase diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesTokens.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesTokens.ts index 3560ad3c5f..30e1d034bc 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesTokens.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useFetchWalletBalancesTokens.ts @@ -1,27 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries, UseQueryResult } from '@tanstack/react-query' import { addressTokensBalancesQuery, AddressTokensBalancesQueryFnData } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' export const useFetchWalletBalancesTokens = ( combine: (results: UseQueryResult[]) => { @@ -31,7 +12,7 @@ export const useFetchWalletBalancesTokens = ( error?: boolean } ) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const allAddressHashes = useUnsortedAddressesHashes() const { data, isLoading, isFetching, error } = useQueries({ diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useMergeAllTokensBalances.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useMergeAllTokensBalances.ts index b2f9d4aa5f..3db7d3fc38 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useMergeAllTokensBalances.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useMergeAllTokensBalances.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ALPH } from '@alephium/token-list' import { useMemo } from 'react' @@ -34,8 +16,8 @@ const useMergeAllTokensBalances = ({ }: UseMergeAllTokensBalancesProps) => useMemo( () => - includeAlph && alphBalances?.totalBalance - ? [{ id: ALPH.id, ...alphBalances }, ...tokensBalances] + includeAlph && alphBalances && alphBalances.totalBalance !== '0' + ? [{ id: ALPH.id, ...alphBalances } as TokenApiBalances, ...tokensBalances] : tokensBalances, [includeAlph, alphBalances, tokensBalances] ) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/utils/useSortedTokenIds.ts b/apps/desktop-wallet/src/api/apiDataHooks/utils/useSortedTokenIds.ts index 8d0c2b4775..a21a9336e0 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/utils/useSortedTokenIds.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/utils/useSortedTokenIds.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useMemo } from 'react' import { ListedFT, TokenId, UnlistedFT } from '@/types/tokens' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchLatestTransactionOfEachAddress.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchLatestTransactionOfEachAddress.ts index 005f5ae19d..2c8643e001 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchLatestTransactionOfEachAddress.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchLatestTransactionOfEachAddress.ts @@ -1,32 +1,13 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries } from '@tanstack/react-query' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import { flatMapCombine } from '@/api/apiDataHooks/apiDataHooksUtils' import { addressLatestTransactionQuery } from '@/api/queries/transactionQueries' -import { useAppSelector } from '@/hooks/redux' import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useFetchLatestTransactionOfEachAddress = (props?: SkipProp) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const allAddressHashes = useUnsortedAddressesHashes() const { data, isLoading } = useQueries({ diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalances.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalances.ts index af093005d1..5809924b71 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalances.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalances.ts @@ -1,23 +1,5 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import useMergeAllTokensBalances from '@/api/apiDataHooks/utils/useMergeAllTokensBalances' -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' +import useFetchWalletBalancesAlph from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlph' import useFetchWalletBalancesTokensArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' interface UseFetchWalletBalancesProps { @@ -27,7 +9,7 @@ interface UseFetchWalletBalancesProps { const useFetchWalletBalances = (props?: UseFetchWalletBalancesProps) => { const includeAlph = props?.includeAlph ?? true - const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlphArray() + const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlph() const { data: tokensBalances, isLoading: isLoadingTokensBalances } = useFetchWalletBalancesTokensArray() const allTokensBalances = useMergeAllTokensBalances({ includeAlph, diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlph.ts similarity index 52% rename from apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray.ts rename to apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlph.ts index 2a4f0623dd..69f668d919 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlph.ts @@ -1,25 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseQueryResult } from '@tanstack/react-query' import { combineError, combineIsFetching, combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import useFetchWalletBalancesAlph from '@/api/apiDataHooks/utils/useFetchWalletBalancesAlph' +import useFetchWalletBalancesAlphBase from '@/api/apiDataHooks/utils/useFetchWalletBalancesAlphBase' import { ApiContextProps } from '@/api/apiTypes' import { createDataContext } from '@/api/context/createDataContext' import { AddressAlphBalancesQueryFnData } from '@/api/queries/addressQueries' @@ -51,18 +33,16 @@ const combineBalances = (results: UseQueryResult ...combineError(results) }) -const { - useData: useFetchWalletBalancesAlphArray, - DataContextProvider: UseFetchWalletBalancesAlphArrayContextProvider -} = createDataContext({ - useDataHook: useFetchWalletBalancesAlph, - combineFn: combineBalances, - defaultValue: { - totalBalance: '0', - lockedBalance: '0', - availableBalance: '0' - } -}) +const { useData: useFetchWalletBalancesAlph, DataContextProvider: UseFetchWalletBalancesAlphArrayContextProvider } = + createDataContext({ + useDataHook: useFetchWalletBalancesAlphBase, + combineFn: combineBalances, + defaultValue: { + totalBalance: '0', + lockedBalance: '0', + availableBalance: '0' + } + }) -export default useFetchWalletBalancesAlphArray +export default useFetchWalletBalancesAlph export { UseFetchWalletBalancesAlphArrayContextProvider } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress.ts deleted file mode 100644 index ba96983aa5..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' -import { UseQueryResult } from '@tanstack/react-query' - -import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import useFetchWalletBalancesAlph from '@/api/apiDataHooks/utils/useFetchWalletBalancesAlph' -import { ApiContextProps } from '@/api/apiTypes' -import { createDataContext } from '@/api/context/createDataContext' -import { AddressAlphBalancesQueryFnData } from '@/api/queries/addressQueries' -import { ApiBalances } from '@/types/tokens' - -// Using undefined to avoid adding noUncheckedIndexedAccess in tsconfig while maintaining strong typing when accessing -// values through indexes, ie: alphBalances[addressHash] -type AddressesAlphBalances = ApiContextProps> - -const combineBalancesByAddress = ( - results: UseQueryResult[] -): AddressesAlphBalances => ({ - data: results.reduce( - (acc, { data }) => { - if (data) { - acc[data.addressHash] = data.balances - } - return acc - }, - {} as AddressesAlphBalances['data'] - ), - ...combineIsLoading(results) -}) - -const { - useData: useFetchWalletBalancesAlphByAddress, - DataContextProvider: UseFetchWalletBalancesAlphByAddressContextProvider -} = createDataContext({ - useDataHook: useFetchWalletBalancesAlph, - combineFn: combineBalancesByAddress, - defaultValue: {} -}) - -export default useFetchWalletBalancesAlphByAddress -export { UseFetchWalletBalancesAlphByAddressContextProvider } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesByAddress.tsx b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesByAddress.tsx new file mode 100644 index 0000000000..f05fa5c4b0 --- /dev/null +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesByAddress.tsx @@ -0,0 +1,92 @@ +import { AddressHash } from '@alephium/shared' +import { ALPH } from '@alephium/token-list' +import { UseQueryResult } from '@tanstack/react-query' +import { createContext, ReactNode, useContext, useMemo } from 'react' + +import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' +import useFetchWalletBalancesAlphBase from '@/api/apiDataHooks/utils/useFetchWalletBalancesAlphBase' +import { useFetchWalletBalancesTokens } from '@/api/apiDataHooks/utils/useFetchWalletBalancesTokens' +import { ApiContextProps } from '@/api/apiTypes' +import { AddressAlphBalancesQueryFnData, AddressTokensBalancesQueryFnData } from '@/api/queries/addressQueries' +import { ApiBalances, TokenApiBalances } from '@/types/tokens' + +type Data = Record | undefined> + +const DataContext = createContext>({ + data: {} as Data, + isLoading: false, + isFetching: false, + error: false +}) + +const UseFetchWalletBalancesByAddressContextProvider = ({ children }: { children: ReactNode }) => { + const { data: alphBalances, isLoading: isLoadingAlphBalances } = + useFetchWalletBalancesAlphBase(combineAlphBalancesByAddress) + const { data: tokensBalances, isLoading: isLoadingTokensBalances } = + useFetchWalletBalancesTokens(combineTokensBalancesByAddress) + + const allTokensBalances = useMemo(() => { + const allTokensBalances: Record = {} + + Object.keys(alphBalances).forEach((addressHash) => { + if (alphBalances[addressHash]) { + allTokensBalances[addressHash] = [ + { id: ALPH.id, ...alphBalances[addressHash] }, + ...(tokensBalances[addressHash] || []) + ] + } + }) + + return allTokensBalances + }, [alphBalances, tokensBalances]) + + const value = useMemo( + () => ({ + data: allTokensBalances, + isLoading: isLoadingAlphBalances || isLoadingTokensBalances + }), + [allTokensBalances, isLoadingAlphBalances, isLoadingTokensBalances] + ) + + return {children} +} + +const useFetchWalletBalancesByAddress = () => useContext(DataContext) + +export default useFetchWalletBalancesByAddress + +export { UseFetchWalletBalancesByAddressContextProvider } + +type AddressesAlphBalances = ApiContextProps> + +const combineAlphBalancesByAddress = ( + results: UseQueryResult[] +): AddressesAlphBalances => ({ + data: results.reduce( + (acc, { data }) => { + if (data) { + acc[data.addressHash] = data.balances + } + return acc + }, + {} as AddressesAlphBalances['data'] + ), + ...combineIsLoading(results) +}) + +type AddressesTokensBalances = ApiContextProps> + +const combineTokensBalancesByAddress = ( + results: UseQueryResult[] +): AddressesTokensBalances => ({ + data: results.reduce( + (acc, { data }) => { + if (data) { + acc[data.addressHash] = data.balances + } + return acc + }, + {} as AddressesTokensBalances['data'] + ), + ...combineIsLoading(results) +}) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray.ts index 56cbd0b11d..1b5508fea4 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray.ts @@ -1,20 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ import { UseQueryResult } from '@tanstack/react-query' import { combineError, combineIsFetching, combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' @@ -49,14 +32,14 @@ const combineBalancesToArray = (results: UseQueryResult({ +const { useData, DataContextProvider } = createDataContext>({ useDataHook: useFetchWalletBalancesTokens, combineFn: combineBalancesToArray, defaultValue: [] }) +const useFetchWalletBalancesTokensArray = useData +const UseFetchWalletBalancesTokensArrayContextProvider = DataContextProvider + export default useFetchWalletBalancesTokensArray export { UseFetchWalletBalancesTokensArrayContextProvider } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress.ts deleted file mode 100644 index 19c69973eb..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' -import { UseQueryResult } from '@tanstack/react-query' - -import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import { useFetchWalletBalancesTokens } from '@/api/apiDataHooks/utils/useFetchWalletBalancesTokens' -import { ApiContextProps } from '@/api/apiTypes' -import { createDataContext } from '@/api/context/createDataContext' -import { AddressTokensBalancesQueryFnData } from '@/api/queries/addressQueries' -import { TokenApiBalances } from '@/types/tokens' - -type AddressesTokensBalances = ApiContextProps> - -const combineBalancesByAddress = ( - results: UseQueryResult[] -): AddressesTokensBalances => ({ - data: results.reduce( - (acc, { data }) => { - if (data) { - acc[data.addressHash] = data.balances - } - return acc - }, - {} as AddressesTokensBalances['data'] - ), - ...combineIsLoading(results) -}) - -const { - useData: useFetchWalletBalancesTokensByAddress, - DataContextProvider: UseFetchWalletBalancesTokensByAddressContextProvider -} = createDataContext({ - useDataHook: useFetchWalletBalancesTokens, - combineFn: combineBalancesByAddress, - defaultValue: {} -}) - -export default useFetchWalletBalancesTokensByAddress -export { UseFetchWalletBalancesTokensByAddressContextProvider } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletFts.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletFts.ts index 22a7e29b7c..289c65ae14 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletFts.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletFts.ts @@ -1,38 +1,21 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import useFetchSortedFts from '@/api/apiDataHooks/utils/useFetchSortedFts' import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' interface UseWalletFTsProps { sort: boolean + includeHidden: boolean } -const useFetchWalletFts = (props?: UseWalletFTsProps) => { +const useFetchWalletFts = ({ sort, includeHidden }: UseWalletFTsProps) => { const { data: { listedFts, unlistedFtIds }, isLoading: isLoadingTokensByType - } = useFetchWalletTokensByType({ includeAlph: true }) + } = useFetchWalletTokensByType({ includeHidden }) const { sortedListedFts, sortedUnlistedFts, isLoading } = useFetchSortedFts({ listedFts, unlistedFtIds, - skip: props?.sort === false + skip: sort === false }) return { diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletLatestTransaction.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletLatestTransaction.ts index 4a4c70f2c7..a8d14894f6 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletLatestTransaction.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletLatestTransaction.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { maxBy } from 'lodash' import { useMemo } from 'react' diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletNftsSearchStrings.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletNftsSearchStrings.ts index 4f2f9f33ab..c60b7d1dfa 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletNftsSearchStrings.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletNftsSearchStrings.ts @@ -1,37 +1,18 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries } from '@tanstack/react-query' import { useMemo } from 'react' import { combineDefined } from '@/api/apiDataHooks/apiDataHooksUtils' import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' import { nftDataQuery, nftMetadataQuery } from '@/api/queries/tokenQueries' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' import { TokenId } from '@/types/tokens' const useFetchWalletNftsSearchStrings = () => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const { data: { nftIds }, isLoading: isLoadingTokensByType - } = useFetchWalletTokensByType({ includeAlph: true }) + } = useFetchWalletTokensByType({ includeHidden: false }) const { data: nftsMetadata, isLoading: isLoadingNftsMetadata } = useQueries({ queries: nftIds.map((id) => nftMetadataQuery({ id, networkId })), diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances.ts index e904af31e5..38bf4e7352 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances.ts @@ -1,87 +1,28 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ALPH } from '@alephium/token-list' -import { useQueries, UseQueryResult } from '@tanstack/react-query' import { useMemo } from 'react' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' -import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' -import { addressTokensBalancesQuery, AddressTokensBalancesQueryFnData } from '@/api/queries/addressQueries' -import { useAppSelector } from '@/hooks/redux' -import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' -import { ApiBalances, TokenId } from '@/types/tokens' +import useFetchWalletBalancesAlph from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlph' +import useFetchWalletBalancesTokensArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' +import { TokenId } from '@/types/tokens' interface UseFetchWalletSingleTokenBalancesProps extends SkipProp { tokenId: TokenId } const useFetchWalletSingleTokenBalances = ({ tokenId, skip }: UseFetchWalletSingleTokenBalancesProps) => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - const allAddressHashes = useUnsortedAddressesHashes() + const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlph() + const { data: tokensBalances, isLoading: isLoadingTokensBalances } = useFetchWalletBalancesTokensArray() const isALPH = tokenId === ALPH.id - const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlphArray() - - const { data: tokenBalances, isLoading: isLoadingTokenBalances } = useQueries({ - queries: - !isALPH && !skip - ? allAddressHashes.map((addressHash) => addressTokensBalancesQuery({ addressHash, networkId })) - : [], - combine: useMemo( - () => (results: UseQueryResult[]) => combineTokenBalances(tokenId, results), - [tokenId] - ) - }) - return { - data: isALPH ? alphBalances : tokenBalances, - isLoading: isALPH ? isLoadingAlphBalances : isLoadingTokenBalances + data: useMemo( + () => (isALPH ? alphBalances : tokensBalances.find(({ id }) => id === tokenId)), + [alphBalances, isALPH, tokenId, tokensBalances] + ), + isLoading: isALPH ? isLoadingAlphBalances : isLoadingTokensBalances } } export default useFetchWalletSingleTokenBalances - -const combineTokenBalances = (tokenId: string, results: UseQueryResult[]) => ({ - data: results.reduce( - (totalBalances, { data }) => { - const balances = data?.balances.find(({ id }) => id === tokenId) - - totalBalances.totalBalance = ( - BigInt(totalBalances.totalBalance) + BigInt(balances ? balances.totalBalance : 0) - ).toString() - totalBalances.lockedBalance = ( - BigInt(totalBalances.lockedBalance) + BigInt(balances ? balances.lockedBalance : 0) - ).toString() - totalBalances.availableBalance = ( - BigInt(totalBalances.availableBalance) + BigInt(balances ? balances.availableBalance : 0) - ).toString() - - return totalBalances - }, - { - totalBalance: '0', - lockedBalance: '0', - availableBalance: '0' - } as ApiBalances - ), - ...combineIsLoading(results) -}) diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.ts deleted file mode 100644 index e7f8a62945..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import useFetchTokensSeparatedByType from '@/api/apiDataHooks/utils/useFetchTokensSeparatedByType' -import useMergeAllTokensBalances from '@/api/apiDataHooks/utils/useMergeAllTokensBalances' -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' -import useFetchWalletBalancesTokensArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' - -interface UseFetchWalletTokensByType { - includeAlph: boolean -} - -const useFetchWalletTokensByType = ({ includeAlph }: UseFetchWalletTokensByType) => { - const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlphArray() - const { data: tokensBalances, isLoading: isLoadingTokensBalances } = useFetchWalletBalancesTokensArray() - const allTokensBalances = useMergeAllTokensBalances({ - includeAlph, - alphBalances, - tokensBalances - }) - const { data, isLoading } = useFetchTokensSeparatedByType(allTokensBalances) - - return { - data, - isLoading: isLoading || isLoadingTokensBalances || (includeAlph ? isLoadingAlphBalances : false) - } -} - -export default useFetchWalletTokensByType diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.tsx b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.tsx new file mode 100644 index 0000000000..2f24890264 --- /dev/null +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTokensByType.tsx @@ -0,0 +1,76 @@ +import { createContext, ReactNode, useContext, useMemo } from 'react' + +import useFetchTokensSeparatedByType from '@/api/apiDataHooks/utils/useFetchTokensSeparatedByType' +import useMergeAllTokensBalances from '@/api/apiDataHooks/utils/useMergeAllTokensBalances' +import useFetchWalletBalancesAlph from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlph' +import useFetchWalletBalancesTokensArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' +import { ApiContextProps } from '@/api/apiTypes' +import { useAppSelector } from '@/hooks/redux' +import { ApiBalances, ListedFT, TokenId, UnlistedToken } from '@/types/tokens' + +type Data = { + listedFts: Array + unlistedTokens: Array + unlistedFtIds: Array + nftIds: Array + nstIds: Array +} + +const DataContext = createContext>({ + data: { + listedFts: [], + unlistedTokens: [], + unlistedFtIds: [], + nftIds: [], + nstIds: [] + }, + isLoading: false, + isFetching: false, + error: false +}) + +const UseFetchWalletTokensByTypeContextProvider = ({ children }: { children: ReactNode }) => { + const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlph() + const { data: tokensBalances, isLoading: isLoadingTokensBalances } = useFetchWalletBalancesTokensArray() + + const allTokensBalances = useMergeAllTokensBalances({ includeAlph: true, alphBalances, tokensBalances }) + const { data, isLoading } = useFetchTokensSeparatedByType(allTokensBalances) + + const value = useMemo( + () => ({ + data, + isLoading: isLoading || isLoadingTokensBalances || isLoadingAlphBalances + }), + [data, isLoading, isLoadingTokensBalances, isLoadingAlphBalances] + ) + + return {children} +} + +interface UseFetchWalletTokensByTypeProps { + includeHidden: boolean +} + +const useFetchWalletTokensByType = ({ includeHidden }: UseFetchWalletTokensByTypeProps) => { + const contextData = useContext(DataContext) + const hiddenTokenIds = useAppSelector((s) => s.hiddenTokens.hiddenTokensIds) + const isNotHiddenTokenId = (tokenId: TokenId) => !hiddenTokenIds.includes(tokenId) + const isNotHiddenToken = (token: { id: TokenId }) => isNotHiddenTokenId(token.id) + + return includeHidden + ? contextData + : { + ...contextData, + data: { + listedFts: contextData.data.listedFts.filter(isNotHiddenToken), + unlistedTokens: contextData.data.unlistedTokens.filter(isNotHiddenToken), + unlistedFtIds: contextData.data.unlistedFtIds.filter(isNotHiddenTokenId), + nftIds: contextData.data.nftIds.filter(isNotHiddenTokenId), + nstIds: contextData.data.nstIds.filter(isNotHiddenTokenId) + } + } +} + +export default useFetchWalletTokensByType + +export { UseFetchWalletTokensByTypeContextProvider } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsInfinite.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsInfinite.ts index dcdc6aa978..a92a63a02b 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsInfinite.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsInfinite.ts @@ -1,57 +1,36 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useInfiniteQuery } from '@tanstack/react-query' -import { useCallback, useMemo } from 'react' +import { useMemo } from 'react' import useFetchWalletLatestTransaction from '@/api/apiDataHooks/wallet/useFetchWalletLatestTransaction' import { walletTransactionsInfiniteQuery } from '@/api/queries/transactionQueries' -import queryClient from '@/api/queryClient' -import { useAppSelector } from '@/hooks/redux' -import { useCappedAddressesHashes } from '@/hooks/useAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' +import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' const useFetchWalletTransactionsInfinite = () => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - const { addressHashes, isCapped } = useCappedAddressesHashes() + const networkId = useCurrentlyOnlineNetworkId() + const addressHashes = useUnsortedAddressesHashes() - const { data: latestTx, isLoading: isLoadingLatestTx } = useFetchWalletLatestTransaction() + const { isLoading: isLoadingLatestTx } = useFetchWalletLatestTransaction() const query = walletTransactionsInfiniteQuery({ addressHashes, networkId, skip: isLoadingLatestTx }) - const { data, fetchNextPage, isLoading, hasNextPage, isFetchingNextPage } = useInfiniteQuery(query) + const { data, fetchNextPage, isLoading, isFetching, hasNextPage, isFetchingNextPage } = useInfiniteQuery(query) - const refresh = useCallback(() => queryClient.refetchQueries({ queryKey: query.queryKey }), [query.queryKey]) + const fetchedConfirmedTxs = useMemo( + () => data?.pages.flatMap(({ pageTransactions }) => pageTransactions) ?? [], + [data?.pages] + ) - const fetchedConfirmedTxs = useMemo(() => data?.pages.flat() ?? [], [data?.pages]) - const latestFetchedTxHash = fetchedConfirmedTxs[0]?.hash - const latestUnfetchedTxHash = latestTx?.hash - const showNewTxsMessage = !isLoading && latestUnfetchedTxHash && latestFetchedTxHash !== latestUnfetchedTxHash + const pagesLoaded = data?.pageParams.length return { data: fetchedConfirmedTxs, fetchNextPage, isLoading, + isFetching, hasNextPage, isFetchingNextPage, - refresh, - showNewTxsMessage, - isDataComplete: !isCapped + pagesLoaded } } diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsLimited.tsx b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsLimited.tsx deleted file mode 100644 index 53351f2c5a..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletTransactionsLimited.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { useQuery } from '@tanstack/react-query' - -import { walletLatestTransactionsQuery } from '@/api/queries/transactionQueries' -import { useAppSelector } from '@/hooks/redux' -import { useCappedAddressesHashes } from '@/hooks/useAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' - -const useFetchWalletTransactionsLimited = () => { - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - const { addressHashes, isCapped } = useCappedAddressesHashes() - - const { data: confirmedTxs, isLoading } = useQuery(walletLatestTransactionsQuery({ addressHashes, networkId })) - - return { - data: confirmedTxs, - isLoading, - isDataComplete: !isCapped - } -} - -export default useFetchWalletTransactionsLimited diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorth.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorth.ts index b6c59b848a..d15a84a2db 100644 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorth.ts +++ b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorth.ts @@ -1,25 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import useFetchListedFtsWorth from '@/api/apiDataHooks/utils/useFetchListedFtsWorth' import useFetchTokensSeparatedByListing from '@/api/apiDataHooks/utils/useFetchTokensSeparatedByListing' import useMergeAllTokensBalances from '@/api/apiDataHooks/utils/useMergeAllTokensBalances' -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' +import useFetchWalletBalancesAlph from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlph' import useFetchWalletBalancesTokensArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' const useFetchWalletWorth = () => { @@ -28,7 +10,7 @@ const useFetchWalletWorth = () => { isLoading: isLoadingAlphBalances, isFetching: isFetchingAlphBalances, error: errorAlphBalances - } = useFetchWalletBalancesAlphArray() + } = useFetchWalletBalancesAlph() const { data: tokensBalances, isLoading: isLoadingTokensBalances, diff --git a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorthAlph.ts b/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorthAlph.ts deleted file mode 100644 index 66a5ed1f77..0000000000 --- a/apps/desktop-wallet/src/api/apiDataHooks/wallet/useFetchWalletWorthAlph.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { calculateAmountWorth } from '@alephium/shared' -import { ALPH } from '@alephium/token-list' -import { useMemo } from 'react' - -import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' - -const useFetchWalletWorthAlph = () => { - const { data: alphBalances, isLoading: isLoadingAlphBalances } = useFetchWalletBalancesAlphArray() - const { data: alphPrice, isLoading: isLoadingAlphPrice } = useFetchTokenPrice(ALPH.symbol) - - return { - data: useMemo( - () => calculateAmountWorth(BigInt(alphBalances?.totalBalance ?? 0), alphPrice ?? 0, ALPH.decimals), - [alphBalances?.totalBalance, alphPrice] - ), - isLoading: isLoadingAlphBalances || isLoadingAlphPrice - } -} - -export default useFetchWalletWorthAlph diff --git a/apps/desktop-wallet/src/api/apiTypes.ts b/apps/desktop-wallet/src/api/apiTypes.ts index 94c3757ba9..070118295f 100644 --- a/apps/desktop-wallet/src/api/apiTypes.ts +++ b/apps/desktop-wallet/src/api/apiTypes.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - export interface ApiContextProps { data: T isLoading: boolean diff --git a/apps/desktop-wallet/src/api/apiUtils.ts b/apps/desktop-wallet/src/api/apiUtils.ts index 5c8b134b20..7449ee0dec 100644 --- a/apps/desktop-wallet/src/api/apiUtils.ts +++ b/apps/desktop-wallet/src/api/apiUtils.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { explorer as e, NFTTokenUriMetaData } from '@alephium/web3' import { isArray } from 'lodash' diff --git a/apps/desktop-wallet/src/api/context/apiContext.tsx b/apps/desktop-wallet/src/api/context/apiContext.tsx index 1a1656b779..0ff5a68295 100644 --- a/apps/desktop-wallet/src/api/context/apiContext.tsx +++ b/apps/desktop-wallet/src/api/context/apiContext.tsx @@ -1,36 +1,33 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ReactNode } from 'react' -import { UseFetchWalletBalancesAlphArrayContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' -import { UseFetchWalletBalancesAlphByAddressContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress' +import { UseFetchWalletBalancesAlphArrayContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlph' +import { UseFetchWalletBalancesByAddressContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesByAddress' import { UseFetchWalletBalancesTokensArrayContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensArray' -import { UseFetchWalletBalancesTokensByAddressContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress' -import { composeProviders } from '@/api/context/apiContextUtils' +import { UseFetchWalletTokensByTypeContextProvider } from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' + +type ProviderProps = { children: ReactNode } +type ProviderComponent = FC -const Providers = composeProviders([ +const providers: Array = [ UseFetchWalletBalancesTokensArrayContextProvider, - UseFetchWalletBalancesTokensByAddressContextProvider, UseFetchWalletBalancesAlphArrayContextProvider, - UseFetchWalletBalancesAlphByAddressContextProvider -]) - -const ApiContextProvider = ({ children }: { children: ReactNode }) => {children} + UseFetchWalletTokensByTypeContextProvider, + UseFetchWalletBalancesByAddressContextProvider +] + +const ComposedProviders = providers.reduce( + (AccumulatedProviders, CurrentProvider) => { + const CombinedProvider: ProviderComponent = ({ children }) => ( + + {children} + + ) + + return CombinedProvider + }, + ({ children }) => children +) + +const ApiContextProvider = ({ children }: ProviderProps) => {children} export default ApiContextProvider diff --git a/apps/desktop-wallet/src/api/context/apiContextUtils.tsx b/apps/desktop-wallet/src/api/context/apiContextUtils.tsx deleted file mode 100644 index 0f9d3d5570..0000000000 --- a/apps/desktop-wallet/src/api/context/apiContextUtils.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ReactNode } from 'react' - -type ProviderProps = { children: ReactNode } -type ProviderComponent = FC - -export const composeProviders = (providers: ProviderComponent[]): ProviderComponent => - providers.reduce( - (AccumulatedProviders, CurrentProvider) => { - const CombinedProvider: ProviderComponent = ({ children }) => ( - - {children} - - ) - - return CombinedProvider - }, - ({ children }) => children - ) diff --git a/apps/desktop-wallet/src/api/context/createDataContext.tsx b/apps/desktop-wallet/src/api/context/createDataContext.tsx index f542fba3b5..3d9f092414 100644 --- a/apps/desktop-wallet/src/api/context/createDataContext.tsx +++ b/apps/desktop-wallet/src/api/context/createDataContext.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { UseQueryResult } from '@tanstack/react-query' import { createContext, ReactNode, useContext, useMemo } from 'react' diff --git a/apps/desktop-wallet/src/api/persistQueryClientContext.tsx b/apps/desktop-wallet/src/api/persistQueryClientContext.tsx index 19950c8648..8f89502f84 100644 --- a/apps/desktop-wallet/src/api/persistQueryClientContext.tsx +++ b/apps/desktop-wallet/src/api/persistQueryClientContext.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { sleep } from '@alephium/web3' import { PersistQueryClientOptions, diff --git a/apps/desktop-wallet/src/api/persistedCacheVersionStorage.ts b/apps/desktop-wallet/src/api/persistedCacheVersionStorage.ts index 587f6ae9c1..af65751fcd 100644 --- a/apps/desktop-wallet/src/api/persistedCacheVersionStorage.ts +++ b/apps/desktop-wallet/src/api/persistedCacheVersionStorage.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - export type PersistedQueryCacheVersion = string class PersistedQueryCacheVersionStorage { diff --git a/apps/desktop-wallet/src/api/queries/addressQueries.ts b/apps/desktop-wallet/src/api/queries/addressQueries.ts index ef8f481aa6..21c6334d53 100644 --- a/apps/desktop-wallet/src/api/queries/addressQueries.ts +++ b/apps/desktop-wallet/src/api/queries/addressQueries.ts @@ -1,28 +1,12 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash, PAGINATION_PAGE_LIMIT, throttledClient } from '@alephium/shared' +import { getQueryConfig } from '@alephium/shared-react' import { explorer as e } from '@alephium/web3' import { queryOptions, skipToken } from '@tanstack/react-query' -import { getQueryConfig } from '@/api/apiDataHooks/utils/getQueryConfig' +import { tokenQuery } from '@/api/queries/tokenQueries' import { AddressLatestTransactionQueryProps } from '@/api/queries/transactionQueries' -import { ApiBalances, TokenApiBalances } from '@/types/tokens' +import queryClient from '@/api/queryClient' +import { ApiBalances, isFT, isNFT, TokenApiBalances } from '@/types/tokens' export type AddressAlphBalancesQueryFnData = { addressHash: AddressHash @@ -101,3 +85,49 @@ export const addressTokensBalancesQuery = ({ addressHash, networkId, skip }: Add } : skipToken }) + +export type AddressTokensSearchStringQueryFnData = { + addressHash: AddressHash + searchString: string +} + +// Generates a string that includes the names and symbols of all tokens in the address, useful for filtering addresses +export const addressTokensSearchStringQuery = ({ addressHash, networkId }: AddressLatestTransactionQueryProps) => + queryOptions({ + queryKey: ['address', addressHash, 'tokensSearchString', { networkId }], + queryFn: async (): Promise => { + let searchString = '' + + const addressTokensBalances = await queryClient.fetchQuery(addressTokensBalancesQuery({ addressHash, networkId })) + const hasTokens = addressTokensBalances.balances.length > 0 + let hasAlph = hasTokens + + if (hasTokens) { + const tokens = await Promise.all( + addressTokensBalances.balances.map(({ id }) => queryClient.fetchQuery(tokenQuery({ id, networkId }))) + ) + + searchString = tokens + .map((token) => + isFT(token) + ? `${token.name.toLowerCase()} ${token.symbol.toLowerCase()} ${token.id}` + : isNFT(token) + ? `${token.name.toLowerCase()} ${token.id}` + : token.id + ) + .join(' ') + } else { + const addressAlphBalances = await queryClient.fetchQuery(addressAlphBalancesQuery({ addressHash, networkId })) + hasAlph = addressAlphBalances.balances.totalBalance !== '0' + } + + if (hasAlph) { + searchString += ' alph alephium' + } + + return { + addressHash, + searchString + } + } + }) diff --git a/apps/desktop-wallet/src/api/queries/priceQueries.ts b/apps/desktop-wallet/src/api/queries/priceQueries.ts index 0e907a1b1f..f02834c8cb 100644 --- a/apps/desktop-wallet/src/api/queries/priceQueries.ts +++ b/apps/desktop-wallet/src/api/queries/priceQueries.ts @@ -1,26 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { FIVE_MINUTES_MS, ONE_MINUTE_MS, throttledClient } from '@alephium/shared' +import { getQueryConfig } from '@alephium/shared-react' import { queryOptions, skipToken } from '@tanstack/react-query' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' -import { getQueryConfig } from '@/api/apiDataHooks/utils/getQueryConfig' interface TokensPriceQueryProps extends SkipProp { symbols: string[] diff --git a/apps/desktop-wallet/src/api/queries/tokenQueries.ts b/apps/desktop-wallet/src/api/queries/tokenQueries.ts index 844adbcd31..3a3429e948 100644 --- a/apps/desktop-wallet/src/api/queries/tokenQueries.ts +++ b/apps/desktop-wallet/src/api/queries/tokenQueries.ts @@ -1,31 +1,15 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { batchers, ONE_DAY_MS } from '@alephium/shared' +import { getQueryConfig } from '@alephium/shared-react' +import { ALPH, getTokensURL, mainnet, testnet, TokenList } from '@alephium/token-list' import { explorer as e, NFTMetaData, NFTTokenUriMetaData } from '@alephium/web3' import { queryOptions, skipToken, UseQueryResult } from '@tanstack/react-query' import axios from 'axios' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import { getQueryConfig } from '@/api/apiDataHooks/utils/getQueryConfig' import { convertTokenDecimalsToNumber, matchesNFTTokenUriMetaDataSchema } from '@/api/apiUtils' -import { TokenId } from '@/types/tokens' +import queryClient from '@/api/queryClient' +import { NonStandardToken, Token, TokenId } from '@/types/tokens' export type TokenTypesQueryFnData = Record @@ -49,6 +33,25 @@ enum NFTDataTypes { type NFTDataType = keyof typeof NFTDataTypes +export const ftListQuery = ({ networkId, skip }: Omit) => { + const network = networkId === 0 ? 'mainnet' : networkId === 1 ? 'testnet' : networkId === 4 ? 'devnet' : undefined + + return queryOptions({ + queryKey: ['tokenList', { networkId }], + // The token list is essential for the whole app so we don't want to ever delete it. Even if we set a lower gcTime, + // it will never become inactive (since it's always used by a mount component). + ...getQueryConfig({ staleTime: ONE_DAY_MS, gcTime: Infinity, networkId }), + queryFn: + !network || skip + ? skipToken + : () => + network === 'devnet' + ? [ALPH] + : axios.get(getTokensURL(network)).then(({ data }) => (data as TokenList)?.tokens), + placeholderData: network === 'mainnet' ? mainnet.tokens : network === 'testnet' ? testnet.tokens : [] + }) +} + export const tokenTypeQuery = ({ id, networkId, skip }: TokenQueryProps) => queryOptions({ queryKey: ['token', 'type', id], @@ -95,7 +98,6 @@ export const fungibleTokenMetadataQuery = ({ id, networkId, skip }: TokenQueryPr queryOptions({ queryKey: ['token', 'fungible', 'metadata', id], ...getQueryConfig({ staleTime: Infinity, gcTime: Infinity, networkId }), - meta: { isMainnet: networkId === 0 }, queryFn: !skip ? async () => { const tokenMetadata = await batchers.ftMetadataBatcher.fetch(id) @@ -144,3 +146,43 @@ export const nftDataQuery = ({ id, tokenUri, networkId, skip }: NFTQueryProps) = } : skipToken }) + +export const tokenQuery = ({ id, networkId, skip }: TokenQueryProps) => + queryOptions({ + queryKey: ['token', id, { networkId }], + ...getQueryConfig({ staleTime: Infinity, gcTime: Infinity, networkId }), + queryFn: async (): Promise => { + const nst = { id } as NonStandardToken + + // 1. First check if the token is in the token list + const fTList = await queryClient.fetchQuery(ftListQuery({ networkId })) + const listedFT = fTList.find((t) => t.id === id) + + if (listedFT) return listedFT + + // 2. If not, find the type of the token + const tokenInfo = await queryClient.fetchQuery(tokenTypeQuery({ id, networkId })) + + // 3. If it is a fungible token, fetch the fungible token metadata + if (tokenInfo?.stdInterfaceId === e.TokenStdInterfaceId.Fungible) { + const ftMetadata = await queryClient.fetchQuery(fungibleTokenMetadataQuery({ id, networkId })) + + return ftMetadata ?? nst + } + + // 4. If it is an NFT, fetch the NFT metadata and data + if (tokenInfo?.stdInterfaceId === e.TokenStdInterfaceId.NonFungible) { + const nftMetadata = await queryClient.fetchQuery(nftMetadataQuery({ id, networkId })) + + if (!nftMetadata) return nst + + const nftData = await queryClient.fetchQuery(nftDataQuery({ id, tokenUri: nftMetadata.tokenUri, networkId })) + + return { ...nftData, ...nftMetadata } + } + + // 5. If the type of the token cannot be determined, return the non-standard token + return nst + }, + enabled: !skip + }) diff --git a/apps/desktop-wallet/src/api/queries/transactionQueries.ts b/apps/desktop-wallet/src/api/queries/transactionQueries.ts index a07bad93c8..733abdbc7b 100644 --- a/apps/desktop-wallet/src/api/queries/transactionQueries.ts +++ b/apps/desktop-wallet/src/api/queries/transactionQueries.ts @@ -1,27 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash, FIVE_MINUTES_MS, throttledClient } from '@alephium/shared' +import { AddressHash, FIVE_MINUTES_MS, throttledClient, TRANSACTIONS_PAGE_DEFAULT_LIMIT } from '@alephium/shared' +import { getQueryConfig } from '@alephium/shared-react' import { explorer as e, sleep } from '@alephium/web3' import { infiniteQueryOptions, queryOptions, skipToken } from '@tanstack/react-query' import { SkipProp } from '@/api/apiDataHooks/apiDataHooksTypes' -import { getQueryConfig } from '@/api/apiDataHooks/utils/getQueryConfig' import queryClient from '@/api/queryClient' export interface AddressLatestTransactionQueryProps { @@ -58,7 +40,8 @@ export const addressLatestTransactionQuery = ({ addressHash, networkId, skip }: // See https://github.com/alephium/alephium-frontend/issues/981#issuecomment-2535493157 await sleep(2000) queryClient.invalidateQueries({ queryKey: ['address', addressHash, 'balance'] }) - queryClient.invalidateQueries({ queryKey: ['wallet', 'transactions', 'latest'] }) + queryClient.invalidateQueries({ queryKey: ['address', addressHash, 'transactions', 'latest'] }) + queryClient.invalidateQueries({ queryKey: ['wallet', 'transactions'] }) } return { @@ -69,6 +52,16 @@ export const addressLatestTransactionQuery = ({ addressHash, networkId, skip }: : skipToken }) +export const addressLatestTransactionsQuery = ({ addressHash, networkId }: AddressLatestTransactionQueryProps) => + queryOptions({ + queryKey: ['address', addressHash, 'transactions', 'latest', { networkId }], + // When the user navigates away from the Overview page for 5 minutes or when addresses are generated/removed + // the cached data will be deleted. + ...getQueryConfig({ staleTime: Infinity, gcTime: FIVE_MINUTES_MS, networkId }), + queryFn: () => + throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHash, { page: 1, limit: 5 }) + }) + interface TransactionsInfiniteQueryBaseProps { networkId?: number skip?: boolean @@ -84,13 +77,16 @@ export const addressTransactionsInfiniteQuery = ({ skip }: AddressTransactionsInfiniteQueryProps) => infiniteQueryOptions({ - queryKey: ['address', addressHash, 'transactions', { networkId }], + queryKey: ['address', addressHash, 'transactions', 'infinite', { networkId }], // 5 minutes after the user navigates away from the address details modal, the cached data will be deleted. ...getQueryConfig({ staleTime: Infinity, gcTime: FIVE_MINUTES_MS, networkId }), queryFn: !skip && networkId !== undefined ? ({ pageParam }) => - throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHash, { page: pageParam }) + throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHash, { + page: pageParam, + limit: TRANSACTIONS_PAGE_DEFAULT_LIMIT + }) : skipToken, initialPageParam: 1, getNextPageParam: (lastPage, _, lastPageParam) => (lastPage.length > 0 ? (lastPageParam += 1) : null) @@ -100,6 +96,11 @@ interface WalletTransactionsInfiniteQueryProps extends TransactionsInfiniteQuery addressHashes: AddressHash[] } +type PageParam = { + page: number + addressesWithMoreTxPages: AddressHash[] +} + export const walletTransactionsInfiniteQuery = ({ addressHashes, networkId, @@ -113,48 +114,40 @@ export const walletTransactionsInfiniteQuery = ({ queryFn: !skip && networkId !== undefined ? async ({ pageParam }) => { - let results: e.Transaction[] = [] - const args = { page: pageParam } + const addresses = pageParam.page === 1 ? addressHashes : pageParam.addressesWithMoreTxPages + const pageResults = await Promise.all( + addresses.map(async (addressHash) => ({ + addressHash, + transactions: await throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHash, { + page: pageParam.page, + limit: TRANSACTIONS_PAGE_DEFAULT_LIMIT + }) + })) + ) + + const addressesWithMoreTxPages = addresses.filter((hash) => { + const txs = pageResults.find(({ addressHash }) => addressHash === hash)?.transactions + + return txs && txs.length > 0 + }) - if (addressHashes.length === 1) { - results = await throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHashes[0], args) - } else if (addressHashes.length > 1) { - results = await throttledClient.explorer.addresses.postAddressesTransactions(args, addressHashes) + return { + pageTransactions: pageResults.flatMap(({ transactions }) => transactions), + addressesWithMoreTxPages } - - return results } : skipToken, - initialPageParam: 1, - getNextPageParam: (lastPage, _, lastPageParam) => (lastPage.length > 0 ? (lastPageParam += 1) : null) - }) - -interface WalletLatestTransactionsQueryProps { - networkId?: number - addressHashes: AddressHash[] -} - -export const walletLatestTransactionsQuery = ({ addressHashes, networkId }: WalletLatestTransactionsQueryProps) => - queryOptions({ - queryKey: ['wallet', 'transactions', 'latest', { networkId, addressHashes }], - // When the user navigates away from the Overview page for 5 minutes or when addresses are generated/removed the - // cached data will be deleted. - ...getQueryConfig({ staleTime: Infinity, gcTime: FIVE_MINUTES_MS, networkId }), - queryFn: - networkId !== undefined - ? async () => { - let results: e.Transaction[] = [] - const args = { page: 1, limit: 5 } - - if (addressHashes.length === 1) { - results = await throttledClient.explorer.addresses.getAddressesAddressTransactions(addressHashes[0], args) - } else if (addressHashes.length > 1) { - results = await throttledClient.explorer.addresses.postAddressesTransactions(args, addressHashes) - } - - return results - } - : skipToken + initialPageParam: { + page: 1, + addressesWithMoreTxPages: [] + } as PageParam, + getNextPageParam: ({ addressesWithMoreTxPages }, _, lastPageParam) => + lastPageParam.page !== 1 && lastPageParam.addressesWithMoreTxPages.length === 0 + ? null + : ({ + page: lastPageParam.page + 1, + addressesWithMoreTxPages + } as PageParam) }) interface TransactionQueryProps extends SkipProp { diff --git a/apps/desktop-wallet/src/api/queryClient.ts b/apps/desktop-wallet/src/api/queryClient.ts index 38cfcc6ffa..ae3fec3cc5 100644 --- a/apps/desktop-wallet/src/api/queryClient.ts +++ b/apps/desktop-wallet/src/api/queryClient.ts @@ -1,43 +1,27 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { FIVE_MINUTES_MS, ONE_MINUTE_MS } from '@alephium/shared' -import { QueryClient } from '@tanstack/react-query' +import { QueryClient, QueryClientConfig } from '@tanstack/react-query' import { AxiosError } from 'axios' -const queryClient = new QueryClient({ +const MAX_RETRIES = 3 +const RETRY_ERROR_CODE = 429 + +// Unfortunately, Tanstack's retry callback does not include an argument for the status code. The web3 package returns +// a string message that includes the status code (see convertHttpResponse). +const shouldRetryAlephiumApi = (error: Error) => error?.message?.includes(`Status code: ${RETRY_ERROR_CODE}`) +const shouldRetryAxios = (error: AxiosError) => error?.response?.status === RETRY_ERROR_CODE + +export const queryClientConfig: QueryClientConfig = { defaultOptions: { queries: { staleTime: ONE_MINUTE_MS, gcTime: FIVE_MINUTES_MS, - retry: (failureCount, error) => { - if ( - (error instanceof AxiosError && error.response?.status !== 429) || - (error instanceof String && !error?.message?.includes('Status code: 429')) - ) { - return false - } - - return true - } + retry: (failureCount, error) => + failureCount < MAX_RETRIES && (shouldRetryAlephiumApi(error) || shouldRetryAxios(error as AxiosError)) } } -}) +} + +const queryClient = new QueryClient(queryClientConfig) // Useful for debugging // queryClient.getQueryCache().subscribe((event) => { diff --git a/apps/desktop-wallet/src/api/transactions.ts b/apps/desktop-wallet/src/api/transactions.ts index 391c5124f5..bf552a8292 100644 --- a/apps/desktop-wallet/src/api/transactions.ts +++ b/apps/desktop-wallet/src/api/transactions.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { keyring } from '@alephium/keyring' import { AddressHash, throttledClient } from '@alephium/shared' diff --git a/apps/desktop-wallet/src/components/ActionLink.tsx b/apps/desktop-wallet/src/components/ActionLink.tsx index 7b3f11ffe4..b22817ce4a 100644 --- a/apps/desktop-wallet/src/components/ActionLink.tsx +++ b/apps/desktop-wallet/src/components/ActionLink.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { LucideIcon } from 'lucide-react' import { ReactNode } from 'react' @@ -24,8 +6,8 @@ import styled, { css } from 'styled-components' import { HasTooltip } from '@/components/Tooltip' interface ActionLinkProps { - children: ReactNode onClick: () => void + children?: ReactNode Icon?: LucideIcon iconPosition?: 'right' | 'left' withBackground?: boolean @@ -46,7 +28,7 @@ const ActionLink = ({ Icon, children, tooltip, ...props }: HasTooltip` - color: ${({ theme }) => theme.global.accent}; + color: ${({ theme }) => theme.font.accent}; display: inline-flex; align-items: center; cursor: pointer; @@ -58,7 +40,7 @@ const ActionLinkStyled = styled.button` max-width: 100%; &:hover { - color: ${({ theme }) => colord(theme.global.accent).darken(0.1).toRgbString()}; + color: ${({ theme }) => colord(theme.font.accent).darken(0.1).toRgbString()}; } &:focus-visible { diff --git a/apps/desktop-wallet/src/components/AddressBadge.tsx b/apps/desktop-wallet/src/components/AddressBadge.tsx index 1993c5ac12..24d446f101 100644 --- a/apps/desktop-wallet/src/components/AddressBadge.tsx +++ b/apps/desktop-wallet/src/components/AddressBadge.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -39,6 +21,7 @@ interface AddressBadgeProps { displayHashUnder?: boolean isShort?: boolean className?: string + hashWidth?: number } const AddressBadge = ({ @@ -52,7 +35,8 @@ const AddressBadge = ({ appendHash = false, displayHashUnder = false, isShort, - withBorders + withBorders, + hashWidth }: AddressBadgeProps) => { const { t } = useTranslation() const address = useAppSelector((s) => selectAddressByHash(s, addressHash)) @@ -63,6 +47,7 @@ const AddressBadge = ({ @@ -71,9 +56,13 @@ const AddressBadge = ({ {disableCopy ? ( contact.name ) : ( - + {contact.name} - + )} ) : !address ? ( @@ -87,17 +76,26 @@ const AddressBadge = ({ {disableCopy || appendHash ? ( address.label ) : ( - + {address.label} - + )} {appendHash && ( - + )} ) : ( - + )} )} @@ -107,9 +105,13 @@ const AddressBadge = ({ export default AddressBadge -const AddressBadgeStyled = styled.div>` +type AddressBadgeStyledProps = Pick + +const AddressBadgeStyled = styled.div` display: flex; + position: relative; align-items: center; + text-align: ${({ hideColorIndication }) => (hideColorIndication ? 'left' : 'center')}; gap: 6px; ${({ withBorders }) => @@ -117,8 +119,7 @@ const AddressBadgeStyled = styled.div theme.border.primary}; border-radius: 25px; - padding: 4px 10px; - background: ${({ theme }) => theme.bg.highlight}; + padding: 2px 6px; `} ${({ truncate }) => @@ -150,9 +151,10 @@ const LabelAndHash = styled.div<{ isColumn: boolean }>` ` const Label = styled.span>` + position: relative; margin-right: 2px; white-space: nowrap; - max-width: 125px; + min-width: 40px; ${({ truncate }) => truncate && @@ -165,9 +167,12 @@ const Label = styled.span>` const NotKnownAddress = styled(HashEllipsed)`` const ShortHashEllipsed = styled(HashEllipsed)` - max-width: 150px; min-width: 80px; font-size: 12px; color: ${({ theme }) => theme.font.secondary}; - width: 100%; +` + +const ClipboardButtonStyled = styled(ClipboardButton)` + position: absolute; + right: 6px; ` diff --git a/apps/desktop-wallet/src/components/AddressColorIndicator.tsx b/apps/desktop-wallet/src/components/AddressColorIndicator.tsx index 2d6aff5fa4..d1adfa452e 100644 --- a/apps/desktop-wallet/src/components/AddressColorIndicator.tsx +++ b/apps/desktop-wallet/src/components/AddressColorIndicator.tsx @@ -1,28 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import styled from 'styled-components' -import DotIcon from '@/components/DotIcon' import { useAppSelector } from '@/hooks/redux' import { ReactComponent as IndicatorLogo } from '@/images/main_address_badge.svg' import { selectAddressByHash } from '@/storage/addresses/addressesSelectors' +import { labelColorPalette, useDisplayColor } from '@/utils/colors' interface AddressColorIndicatorProps { addressHash: AddressHash @@ -34,22 +16,25 @@ interface AddressColorIndicatorProps { const AddressColorIndicator = ({ addressHash, hideMainAddressBadge, - size = 12, + size = 10, className }: AddressColorIndicatorProps) => { const address = useAppSelector((s) => selectAddressByHash(s, addressHash)) const isPassphraseUsed = useAppSelector((s) => s.activeWallet.isPassphraseUsed) + const displayColor = useDisplayColor(address?.color, labelColorPalette) if (!address) return null + const color = displayColor || address.color + return (
{address.isDefault && !isPassphraseUsed && !hideMainAddressBadge ? ( - + ) : ( - + )}
) @@ -63,10 +48,17 @@ export default styled(AddressColorIndicator)` const DefaultAddressIndicator = styled.div<{ color: string; size: number }>` position: relative; - width: ${({ size }) => size}px; + width: ${({ size }) => size * 1.2}px; transform: scale(1.1); svg * { fill: ${({ color }) => color} !important; } ` + +const Indicator = styled.div<{ size: number; color: string }>` + height: ${({ size }) => size}px; + width: ${({ size }) => size}px; + background-color: ${({ color }) => color}; + border-radius: ${({ size }) => size / 3}px; +` diff --git a/apps/desktop-wallet/src/components/AddressMetadataForm.tsx b/apps/desktop-wallet/src/components/AddressMetadataForm.tsx index 5e44d979df..d8ca26bcfa 100644 --- a/apps/desktop-wallet/src/components/AddressMetadataForm.tsx +++ b/apps/desktop-wallet/src/components/AddressMetadataForm.tsx @@ -1,25 +1,6 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import HorizontalDivider from '@/components/Dividers/HorizontalDivider' import ColoredLabelInput, { ColoredLabelInputValue } from '@/components/Inputs/ColoredLabelInput' import InlineLabelValueInput from '@/components/Inputs/InlineLabelValueInput' import Toggle from '@/components/Inputs/Toggle' @@ -49,27 +30,25 @@ const AddressMetadataForm = ({ return ( <> + {singleAddress && ( - <> - - - {t('Default address')} - - } - description={defaultAddressMessage} - noHorizontalPadding - InputComponent={ - setIsDefault(!isDefault)} - disabled={!isDefaultAddressToggleEnabled} - /> - } - /> - + + {t('Default address')} + + } + description={defaultAddressMessage} + noHorizontalPadding + InputComponent={ + setIsDefault(!isDefault)} + disabled={!isDefaultAddressToggleEnabled} + /> + } + /> )} ) diff --git a/apps/desktop-wallet/src/components/AddressRow.tsx b/apps/desktop-wallet/src/components/AddressRow.tsx index b0b89b4a93..9654f4e415 100644 --- a/apps/desktop-wallet/src/components/AddressRow.tsx +++ b/apps/desktop-wallet/src/components/AddressRow.tsx @@ -1,28 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { ReactNode } from 'react' import styled from 'styled-components' import AddressBadge from '@/components/AddressBadge' import AddressColorIndicator from '@/components/AddressColorIndicator' -import { TableRow } from '@/components/Table' +import { TableCell, TableRow } from '@/components/Table' interface AddressRowProps { addressHash: AddressHash @@ -41,33 +23,27 @@ const AddressRow = ({ addressHash, onClick, children, className, subtitle }: Add onKeyDown={() => onClick && onClick(addressHash)} className={className} > - + - {children} - + + {children && {children}} ) export default AddressRow -const Row = styled.div` - display: flex; - align-items: center; - flex-grow: 1; -` - const AddressColorIndicatorStyled = styled(AddressColorIndicator)` margin-right: 15px; ` const Label = styled.div` - font-size: 14px; - font-weight: var(--fontWeight-medium); - max-width: 120px; + font-size: 13px; + font-weight: var(--fontWeight-semiBold); + max-width: 160px; ` const AddressSubtitle = styled.span` diff --git a/apps/desktop-wallet/src/components/AlefSymbol.tsx b/apps/desktop-wallet/src/components/AlefSymbol.tsx index 9c5613865d..03e4aec417 100644 --- a/apps/desktop-wallet/src/components/AlefSymbol.tsx +++ b/apps/desktop-wallet/src/components/AlefSymbol.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' import alefSymbol from '@/images/alef.svg' diff --git a/apps/desktop-wallet/src/components/AlephiumLogo.tsx b/apps/desktop-wallet/src/components/AlephiumLogo.tsx new file mode 100644 index 0000000000..1a587ace13 --- /dev/null +++ b/apps/desktop-wallet/src/components/AlephiumLogo.tsx @@ -0,0 +1,24 @@ +import styled from 'styled-components' + +import { ReactComponent as AlephiumLogoSVG } from '@/images/alephium_logo_monochrome.svg' + +const AlephiumLogo = ({ contrasted }: { contrasted?: boolean }) => ( + + + +) + +export default AlephiumLogo + +const AlephiumLogoStyled = styled.div<{ contrasted?: boolean }>` + display: flex; + + svg { + width: 100%; + height: 100%; + + path { + fill: ${({ theme, contrasted }) => (contrasted ? theme.font.contrastPrimary : theme.font.primary)} !important; + } + } +` diff --git a/apps/desktop-wallet/src/components/Amount.tsx b/apps/desktop-wallet/src/components/Amount.tsx index 64497498ea..d82b50879e 100644 --- a/apps/desktop-wallet/src/components/Amount.tsx +++ b/apps/desktop-wallet/src/components/Amount.tsx @@ -1,51 +1,34 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { convertToPositive, formatAmountForDisplay } from '@alephium/shared' import { Optional } from '@alephium/web3' import { useTranslation } from 'react-i18next' -import styled, { css } from 'styled-components' +import styled, { css, useTheme } from 'styled-components' -import useFetchToken, { isFT } from '@/api/apiDataHooks/token/useFetchToken' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' import SkeletonLoader from '@/components/SkeletonLoader' import { discreetModeToggled } from '@/features/settings/settingsActions' import { useAppDispatch, useAppSelector } from '@/hooks/redux' -import { TokenId } from '@/types/tokens' +import { isFT, TokenId } from '@/types/tokens' -interface AmountBaseProps { +export interface AmountBaseProps { fadeDecimals?: boolean color?: string overrideSuffixColor?: boolean tabIndex?: number highlight?: boolean showPlusMinus?: boolean + semiBold?: boolean className?: string useTinyAmountShorthand?: boolean } -interface TokenAmountProps extends AmountBaseProps { +export interface TokenAmountProps extends AmountBaseProps { tokenId: TokenId value: bigint fullPrecision?: boolean nbOfDecimalsToShow?: number } -interface FiatAmountProps extends AmountBaseProps { +export interface FiatAmountProps extends AmountBaseProps { isFiat: true value: number } @@ -79,6 +62,7 @@ const Amount = ({ const dispatch = useAppDispatch() const discreetMode = useAppSelector((state) => state.settings.discreetMode) const { t } = useTranslation() + const theme = useTheme() if (isLoading) return @@ -87,13 +71,23 @@ const Amount = ({ // Since we checked above that value is defined it's safe to cast the type so that the stricter components can work const amountProps = props as AmountProps - const { className, color, value, highlight, tabIndex, showPlusMinus } = amountProps + const { className, value, highlight, tabIndex, showPlusMinus, semiBold } = amountProps + + const color = props.color + ? props.color + : highlight && value !== undefined + ? value < 0 + ? theme.font.primary + : theme.global.valid + : 'inherit' + + amountProps.color = color const toggleDiscreetMode = () => discreetMode && dispatch(discreetModeToggled()) return ( { const { data: token } = useFetchToken(tokenId) + if (!token) return null + const amount = isFT(token) ? formatAmountForDisplay({ amount: convertToPositive(value), @@ -141,7 +138,7 @@ const TokenAmount = ({ <> - {isFT(token) && {token.symbol}} + {isFT(token) && {token.symbol}} ) } @@ -150,17 +147,26 @@ const FiatAmount = ({ value }: FiatAmountProps) => { const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency) const region = useAppSelector((s) => s.settings.region) + if (value === null) return null + return new Intl.NumberFormat(region, { style: 'currency', currency: fiatCurrency }).format(value) } -const CustomAmount = ({ value, fadeDecimals, overrideSuffixColor, color, suffix }: CustomAmountProps) => { +const CustomAmount = ({ + value, + fadeDecimals, + overrideSuffixColor, + color, + suffix, + showPlusMinus +}: CustomAmountProps) => { const amount = (value < 1 ? value * -1 : value).toString() return ( <> - {suffix} + {suffix} ) } @@ -216,19 +222,14 @@ const isFiat = (asset: AmountProps): asset is FiatAmountProps => (asset as FiatA const isCustom = (asset: AmountProps): asset is CustomAmountProps => (asset as CustomAmountProps).suffix !== undefined -const AmountStyled = styled.div & { discreetMode: boolean }>` - color: ${({ color, highlight, value, theme }) => - color - ? color - : highlight && value !== undefined - ? value < 0 - ? theme.font.highlight - : theme.global.valid - : 'inherit'}; +const AmountStyled = styled.div< + Pick & { discreetMode: boolean } +>` + color: ${({ color }) => color}; display: inline-flex; position: relative; + font-weight: var(--fontWeight-${({ semiBold }) => (semiBold ? 'bold' : 'medium')}); white-space: pre; - font-weight: var(--fontWeight-bold); font-feature-settings: 'tnum' on; ${({ discreetMode }) => discreetMode && @@ -245,8 +246,8 @@ const Decimals = styled.span` ` const Suffix = styled.span<{ color?: string }>` - color: ${({ color, theme }) => color ?? theme.font.primary}; - font-weight: var(--fontWeight-medium); + color: ${({ color, theme }) => color ?? theme.font.secondary}; + font-weight: var(--fontWeight-semiBold); ` const DataFetchIndicatorStyled = styled.div` diff --git a/apps/desktop-wallet/src/components/AmountsOverviewPanel.tsx b/apps/desktop-wallet/src/components/AmountsOverviewPanel.tsx deleted file mode 100644 index 7853a4e498..0000000000 --- a/apps/desktop-wallet/src/components/AmountsOverviewPanel.tsx +++ /dev/null @@ -1,175 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import dayjs from 'dayjs' -import { ReactNode, useState } from 'react' -import { useTranslation } from 'react-i18next' -import styled from 'styled-components' - -import TotalAlphBalance from '@/features/balancesOverview/TotalAlphBalance' -import ChartLengthBadges from '@/features/historicChart/ChartLengthBadges' -import FiatDeltaPercentage from '@/features/historicChart/FiatDeltaPercentage' -import { DataPoint } from '@/features/historicChart/historicChartTypes' -import HistoricWorthChart from '@/features/historicChart/HistoricWorthChart' -import AddressWorth from '@/modals/AddressDetailsModal/AddressWorth' -import WalletWorth from '@/pages/UnlockedWallet/OverviewPage/WalletWorth' -import { UnlockedWalletPanel } from '@/pages/UnlockedWallet/UnlockedWalletLayout' - -interface AmountsOverviewPanelProps { - addressHash?: string - isLoading?: boolean - className?: string - chartVisible?: boolean - chartInitiallyHidden?: boolean - animateChartEntry?: boolean - children?: ReactNode -} - -const AmountsOverviewPanel = ({ - className, - addressHash, - children, - chartVisible, - chartInitiallyHidden -}: AmountsOverviewPanelProps) => { - const { t } = useTranslation() - - const [hoveredDataPoint, setHoveredDataPoint] = useState() - const [worthInBeginningOfChart, setWorthInBeginningOfChart] = useState() - - const hoveredDataPointDate = hoveredDataPoint ? dayjs(hoveredDataPoint.date).format('DD/MM/YYYY') : undefined - const hoveredDataPointWorth = hoveredDataPoint?.worth - - const singleAddress = !!addressHash - const isHoveringChart = hoveredDataPointWorth !== undefined - - return ( - - - - - - {!hoveredDataPointDate ? t('Value today') : `${hoveredDataPointDate} (${t('ALPH only')})`} - - {singleAddress ? ( - - ) : ( - - )} - - {isHoveringChart && ( - - - - )} - - - - - {!singleAddress && ( - <> - - - - - - - )} - - - {children && {children}} - - - - - ) -} - -export default AmountsOverviewPanel - -const UnlockedWalletPanelStyled = styled(UnlockedWalletPanel)` - position: relative; - padding: 0; -` - -const Panel = styled.div` - position: relative; - display: flex; - gap: 30px; - align-items: center; - - padding: 40px 60px 0; -` - -const Balances = styled.div` - flex: 2; - display: flex; - align-items: center; -` - -const BalancesRow = styled.div` - display: flex; - padding: 0 6%; -` - -const Opacity = styled.div<{ fadeOut?: boolean }>` - transition: opacity 0.2s ease-out; - opacity: ${({ fadeOut }) => (fadeOut ? 0.23 : 1)}; -` - -const RightColumnContent = styled(Opacity)` - display: flex; - flex-direction: column; - flex: 1; -` - -const BalancesColumn = styled(Opacity)` - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - min-width: 200px; -` - -const AvailableLockedBalancesColumn = styled(BalancesColumn)` - display: flex; - flex-direction: column; - justify-content: center; - gap: 20px; -` - -const Divider = styled.div` - width: 1px; - background-color: ${({ theme }) => theme.border.secondary}; - margin: 17px 55px; -` - -const Today = styled.div` - color: ${({ theme }) => theme.font.secondary}; - font-size: 16px; - margin-bottom: 8px; -` diff --git a/apps/desktop-wallet/src/components/AnimatedBackground.tsx b/apps/desktop-wallet/src/components/AnimatedBackground.tsx new file mode 100644 index 0000000000..8616da43ce --- /dev/null +++ b/apps/desktop-wallet/src/components/AnimatedBackground.tsx @@ -0,0 +1,295 @@ +import { colord } from 'colord' +import { animate, motion, MotionValue, useMotionValue, useSpring, useTransform } from 'framer-motion' +import { useEffect, useState } from 'react' +import styled, { useTheme } from 'styled-components' + +interface AnimatedBackgroundProps { + height?: number | string + width?: number | string + opacity?: number + className?: string + shade?: 'dark' | 'light' | string + anchorPosition?: AnchorPosition + reactToPointer?: boolean + hiddenOverflow?: boolean + verticalOffset?: number + horizontalOffset?: number + scaleY?: number +} + +type AnchorPosition = 'top' | 'bottom' + +const DARK_COLORS = ['#3f3fff', '#8394ff', '#276bff', '#ff7f58', '#381eff'] +const LIGHT_COLORS = ['#ad6eff', '#ffb47f', '#ffaaaa', '#ffc089', '#ff9bc8'] + +const AnimatedBackground = ({ + height = '100%', + width = '100%', + opacity = 1, + className, + shade, + reactToPointer = true, + anchorPosition = 'top', + hiddenOverflow, + scaleY, + verticalOffset = 0, + horizontalOffset = 0 +}: AnimatedBackgroundProps) => { + const theme = useTheme() + const isDarkTheme = theme.name === 'dark' + const backgroundColors = getBackgroundColors(shade, isDarkTheme) + const [windowSize, setWindowSize] = useState({ width: 0, height: 0 }) + const mouseX = useMotionValue(0) + const mouseY = useMotionValue(0) + + useEffect(() => { + const updateSize = () => { + if (typeof window !== 'undefined') { + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight + }) + mouseX.set(window.innerWidth / 2) + mouseY.set(anchorPosition === 'top' ? 0 : window.innerHeight) + } + } + updateSize() + window.addEventListener('resize', updateSize) + return () => window.removeEventListener('resize', updateSize) + }, [mouseX, mouseY, anchorPosition]) + + useEffect(() => { + let animationFrameId: number + + const handleMouseMove = (event: MouseEvent) => { + animationFrameId = window.requestAnimationFrame(() => { + mouseX.set(event.clientX) + mouseY.set(event.clientY) + }) + } + + if (reactToPointer && typeof window !== 'undefined') { + window.addEventListener('mousemove', handleMouseMove) + } + + return () => { + if (reactToPointer && typeof window !== 'undefined') { + window.removeEventListener('mousemove', handleMouseMove) + window.cancelAnimationFrame(animationFrameId) + } + } + }, [reactToPointer, mouseX, mouseY]) + + // Offsets for each circle + const offsets = [80, 200, 100, 140, 20] + + const circle1 = useCircleAnimation( + offsets[0], + windowSize, + mouseX, + mouseY, + reactToPointer, + verticalOffset, + horizontalOffset, + anchorPosition + ) + const circle2 = useCircleAnimation( + offsets[1], + windowSize, + mouseX, + mouseY, + reactToPointer, + verticalOffset, + horizontalOffset, + anchorPosition + ) + const circle3 = useCircleAnimation( + offsets[2], + windowSize, + mouseX, + mouseY, + reactToPointer, + verticalOffset, + horizontalOffset, + anchorPosition + ) + const circle4 = useCircleAnimation( + offsets[3], + windowSize, + mouseX, + mouseY, + reactToPointer, + verticalOffset, + horizontalOffset, + anchorPosition + ) + const circle5 = useCircleAnimation( + offsets[4], + windowSize, + mouseX, + mouseY, + reactToPointer, + verticalOffset, + horizontalOffset, + anchorPosition + ) + + const circlesDimensions = [ + { width: 500, height: 70 }, + { width: 600, height: 80 }, + { width: 550, height: 70 }, + { width: 650, height: 90 }, + { width: 920, height: 80 } + ] + + return ( + + + {circlesDimensions.map((dim, index) => { + const circleAnimation = [circle1, circle2, circle3, circle4, circle5][index] + return ( + + ) + })} + + + + ) +} + +export default AnimatedBackground + +const useCircleAnimation = ( + offset: number, + windowSize: { width: number; height: number }, + mouseX: MotionValue, + mouseY: MotionValue, + reactToPointer: boolean, + verticalOffset: number, + horizontalOffset: number, + anchorPosition: AnchorPosition +) => { + const xAnim = useMotionValue(0) + const yAnim = useMotionValue(0) + + useEffect(() => { + const xValues = [0, offset, 0, -offset, 0] + const yValues = [0, offset / 2, -offset / 2, offset / 2, 0] + // Increase the duration to slow down the animation (e.g. *2) + const duration = Math.abs(offset) / 5 + + const xControl = animate(xAnim, xValues, { + duration, + repeat: Infinity, + ease: 'easeInOut' + }) + + const yControl = animate(yAnim, yValues, { + duration, + repeat: Infinity, + ease: 'easeInOut' + }) + + return () => { + xControl.stop() + yControl.stop() + } + }, [offset, xAnim, yAnim]) + + const xSpring = useSpring(useTransform(mouseX, [0, windowSize.width], [-offset, offset]), { + stiffness: 90, + damping: 100 + }) + const ySpring = useSpring(useTransform(mouseY, [0, windowSize.height * 2], [-offset, offset]), { + stiffness: 90, + damping: 100 + }) + + const xTotal = useTransform([xAnim, xSpring], (inputs) => { + const [xA, xP] = inputs as [number, number] + return xA + xP + }) + + const yTotal = useTransform([yAnim, ySpring], (inputs) => { + const [yA, yP] = inputs as [number, number] + return yA + yP + }) + + const x = reactToPointer ? xTotal : xAnim + const y = reactToPointer ? yTotal : yAnim + + const yWithOffset = useTransform(y, (value) => + anchorPosition === 'top' ? value + verticalOffset : value - verticalOffset + ) + const xWithOffset = useTransform(x, (value) => value + horizontalOffset) + + return { x: xWithOffset, y: yWithOffset } +} + +const getBackgroundColors = (shade: AnimatedBackgroundProps['shade'], isDarkTheme: boolean): string[] => { + if (shade === 'dark') return DARK_COLORS + if (shade === 'light') return LIGHT_COLORS + if (typeof shade === 'string') { + const base = colord(shade) + const offsets = [-40, -20, 0, 20, 40] + return offsets.map((offset) => base.rotate(offset).toHex()) + } + return isDarkTheme ? DARK_COLORS : LIGHT_COLORS +} + +const SvgFilters = () => ( + + + + + +) + +const AnimatedContainer = styled.div` + flex: 1; + width: 100%; + position: absolute; + right: 0; + left: 0; + z-index: 0; + pointer-events: none; +` + +const Circle = styled(motion.div)` + position: absolute; + border-radius: 50%; + mix-blend-mode: color-burn; +` diff --git a/apps/desktop-wallet/src/components/AnnouncementBanner.tsx b/apps/desktop-wallet/src/components/AnnouncementBanner.tsx index 800de69c7d..2ae43b4f5a 100644 --- a/apps/desktop-wallet/src/components/AnnouncementBanner.tsx +++ b/apps/desktop-wallet/src/components/AnnouncementBanner.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { exponentialBackoffFetchRetry } from '@alephium/shared' import { colord } from 'colord' import { AnimatePresence, motion } from 'framer-motion' @@ -174,7 +156,7 @@ const Texts = styled.div` ` const Title = styled.div` - font-size: 14px; + font-size: 13px; font-weight: var(--fontWeight-medium); color: ${({ theme }) => theme.font.primary}; ` diff --git a/apps/desktop-wallet/src/components/AppHeader.tsx b/apps/desktop-wallet/src/components/AppHeader.tsx index 12604b6691..5919875f1a 100644 --- a/apps/desktop-wallet/src/components/AppHeader.tsx +++ b/apps/desktop-wallet/src/components/AppHeader.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { motion, useMotionValue, useTransform } from 'framer-motion' import { Eye, EyeOff, WifiOff } from 'lucide-react' @@ -25,8 +7,8 @@ import styled, { useTheme } from 'styled-components' import Button from '@/components/Button' import DefaultAddressSwitch from '@/components/DefaultAddressSwitch' import CompactToggle from '@/components/Inputs/CompactToggle' -import NetworkSwitch from '@/components/NetworkSwitch' import VerticalDivider from '@/components/PageComponents/VerticalDivider' +import TitleBar from '@/components/TitleBar.tsx' import { useScrollContext } from '@/contexts/scroll' import { openModal } from '@/features/modals/modalActions' import RefreshButton from '@/features/refreshData/RefreshButton' @@ -36,15 +18,17 @@ import { useAppDispatch, useAppSelector } from '@/hooks/redux' import useWalletLock from '@/hooks/useWalletLock' import { ReactComponent as WalletConnectLogo } from '@/images/wallet-connect-logo.svg' import { selectDefaultAddress } from '@/storage/addresses/addressesSelectors' -import { appHeaderHeightPx, walletSidebarWidthPx } from '@/style/globalStyles' +import { appHeaderHeightPx } from '@/style/globalStyles' +import { platform } from '@/utils/platform.ts' interface AppHeader { title?: string invisible?: boolean + position?: 'fixed' | 'sticky' className?: string } -const AppHeader: FC = ({ children, title, className, invisible }) => { +const AppHeader: FC = ({ children, title, className, invisible, position = 'sticky' }) => { const { t } = useTranslation() const { scrollY: scrollYContext } = useScrollContext() const initialScroll = useMotionValue(0) @@ -62,118 +46,116 @@ const AppHeader: FC = ({ children, title, className, invisible }) => const toggleDiscreetMode = () => dispatch(discreetModeToggled()) - const headerStyles = { - backgroundColor: useTransform( - scrollY, - [0, 100], - [colord(theme.bg.background1).alpha(0).toHex(), colord(theme.bg.tertiary).alpha(0.9).toHex()] - ) - } + const gradientOpacity = useTransform(scrollY, [0, 100], [0, 1]) const titleStyles = { opacity: useTransform(scrollY, [0, 100, 100], [0, 0, 1]), transition: 'opacity 0.2s ease-out' } - if (invisible) return + if (invisible) return const openWalletConnectModal = () => dispatch(openModal({ name: 'WalletConnectModal' })) return ( - - {title} - - {networkStatus === 'offline' && ( - <> - - - - - - - )} - - {children && ( - <> - {children} - - - )} - - {isWalletUnlocked && ( - <> - - - - - )} - - - - - {isWalletUnlocked && ( - <> - - - - )} - - {defaultAddress && !isPassphraseUsed && ( - <> - - - - )} - - - + + + + {!platform.isMac && } + {title} + + {networkStatus === 'offline' && ( + <> + + + + + + )} + {children && ( + <> + {children} + + + )} + {isWalletUnlocked && ( + <> + + + + + )} + + {isWalletUnlocked && ( + <> + + ) } const AddressBadgeStyled = styled(AddressBadge)` width: 100%; + overflow: hidden; ` const OptionContent = styled.div` diff --git a/apps/desktop-wallet/src/components/DeltaPercentage.tsx b/apps/desktop-wallet/src/components/DeltaPercentage.tsx deleted file mode 100644 index 917425e8d5..0000000000 --- a/apps/desktop-wallet/src/components/DeltaPercentage.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ArrowDownRight, ArrowUpRight } from 'lucide-react' -import { useTranslation } from 'react-i18next' -import styled, { css, useTheme } from 'styled-components' - -import { discreetModeToggled } from '@/features/settings/settingsActions' -import { useAppDispatch, useAppSelector } from '@/hooks/redux' - -interface DeltaPercentageProps { - initialValue: number - latestValue: number - className?: string -} - -const DeltaPercentage = ({ initialValue, latestValue, className }: DeltaPercentageProps) => { - const theme = useTheme() - const dispatch = useAppDispatch() - const { t } = useTranslation() - const discreetMode = useAppSelector((state) => state.settings.discreetMode) - - const percentage = Math.round(((latestValue - initialValue) / initialValue) * 10000) / 100 - const isUp = percentage >= 0 - const color = discreetMode ? theme.font.primary : isUp ? theme.global.valid : theme.global.alert - - const DirectionArrow = percentage >= 0 ? ArrowUpRight : ArrowDownRight - - return ( - discreetMode && dispatch(discreetModeToggled())} - > - - {percentage}% - - ) -} - -export default DeltaPercentage - -const DeltaPercentageStyled = styled.div<{ discreetMode: boolean }>` - display: flex; - align-items: center; - height: 24px; - - ${({ discreetMode }) => - discreetMode && - css` - filter: blur(10px); - overflow: hidden; - cursor: pointer; - `} -` diff --git a/apps/desktop-wallet/src/components/Dividers/HorizontalDivider.tsx b/apps/desktop-wallet/src/components/Dividers/HorizontalDivider.tsx index 2ce33af965..8a86cf95e3 100644 --- a/apps/desktop-wallet/src/components/Dividers/HorizontalDivider.tsx +++ b/apps/desktop-wallet/src/components/Dividers/HorizontalDivider.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' export default styled.div<{ narrow?: boolean; secondary?: boolean }>` diff --git a/apps/desktop-wallet/src/components/DotIcon.tsx b/apps/desktop-wallet/src/components/DotIcon.tsx index 13756110af..165d0a64c7 100644 --- a/apps/desktop-wallet/src/components/DotIcon.tsx +++ b/apps/desktop-wallet/src/components/DotIcon.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' import { ReactComponent as DotSvg } from '@/images/dot.svg' diff --git a/apps/desktop-wallet/src/components/DropdownButton.tsx b/apps/desktop-wallet/src/components/DropdownButton.tsx new file mode 100644 index 0000000000..8e5ce6a506 --- /dev/null +++ b/apps/desktop-wallet/src/components/DropdownButton.tsx @@ -0,0 +1,53 @@ +import styled from 'styled-components' + +import Button, { ButtonProps } from '@/components/Button' +import Popup, { PopupProps, useElementAnchorCoordinates } from '@/components/Popup' +import ModalPortal from '@/modals/ModalPortal' + +interface DropdownProps extends ButtonProps { + options: Array +} + +export interface DropdownOption { + label: string + onClick: () => void +} + +const DropdownButton = ({ options, ...props }: DropdownProps) => { + const { containerRef, hookCoordinates, openModal, closeModal, isModalOpen } = useElementAnchorCoordinates() + + return ( +
+
+ ) +} + +export default DropdownButton + +const DropdownMenuModal = ({ options, ...props }: Pick & PopupProps) => { + const handleMenuItemClick = (option: DropdownOption) => { + option.onClick() + props.onClose() + } + + return ( + + {options.map((option) => ( + handleMenuItemClick(option)} transparent> + {option.label} + + ))} + + ) +} + +const MenuItem = styled(Button)` + padding: var(--spacing-3); + border-radius: 0; + margin: 0; + width: 100%; +` diff --git a/apps/desktop-wallet/src/components/Ellipsed.tsx b/apps/desktop-wallet/src/components/Ellipsed.tsx index 98db4b17ab..192a5af496 100644 --- a/apps/desktop-wallet/src/components/Ellipsed.tsx +++ b/apps/desktop-wallet/src/components/Ellipsed.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { HTMLAttributes, useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/components/EmptyPlaceholder.tsx b/apps/desktop-wallet/src/components/EmptyPlaceholder.tsx new file mode 100644 index 0000000000..9c86a70dfe --- /dev/null +++ b/apps/desktop-wallet/src/components/EmptyPlaceholder.tsx @@ -0,0 +1,38 @@ +import styled from 'styled-components' + +interface EmptyPlaceholderProps { + children: string + emoji?: string + className?: string +} + +const EmptyPlaceholder = ({ children, emoji, className }: EmptyPlaceholderProps) => ( + + {emoji && {emoji}} + {children} + +) + +export default EmptyPlaceholder + +const EmptyPlaceholderStyled = styled.div` + flex: 1; + display: flex; + flex-direction: column; + gap: 20px; + padding: 20px; + margin: 20px 0; + display: flex; + align-items: center; + justify-content: center; + border: 1px dashed ${({ theme }) => theme.border.primary}; + border-radius: var(--radius-big); +` + +const Emoji = styled.div` + font-size: 30px; +` + +const Text = styled.div` + color: ${({ theme }) => theme.font.tertiary}; +` diff --git a/apps/desktop-wallet/src/components/ExpandableSection.tsx b/apps/desktop-wallet/src/components/ExpandableSection.tsx index b5767c78a2..c841648921 100644 --- a/apps/desktop-wallet/src/components/ExpandableSection.tsx +++ b/apps/desktop-wallet/src/components/ExpandableSection.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { ChevronDown } from 'lucide-react' import { useEffect, useState } from 'react' diff --git a/apps/desktop-wallet/src/components/FloatingLogo.tsx b/apps/desktop-wallet/src/components/FloatingLogo.tsx deleted file mode 100644 index 7c3d8de4c2..0000000000 --- a/apps/desktop-wallet/src/components/FloatingLogo.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled from 'styled-components' - -import { ReactComponent as AlephiumLogoSVG } from '@/images/alephium_logo_monochrome.svg' -import { deviceBreakPoints } from '@/style/globalStyles' - -export default styled(AlephiumLogoSVG)<{ position?: 'top' | 'bottom' }>` - position: fixed; - left: var(--spacing-5); - width: 35px; - height: auto; - ${({ position }) => (position === 'bottom' ? 'bottom: var(--spacing-5)' : 'top: 50px')}; - - path { - fill: ${({ theme }) => - (theme.name === 'light' ? 'rgba(0, 0, 0, 0.04)' : 'rgba(255, 255, 255, 0.03)') + ' !important'}; - } - - @media ${deviceBreakPoints.mobile} { - display: none; - } -` diff --git a/apps/desktop-wallet/src/components/FocusableContent.tsx b/apps/desktop-wallet/src/components/FocusableContent.tsx index c230fe8408..2869495574 100644 --- a/apps/desktop-wallet/src/components/FocusableContent.tsx +++ b/apps/desktop-wallet/src/components/FocusableContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AnimatePresence } from 'framer-motion' import { ChevronsDownUp } from 'lucide-react' import { KeyboardEvent, useEffect, useRef } from 'react' diff --git a/apps/desktop-wallet/src/components/HashEllipsed.tsx b/apps/desktop-wallet/src/components/HashEllipsed.tsx index be86365dda..9c41832070 100644 --- a/apps/desktop-wallet/src/components/HashEllipsed.tsx +++ b/apps/desktop-wallet/src/components/HashEllipsed.tsx @@ -1,27 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { HTMLAttributes } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import ClipboardButton from '@/components/Buttons/ClipboardButton' import Ellipsed from '@/components/Ellipsed' +import Truncate from '@/components/Truncate' interface HashEllipsedProps extends HTMLAttributes { hash: string @@ -30,6 +13,8 @@ interface HashEllipsedProps extends HTMLAttributes { disableCopy?: boolean className?: string showSnackbarOnCopied?: boolean + truncate?: boolean + width?: number } const HashEllipsed = ({ @@ -39,31 +24,40 @@ const HashEllipsed = ({ tooltipText, className, showSnackbarOnCopied = true, + truncate = true, + width = 100, ...props }: HashEllipsedProps) => { const { t } = useTranslation() - return disableCopy ? ( - - - + const content = disableCopy ? ( + ) : ( - - - + + + ) + + return ( + {truncate ? {content} : content} ) } export default HashEllipsed const Container = styled.div` + position: relative; display: flex; align-items: center; overflow: hidden; ` + +const ClipboardButtonStyled = styled(ClipboardButton)` + position: absolute; + right: 6px; +` diff --git a/apps/desktop-wallet/src/components/HiddenLabel.tsx b/apps/desktop-wallet/src/components/HiddenLabel.tsx index 98b274a78a..12f3c55e80 100644 --- a/apps/desktop-wallet/src/components/HiddenLabel.tsx +++ b/apps/desktop-wallet/src/components/HiddenLabel.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' interface HiddenLabelProps { diff --git a/apps/desktop-wallet/src/components/IOList.tsx b/apps/desktop-wallet/src/components/IOList.tsx index 2dbc5db59d..4369f19095 100644 --- a/apps/desktop-wallet/src/components/IOList.tsx +++ b/apps/desktop-wallet/src/components/IOList.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash, GENESIS_TIMESTAMP } from '@alephium/shared' import { explorer as e } from '@alephium/web3' import _ from 'lodash' diff --git a/apps/desktop-wallet/src/components/InfoBox.tsx b/apps/desktop-wallet/src/components/InfoBox.tsx index 77654df994..1f450b7ad3 100644 --- a/apps/desktop-wallet/src/components/InfoBox.tsx +++ b/apps/desktop-wallet/src/components/InfoBox.tsx @@ -1,28 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' -import { motion } from 'framer-motion' import { LucideIcon } from 'lucide-react' import styled, { css, DefaultTheme, useTheme } from 'styled-components' -import { sectionChildrenVariants } from '@/components/PageComponents/PageContainers' - type InfoBoxImportance = 'default' | 'accent' | 'alert' | 'warning' export interface InfoBoxProps { @@ -36,7 +15,6 @@ export interface InfoBoxProps { small?: boolean short?: boolean contrast?: boolean - noBorders?: boolean className?: string } @@ -51,21 +29,14 @@ const InfoBox: FC = ({ onClick, short, children, - contrast, - noBorders + contrast }) => { const theme = useTheme() return (
- {label && } - + {label && } + {Icon && ( @@ -82,7 +53,7 @@ const InfoBox: FC = ({ const getImportanceColor = (theme: DefaultTheme, importance?: InfoBoxImportance) => importance ? { - default: theme.font.secondary, + default: theme.bg.accent, alert: theme.global.alert, warning: theme.global.highlight, accent: theme.global.accent @@ -92,7 +63,6 @@ const getImportanceColor = (theme: DefaultTheme, importance?: InfoBoxImportance) export default styled(InfoBox)` width: 100%; margin: 0 auto var(--spacing-4) auto; - margin-top: var(--spacing-2); max-width: ${({ small }) => (small ? '300px' : 'initial')}; line-height: 1.5em; ` @@ -107,7 +77,7 @@ const TextContainer = styled.div<{ wordBreak?: boolean; ellipsis?: boolean }>` flex: 2; font-weight: var(--fontWeight-medium); word-break: ${({ wordBreak }) => (wordBreak ? 'break-all' : 'initial')}; - text-align: left; + text-align: center; ${({ ellipsis }) => ellipsis @@ -120,11 +90,19 @@ const TextContainer = styled.div<{ wordBreak?: boolean; ellipsis?: boolean }>` `} ` -const StyledBox = styled(motion.div)<{ +const Label = styled.label` + display: block; + width: 100%; + margin-left: var(--spacing-3); + margin-bottom: 7px; + color: ${({ theme }) => theme.font.secondary}; + font-weight: var(--fontWeight-semiBold); +` + +const StyledBox = styled.div<{ importance?: InfoBoxImportance short?: boolean contrast?: boolean - noBorders?: boolean }>` padding: var(--spacing-3); height: ${({ short }) => (short ? 'var(--inputHeight)' : 'auto')}; @@ -139,19 +117,4 @@ const StyledBox = styled(motion.div)<{ border-radius: var(--radius-big); align-items: center; gap: 15px; - - ${({ theme, importance, noBorders }) => - !noBorders && - css` - border: 1px solid ${colord(getImportanceColor(theme, importance)).alpha(0.2).toHex()}; - `} -` - -const Label = styled(motion.label)` - display: block; - width: 100%; - margin-left: var(--spacing-3); - margin-bottom: 7px; - color: ${({ theme }) => theme.font.secondary}; - font-weight: var(--fontWeight-medium); ` diff --git a/apps/desktop-wallet/src/components/InfoMessage.tsx b/apps/desktop-wallet/src/components/InfoMessage.tsx index 9bb366f5ec..b912c6480b 100644 --- a/apps/desktop-wallet/src/components/InfoMessage.tsx +++ b/apps/desktop-wallet/src/components/InfoMessage.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { motion } from 'framer-motion' import { Info, X } from 'lucide-react' diff --git a/apps/desktop-wallet/src/components/InputFieldsColumn.tsx b/apps/desktop-wallet/src/components/InputFieldsColumn.tsx index 2810dc1f1c..d81922249f 100644 --- a/apps/desktop-wallet/src/components/InputFieldsColumn.tsx +++ b/apps/desktop-wallet/src/components/InputFieldsColumn.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' export const InputFieldsColumn = styled.div` diff --git a/apps/desktop-wallet/src/components/Inputs/AddressInput.tsx b/apps/desktop-wallet/src/components/Inputs/AddressInput.tsx index e3c7435d04..d924bca46d 100644 --- a/apps/desktop-wallet/src/components/Inputs/AddressInput.tsx +++ b/apps/desktop-wallet/src/components/Inputs/AddressInput.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { useMemo, useRef, useState } from 'react' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/components/Inputs/AddressSelect.tsx b/apps/desktop-wallet/src/components/Inputs/AddressSelect.tsx index 658162b232..ddaa597722 100644 --- a/apps/desktop-wallet/src/components/Inputs/AddressSelect.tsx +++ b/apps/desktop-wallet/src/components/Inputs/AddressSelect.tsx @@ -1,30 +1,11 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' -import { MoreVertical } from 'lucide-react' import { useEffect, useState } from 'react' import styled, { css } from 'styled-components' import AddressBadge from '@/components/AddressBadge' import { inputDefaultStyle, InputLabel, InputProps } from '@/components/Inputs' -import { MoreIcon, SelectContainer } from '@/components/Inputs/Select' -import { sectionChildrenVariants } from '@/components/PageComponents/PageContainers' +import { SelectOutterContainer } from '@/components/Inputs/Select' +import SelectMoreIcon from '@/components/Inputs/SelectMoreIcon' import AddressSelectModal from '@/modals/AddressSelectModal' import { useMoveFocusOnPreviousModal } from '@/modals/ModalContainer' import ModalPortal from '@/modals/ModalPortal' @@ -79,7 +60,6 @@ function AddressSelect({ return ( <> setCanBeAnimated(true)} custom={disabled} @@ -90,16 +70,8 @@ function AddressSelect({ simpleMode={simpleMode} noMargin={noMargin} > - {label && ( - - {label} - - )} - {!disabled && !simpleMode && ( - - - - )} + {label && {label}} + {!disabled && !simpleMode && } - + @@ -130,7 +102,7 @@ function AddressSelect({ export default AddressSelect -const AddressSelectContainer = styled(SelectContainer)>` +const AddressSelectContainer = styled(SelectOutterContainer)>` ${({ disabled }) => disabled && css` @@ -148,7 +120,7 @@ const AddressSelectContainer = styled(SelectContainer)>` ${({ isValid, Icon, simpleMode, value, label }) => - inputDefaultStyle(isValid || !!Icon, !!value, !!label, simpleMode ? 'normal' : 'big', false, true)}; + inputDefaultStyle(isValid || !!Icon, !!value, !!label, simpleMode ? 'normal' : 'big', true)}; display: flex; align-items: center; padding-right: 50px; diff --git a/apps/desktop-wallet/src/components/Inputs/ColorPicker.tsx b/apps/desktop-wallet/src/components/Inputs/ColorPicker.tsx index c94a233a73..0c4da2352a 100644 --- a/apps/desktop-wallet/src/components/Inputs/ColorPicker.tsx +++ b/apps/desktop-wallet/src/components/Inputs/ColorPicker.tsx @@ -1,24 +1,6 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AnimatePresence, motion } from 'framer-motion' import { useState } from 'react' -import { TwitterPicker } from 'react-color' +import { CirclePicker } from 'react-color' import { useDetectClickOutside } from 'react-detect-click-outside' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -54,17 +36,12 @@ const ColorPicker = ({ value, onChange }: ColorPickerProps) => { onClick={handlePopupOpen} onKeyDown={(e) => onEnterOrSpace(e, handlePopupOpen)} > - + {isPopupOpen && ( - + )} @@ -93,12 +70,15 @@ const Popup = styled(motion.div)` z-index: 1; position: absolute; top: calc(var(--inputHeight) + 10px); + background-color: ${({ theme }) => theme.bg.background1}; + padding: 10px; + border-radius: var(--radius-big); + box-shadow: ${({ theme }) => theme.shadow.secondary}; + border: 1px solid ${({ theme }) => theme.border.primary}; right: 0; ` -const TwitterPickerStyled = styled(TwitterPicker)`` - -const Circle = styled.div<{ color: string }>` +const Color = styled.div<{ color: string }>` width: 16px; height: 16px; border-radius: 16px; diff --git a/apps/desktop-wallet/src/components/Inputs/ColoredLabelInput.tsx b/apps/desktop-wallet/src/components/Inputs/ColoredLabelInput.tsx index d92eb2ad57..45143a08eb 100644 --- a/apps/desktop-wallet/src/components/Inputs/ColoredLabelInput.tsx +++ b/apps/desktop-wallet/src/components/Inputs/ColoredLabelInput.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useEffect, useState } from 'react' import styled from 'styled-components' @@ -46,25 +28,35 @@ const ColoredLabelInput = ({ label, onChange, value, className, id, maxLength }: }, [title, color, onChange]) return ( -
- setTitle(e.target.value)} - value={title} - id={id} - color={color} - maxLength={maxLength} - /> - -
+ <> + {label && } + + setTitle(e.target.value)} + value={title} + id={id} + color={color} + maxLength={maxLength} + /> + + + ) } -export default styled(ColoredLabelInput)` +export default ColoredLabelInput + +const ColoredLabelInputStyled = styled.div` display: flex; gap: 17px; width: 100%; align-items: center; position: relative; ` + +const Label = styled.label` + font-size: 13px; + font-weight: var(--fontWeight-semiBold); +` diff --git a/apps/desktop-wallet/src/components/Inputs/CompactToggle.tsx b/apps/desktop-wallet/src/components/Inputs/CompactToggle.tsx index 23f0f1a00f..b1f6c841bb 100644 --- a/apps/desktop-wallet/src/components/Inputs/CompactToggle.tsx +++ b/apps/desktop-wallet/src/components/Inputs/CompactToggle.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { LucideIcon } from 'lucide-react' import Button, { ButtonProps } from '@/components/Button' @@ -28,9 +10,14 @@ interface CompactToggleProps extends ButtonProps { } const CompactToggle = ({ toggled, onToggle, IconOn, IconOff, ...props }: CompactToggleProps) => ( - + + ) } @@ -93,8 +75,5 @@ export default PasswordConfirmation const Children = styled.div` width: 100%; -` - -const ButtonStyled = styled(Button)` margin-top: var(--spacing-4); ` diff --git a/apps/desktop-wallet/src/components/Popup.tsx b/apps/desktop-wallet/src/components/Popup.tsx index 9d26ae05e8..14cb1fa595 100644 --- a/apps/desktop-wallet/src/components/Popup.tsx +++ b/apps/desktop-wallet/src/components/Popup.tsx @@ -1,32 +1,15 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' -import { MouseEvent, ReactNode, useEffect, useRef, useState } from 'react' +import { MouseEvent, ReactNode, useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' -import { fadeInOutScaleFast, fastTransition } from '@/animations' +import { fadeInOutBottomFast, fastTransition } from '@/animations' import Scrollbar from '@/components/Scrollbar' import ModalContainer from '@/modals/ModalContainer' +import { appHeaderHeightPx } from '@/style/globalStyles' import { Coordinates } from '@/types/numbers' import { useWindowSize } from '@/utils/hooks' -interface PopupProps { +export interface PopupProps { onClose: () => void children?: ReactNode | ReactNode[] title?: string @@ -36,7 +19,7 @@ interface PopupProps { } const minMarginToEdge = 20 -const headerHeight = 50 +const headerHeight = 40 const Popup = ({ children, onClose, title, hookCoordinates, extraHeaderContent, minWidth = 200 }: PopupProps) => { const { height: windowHeight, width: windowWidth } = useWindowSize() // Recompute position on window resize @@ -78,19 +61,19 @@ const Popup = ({ children, onClose, title, hookCoordinates, extraHeaderContent, {title && (
-

{title}

- {extraHeaderContent} + {title} + {extraHeaderContent}
)} - {children} + {children}
) @@ -113,6 +96,40 @@ const Popup = ({ children, onClose, title, hookCoordinates, extraHeaderContent, export default Popup +export const useElementAnchorCoordinates = () => { + const containerRef = useRef(null) + const [hookCoordinates, setHookCoordinates] = useState(undefined) + const [isModalOpen, setIsModalOpen] = useState(false) + + const openModal = useCallback(() => { + setHookCoordinates(() => { + if (containerRef?.current) { + const containerElement = containerRef.current + const containerElementRect = containerElement.getBoundingClientRect() + + return { + x: containerElementRect.x + containerElement.clientWidth / 2, + y: Math.max(containerElementRect.y + containerElement.clientHeight / 2, appHeaderHeightPx) + } + } + }) + setIsModalOpen(true) + }, []) + + const closeModal = useCallback(() => { + setIsModalOpen(false) + containerRef.current?.focus() + }, []) + + return { + containerRef, + hookCoordinates, + isModalOpen, + openModal, + closeModal + } +} + const Hook = styled.div<{ hookCoordinates: Coordinates; contentWidth: number }>` position: absolute; display: flex; @@ -129,6 +146,7 @@ const Content = styled(motion.div)>` overflow-y: auto; display: flex; flex-direction: column; + padding-bottom: var(--spacing-1); min-width: ${({ minWidth }) => minWidth}px; max-height: 660px; @@ -137,17 +155,28 @@ const Content = styled(motion.div)>` box-shadow: ${({ theme }) => theme.shadow.tertiary}; border: 1px solid ${({ theme }) => theme.border.primary}; border-radius: var(--radius-big); - background-color: ${({ theme }) => theme.bg.primary}; + background-color: ${({ theme }) => theme.bg.background1}; ` const Header = styled.div<{ hasExtraContent: boolean }>` height: ${({ hasExtraContent }) => (hasExtraContent ? 'auto' : `${headerHeight}px`)}; - padding: var(--spacing-2) var(--spacing-2) var(--spacing-2) var(--spacing-4); - border-bottom: 1px solid ${({ theme }) => theme.border.primary}; - background-color: ${({ theme }) => theme.bg.tertiary}; - + min-height: ${headerHeight}px; + padding: 0 var(--spacing-1) 0 var(--spacing-3); display: flex; + flex-grow: 1; + flex-shrink: 0; align-items: center; z-index: 1; gap: var(--spacing-3); ` + +const Title = styled.span` + font-size: 12px; + color: ${({ theme }) => theme.font.secondary}; + text-transform: uppercase; + flex-shrink: 0; +` + +const ExtraHeaderContentContainer = styled.div` + flex: 1; +` diff --git a/apps/desktop-wallet/src/components/QRCode.tsx b/apps/desktop-wallet/src/components/QRCode.tsx index 8e3ef20a1a..16aae10da8 100644 --- a/apps/desktop-wallet/src/components/QRCode.tsx +++ b/apps/desktop-wallet/src/components/QRCode.tsx @@ -1,27 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { CopyIcon } from 'lucide-react' import { useCallback } from 'react' import RawQRCode from 'react-qr-code' import styled, { useTheme } from 'styled-components' -import Box from '@/components/Box' import Button from '@/components/Button' import { useAppDispatch } from '@/hooks/redux' import { copiedToClipboard, copyToClipboardFailed } from '@/storage/global/globalActions' @@ -55,10 +36,10 @@ const QRCode = ({ value, size, copyButtonLabel, className }: QRCodeProps) => { return (
- + {copyButtonLabel && ( - )} @@ -74,10 +55,13 @@ export default styled(QRCode)` gap: 10px; ` -const StyledBox = styled(Box)<{ size: number }>` +const StyledBox = styled.div<{ size: number }>` display: flex; align-items: center; justify-content: center; + border-radius: var(--radius-big); + border: 1px solid ${({ theme }) => theme.border.primary}; width: ${({ size }) => size + 25}px; height: ${({ size }) => size + 25}px; + overflow: hidden; ` diff --git a/apps/desktop-wallet/src/components/Scrollbar.tsx b/apps/desktop-wallet/src/components/Scrollbar.tsx index 234d1265a0..2959c9c54a 100644 --- a/apps/desktop-wallet/src/components/Scrollbar.tsx +++ b/apps/desktop-wallet/src/components/Scrollbar.tsx @@ -1,200 +1,33 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -// Used as reference: https://github.com/xobotyi/react-scrollbars-custom/issues/46#issuecomment-897506147 - -import { colord } from 'colord' import { useMotionValue } from 'framer-motion' -import { isNumber } from 'lodash' -import { CSSProperties, useCallback, useRef, useState, WheelEvent } from 'react' -import Scrollbar, { ScrollbarProps } from 'react-scrollbars-custom' -import { ElementPropsWithElementRef, ScrollState } from 'react-scrollbars-custom/dist/types/types' -import { useTheme } from 'styled-components' - -import { ScrollContextProvider, ScrollContextType, ScrollDirection } from '@/contexts/scroll' - -const scrollDirectionDeltaThreshold = 10 - -const paddingRight = '6px' -const width = `calc(6px + ${paddingRight})` - -const paddingTop = '6px' -const height = `calc(6px + ${paddingTop})` +import { ReactNode, UIEvent, useRef } from 'react' +import { CustomScroll } from 'react-custom-scroll' -const createScrollbarPiece = (regular: CSSProperties, additional?: CSSProperties, useAdditionalStyling?: boolean) => ({ - renderer: ({ elementRef, style, ...restProps }: ElementPropsWithElementRef) => { - // The only way to know the scrollbar is being rendered is if bottom > 0... - const overflowStyle = { - overflow: - (restProps.key === 'ScrollbarsCustom-Wrapper' && style?.bottom && isNumber(style.bottom) && style.bottom > 0) || - (style?.right && isNumber(style.right) && style.right > 0) - ? 'hidden' - : 'unset' - } - - return ( -
- ) - } -}) +import { ScrollContextProvider, ScrollContextType } from '@/contexts/scroll' -interface ScrollbarCustomProps extends ScrollbarProps { - // When children have dynamic sizes, set this to true (like the AddressSummaryCards in OverviewPage/Header). - isDynamic?: boolean +interface ScrollbarCustomProps { + children?: ReactNode + className?: string + onScroll?: (scrollTop: number) => void } -const ScrollbarCustom = ({ isDynamic, noScrollX, ...props }: ScrollbarCustomProps) => { - const theme = useTheme() +const ScrollbarCustom = ({ children, className, onScroll }: ScrollbarCustomProps) => { const scrollY = useMotionValue(0) - const scrollDirection = useMotionValue(undefined as ScrollDirection) - - const [isScrolling, setIsScrolling] = useState(false) - const [isMouseOver, setIsMouseOver] = useState(false) - - const scrollerElemRef = useRef(null) - const prevScrollY = useRef(0) - const contextValueRef = useRef({ scrollY, scrollDirection }) - - const isShow = isScrolling || isMouseOver - - const onScrollStart = () => setIsScrolling(true) - const onScrollStop = () => setIsScrolling(false) - const onMouseEnter = () => setIsMouseOver(true) - const onMouseLeave = () => setIsMouseOver(false) - - const onWheelY = useCallback( - (e: WheelEvent) => { - if (!scrollerElemRef || !scrollerElemRef.current) return - - scrollerElemRef.current.scrollTop += e.deltaY - }, - [scrollerElemRef] - ) - - const transparencyStyle = { - backgroundColor: 'transparent', - opacity: isShow ? 1 : 0, - transition: 'opacity 0.2s ease-out', - zIndex: 1 - } - - const trackXProps = createScrollbarPiece({ height, paddingTop }, transparencyStyle, true) - - const trackYProps = { - renderer: ({ elementRef, style, ...restProps }: ElementPropsWithElementRef) => ( -
- ) - } - - const thumbProps = createScrollbarPiece({ backgroundColor: colord(theme.font.primary).alpha(0.15).toHex() }) - - const rendererProps = createScrollbarPiece( - {}, - { position: 'relative' as const, width: 'unset', height: 'unset', overflow: 'hidden' }, - isDynamic - ).renderer - - const wrapperProps = createScrollbarPiece( - { right: 0 }, - { position: 'relative' as const, height: '100%', top: !noScrollX ? '-16px' : '' }, - isDynamic - ) + const contextValueRef = useRef({ scrollY }) - const scrollerProps = { - renderer: ({ elementRef, style, ...restProps }: ElementPropsWithElementRef) => { - const onElementRef = (element: HTMLDivElement | null) => { - if (!elementRef) return + const handleScrollUpdate = (e: UIEvent) => { + const scrollTop = (e.target as HTMLElement).scrollTop - scrollerElemRef.current = element - elementRef(element) - } + scrollY.set(scrollTop) - return ( -
- ) + if (onScroll) { + onScroll(scrollTop) } } - const contentProps = createScrollbarPiece( - { display: 'block', height: '100%' }, - { position: 'unset' as const, height: '100%' }, - isDynamic - ) - - const handleScrollUpdate = (s: ScrollState) => { - contextValueRef.current.scrollY?.set(s.scrollTop) - - const delta = prevScrollY.current - s.scrollTop - const direction = delta > 0 ? 'up' : 'down' - - if (s.scrollTop === 0) { - contextValueRef.current.scrollDirection?.set(undefined) - } else if (direction === 'up' && delta > scrollDirectionDeltaThreshold) { - contextValueRef.current.scrollDirection?.set('up') - } else if (direction === 'down' && delta < -scrollDirectionDeltaThreshold) { - contextValueRef.current.scrollDirection?.set('down') - } - - prevScrollY.current = s.scrollTop - } - - // react-scrollbars-custom has a type issue where you can't just spread props - // onto the component. That's why needed props are added as necessary. return ( - - {props.children} - + + {children} + ) } diff --git a/apps/desktop-wallet/src/components/SkeletonLoader.tsx b/apps/desktop-wallet/src/components/SkeletonLoader.tsx index 92f2faa411..e91e9d04df 100644 --- a/apps/desktop-wallet/src/components/SkeletonLoader.tsx +++ b/apps/desktop-wallet/src/components/SkeletonLoader.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { CSSProperties } from 'react' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/components/SnackbarManager.tsx b/apps/desktop-wallet/src/components/SnackbarManager.tsx index 77e66ac917..3dc18dac7a 100644 --- a/apps/desktop-wallet/src/components/SnackbarManager.tsx +++ b/apps/desktop-wallet/src/components/SnackbarManager.tsx @@ -1,25 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { memo, useEffect } from 'react' import styled from 'styled-components' -import { fadeInBottom, fadeOut } from '@/animations' +import { fadeInTop, fadeOutTop } from '@/animations' import LedgerAddressDiscoverySnackbar from '@/features/ledger/LedgerAddressDiscoverySnackbar' import SentTransactionSnackbarPopup from '@/features/send/sentTransactions/SentTransactionSnackbarPopup' import { selectAllSentTransactions } from '@/features/send/sentTransactions/sentTransactionsSelectors' @@ -27,7 +9,7 @@ import SnackbarBox from '@/features/snackbar/SnackbarBox' import { useAppDispatch, useAppSelector } from '@/hooks/redux' import ModalPortal from '@/modals/ModalPortal' import { snackbarDisplayTimeExpired } from '@/storage/global/globalActions' -import { deviceBreakPoints, walletSidebarWidthPx } from '@/style/globalStyles' +import { deviceBreakPoints } from '@/style/globalStyles' import { SnackbarMessage } from '@/types/snackbar' const SnackbarManager = () => { @@ -67,7 +49,7 @@ const SnackbarPopup = memo(({ message }: { message: Required }) }, [dispatch, message]) return ( - + {message.text} ) @@ -75,9 +57,15 @@ const SnackbarPopup = memo(({ message }: { message: Required }) export const SnackbarManagerContainer = styled.div` position: fixed; - bottom: 0; - left: ${walletSidebarWidthPx}px; + top: 0; + left: 0; + right: 0; z-index: 3; + display: flex; + flex-direction: column-reverse; + align-items: center; + justify-content: center; + pointer-events: none; @media ${deviceBreakPoints.mobile} { justify-content: center; diff --git a/apps/desktop-wallet/src/components/Spinner.tsx b/apps/desktop-wallet/src/components/Spinner.tsx index 746b49adb6..1b08a14ec3 100644 --- a/apps/desktop-wallet/src/components/Spinner.tsx +++ b/apps/desktop-wallet/src/components/Spinner.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { Loader } from 'lucide-react' diff --git a/apps/desktop-wallet/src/components/SplashScreen.tsx b/apps/desktop-wallet/src/components/SplashScreen.tsx index 696fc61cc2..d7cfec26b8 100644 --- a/apps/desktop-wallet/src/components/SplashScreen.tsx +++ b/apps/desktop-wallet/src/components/SplashScreen.tsx @@ -1,27 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { useState } from 'react' import styled from 'styled-components' -import { slowTransition } from '@/animations' -import alephiumLogo from '@/images/alephium_logo_light.svg' +import { fastTransition } from '@/animations' +import alephiumLogo from '@/images/alephium_logo_monochrome.svg' const SplashScreen = () => { const [splashScreenVisible, setSplashScreenVisible] = useState(true) @@ -32,13 +14,13 @@ const SplashScreen = () => { setSplashScreenVisible(false)} > @@ -57,8 +39,8 @@ const StyledSplashScreen = styled(motion.div)` display: flex; justify-content: center; align-items: center; - z-index: 1; - background-color: ${({ theme }) => theme.bg.primary}; + z-index: 2; + background-color: ${({ theme }) => theme.bg.background1}; ` const AlephiumLogoContainer = styled(motion.div)` diff --git a/apps/desktop-wallet/src/components/TabBar.tsx b/apps/desktop-wallet/src/components/TabBar.tsx deleted file mode 100644 index e909501337..0000000000 --- a/apps/desktop-wallet/src/components/TabBar.tsx +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ChevronRight } from 'lucide-react' -import { useTranslation } from 'react-i18next' -import styled, { css } from 'styled-components' - -import ActionLink from '@/components/ActionLink' - -export interface TabItem { - value: T - label: string -} - -export interface TabBarProps { - items: TabItem[] - onTabChange: (tab: TabItem) => void - activeTab: TabItem - linkText?: string - onLinkClick?: () => void - TabComponent?: typeof Tab - className?: string -} - -const TabBar = ({ - items, - onTabChange, - activeTab, - linkText, - onLinkClick, - TabComponent = Tab, - className -}: TabBarProps) => { - const { t } = useTranslation() - - return ( - - {items.map((item) => { - const isActive = activeTab.value === item.value - - return ( - onTabChange(item)} - onKeyDown={() => onTabChange(item)} - role="tab" - tabIndex={0} - aria-selected={isActive} - isActive={isActive} - > - {item.label} - - ) - })} - {linkText && onLinkClick && ( - - {linkText} - - )} - - ) -} - -export default TabBar - -const TabBarStyled = styled.div` - display: flex; - height: 55px; -` - -export const Tab = styled.div<{ isActive: boolean }>` - flex: 1; - display: flex; - min-width: 50px; - text-align: center; - justify-content: center; - align-items: center; - background-color: ${({ isActive, theme }) => (isActive ? theme.bg.background1 : 'transparent')}; - border: 1px solid ${({ theme }) => theme.border.primary}; - border-bottom: none; - cursor: pointer; - font-size: 14px; - font-weight: var(--fontWeight-semiBold); - margin-bottom: -1px; - - ${({ isActive, theme }) => - isActive - ? css` - color: ${theme.font.primary}; - ` - : css` - color: ${theme.font.tertiary}; - `} - - &:not(:first-child) { - border-left: none; - } - - &:hover { - color: ${({ theme }) => theme.font.primary}; - } -` - -const TabLabel = styled.span<{ isActive: boolean }>` - ${({ isActive }) => - !isActive && - css` - filter: saturate(10%); - `} -` - -const ActionLinkStyled = styled(ActionLink)` - margin-left: auto; - margin-right: 20px; -` diff --git a/apps/desktop-wallet/src/components/Table.tsx b/apps/desktop-wallet/src/components/Table.tsx index 0a834b415a..bb70845a54 100644 --- a/apps/desktop-wallet/src/components/Table.tsx +++ b/apps/desktop-wallet/src/components/Table.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { ChevronsUpDown } from 'lucide-react' import { HTMLProps } from 'react' @@ -24,16 +6,15 @@ import styled, { css } from 'styled-components' import Button from '@/components/Button' -type AlignType = 'start' | 'center' | 'end' - export interface TableProps { minWidth?: string className?: string } interface TableCellProps { - truncate?: boolean - align?: AlignType + fixedWidth?: number | string + noBorder?: boolean + align?: 'left' | 'center' | 'right' } const Table: FC = ({ className, children, minWidth }) => ( @@ -48,12 +29,6 @@ export default Table const TableWrapper = styled(motion.div)>` width: 100%; - overflow: auto; - border-radius: var(--radius-big); - border: 1px solid ${({ theme }) => theme.border.primary}; - - background-color: ${({ theme }) => theme.bg.primary}; - box-shadow: ${({ theme }) => theme.shadow.primary}; ${({ minWidth }) => minWidth && @@ -63,26 +38,18 @@ const TableWrapper = styled(motion.div)>` ` export const TableCell = styled.div` - display: inline-flex; - font-weight: var(--fontWeight-semiBold); + display: flex; + flex: ${({ fixedWidth }) => (fixedWidth ? '0' : '1')}; + align-items: center; + justify-content: ${({ align }) => (align === 'right' ? 'flex-end' : align === 'center' ? 'center' : 'flex-start')}; position: relative; - ${({ truncate }) => - truncate && - css` - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - `} - - &:not(:last-child) { - padding-right: var(--spacing-5); - } - ${({ align }) => - align && - css` - justify-self: ${align}; - text-align: ${align === 'end' ? 'right' : 'auto'}; - `}; + border-bottom: ${({ theme, noBorder }) => `1px solid ${noBorder ? 'transparent' : theme.border.secondary}`}; + padding: 10px 0; + min-width: ${({ fixedWidth }) => + fixedWidth ? (typeof fixedWidth === 'number' ? `${fixedWidth}px` : fixedWidth) : 'auto'}; + min-height: 52px; + overflow: hidden; + text-overflow: ellipsis; ` interface TableColumnsProps extends Omit, 'ref'> { @@ -90,20 +57,7 @@ interface TableColumnsProps extends Omit, 'ref'> { } const TableColumns = styled.div` - display: grid; - ${({ columnWidths }) => - columnWidths - ? css` - grid-template-columns: ${columnWidths.map((columnWidth) => `minmax(${columnWidth || '0px'}, 1fr)`).join(' ')}; - ` - : css` - grid-auto-columns: minmax(0px, 1fr); - grid-auto-flow: column; - `}; - - align-items: center; - padding: 18px 20px; - min-height: 55px; + display: flex; ` export interface TableRowProps extends TableColumnsProps { @@ -111,20 +65,29 @@ export interface TableRowProps extends TableColumnsProps { } export const TableRow = styled(TableColumns)` - border-bottom: 1px solid ${({ theme }) => theme.border.secondary}; + position: relative; + z-index: 0; - &:last-child { - border-bottom: none; - border-bottom-left-radius: var(--radius-small); - border-bottom-right-radius: var(--radius-small); - } + min-height: var(--inputHeight); - ${({ onClick }) => + ${({ onClick, theme }) => onClick && css` &:hover { cursor: pointer; - background-color: ${({ theme }) => theme.bg.hover}; + + &::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: -8px; + right: -8px; + + border-radius: var(--radius-big); + background-color: ${theme.bg.hover}; + z-index: -1; + } } `} @@ -132,7 +95,6 @@ export const TableRow = styled(TableColumns)` blinking && css` opacity: 0.5; - background: linear-gradient(90deg, rgba(200, 200, 200, 0.4), rgba(200, 200, 200, 0.05)); background-size: 400% 400%; animation: gradient 2s ease infinite; @@ -152,17 +114,29 @@ export const TableRow = styled(TableColumns)` } } `} + + &:last-child { + ${TableCell} { + border-bottom: none; + } + } ` export const TableFooter = styled(TableColumns)`` export const TableCellPlaceholder = styled(TableCell)` color: ${({ theme }) => theme.font.tertiary}; + align-self: center; + justify-self: center; ` -export const TableHeader: FC<{ title: string; className?: string }> = ({ title, children, className }) => ( +interface TableHeaderProps extends TableRowProps { + title?: string +} + +export const TableHeader = ({ title, children, className }: TableHeaderProps) => ( - {title} + {title && {title}} {children} ) @@ -170,14 +144,18 @@ export const TableHeader: FC<{ title: string; className?: string }> = ({ title, const TableHeaderRow = styled(TableRow)` display: flex; justify-content: space-between; - height: 55px; - background-color: ${({ theme }) => theme.bg.tertiary}; - border-bottom: 1px solid ${({ theme }) => theme.border.secondary}; - padding-right: var(--spacing-2); + align-items: center; + + color: ${({ theme }) => theme.font.tertiary}; + + ${TableCell} { + min-height: 48px; + border: none; + } ` const TableTitle = styled.div` - font-size: 15px; + font-size: 13px; font-weight: var(--fontWeight-semiBold); ` @@ -209,31 +187,10 @@ const ExpandRowStyled = styled.div` pointer-events: none; ${({ theme }) => { - const gradientMaxOpacity = theme.name === 'light' ? 0.05 : 0.25 + const gradientMaxOpacity = theme.name === 'light' ? 0.02 : 0.25 return css` background: linear-gradient(0deg, rgba(0, 0, 0, ${gradientMaxOpacity}) 0%, rgba(0, 0, 0, 0) 100%); ` }} ` - -export const ExpandableTable = styled(Table)<{ isExpanded: boolean; maxHeightInPx?: number }>` - max-height: ${({ maxHeightInPx }) => maxHeightInPx && maxHeightInPx}px; - overflow: hidden; - position: relative; - height: 100%; - - ${({ isExpanded }) => - isExpanded && - css` - max-height: none; - box-shadow: ${({ theme }) => theme.shadow.tertiary}; - `} - - &:hover { - ${ExpandRowStyled} { - opacity: 1; - z-index: 3; // Make sure it is displayed above copy btns - } - } -` diff --git a/apps/desktop-wallet/src/components/TableCellAmount.tsx b/apps/desktop-wallet/src/components/TableCellAmount.tsx deleted file mode 100644 index 881dc6706f..0000000000 --- a/apps/desktop-wallet/src/components/TableCellAmount.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled, { css } from 'styled-components' - -export default styled.div<{ color?: string }>` - display: flex; - flex-direction: column; - align-items: flex-end; - flex-grow: 1; - gap: 6px; - font-weight: var(--fontWeight-semiBold); - - ${({ color }) => - color && - css` - color: ${color}; - `} -` diff --git a/apps/desktop-wallet/src/components/TableTabBar.tsx b/apps/desktop-wallet/src/components/TableTabBar.tsx index af78c761f6..058e4bf860 100644 --- a/apps/desktop-wallet/src/components/TableTabBar.tsx +++ b/apps/desktop-wallet/src/components/TableTabBar.tsx @@ -1,24 +1,6 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. +import styled from 'styled-components' -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled, { css } from 'styled-components' - -import TabBar, { Tab, TabBarProps } from '@/components/TabBar' +import TabBar, { Tab, TabBarProps } from '@/components/tabs/TabBar' const TableTabBar = (props: TabBarProps) => ( @@ -27,29 +9,10 @@ const TableTabBar = (props: TabBarProps) => ( export default TableTabBar const TableTabBarStyled = styled(TabBar)` - background-color: ${({ theme }) => theme.bg.secondary}; + height: 50px; + gap: 10px; ` as typeof TabBar const TableTab = styled(Tab)` - min-width: 60px; - background-color: ${({ isActive, theme }) => (isActive ? theme.bg.primary : theme.bg.tertiary)}; - border: none; - - border-bottom: 1px solid ${({ theme }) => theme.border.secondary}; - margin-bottom: 0; - - ${({ isActive, theme }) => - isActive && - css` - border-bottom: 1px solid transparent; - `} - - &:not(:last-child) { - border-right: 1px solid ${({ theme }) => theme.border.primary}; - } - - &:first-child, - &:last-child { - border-radius: 0; - } + overflow: hidden; ` diff --git a/apps/desktop-wallet/src/components/ThemeSwitcher.tsx b/apps/desktop-wallet/src/components/ThemeSwitcher.tsx index 5733bf47d8..24aa3891a8 100644 --- a/apps/desktop-wallet/src/components/ThemeSwitcher.tsx +++ b/apps/desktop-wallet/src/components/ThemeSwitcher.tsx @@ -1,22 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { colord } from 'colord' import { motion } from 'framer-motion' import { Moon, Sun } from 'lucide-react' import styled from 'styled-components' @@ -30,10 +11,10 @@ interface ThemeSwitcherProps { className?: string } -const switcherSize = 32 +const switcherSize = 34 -const lightColor = '#fab01e' -const darkColor = '#422c08' +const lightColor = '#ffc95c' +const darkColor = '#ffb623' const ThemeSwitcher = ({ className }: ThemeSwitcherProps) => { const theme = useAppSelector((state) => state.global.theme) @@ -56,11 +37,11 @@ const ThemeSwitcher = ({ className }: ThemeSwitcherProps) => { onKeyDown={(e) => onEnterOrSpace(e, handleThemeToggle)} > - - + + - - + +
diff --git a/apps/desktop-wallet/src/components/TimeSince.tsx b/apps/desktop-wallet/src/components/TimeSince.tsx index 2933c81ee9..f7c55d0f4c 100644 --- a/apps/desktop-wallet/src/components/TimeSince.tsx +++ b/apps/desktop-wallet/src/components/TimeSince.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import dayjs from 'dayjs' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/components/TitleBar.tsx b/apps/desktop-wallet/src/components/TitleBar.tsx new file mode 100644 index 0000000000..e83bde7b40 --- /dev/null +++ b/apps/desktop-wallet/src/components/TitleBar.tsx @@ -0,0 +1,94 @@ +import { Minus, Square, X } from 'lucide-react' +import styled from 'styled-components' + +const TitleBar = () => { + const handleMinimize = () => { + window.electron?.window.minimize() + } + + const handleMaximize = () => { + window.electron?.window?.maximize() + } + + const handleClose = () => { + window.electron?.window.close() + } + + return ( + + + + + + + + + + + + + + + + + + + + + ) +} + +export default TitleBar + +const IconStyled = styled.div` + color: ${({ theme }) => theme.font.secondary}; +` + +const DragRegion = styled.div` + flex: 1; +` + +const WindowControls = styled.div` + display: flex; + -webkit-app-region: no-drag; +` + +const TitleBarContainer = styled.div` + position: fixed; + top: 0; + right: 0; + height: 32px; + width: 100%; + display: flex; + -webkit-app-region: drag; + background-color: ${({ theme }) => theme.bg.secondary}; +` + +const ControlButton = styled.button` + width: 46px; + height: 32px; + border: none; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + + &:hover { + cursor: pointer; + background-color: ${({ theme }) => theme.bg.highlight}; + + ${IconStyled} { + color: ${({ theme }) => theme.font.primary}; + } + } +` + +const CloseButton = styled(ControlButton)` + &:hover { + background-color: ${({ theme }) => theme.global.alert}; + + ${IconStyled} { + color: ${({ theme }) => theme.font.primary}; + } + } +` diff --git a/apps/desktop-wallet/src/components/ToggleSection.tsx b/apps/desktop-wallet/src/components/ToggleSection.tsx index 5efd7047e5..33196e17dc 100644 --- a/apps/desktop-wallet/src/components/ToggleSection.tsx +++ b/apps/desktop-wallet/src/components/ToggleSection.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { motion } from 'framer-motion' import { ReactNode, useEffect, useState } from 'react' @@ -81,8 +63,8 @@ const ToggleSection = ({ export default styled(ToggleSection)` display: flex; flex-direction: column; - border: 1px solid ${({ theme }) => theme.border.primary}; - background-color: ${({ theme }) => colord(theme.bg.background2).alpha(0.5).toHex()}; + border: 1px solid ${({ theme }) => theme.border.secondary}; + background-color: ${({ theme }) => colord(theme.bg.tertiary).alpha(0.02).toHex()}; border-radius: var(--radius-big); padding-bottom: 16px; ` diff --git a/apps/desktop-wallet/src/components/TokenBadge.tsx b/apps/desktop-wallet/src/components/TokenBadge.tsx index fbcd6c95ca..aba42a2062 100644 --- a/apps/desktop-wallet/src/components/TokenBadge.tsx +++ b/apps/desktop-wallet/src/components/TokenBadge.tsx @@ -1,29 +1,12 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - +import { colord } from 'colord' import { memo } from 'react' -import styled, { css } from 'styled-components' +import styled, { css, useTheme } from 'styled-components' -import useFetchToken, { isFT, isNFT } from '@/api/apiDataHooks/token/useFetchToken' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' import Amount from '@/components/Amount' import AssetLogo from '@/components/AssetLogo' import SkeletonLoader from '@/components/SkeletonLoader' -import { TokenId } from '@/types/tokens' +import { isFT, isNFT, TokenId } from '@/types/tokens' export interface TokenBadgeStyleProps { withBorder?: boolean @@ -31,6 +14,7 @@ export interface TokenBadgeStyleProps { className?: string showNftName?: boolean showAmount?: boolean + displaySign?: boolean } interface TokenBadgeProps extends TokenBadgeStyleProps { @@ -39,29 +23,64 @@ interface TokenBadgeProps extends TokenBadgeStyleProps { isLoadingAmount?: boolean } -const TokenBadge = memo(({ tokenId, className, ...props }: TokenBadgeProps) => { +const TokenBadge = memo(({ tokenId, className, displaySign, withBackground, amount, ...props }: TokenBadgeProps) => { const { data: token } = useFetchToken(tokenId) + const theme = useTheme() + + if (!token) return null const tooltipContent = isFT(token) || isNFT(token) ? token.name : tokenId return ( - - - - {(props.showNftName || props.showAmount) && } + 0 ? theme.global.valid : theme.global.alert) : undefined} + style={{ + backgroundColor: displaySign + ? amount && amount > 0 + ? colord(theme.global.valid).alpha(0.02).toHex() + : colord(theme.font.highlight).alpha(0.02).toHex() + : colord(theme.bg.highlight).alpha(0.02).toHex() + }} + > + {(props.showNftName || props.showAmount) && ( + + )} + ) }) -const TokenBadgeText = ({ tokenId, amount, isLoadingAmount, showNftName, showAmount }: TokenBadgeProps) => { +const TokenBadgeText = ({ + tokenId, + amount, + isLoadingAmount, + showNftName, + showAmount, + displaySign +}: TokenBadgeProps) => { const { data: token, isLoading: isLoadingToken } = useFetchToken(tokenId) if (isLoadingToken) return + if (!token) return null + if (isNFT(token) && showNftName) return {token.name} if (!isNFT(token) && showAmount) - return + return ( + + ) return null } @@ -84,7 +103,6 @@ const TokenBadgeStyled = styled.div withBackground && css` - background-color: ${({ theme }) => theme.bg.highlight}; border-radius: var(--radius-huge); padding: 4px 10px 4px 4px; `} diff --git a/apps/desktop-wallet/src/components/Tooltip.tsx b/apps/desktop-wallet/src/components/Tooltip.tsx index 682d249918..e19c0da0df 100644 --- a/apps/desktop-wallet/src/components/Tooltip.tsx +++ b/apps/desktop-wallet/src/components/Tooltip.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import 'react-tooltip/dist/react-tooltip.css' import { Tooltip } from 'react-tooltip' @@ -26,5 +8,5 @@ export type HasTooltip = T & { tooltip?: string } export default styled(Tooltip)` background-color: rgb(5, 5, 5) !important; color: #fff; - z-index: 2; + z-index: 3; ` diff --git a/apps/desktop-wallet/src/components/Tooltips.tsx b/apps/desktop-wallet/src/components/Tooltips.tsx index 4cf85736bd..2c50d3a753 100644 --- a/apps/desktop-wallet/src/components/Tooltips.tsx +++ b/apps/desktop-wallet/src/components/Tooltips.tsx @@ -1,26 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import Tooltip from '@/components/Tooltip' const Tooltips = () => ( <> - + diff --git a/apps/desktop-wallet/src/components/Truncate.tsx b/apps/desktop-wallet/src/components/Truncate.tsx index c0c6240bcc..b31dbd6d73 100644 --- a/apps/desktop-wallet/src/components/Truncate.tsx +++ b/apps/desktop-wallet/src/components/Truncate.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { HTMLAttributes } from 'react' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/components/WalletNameButton.tsx b/apps/desktop-wallet/src/components/WalletNameButton.tsx new file mode 100644 index 0000000000..3a46db9b19 --- /dev/null +++ b/apps/desktop-wallet/src/components/WalletNameButton.tsx @@ -0,0 +1,96 @@ +import styled from 'styled-components' + +import SelectMoreIcon from '@/components/Inputs/SelectMoreIcon' +import { openModal } from '@/features/modals/modalActions' +import { useAppDispatch, useAppSelector } from '@/hooks/redux' +import { sidebarExpandThresholdPx } from '@/style/globalStyles' +import { useDisplayColor, useHashToColor, walletColorPalette } from '@/utils/colors' +import { getInitials, onEnterOrSpace } from '@/utils/misc' + +const WalletNameButton = () => { + const dispatch = useAppDispatch() + const activeWalletName = useAppSelector((s) => s.activeWallet.name) + const activeWalletHash = useAppSelector((s) => s.activeWallet.id) + const walletColor = useDisplayColor(useHashToColor(activeWalletHash), walletColorPalette, 'vivid') + + if (!activeWalletName) return null + + const openCurrentWalletModal = () => dispatch(openModal({ name: 'CurrentWalletModal' })) + + return ( + onEnterOrSpace(e, openCurrentWalletModal)} + key={`initials-${activeWalletName}`} + role="button" + tabIndex={0} + > + + {getInitials(activeWalletName)} + {activeWalletName} + + + + + + ) +} + +export default WalletNameButton + +const WalletNameButtonStyled = styled.div` + position: relative; + border-radius: var(--radius-medium); + display: flex; + align-items: center; + padding: 4px; + border: 1px solid ${({ theme }) => theme.border.secondary}; + margin-bottom: var(--spacing-3); + height: 36px; + + overflow: hidden; + z-index: 1; + + &:hover { + cursor: pointer; + background-color: ${({ theme }) => theme.bg.primary}; + } + + @media (max-width: ${sidebarExpandThresholdPx}px) { + justify-content: center; + } +` + +const WalletNameContainer = styled.div` + display: flex; + align-items: center; + gap: 10px; +` + +const Name = styled.span` + flex: 1; + text-align: center; + @media (max-width: ${sidebarExpandThresholdPx}px) { + display: none; + } +` + +const Initials = styled.div` + display: flex; + align-items: center; + justify-content: center; + color: ${({ theme }) => theme.font.contrastPrimary}; + height: 26px; + width: 26px; + border-radius: var(--radius-tiny); + font-size: 12px; +` + +const SelectMoreIconContainer = styled.div` + display: flex; + align-items: center; + + @media (max-width: ${sidebarExpandThresholdPx}px) { + display: none; + } +` diff --git a/apps/desktop-wallet/src/components/WorthOverview.tsx b/apps/desktop-wallet/src/components/WorthOverview.tsx index efd39420a5..5c7679be9d 100644 --- a/apps/desktop-wallet/src/components/WorthOverview.tsx +++ b/apps/desktop-wallet/src/components/WorthOverview.tsx @@ -1,37 +1,18 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' import Amount, { AmountLoaderProps } from '@/components/Amount' interface WorthOverviewProps extends AmountLoaderProps { worth: number - overrideWorth?: number + className?: string } -const WorthOverview = ({ overrideWorth, worth, ...props }: WorthOverviewProps) => ( - +const WorthOverview = ({ worth, ...props }: WorthOverviewProps) => ( + ) export default WorthOverview const WorthOverviewStyled = styled(Amount)` - font-size: 34px; - font-weight: var(--fontWeight-bold); + font-size: 38px; ` diff --git a/apps/desktop-wallet/src/components/amounts/FTAmounts.tsx b/apps/desktop-wallet/src/components/amounts/FTAmounts.tsx new file mode 100644 index 0000000000..990b990240 --- /dev/null +++ b/apps/desktop-wallet/src/components/amounts/FTAmounts.tsx @@ -0,0 +1,39 @@ +import { useTranslation } from 'react-i18next' +import styled, { useTheme } from 'styled-components' + +import Amount, { TokenAmountProps } from '@/components/Amount' +import { TokenId } from '@/types/tokens' + +export interface FTAmountsBaseProp { + isLoading: boolean + tokenId: TokenId + totalBalance?: bigint + availableBalance?: bigint +} + +type FTAmountsProps = FTAmountsBaseProp & Omit + +const FTAmounts = ({ totalBalance, availableBalance, isLoading, ...props }: FTAmountsProps) => { + const { t } = useTranslation() + const theme = useTheme() + + return ( + <> + {totalBalance !== undefined && } + + {availableBalance !== totalBalance && availableBalance !== undefined && ( + + {`${t('Available')}: `} + + + )} + + ) +} + +export default FTAmounts + +const AmountSubtitle = styled.div` + color: ${({ theme }) => theme.font.tertiary}; + font-size: 10px; +` diff --git a/apps/desktop-wallet/src/components/amounts/FTWorthAmount.tsx b/apps/desktop-wallet/src/components/amounts/FTWorthAmount.tsx new file mode 100644 index 0000000000..742800feef --- /dev/null +++ b/apps/desktop-wallet/src/components/amounts/FTWorthAmount.tsx @@ -0,0 +1,27 @@ +import { calculateAmountWorth } from '@alephium/shared' +import { isNumber } from 'lodash' + +import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' +import Amount, { AmountLoaderProps, FiatAmountProps } from '@/components/Amount' + +type FTWorthAmountProps = Omit & + AmountLoaderProps & { + symbol: string + decimals: number + totalBalance?: bigint + } + +const FTWorthAmount = ({ symbol, totalBalance, decimals, ...props }: FTWorthAmountProps) => { + const { data: tokenPrice } = useFetchTokenPrice(symbol) + + const worth = + totalBalance !== undefined && isNumber(tokenPrice) + ? calculateAmountWorth(totalBalance, tokenPrice, decimals) + : undefined + + if (worth === undefined) return '-' + + return +} + +export default FTWorthAmount diff --git a/apps/desktop-wallet/src/components/tabs/TabBar.tsx b/apps/desktop-wallet/src/components/tabs/TabBar.tsx new file mode 100644 index 0000000000..bf387e3568 --- /dev/null +++ b/apps/desktop-wallet/src/components/tabs/TabBar.tsx @@ -0,0 +1,132 @@ +import { ChevronRight } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import styled, { css } from 'styled-components' + +import ActionLink from '@/components/ActionLink' +import { TabItem } from '@/components/tabs/tabsTypes' + +export interface TabBarProps { + items: TabItem[] + onTabChange: (tab: TabItem) => void + activeTab: TabItem + linkText?: string + onLinkClick?: () => void + justifyTabs?: 'left' | 'center' + TabComponent?: typeof Tab + className?: string +} + +const TabBar = ({ + items, + onTabChange, + activeTab, + linkText, + onLinkClick, + TabComponent = Tab, + justifyTabs = 'center', + className +}: TabBarProps) => { + const { t } = useTranslation() + + return ( + + + {items.map((item) => { + const isActive = activeTab.value === item.value + + return ( + onTabChange(item)} + onKeyDown={() => onTabChange(item)} + role="tab" + tabIndex={0} + aria-selected={isActive} + isActive={isActive} + > + + {item.Icon && ( + + + + )} + {item.label} + + + ) + })} + + {linkText && onLinkClick && ( + + {linkText} + + )} + + ) +} + +export default TabBar + +const TabBarStyled = styled.div` + display: flex; + align-items: center; + margin-bottom: 10px; +` + +const TabsContainer = styled.div` + flex: 1; + display: flex; + gap: 30px; + border-bottom: 1px solid ${({ theme }) => theme.border.primary}; +` + +export const Tab = styled.div<{ isActive: boolean }>` + display: flex; + text-align: center; + cursor: pointer; + font-size: 15px; + font-weight: var(--fontWeight-semiBold); + height: 38px; + box-shadow: 0 1px 0 ${({ theme, isActive }) => (isActive ? theme.bg.contrast : 'transparent')}; + + ${({ isActive, theme }) => + isActive + ? css` + color: ${theme.font.primary}; + ` + : css` + color: ${theme.font.tertiary}; + `} + + &:hover { + color: ${({ isActive, theme }) => (isActive ? theme.font.primary : theme.font.primary)}; + } + z-index: 1; +` + +const TabLabel = styled.span<{ isActive: boolean }>` + display: flex; + align-items: center; + + ${({ isActive }) => + !isActive && + css` + filter: saturate(10%); + `} +` + +const TabIcon = styled.div` + margin-right: 8px; + display: flex; + align-items: center; +` + +const ActionLinkStyled = styled(ActionLink)` + margin-left: auto; + margin-right: 20px; +` diff --git a/apps/desktop-wallet/src/components/tabs/TabContent.tsx b/apps/desktop-wallet/src/components/tabs/TabContent.tsx new file mode 100644 index 0000000000..efe18c8c42 --- /dev/null +++ b/apps/desktop-wallet/src/components/tabs/TabContent.tsx @@ -0,0 +1,49 @@ +import { motion } from 'framer-motion' +import { memo, useEffect, useState } from 'react' +import { Freeze } from 'react-freeze' +import styled from 'styled-components' + +import { fastTransition } from '@/animations' +import { TabItem } from '@/components/tabs/tabsTypes' + +interface TabContentProps extends Pick, 'renderContent'> { + isActive: boolean + isMouseOverTabHeaders: boolean +} + +const TabContent = ({ isActive, renderContent, isMouseOverTabHeaders }: TabContentProps) => { + const isFrozen = !isActive && !isMouseOverTabHeaders + + return ( + + + + + + ) +} + +export default TabContent + +type TabTabContainerProps = Pick, 'isActive' | 'renderContent'> + +const TabContainer = memo(({ renderContent, isActive }: TabTabContainerProps) => { + const [shouldRender, setShouldRender] = useState(isActive) + + useEffect(() => { + if (isActive) setShouldRender(true) + }, [isActive]) + + if (!shouldRender) return null + + return {renderContent()} +}) + +const TabAnimation = styled(motion.div)` + position: relative; +` + +const TabContentStyled = styled.div` + position: absolute; + width: 100%; +` diff --git a/apps/desktop-wallet/src/components/tabs/Tabs.tsx b/apps/desktop-wallet/src/components/tabs/Tabs.tsx new file mode 100644 index 0000000000..50a46e3143 --- /dev/null +++ b/apps/desktop-wallet/src/components/tabs/Tabs.tsx @@ -0,0 +1,31 @@ +import { motion } from 'framer-motion' +import { useState } from 'react' + +import TableTabBar from '@/components/TableTabBar' +import TabContent from '@/components/tabs/TabContent' +import { TabsProps } from '@/components/tabs/tabsTypes' + +const Tabs = ({ tabs }: TabsProps) => { + const [currentTab, setCurrentTab] = useState(tabs[0]) + const [isMouseOverTabHeaders, setIsMouseOverTabHeaders] = useState(false) + + return ( + setIsMouseOverTabHeaders(true)} + onMouseLeave={() => setIsMouseOverTabHeaders(false)} + > + + + {tabs.map(({ value, renderContent }) => ( + + ))} + + ) +} + +export default Tabs diff --git a/apps/desktop-wallet/src/components/tabs/tabsTypes.ts b/apps/desktop-wallet/src/components/tabs/tabsTypes.ts new file mode 100644 index 0000000000..2f73fa02d3 --- /dev/null +++ b/apps/desktop-wallet/src/components/tabs/tabsTypes.ts @@ -0,0 +1,17 @@ +import { LucideIcon } from 'lucide-react' +import { ReactNode } from 'react' + +export interface TabItemSimple { + value: T + label: string + Icon?: LucideIcon +} + +export interface TabItem extends TabItemSimple { + renderContent: () => ReactNode +} + +export interface TabsProps { + tabs: TabItem[] + className?: string +} diff --git a/apps/desktop-wallet/src/contexts/scroll.tsx b/apps/desktop-wallet/src/contexts/scroll.tsx index 5d5de6fc9c..b6252a47f0 100644 --- a/apps/desktop-wallet/src/contexts/scroll.tsx +++ b/apps/desktop-wallet/src/contexts/scroll.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { MotionValue } from 'framer-motion' import { createContext, useContext } from 'react' @@ -23,12 +5,10 @@ export type ScrollDirection = 'up' | 'down' | undefined export interface ScrollContextType { scrollY?: MotionValue - scrollDirection?: MotionValue } const ScrollContext = createContext({ - scrollY: undefined, - scrollDirection: undefined + scrollY: undefined }) export const ScrollContextProvider = ScrollContext.Provider diff --git a/apps/desktop-wallet/src/contexts/steps.tsx b/apps/desktop-wallet/src/contexts/steps.tsx index e69c0b88ab..7f7e016767 100644 --- a/apps/desktop-wallet/src/contexts/steps.tsx +++ b/apps/desktop-wallet/src/contexts/steps.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createContext, useContext } from 'react' import { useNavigate, useParams } from 'react-router-dom' diff --git a/apps/desktop-wallet/src/contexts/wallet.tsx b/apps/desktop-wallet/src/contexts/wallet.tsx index 4a704790bd..a9cbf5b3da 100644 --- a/apps/desktop-wallet/src/contexts/wallet.tsx +++ b/apps/desktop-wallet/src/contexts/wallet.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { resetArray } from '@alephium/shared' import { createContext, useContext, useState } from 'react' diff --git a/apps/desktop-wallet/src/features/addressDeletion/DeleteAddressesModal.tsx b/apps/desktop-wallet/src/features/addressDeletion/DeleteAddressesModal.tsx index 159ac77f38..8514d45eda 100644 --- a/apps/desktop-wallet/src/features/addressDeletion/DeleteAddressesModal.tsx +++ b/apps/desktop-wallet/src/features/addressDeletion/DeleteAddressesModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { memo, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -29,14 +11,15 @@ import ForgetMulitpleAddressesButton from '@/features/addressDeletion/ForgetMuli import { closeModal } from '@/features/modals/modalActions' import { ModalBaseProp } from '@/features/modals/modalTypes' import { useAppDispatch, useAppSelector } from '@/hooks/redux' -import { useFetchSortedAddressesHashesWithLatestTx } from '@/hooks/useAddresses' +import { useFetchAddressesHashesSortedByLastUseWithLatestTx } from '@/hooks/useAddresses' import CenteredModal, { ModalFooterButton, ModalFooterButtons, ScrollableModalContent } from '@/modals/CenteredModal' -import AddressLastActivity from '@/pages/UnlockedWallet/AddressesPage/addressListRow/AddressLastActivity' +import AddressLastActivity from '@/pages/unlockedWallet/addressesPage/addressListRow/AddressLastActivity' import { selectDefaultAddress, selectInitialAddress } from '@/storage/addresses/addressesSelectors' const DeleteAddressesModal = memo(({ id }: ModalBaseProp) => { const { t } = useTranslation() - const { data: sortedAddresses, isLoading: isLoadingSortedAddresses } = useFetchSortedAddressesHashesWithLatestTx() + const { data: sortedAddresses, isLoading: isLoadingSortedAddresses } = + useFetchAddressesHashesSortedByLastUseWithLatestTx() const { hash: defaultAddressHash } = useAppSelector(selectDefaultAddress) const initialAddress = useAppSelector(selectInitialAddress) const dispatch = useAppDispatch() @@ -106,7 +89,6 @@ const DeleteAddressesModal = memo(({ id }: ModalBaseProp) => { > } /> diff --git a/apps/desktop-wallet/src/features/addressDeletion/ForgetAddressSection.tsx b/apps/desktop-wallet/src/features/addressDeletion/ForgetAddressSection.tsx index 1c4de9de5d..3c0de53248 100644 --- a/apps/desktop-wallet/src/features/addressDeletion/ForgetAddressSection.tsx +++ b/apps/desktop-wallet/src/features/addressDeletion/ForgetAddressSection.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { Trash2 } from 'lucide-react' import { useTranslation } from 'react-i18next' @@ -66,7 +48,6 @@ const ForgetAddressSection = ({ addressHash, addressName }: ForgetAddressSection disabled={isDefault} role="secondary" variant="alert" - borderless style={{ minWidth: 120 }} Icon={Trash2} > diff --git a/apps/desktop-wallet/src/features/addressDeletion/ForgetMulitpleAddressesButton.tsx b/apps/desktop-wallet/src/features/addressDeletion/ForgetMulitpleAddressesButton.tsx index 63f95e1c64..4999265e35 100644 --- a/apps/desktop-wallet/src/features/addressDeletion/ForgetMulitpleAddressesButton.tsx +++ b/apps/desktop-wallet/src/features/addressDeletion/ForgetMulitpleAddressesButton.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/addressDeletion/useConfirmDeleteAddresses.ts b/apps/desktop-wallet/src/features/addressDeletion/useConfirmDeleteAddresses.ts index aa8b392cb4..25420bdb1e 100644 --- a/apps/desktop-wallet/src/features/addressDeletion/useConfirmDeleteAddresses.ts +++ b/apps/desktop-wallet/src/features/addressDeletion/useConfirmDeleteAddresses.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { Trash2 } from 'lucide-react' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/addressDeletion/useDeleteAddress.ts b/apps/desktop-wallet/src/features/addressDeletion/useDeleteAddress.ts index 694ebf5f47..fad8b79458 100644 --- a/apps/desktop-wallet/src/features/addressDeletion/useDeleteAddress.ts +++ b/apps/desktop-wallet/src/features/addressDeletion/useDeleteAddress.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import queryClient from '@/api/queryClient' diff --git a/apps/desktop-wallet/src/features/addressFiltering/addressFilteringHooks.tsx b/apps/desktop-wallet/src/features/addressFiltering/addressFilteringHooks.tsx index 20ee3ae447..0704e7ac4d 100644 --- a/apps/desktop-wallet/src/features/addressFiltering/addressFilteringHooks.tsx +++ b/apps/desktop-wallet/src/features/addressFiltering/addressFilteringHooks.tsx @@ -1,35 +1,14 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useMemo } from 'react' -import useFetchWalletBalancesAlphByAddress from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphByAddress' -import useFetchWalletBalancesTokensByAddress from '@/api/apiDataHooks/wallet/useFetchWalletBalancesTokensByAddress' +import useFetchWalletBalancesByAddress from '@/api/apiDataHooks/wallet/useFetchWalletBalancesByAddress' import useFetchWalletFts from '@/api/apiDataHooks/wallet/useFetchWalletFts' -import { useFetchSortedAddressesHashes } from '@/hooks/useAddresses' -import { useUnsortedAddresses } from '@/hooks/useUnsortedAddresses' +import { useUnsortedAddresses, useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' export const useFilterAddressesByText = (text = '') => { const allAddresses = useUnsortedAddresses() - const { data: allAddressHashes } = useFetchSortedAddressesHashes() - const { listedFts, unlistedFts } = useFetchWalletFts({ sort: false }) - const { data: addressesAlphBalances } = useFetchWalletBalancesAlphByAddress() - const { data: addressesTokensBalances } = useFetchWalletBalancesTokensByAddress() + const allAddressHashes = useUnsortedAddressesHashes() + const { listedFts, unlistedFts } = useFetchWalletFts({ sort: false, includeHidden: false }) + const { data: addressesBalances } = useFetchWalletBalancesByAddress() return useMemo( () => @@ -46,14 +25,10 @@ export const useFilterAddressesByText = (text = '') => { if (address.label?.toLowerCase().includes(text)) return true // Step 3. Validate against token names - const addressAlphBalances = addressesAlphBalances[addressHash] - const addressHasAlphBalances = BigInt(addressAlphBalances?.totalBalance ?? 0) > 0 - - if (addressHasAlphBalances) { - if ('alephium alph'.includes(text)) return true + const addressBalances = addressesBalances[addressHash] - const addressTokensBalances = addressesTokensBalances[addressHash] ?? [] - const addressSearchableString = addressTokensBalances + if (addressBalances) { + const addressSearchableString = addressBalances .map(({ id }) => { const listedFt = listedFts.find((token) => token.id === id) @@ -70,6 +45,6 @@ export const useFilterAddressesByText = (text = '') => { return false } }), - [addressesAlphBalances, addressesTokensBalances, allAddressHashes, allAddresses, listedFts, text, unlistedFts] + [addressesBalances, allAddressHashes, allAddresses, listedFts, text, unlistedFts] ) } diff --git a/apps/desktop-wallet/src/features/analytics/AnalyticsProvider.tsx b/apps/desktop-wallet/src/features/analytics/AnalyticsProvider.tsx index 602b4ff4cf..eaa3509e32 100644 --- a/apps/desktop-wallet/src/features/analytics/AnalyticsProvider.tsx +++ b/apps/desktop-wallet/src/features/analytics/AnalyticsProvider.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { PostHogConfig } from 'posthog-js' import { PostHogProvider } from 'posthog-js/react' diff --git a/apps/desktop-wallet/src/features/analytics/analyticsPersistentStorage.ts b/apps/desktop-wallet/src/features/analytics/analyticsPersistentStorage.ts index 319e09d102..34a538dc04 100644 --- a/apps/desktop-wallet/src/features/analytics/analyticsPersistentStorage.ts +++ b/apps/desktop-wallet/src/features/analytics/analyticsPersistentStorage.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { nanoid } from 'nanoid' export type AnalyticsId = string diff --git a/apps/desktop-wallet/src/features/analytics/useAnalytics.ts b/apps/desktop-wallet/src/features/analytics/useAnalytics.ts index de8217600e..2a561c5844 100644 --- a/apps/desktop-wallet/src/features/analytics/useAnalytics.ts +++ b/apps/desktop-wallet/src/features/analytics/useAnalytics.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AnalyticsProps, cleanExceptionMessage, getHumanReadableError, throttleEvent } from '@alephium/shared' import { CaptureOptions } from 'posthog-js' import { usePostHog } from 'posthog-js/react' diff --git a/apps/desktop-wallet/src/features/analytics/useTrackUserSettings.ts b/apps/desktop-wallet/src/features/analytics/useTrackUserSettings.ts index 23126d86a6..8da0a550d4 100644 --- a/apps/desktop-wallet/src/features/analytics/useTrackUserSettings.ts +++ b/apps/desktop-wallet/src/features/analytics/useTrackUserSettings.ts @@ -1,24 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { usePostHog } from 'posthog-js/react' import { useEffect } from 'react' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useAppSelector } from '@/hooks/redux' import { currentVersion } from '@/utils/app-data' @@ -29,9 +12,10 @@ const useTrackUserSettings = () => { const devTools = useAppSelector((s) => s.settings.devTools) const walletLockTimeInMinutes = useAppSelector((s) => s.settings.walletLockTimeInMinutes) const language = useAppSelector((s) => s.settings.language) - const passwordRequirement = useAppSelector((s) => s.settings.passwordRequirement) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) const fiatCurrency = useAppSelector((s) => s.settings.fiatCurrency) const network = useAppSelector((s) => s.network.name) + const region = useAppSelector((s) => s.settings.region) useEffect(() => { if (posthog.__loaded) @@ -44,7 +28,8 @@ const useTrackUserSettings = () => { language, passwordRequirement, fiatCurrency, - network + network, + region }) }, [ devTools, @@ -55,6 +40,7 @@ const useTrackUserSettings = () => { passwordRequirement, posthog.__loaded, posthog.people, + region, theme, walletLockTimeInMinutes ]) diff --git a/apps/desktop-wallet/src/features/assetsLists/AddressTokenBadge.tsx b/apps/desktop-wallet/src/features/assetsLists/AddressTokenBadge.tsx deleted file mode 100644 index 6e6ac72508..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/AddressTokenBadge.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' - -import useFetchAddressSingleTokenBalances from '@/api/apiDataHooks/address/useFetchAddressSingleTokenBalances' -import useFetchToken, { isNFT } from '@/api/apiDataHooks/token/useFetchToken' -import TokenBadge, { TokenBadgeStyleProps } from '@/components/TokenBadge' -import { TokenId } from '@/types/tokens' - -interface AddressTokenBadgeProps extends TokenBadgeStyleProps { - tokenId: TokenId - addressHash: AddressHash -} - -const AddressTokenBadge = ({ addressHash, tokenId, ...props }: AddressTokenBadgeProps) => { - const { data: token, isLoading: isLoadingToken } = useFetchToken(tokenId) - const { data, isLoading } = useFetchAddressSingleTokenBalances({ - addressHash, - tokenId, - skip: isNFT(token) || isLoadingToken - }) - - const amount = data?.totalBalance ? BigInt(data.totalBalance) : undefined - - return -} - -export default AddressTokenBadge diff --git a/apps/desktop-wallet/src/features/assetsLists/AddressTokensBadgesList.tsx b/apps/desktop-wallet/src/features/assetsLists/AddressTokensBadgesList.tsx index 537108e9d2..9a7003734f 100644 --- a/apps/desktop-wallet/src/features/assetsLists/AddressTokensBadgesList.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/AddressTokensBadgesList.tsx @@ -1,29 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { useMemo } from 'react' import styled from 'styled-components' import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' import SkeletonLoader from '@/components/SkeletonLoader' -import { TokenBadgeStyleProps } from '@/components/TokenBadge' -import AddressTokenBadge from '@/features/assetsLists/AddressTokenBadge' +import TokenBadge, { TokenBadgeStyleProps } from '@/components/TokenBadge' interface AddressTokensBadgesListProps extends TokenBadgeStyleProps { addressHash: AddressHash @@ -59,7 +40,7 @@ const AddressTokensBadgesList = ({ return ( {displayedStandardTokenIds.map((tokenId) => ( - + ))} {nbOfAdditionalTokens > 0 && +{nbOfAdditionalTokens}} diff --git a/apps/desktop-wallet/src/features/assetsLists/ExpandRowButton.tsx b/apps/desktop-wallet/src/features/assetsLists/ExpandRowButton.tsx deleted file mode 100644 index 9b82ace5bb..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/ExpandRowButton.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ExpandRow } from '@/components/Table' -import { TokensTabsBaseProps } from '@/features/assetsLists/types' - -interface ExpandRowButtonProps extends TokensTabsBaseProps { - isEnabled: boolean -} - -const ExpandRowButton = ({ isExpanded, isEnabled, onExpand }: ExpandRowButtonProps) => - !isExpanded && isEnabled && onExpand && - -export default ExpandRowButton diff --git a/apps/desktop-wallet/src/features/assetsLists/ExpandableTokensBalancesList.tsx b/apps/desktop-wallet/src/features/assetsLists/ExpandableTokensBalancesList.tsx deleted file mode 100644 index 8d976a3d28..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/ExpandableTokensBalancesList.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { motion } from 'framer-motion' -import { ReactNode } from 'react' - -import { fadeIn } from '@/animations' -import ExpandRowButton from '@/features/assetsLists/ExpandRowButton' -import { TokensTabsBaseProps } from '@/features/assetsLists/types' - -interface ExpandableTokensBalancesListProps extends TokensTabsBaseProps { - children: ReactNode[] - nbOfItems?: number -} - -const ExpandableTokensBalancesList = ({ - className, - children, - nbOfItems, - ...props -}: ExpandableTokensBalancesListProps) => ( - <> - - {children} - - {nbOfItems !== undefined && 3} />} - -) - -export default ExpandableTokensBalancesList diff --git a/apps/desktop-wallet/src/features/assetsLists/FTsBalancesList.tsx b/apps/desktop-wallet/src/features/assetsLists/FTsBalancesList.tsx index 283d5410ea..ccf9937c82 100644 --- a/apps/desktop-wallet/src/features/assetsLists/FTsBalancesList.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/FTsBalancesList.tsx @@ -1,77 +1,95 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import useFetchAddressFts from '@/api/apiDataHooks/address/useFetchAddressFts' +import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' import useFetchWalletFts from '@/api/apiDataHooks/wallet/useFetchWalletFts' +import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' +import EmptyPlaceholder from '@/components/EmptyPlaceholder' import SkeletonLoader from '@/components/SkeletonLoader' -import { TableRow } from '@/components/Table' -import ExpandableTokensBalancesList from '@/features/assetsLists/ExpandableTokensBalancesList' -import PlaceholderText from '@/features/assetsLists/PlaceholderText' -import { AddressFTBalancesRow } from '@/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow' -import { WalletFTBalancesRow } from '@/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow' -import { AddressTokensTabsProps, TokensTabsBaseProps } from '@/features/assetsLists/types' +import Table, { TableRow } from '@/components/Table' +import { + AddressFTBalancesRow, + AddressNSTBalancesRow +} from '@/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow' +import { + WalletFTBalancesRow, + WalletNSTBalancesRow +} from '@/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow' +import TokensBalancesHeader from '@/features/assetsLists/TokensBalancesHeader' +import { + HiddenAddressTokensBalancesListSection, + HiddenWalletTokensBalancesListSection +} from '@/features/hiddenTokens/HiddenTokensBalancesLists' +import { AddressModalBaseProp } from '@/features/modals/modalTypes' -export const AddressFTsBalancesList = ({ addressHash, ...props }: AddressTokensTabsProps) => { +export const AddressFTsBalancesList = ({ addressHash }: AddressModalBaseProp) => { const { t } = useTranslation() const { listedFts, unlistedFts, isLoading } = useFetchAddressFts({ addressHash }) + const isEmpty = !isLoading && listedFts.length === 0 && unlistedFts.length === 0 + + const { + data: { nstIds } + } = useFetchAddressTokensByType({ addressHash, includeAlph: false }) return ( - - {listedFts.map(({ id }) => ( - - ))} - {unlistedFts.map(({ id }) => ( - - ))} - {!isLoading && listedFts.length === 0 && unlistedFts.length === 0 && ( - {t("This address doesn't have any tokens yet.")} - )} - {isLoading && } - + <> + + {!isEmpty && } + {listedFts.map(({ id }) => ( + + ))} + {unlistedFts.map(({ id }) => ( + + ))} + {nstIds.map((tokenId) => ( + + ))} + {!isLoading && listedFts.length === 0 && unlistedFts.length === 0 && nstIds.length === 0 && ( + {t("This address doesn't have any tokens yet.")} + )} + {isLoading && } +
+ + ) } -export const WalletFTsBalancesList = (props: TokensTabsBaseProps) => { +export const WalletFTsBalancesList = () => { const { t } = useTranslation() - const { listedFts, unlistedFts, isLoading } = useFetchWalletFts() + const { listedFts, unlistedFts, isLoading } = useFetchWalletFts({ includeHidden: false, sort: true }) + const { + data: { nstIds } + } = useFetchWalletTokensByType({ includeHidden: false }) + + const isEmpty = !isLoading && listedFts.length === 0 && unlistedFts.length === 0 return ( - - {listedFts.map(({ id }) => ( - - ))} - {unlistedFts.map(({ id }) => ( - - ))} - {!isLoading && listedFts.length === 0 && unlistedFts.length === 0 && ( - - {t("The wallet doesn't have any tokens. Tokens of all your addresses will appear here.")} - - )} - {isLoading && } - + <> + + {!isEmpty && } + {listedFts.map(({ id }) => ( + + ))} + {unlistedFts.map(({ id }) => ( + + ))} + {nstIds.map((tokenId) => ( + + ))} + {isEmpty && ( + + {t("The wallet doesn't have any tokens. Tokens of all your addresses will appear here.")} + + )} + {isLoading && } +
+ + ) } const TokensSkeletonLoader = () => ( - + ) diff --git a/apps/desktop-wallet/src/features/assetsLists/NFTsGrid.tsx b/apps/desktop-wallet/src/features/assetsLists/NFTsGrid.tsx index 31841ee2c0..419f8c23e6 100644 --- a/apps/desktop-wallet/src/features/assetsLists/NFTsGrid.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/NFTsGrid.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { useTranslation } from 'react-i18next' import styled, { css } from 'styled-components' @@ -23,16 +5,14 @@ import styled, { css } from 'styled-components' import { fadeIn } from '@/animations' import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' +import EmptyPlaceholder from '@/components/EmptyPlaceholder' import NFTCard from '@/components/NFTCard' import SkeletonLoader from '@/components/SkeletonLoader' -import { TableRow } from '@/components/Table' -import ExpandRowButton from '@/features/assetsLists/ExpandRowButton' -import PlaceholderText from '@/features/assetsLists/PlaceholderText' -import { AddressTokensTabsProps, TokensTabsBaseProps } from '@/features/assetsLists/types' +import { AddressModalBaseProp } from '@/features/modals/modalTypes' import { deviceBreakPoints } from '@/style/globalStyles' import { TokenId } from '@/types/tokens' -export const AddressNFTsGrid = ({ addressHash, ...props }: AddressTokensTabsProps) => { +export const AddressNFTsGrid = ({ addressHash }: AddressModalBaseProp) => { const { t } = useTranslation() const { data: { nftIds }, @@ -41,7 +21,6 @@ export const AddressNFTsGrid = ({ addressHash, ...props }: AddressTokensTabsProp return ( { +export const WalletNFTsGrid = () => { const { t } = useTranslation() const { data: { nftIds }, isLoading - } = useFetchWalletTokensByType({ includeAlph: false }) + } = useFetchWalletTokensByType({ includeHidden: false }) return ( { ) } -interface NFTsGridProps extends TokensTabsBaseProps { +interface NFTsGridProps { columns: number nftIds: TokenId[] isLoading: boolean placeholderText: string } -const NFTsGrid = ({ className, isExpanded, onExpand, columns, nftIds, isLoading, placeholderText }: NFTsGridProps) => ( - <> - - {!isLoading && nftIds.length === 0 && {placeholderText}} - - {isLoading || - (nftIds.length > 0 && ( - - {isLoading ? : nftIds.map((nftId) => )} - - ))} - - - {isExpanded !== undefined && onExpand && ( - 4} /> - )} - +const NFTsGrid = ({ columns, nftIds, isLoading, placeholderText }: NFTsGridProps) => ( + + {!isLoading && nftIds.length === 0 && {placeholderText}} + + {isLoading || + (nftIds.length > 0 && ( + + {isLoading ? : nftIds.map((nftId) => )} + + ))} + ) const NFTsLoader = () => ( @@ -103,17 +75,13 @@ const NFTsLoader = () => ( ) -const Grid = styled(TableRow)<{ columns: number }>` +const Grid = styled.div<{ columns: number }>` display: grid; grid-template-columns: repeat(${({ columns }) => columns}, minmax(0, 1fr)); grid-auto-flow: initial; gap: 15px; - padding: 15px; border-radius: 0 0 12px 12px; - - > * { - width: 100%; - } + margin-top: var(--spacing-4); ${({ columns }) => !columns && diff --git a/apps/desktop-wallet/src/features/assetsLists/NSTsBalancesList.tsx b/apps/desktop-wallet/src/features/assetsLists/NSTsBalancesList.tsx deleted file mode 100644 index ae021e5524..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/NSTsBalancesList.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' -import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' -import ExpandableTokensBalancesList from '@/features/assetsLists/ExpandableTokensBalancesList' -import { AddressNSTBalancesRow } from '@/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow' -import { WalletNSTBalancesRow } from '@/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow' -import { AddressTokensTabsProps, TokensTabsBaseProps } from '@/features/assetsLists/types' - -export const AddressNSTsBalancesList = ({ addressHash, ...props }: AddressTokensTabsProps) => { - const { - data: { nstIds } - } = useFetchAddressTokensByType({ addressHash, includeAlph: false }) - - return ( - - {nstIds.map((tokenId) => ( - - ))} - - ) -} - -export const WalletNSTsBalancesList = (props: TokensTabsBaseProps) => { - const { - data: { nstIds } - } = useFetchWalletTokensByType({ includeAlph: false }) - - return ( - - {nstIds.map((tokenId) => ( - - ))} - - ) -} diff --git a/apps/desktop-wallet/src/features/assetsLists/PlaceholderText.tsx b/apps/desktop-wallet/src/features/assetsLists/PlaceholderText.tsx deleted file mode 100644 index f919ad794a..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/PlaceholderText.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled from 'styled-components' - -const PlaceholderText = styled.div` - padding: 30px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - color: ${({ theme }) => theme.font.tertiary}; -` - -export default PlaceholderText diff --git a/apps/desktop-wallet/src/features/assetsLists/TokensBalancesHeader.tsx b/apps/desktop-wallet/src/features/assetsLists/TokensBalancesHeader.tsx new file mode 100644 index 0000000000..2bc01e1b7d --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/TokensBalancesHeader.tsx @@ -0,0 +1,36 @@ +import { useTranslation } from 'react-i18next' + +import { TableCell, TableHeader } from '@/components/Table' + +interface TokensBalancesHeaderProps { + showAllocation?: boolean +} + +const TokensBalancesHeader = ({ showAllocation }: TokensBalancesHeaderProps) => { + const { t } = useTranslation() + + return ( + + + + {t('Name')} + + + {t('Price')} + + {showAllocation && ( + + {t('Allocation')} + + )} + + {t('Amount')} + + + {t('Worth')} + + + ) +} + +export default TokensBalancesHeader diff --git a/apps/desktop-wallet/src/features/assetsLists/TokensTabs.tsx b/apps/desktop-wallet/src/features/assetsLists/TokensTabs.tsx deleted file mode 100644 index 6045ba87e8..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/TokensTabs.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ReactNode, useState } from 'react' -import { useTranslation } from 'react-i18next' - -import useFetchAddressTokensByType from '@/api/apiDataHooks/address/useFetchAddressTokensByType' -import useFetchWalletTokensByType from '@/api/apiDataHooks/wallet/useFetchWalletTokensByType' -import FocusableContent from '@/components/FocusableContent' -import { TabItem } from '@/components/TabBar' -import Table, { ExpandableTable } from '@/components/Table' -import TableTabBar from '@/components/TableTabBar' -import { AddressFTsBalancesList, WalletFTsBalancesList } from '@/features/assetsLists/FTsBalancesList' -import { AddressNFTsGrid, WalletNFTsGrid } from '@/features/assetsLists/NFTsGrid' -import { AddressNSTsBalancesList, WalletNSTsBalancesList } from '@/features/assetsLists/NSTsBalancesList' -import { AddressTokensTabsProps, TokensTabValue, WalletTokensTabsProps } from '@/features/assetsLists/types' -import useTokensTabs from '@/features/assetsLists/useTokensTabs' - -export const AddressTokensTabs = ({ addressHash }: AddressTokensTabsProps) => { - const { t } = useTranslation() - const { - data: { nstIds } - } = useFetchAddressTokensByType({ addressHash, includeAlph: false }) - - const { tabs, isExpanded, toggleExpansion } = useTokensTabs({ - numberOfNSTs: nstIds.length, - ftsTabTitle: `💰 ${t('Address tokens')}`, - nstsTabTitle: `❔ ${t('Address unknown tokens')}`, - nftsTabTitle: `🖼️ ${t('Address NFTs')}` - }) - - const [currentTab, setCurrentTab] = useState>(tabs[0]) - - const props = { addressHash, isExpanded, onExpand: toggleExpansion } - - const renderTab = (tabValue: TokensTabValue) => { - switch (tabValue) { - case 'fts': - return - case 'nfts': - return - case 'nsts': - return - } - } - - return ( - - {renderTab(currentTab.value)} - - ) -} - -export const WalletTokensTabs = ({ maxHeightInPx, className }: WalletTokensTabsProps) => { - const { t } = useTranslation() - const { - data: { nstIds } - } = useFetchWalletTokensByType({ includeAlph: false }) - - const { tabs, isExpanded, toggleExpansion } = useTokensTabs({ - numberOfNSTs: nstIds.length, - ftsTabTitle: `💰 ${t('Tokens')}`, - nstsTabTitle: `❔ ${t('Unknown tokens')}`, - nftsTabTitle: `🖼️ ${t('NFTs')}` - }) - - const [currentTab, setCurrentTab] = useState>(tabs[0]) - - const props = { isExpanded: isExpanded || !maxHeightInPx, onExpand: toggleExpansion } - - const renderTab = (tabValue: TokensTabValue) => { - switch (tabValue) { - case 'fts': - return - case 'nfts': - return - case 'nsts': - return - } - } - - return ( - - {renderTab(currentTab.value)} - - ) -} - -interface TokensTabsProps { - tabs: TabItem[] - children: ReactNode - currentTab: TabItem - setCurrentTab: (tab: TabItem) => void - isExpanded?: boolean - toggleExpansion?: () => void - className?: string - maxHeightInPx?: number -} - -const TokensTabs = ({ - tabs, - currentTab, - setCurrentTab, - isExpanded, - toggleExpansion, - maxHeightInPx, - className, - children -}: TokensTabsProps) => - isExpanded !== undefined && toggleExpansion ? ( - - - - - {children} - - - ) : ( - - - - {children} -
- ) diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow.tsx index 06c3f026e8..baef861d7c 100644 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow.tsx @@ -1,92 +1,40 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' -import useFetchAddressSingleTokenBalances from '@/api/apiDataHooks/address/useFetchAddressSingleTokenBalances' -import { TableRow } from '@/components/Table' -import AmountsColumn, { RawAmountSubtitle } from '@/features/assetsLists/tokenBalanceRow/AmountsColumn' -import FTWorth from '@/features/assetsLists/tokenBalanceRow/FTWorth' -import { FTNameColumn, NSTNameColumn } from '@/features/assetsLists/tokenBalanceRow/NameColumns' +import { TableCell } from '@/components/Table' +import { FTAddressAmountCell } from '@/features/assetsLists/tokenBalanceRow/FTAmountCells' +import FTPriceCell from '@/features/assetsLists/tokenBalanceRow/FTPriceCell' +import { FTAddressWorthCell } from '@/features/assetsLists/tokenBalanceRow/FTWorthCell' +import { FTNameCell, NSTNameCell } from '@/features/assetsLists/tokenBalanceRow/NameCells' +import TokenBalancesRow from '@/features/assetsLists/tokenBalanceRow/TokenBalancesRow' import TokenLogo from '@/features/assetsLists/tokenBalanceRow/TokenLogo' -import { - AddressTokenBalancesRowAmountsProps, - AddressTokenBalancesRowProps -} from '@/features/assetsLists/tokenBalanceRow/types' +import { AddressTokenBalancesRowProps } from '@/features/assetsLists/tokenBalanceRow/types' export const AddressFTBalancesRow = ({ tokenId, addressHash }: AddressTokenBalancesRowProps) => ( - + - - + + + + - + ) export const AddressNSTBalancesRow = ({ tokenId, addressHash }: AddressTokenBalancesRowProps) => ( - - + + - - - - - - + + + - + + + + - + ) -const FTAmounts = ({ tokenId, addressHash }: AddressTokenBalancesRowProps) => { - const { data: tokenBalances, isLoading: isLoadingTokenBalances } = useFetchAddressSingleTokenBalances({ - addressHash, - tokenId - }) - - const amount = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined - - return ( - - - - ) -} - -const AddressTokenBalancesRowAmounts = ({ tokenId, addressHash, children }: AddressTokenBalancesRowAmountsProps) => { - const { data: tokenBalances, isLoading: isLoadingTokenBalances } = useFetchAddressSingleTokenBalances({ - addressHash, - tokenId - }) - - const totalBalance = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined - const availableBalance = tokenBalances?.availableBalance ? BigInt(tokenBalances.availableBalance) : undefined - - return ( - - {children} - - ) -} - const TokenRow = styled.div` display: flex; align-items: center; diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AmountsColumn.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AmountsColumn.tsx deleted file mode 100644 index 9124e65ae8..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/AmountsColumn.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ReactNode } from 'react' -import { useTranslation } from 'react-i18next' -import styled, { useTheme } from 'styled-components' - -import Amount from '@/components/Amount' -import SkeletonLoader from '@/components/SkeletonLoader' -import TableCellAmount from '@/components/TableCellAmount' -import { TokenId } from '@/types/tokens' - -interface AmountsColumnProps { - isLoading: boolean - tokenId: TokenId - children: ReactNode - totalBalance?: bigint - availableBalance?: bigint -} - -const AmountsColumn = ({ isLoading, totalBalance, availableBalance, children, tokenId }: AmountsColumnProps) => { - const theme = useTheme() - const { t } = useTranslation() - - return ( - - {isLoading ? ( - - ) : ( - <> - {totalBalance && } - - {availableBalance !== totalBalance && availableBalance !== undefined && ( - - {`${t('Available')}: `} - - - )} - - )} - - {children} - - ) -} - -export const RawAmountSubtitle = () => { - const { t } = useTranslation() - - return {t('Raw amount')} -} - -export default AmountsColumn - -const AmountSubtitle = styled.div` - color: ${({ theme }) => theme.font.tertiary}; - font-size: 10px; -` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAllocationCell.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAllocationCell.tsx new file mode 100644 index 0000000000..9a4d6fb552 --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAllocationCell.tsx @@ -0,0 +1,75 @@ +import { calculateAmountWorth } from '@alephium/shared' +import styled from 'styled-components' + +import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' +import useFetchWalletSingleTokenBalances from '@/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances' +import useFetchWalletWorth from '@/api/apiDataHooks/wallet/useFetchWalletWorth' +import SkeletonLoader from '@/components/SkeletonLoader' +import { TableCell } from '@/components/Table' +import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { isListedFT, ListedFT } from '@/types/tokens' + +const FTAllocationCell = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { data: token, isLoading: isLoadingToken } = useFetchToken(tokenId) + const { data: tokenBalances, isLoading: isLoadingBalances } = useFetchWalletSingleTokenBalances({ + tokenId + }) + const { data: walletWorth, isLoading: isLoadingWalletWorth } = useFetchWalletWorth() + + if (!token || !isListedFT(token)) return + + const tokenAmount = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined + + return ( + + + + ) +} + +interface FTAllocationBarProps { + token: ListedFT + tokenAmount?: bigint + walletWorth?: number + isLoading?: boolean +} + +const FTAllocationBar = ({ token, tokenAmount, walletWorth, isLoading }: FTAllocationBarProps) => { + const { data: tokenPrice, isLoading: tokenPriceLoading } = useFetchTokenPrice(token.symbol) + + if (isLoading || tokenPriceLoading) return + + if (!token || !tokenAmount || !walletWorth || !tokenPrice) return null + + const tokenWorth = calculateAmountWorth(tokenAmount, tokenPrice, token.decimals) + + const ratio = tokenWorth / walletWorth + + return ( + + + + ) +} + +export default FTAllocationCell + +const BarContainer = styled.div` + flex: 1; + height: 4px; + border-radius: 10px; + overflow: hidden; + display: flex; + background-color: ${({ theme }) => theme.bg.primary}; +` + +const Bar = styled.div` + background-color: ${({ theme }) => theme.bg.contrast}; + height: 100%; +` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAmountCells.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAmountCells.tsx new file mode 100644 index 0000000000..c33979f27e --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTAmountCells.tsx @@ -0,0 +1,77 @@ +import { AddressHash } from '@alephium/shared' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import useFetchAddressSingleTokenBalances from '@/api/apiDataHooks/address/useFetchAddressSingleTokenBalances' +import useFetchWalletSingleTokenBalances from '@/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances' +import FTAmounts, { FTAmountsBaseProp } from '@/components/amounts/FTAmounts' +import SkeletonLoader from '@/components/SkeletonLoader' +import { TableCell } from '@/components/Table' +import { TokenId } from '@/types/tokens' + +interface FTAddressAmountCellProps { + tokenId: TokenId + addressHash: AddressHash +} + +export const FTAddressAmountCell = ({ tokenId, addressHash }: FTAddressAmountCellProps) => { + const { data: tokenBalances, isLoading } = useFetchAddressSingleTokenBalances({ addressHash, tokenId }) + + const totalBalance = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined + const availableBalance = tokenBalances?.availableBalance ? BigInt(tokenBalances.availableBalance) : undefined + + return ( + + ) +} + +export const FTWalletAmountCell = ({ tokenId }: Omit) => { + const { data: tokenBalances, isLoading } = useFetchWalletSingleTokenBalances({ tokenId }) + + const totalBalance = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined + const availableBalance = tokenBalances?.availableBalance ? BigInt(tokenBalances.availableBalance) : undefined + + return ( + + ) +} + +const FTAmountCell = (props: FTAmountsBaseProp) => ( + + {props.isLoading ? ( + + ) : ( + + + + )} + +) + +export const RawAmountSubtitle = () => { + const { t } = useTranslation() + + return {t('Raw amount')} +} + +const AmountSubtitle = styled.div` + color: ${({ theme }) => theme.font.tertiary}; + font-size: 10px; +` + +const AmountsContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 2px; +` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTPriceCell.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTPriceCell.tsx new file mode 100644 index 0000000000..f2707f0049 --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTPriceCell.tsx @@ -0,0 +1,36 @@ +import { useTheme } from 'styled-components' + +import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' +import Amount from '@/components/Amount' +import SkeletonLoader from '@/components/SkeletonLoader' +import { TableCell } from '@/components/Table' +import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { isFT } from '@/types/tokens' + +const FTPriceCell = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { data: token } = useFetchToken(tokenId) + + if (!token || !isFT(token)) return null + + return ( + + + + ) +} + +const Price = ({ tokenSymbol }: { tokenSymbol: string }) => { + const { data: tokenPrice, isLoading } = useFetchTokenPrice(tokenSymbol) + const theme = useTheme() + + if (tokenPrice === undefined || tokenPrice === null) return '-' + + return isLoading ? ( + + ) : ( + + ) +} + +export default FTPriceCell diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorth.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorth.tsx deleted file mode 100644 index 8a80b8f227..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorth.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { calculateAmountWorth } from '@alephium/shared' -import { isNumber } from 'lodash' -import styled from 'styled-components' - -import useFetchTokenPrices, { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' -import useFetchToken, { isFT } from '@/api/apiDataHooks/token/useFetchToken' -import Amount from '@/components/Amount' -import SkeletonLoader from '@/components/SkeletonLoader' -import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' - -interface FTWorth extends TokenBalancesRowBaseProps { - isLoadingBalance: boolean - totalBalance?: bigint -} - -const FTWorth = ({ tokenId, totalBalance, isLoadingBalance }: FTWorth) => { - const { data: token } = useFetchToken(tokenId) - const { isLoading: isLoadingTokenPrices } = useFetchTokenPrices() - - if (!isFT(token)) return null - - if (isLoadingBalance || isLoadingTokenPrices) return - - return ( - - - - ) -} - -export default FTWorth - -interface FTWorthAmountProps { - symbol: string - decimals: number - totalBalance?: bigint -} - -const FTWorthAmount = ({ symbol, totalBalance, decimals }: FTWorthAmountProps) => { - const { data: tokenPrice } = useFetchTokenPrice(symbol) - - const worth = - totalBalance !== undefined && isNumber(tokenPrice) - ? calculateAmountWorth(totalBalance, tokenPrice, decimals) - : undefined - - if (worth === undefined) return null - - return -} - -const Worth = styled.div` - font-size: 11px; - color: ${({ theme }) => theme.font.secondary}; -` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorthCell.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorthCell.tsx new file mode 100644 index 0000000000..c8cdb1a6b8 --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/FTWorthCell.tsx @@ -0,0 +1,52 @@ +import useFetchAddressSingleTokenBalances from '@/api/apiDataHooks/address/useFetchAddressSingleTokenBalances' +import useFetchTokenPrices from '@/api/apiDataHooks/market/useFetchTokenPrices' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' +import useFetchWalletSingleTokenBalances from '@/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances' +import FTWorthAmount from '@/components/amounts/FTWorthAmount' +import SkeletonLoader from '@/components/SkeletonLoader' +import { TableCell } from '@/components/Table' +import { AddressTokenBalancesRowProps, TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { isFT } from '@/types/tokens' + +export const FTAddressWorthCell = ({ tokenId, addressHash }: AddressTokenBalancesRowProps) => { + const { data: tokenBalances, isLoading: isLoadingBalance } = useFetchAddressSingleTokenBalances({ + tokenId, + addressHash + }) + const { data: token } = useFetchToken(tokenId) + const { isLoading: isLoadingTokenPrices } = useFetchTokenPrices() + + if (!token || !isFT(token)) return null + + const totalBalance = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined + + return ( + + {isLoadingBalance || isLoadingTokenPrices ? ( + + ) : ( + + )} + + ) +} + +export const FTWalletWorthCell = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { data: tokenBalances, isLoading: isLoadingBalances } = useFetchWalletSingleTokenBalances({ tokenId }) + const { data: token } = useFetchToken(tokenId) + const { isLoading: isLoadingTokenPrices } = useFetchTokenPrices() + + if (!token || !isFT(token)) return null + + const totalBalance = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined + + return ( + + {isLoadingBalances || isLoadingTokenPrices ? ( + + ) : ( + + )} + + ) +} diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameCells.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameCells.tsx new file mode 100644 index 0000000000..1a6b8434e8 --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameCells.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' +import Badge from '@/components/Badge' +import HashEllipsed from '@/components/HashEllipsed' +import { TableCell } from '@/components/Table' +import Truncate from '@/components/Truncate' +import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { isFT, isUnlistedFT } from '@/types/tokens' + +export const FTNameCell = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { t } = useTranslation() + const { data: token } = useFetchToken(tokenId) + + if (!token || !isFT(token)) return null + + return ( + + + {token.name} + + {isUnlistedFT(token) && ( + + i + + )} + + + ) +} + +export const NSTNameCell = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { t } = useTranslation() + + return ( + + + + {t('Unknown')} + + + ) +} + +const TokenName = styled(Truncate)` + display: flex; + align-items: center; + font-size: 13px; + font-weight: var(--fontWeight-semiBold); + gap: 5px; + margin-right: 10px; +` + +const InfoIcon = styled.div` + display: inline-flex; + justify-content: center; + align-items: center; + height: 14px; + width: 14px; + font-size: 11px; + font-weight: 600; + color: ${({ theme }) => theme.font.tertiary}; + background-color: ${({ theme }) => theme.bg.tertiary}; + border-radius: 50%; + cursor: default; + margin-left: 6px; +` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameColumns.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameColumns.tsx deleted file mode 100644 index aa9ef6e43f..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/NameColumns.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { isNumber } from 'lodash' -import { useTranslation } from 'react-i18next' -import styled from 'styled-components' - -import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' -import useFetchToken, { isFT, isUnlistedFT } from '@/api/apiDataHooks/token/useFetchToken' -import Amount from '@/components/Amount' -import HashEllipsed from '@/components/HashEllipsed' -import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' - -export const FTNameColumn = ({ tokenId }: TokenBalancesRowBaseProps) => { - const { t } = useTranslation() - const { data: token } = useFetchToken(tokenId) - - if (!token || !isFT(token)) return null - - return ( - - - {token.name} - - {isUnlistedFT(token) && ( - - i - - )} - - - - - ) -} - -const TokenSymbolAndPrice = ({ tokenSymbol }: { tokenSymbol: string }) => { - const { data: tokenPrice } = useFetchTokenPrice(tokenSymbol) - - return ( - - {isNumber(tokenPrice) ? ( - <> - {tokenSymbol} - - - - ) : ( - tokenSymbol - )} - - ) -} - -export const NSTNameColumn = ({ tokenId }: TokenBalancesRowBaseProps) => { - const { t } = useTranslation() - - return ( - - - - - - ) -} - -const Column = styled.div` - display: flex; - flex-direction: column; - gap: 5px; -` - -const NameColumn = styled(Column)` - margin-right: 50px; -` - -const TokenName = styled.div` - display: flex; - font-size: 14px; - font-weight: var(--fontWeight-medium); - gap: 5px; - align-items: center; -` - -const TokenSymbolAndPriceStyled = styled.div` - color: ${({ theme }) => theme.font.tertiary}; - font-size: 12px; - width: 200px; - display: flex; - align-items: center; - gap: 5px; -` - -const AmountStyled = styled(Amount)` - font-weight: var(--fontWeight-medium); -` - -const PriceSeparator = styled.span` - font-size: 9px; -` - -const InfoIcon = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 14px; - width: 14px; - font-size: 11px; - font-weight: 600; - color: ${({ theme }) => theme.font.tertiary}; - background-color: ${({ theme }) => theme.bg.background2}; - border-radius: 50%; - cursor: default; -` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenBalancesRow.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenBalancesRow.tsx new file mode 100644 index 0000000000..0ff7bf6019 --- /dev/null +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenBalancesRow.tsx @@ -0,0 +1,24 @@ +import { ReactNode } from 'react' + +import { TableRow } from '@/components/Table' +import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { openModal } from '@/features/modals/modalActions' +import { useAppDispatch } from '@/hooks/redux' + +interface TokenBalancesRowProps extends TokenBalancesRowBaseProps { + children: ReactNode +} + +const TokenBalancesRow = ({ tokenId, children }: TokenBalancesRowProps) => { + const dispatch = useAppDispatch() + + const openTokenDetailsModal = () => dispatch(openModal({ name: 'TokenDetailsModal', props: { tokenId } })) + + return ( + + {children} + + ) +} + +export default TokenBalancesRow diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenLogo.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenLogo.tsx index 47b942e276..436efdb405 100644 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenLogo.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/TokenLogo.tsx @@ -1,27 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' import AssetLogo from '@/components/AssetLogo' import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' -const TokenLogo = ({ tokenId }: TokenBalancesRowBaseProps) => +const TokenLogo = ({ tokenId }: TokenBalancesRowBaseProps) => export default TokenLogo diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow.tsx b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow.tsx index f389d89d83..312e3f6e91 100644 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow.tsx +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow.tsx @@ -1,91 +1,37 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled from 'styled-components' - -import useFetchWalletSingleTokenBalances from '@/api/apiDataHooks/wallet/useFetchWalletSingleTokenBalances' -import { TableRow } from '@/components/Table' -import AmountsColumn, { RawAmountSubtitle } from '@/features/assetsLists/tokenBalanceRow/AmountsColumn' -import FTWorth from '@/features/assetsLists/tokenBalanceRow/FTWorth' -import { FTNameColumn, NSTNameColumn } from '@/features/assetsLists/tokenBalanceRow/NameColumns' +import { TableCell } from '@/components/Table' +import FTAllocationCell from '@/features/assetsLists/tokenBalanceRow/FTAllocationCell' +import { FTWalletAmountCell } from '@/features/assetsLists/tokenBalanceRow/FTAmountCells' +import FTPriceCell from '@/features/assetsLists/tokenBalanceRow/FTPriceCell' +import { FTWalletWorthCell } from '@/features/assetsLists/tokenBalanceRow/FTWorthCell' +import { FTNameCell, NSTNameCell } from '@/features/assetsLists/tokenBalanceRow/NameCells' +import TokenBalancesRow from '@/features/assetsLists/tokenBalanceRow/TokenBalancesRow' import TokenLogo from '@/features/assetsLists/tokenBalanceRow/TokenLogo' -import { TokenBalancesRowAmountsProps, TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' export const WalletFTBalancesRow = ({ tokenId }: TokenBalancesRowBaseProps) => ( - - + + - - - - + + + + + + + ) export const WalletNSTBalancesRow = ({ tokenId }: TokenBalancesRowBaseProps) => ( - - + + - - - - - - + + + - + + + + + - + ) - -const FTAmounts = ({ tokenId }: TokenBalancesRowBaseProps) => { - const { data: walletTokenBalances, isLoading: isLoadingTokenBalances } = useFetchWalletSingleTokenBalances({ - tokenId - }) - - const totalBalance = walletTokenBalances?.totalBalance ? BigInt(walletTokenBalances.totalBalance) : undefined - - return ( - - - - ) -} - -const WalletTokenBalancesRowAmounts = ({ tokenId, children }: TokenBalancesRowAmountsProps) => { - const { data: walletTokenBalances, isLoading: isLoadingTokenBalances } = useFetchWalletSingleTokenBalances({ - tokenId - }) - - const totalBalance = walletTokenBalances?.totalBalance ? BigInt(walletTokenBalances.totalBalance) : undefined - const availableBalance = walletTokenBalances?.availableBalance - ? BigInt(walletTokenBalances.availableBalance) - : undefined - - return ( - - {children} - - ) -} - -const TokenRow = styled.div` - display: flex; - align-items: center; - flex-grow: 1; -` diff --git a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/types.ts b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/types.ts index b274b5898c..d49e803ee6 100644 --- a/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/types.ts +++ b/apps/desktop-wallet/src/features/assetsLists/tokenBalanceRow/types.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { ReactNode } from 'react' diff --git a/apps/desktop-wallet/src/features/assetsLists/types.ts b/apps/desktop-wallet/src/features/assetsLists/types.ts deleted file mode 100644 index 79f038bb95..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' - -export type TokensTabValue = 'fts' | 'nfts' | 'nsts' - -export interface TokensTabsBaseProps { - className?: string - isExpanded?: boolean - onExpand?: () => void -} - -export interface WalletTokensTabsProps extends TokensTabsBaseProps { - maxHeightInPx: number -} - -export interface AddressTokensTabsProps extends TokensTabsBaseProps { - addressHash: AddressHash -} diff --git a/apps/desktop-wallet/src/features/assetsLists/useTokensTabs.ts b/apps/desktop-wallet/src/features/assetsLists/useTokensTabs.ts deleted file mode 100644 index 5db8caaaed..0000000000 --- a/apps/desktop-wallet/src/features/assetsLists/useTokensTabs.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { useCallback, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' - -import { TabItem } from '@/components/TabBar' -import { TokensTabsBaseProps, TokensTabValue } from '@/features/assetsLists/types' - -interface TokenTabsProps extends TokensTabsBaseProps { - ftsTabTitle: string - nstsTabTitle: string - nftsTabTitle: string - numberOfNSTs: number -} - -const useTokensTabs = ({ numberOfNSTs, ftsTabTitle, nftsTabTitle, nstsTabTitle }: TokenTabsProps) => { - const { t } = useTranslation() - - const [isExpanded, setIsExpanded] = useState(false) - - const [tabs, setTabs] = useState[]>([ - { value: 'fts', label: ftsTabTitle }, - { value: 'nfts', label: nftsTabTitle } - ]) - - useEffect(() => { - if (numberOfNSTs > 0 && tabs.length === 2) { - setTabs([...tabs, { value: 'nsts', label: nstsTabTitle }]) - } - }, [t, tabs, numberOfNSTs, nstsTabTitle]) - - const toggleExpansion = useCallback(() => setIsExpanded(!isExpanded), [isExpanded]) - - return { - tabs, - isExpanded, - toggleExpansion - } -} - -export default useTokensTabs diff --git a/apps/desktop-wallet/src/features/autoUpdate/AutoUpdateSnackbar.tsx b/apps/desktop-wallet/src/features/autoUpdate/AutoUpdateSnackbar.tsx index 205a60f468..0915eb97bd 100644 --- a/apps/desktop-wallet/src/features/autoUpdate/AutoUpdateSnackbar.tsx +++ b/apps/desktop-wallet/src/features/autoUpdate/AutoUpdateSnackbar.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { X } from 'lucide-react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -36,7 +18,7 @@ type UpdateStatus = 'download-available' | 'downloading' | 'download-finished' | const AutoUpdateSnackbar = () => { const { t } = useTranslation() - const { newVersion, requiresManualDownload } = useLatestGitHubRelease() + const { newAutoUpdateVersion, newManualUpdateVersion } = useLatestGitHubRelease() const { sendAnalytics } = useAnalytics() const [isUpdateSnackbarVisible, setIsUpdateSnackbarVisible] = useState(true) @@ -45,12 +27,11 @@ const AutoUpdateSnackbar = () => { const [error, setError] = useState('') useEffect(() => { - if (!newVersion || requiresManualDownload) return + if (!newAutoUpdateVersion) return let timer: ReturnType setStatus('downloading') - window.electron?.updater.startUpdateDownload() const removeUpdateDownloadProgressListener = window.electron?.updater.onUpdateDownloadProgress((info) => setPercent(info.percent.toFixed(2)) @@ -65,13 +46,17 @@ const AutoUpdateSnackbar = () => { sendAnalytics({ type: 'error', error, message: 'Auto-update download failed' }) }) + window.electron?.updater.startUpdateDownload() + return () => { removeUpdateDownloadProgressListener && removeUpdateDownloadProgressListener() removeUpdateDownloadedListener && removeUpdateDownloadedListener() removeonErrorListener && removeonErrorListener() if (timer) clearTimeout(timer) } - }, [newVersion, requiresManualDownload, sendAnalytics]) + }, [newAutoUpdateVersion, sendAnalytics]) + + const newVersion = newAutoUpdateVersion || newManualUpdateVersion if (!newVersion) return null @@ -80,7 +65,7 @@ const AutoUpdateSnackbar = () => { closeSnackbar() sendAnalytics({ event: 'Auto-update modal: Clicked "Download"', - props: { fromVersion: currentVersion, toVersion: newVersion } + props: { fromVersion: currentVersion, toVersion: newManualUpdateVersion } }) } @@ -123,24 +108,17 @@ const AutoUpdateSnackbar = () => { {status === 'downloading' && } {error && ( - + )} - {status === 'download-available' && requiresManualDownload && ( - )} {status === 'download-finished' && !error && ( - )} @@ -158,7 +136,7 @@ const Texts = styled.div` ` const Title = styled.div` - font-weight: var(--fontWeight-bold); + font-weight: var(--fontWeight-semiBold); margin-bottom: var(--spacing-1); ` diff --git a/apps/desktop-wallet/src/features/autoUpdate/useLatestGitHubRelease.ts b/apps/desktop-wallet/src/features/autoUpdate/useLatestGitHubRelease.ts index 542c3d771a..57f6572e14 100644 --- a/apps/desktop-wallet/src/features/autoUpdate/useLatestGitHubRelease.ts +++ b/apps/desktop-wallet/src/features/autoUpdate/useLatestGitHubRelease.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { exponentialBackoffFetchRetry } from '@alephium/shared' import { compareVersions } from 'compare-versions' import { useState } from 'react' @@ -30,8 +12,8 @@ const semverRegex = isRcVersion ? /^(\d+\.\d+\.\d+)(?:-rc(\.\d+)?)?$/ : /^(\d+\. const useLatestGitHubRelease = () => { const { sendAnalytics } = useAnalytics() - const [newVersion, setNewVersion] = useState('') - const [requiresManualDownload, setRequiresManualDownload] = useState(false) + const [newAutoUpdateVersion, setNewAutoUpdateVersion] = useState('') + const [newManualUpdateVersion, setNewManualUpdateVersion] = useState('') const checkForManualDownload = async () => { try { @@ -40,8 +22,7 @@ const useLatestGitHubRelease = () => { const version = data?.tag_name?.replace('alephium-desktop-wallet@', '') if (version && isVersionNewer(version)) { - setNewVersion(version) - setRequiresManualDownload(true) + setNewManualUpdateVersion(version) } } catch (error) { sendAnalytics({ type: 'error', error, message: 'Checking for latest release version for manual download' }) @@ -56,12 +37,15 @@ const useLatestGitHubRelease = () => { if (!version) { await checkForManualDownload() } else if (isVersionNewer(version)) { - setNewVersion(version) + setNewAutoUpdateVersion(version) } } }) - return { newVersion, requiresManualDownload } + return { + newAutoUpdateVersion, + newManualUpdateVersion + } } export default useLatestGitHubRelease diff --git a/apps/desktop-wallet/src/features/balancesOverview/TotalAlphBalance.tsx b/apps/desktop-wallet/src/features/balancesOverview/TotalAlphBalance.tsx deleted file mode 100644 index bc00865685..0000000000 --- a/apps/desktop-wallet/src/features/balancesOverview/TotalAlphBalance.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ALPH } from '@alephium/token-list' -import { useTranslation } from 'react-i18next' -import styled from 'styled-components' - -import useFetchWalletBalancesAlphArray from '@/api/apiDataHooks/wallet/useFetchWalletBalancesAlphArray' -import Amount from '@/components/Amount' - -interface TotalAlphBalanceProps { - type: 'available' | 'locked' - className?: string -} - -const TotalAlphBalance = ({ className, type }: TotalAlphBalanceProps) => { - const { t } = useTranslation() - - const available = type === 'available' - - return ( -
- - {t(available ? 'Available' : 'Locked')} - - {available ? : } -
- ) -} - -const AvailableAlphAmount = () => { - const { data, isLoading } = useFetchWalletBalancesAlphArray() - - const value = data?.availableBalance ? BigInt(data.availableBalance) : undefined - - return -} - -const LockedAlphAmount = () => { - const { data, isLoading } = useFetchWalletBalancesAlphArray() - - const value = data?.lockedBalance ? BigInt(data.lockedBalance) : undefined - - return -} - -export default TotalAlphBalance - -const BalanceLabel = styled.label` - color: ${({ theme }) => theme.font.tertiary}; - font-size: 12px; - display: block; - margin-bottom: 3px; -` - -const AmountStyled = styled(Amount)` - color: ${({ theme }) => theme.font.primary}; - font-size: 21px; - font-weight: var(--fontWeight-semiBold); -` diff --git a/apps/desktop-wallet/src/features/buy/BuyModal.tsx b/apps/desktop-wallet/src/features/buy/BuyModal.tsx new file mode 100644 index 0000000000..92e875b60d --- /dev/null +++ b/apps/desktop-wallet/src/features/buy/BuyModal.tsx @@ -0,0 +1,79 @@ +import { AddressHash, ONRAMP_TARGET_LOCATION } from '@alephium/shared' +import { memo, useEffect, useState } from 'react' +import { Trans, useTranslation } from 'react-i18next' +import { useNavigate } from 'react-router-dom' +import styled from 'styled-components' + +import ActionLink from '@/components/ActionLink' +import FooterButton from '@/components/Buttons/FooterButton' +import useOnramperUrl from '@/features/buy/useOnramperUrl' +import { closeModal } from '@/features/modals/modalActions' +import { ModalBaseProp } from '@/features/modals/modalTypes' +import { useAppDispatch } from '@/hooks/redux' +import CenteredModal from '@/modals/CenteredModal' +import { showToast } from '@/storage/global/globalActions' +import { openInWebBrowser } from '@/utils/misc' + +export interface BuyModalProps { + addressHash: AddressHash +} + +// TODO: Support multiple fiat on-ramp provider. +// Use modal to compare fees, and or let user define how much they want to buy + +const BuyModal = memo(({ id, addressHash }: ModalBaseProp & BuyModalProps) => { + const { t } = useTranslation() + const navigate = useNavigate() + const dispatch = useAppDispatch() + const [disclaimerAccepted, setDisclaimerAccepted] = useState(false) + + const onRamperUrl = useOnramperUrl(addressHash) + + const handleAcceptDisclaimer = () => { + window.electron?.app.openOnRampServiceWindow({ url: onRamperUrl, targetLocation: ONRAMP_TARGET_LOCATION }) + setDisclaimerAccepted(true) + } + + useEffect(() => { + const removeListener = window.electron?.app.onOnRampTargetLocationReached(() => { + showToast({ text: t('Purchase done!'), type: 'success', duration: 'short' }) + navigate('/wallet/activity') + dispatch(closeModal({ id })) + }) + + return removeListener + }, [dispatch, id, navigate, t]) + + return ( + + + {!disclaimerAccepted ? ( + + You are about to access 3rd party services provided by + openInWebBrowser('https://www.onramper.com')}> through + an in-app browser. Alephium does not control Onramper's services. Onramper's terms and conditions will + apply, so please read and understand them before proceeding. + + ) : ( + t('You can now complete your purchase in the dedicated window!') + ' 🤑' + )} + + {!disclaimerAccepted && ( + + {t('Continue')} + + )} + + ) +}) + +export default BuyModal + +const ActionLinkStyled = styled(ActionLink)` + display: inline; +` + +const TextContainer = styled.span` + font-size: 13px; + text-align: center; +` diff --git a/apps/desktop-wallet/src/features/buy/useOnramperUrl.ts b/apps/desktop-wallet/src/features/buy/useOnramperUrl.ts new file mode 100644 index 0000000000..8b783a7469 --- /dev/null +++ b/apps/desktop-wallet/src/features/buy/useOnramperUrl.ts @@ -0,0 +1,15 @@ +import { AddressHash, getOnramperUrl } from '@alephium/shared' +import { useTheme } from 'styled-components' + +const useOnramperUrl = (receiveAddressHash: AddressHash) => { + const theme = useTheme() + + return getOnramperUrl(receiveAddressHash, { + themeName: theme.name, + containerColor: theme.bg.background1.slice(1), + primaryTextColor: theme.font.primary.slice(1), + primaryColor: theme.global.accent.slice(1) + }) +} + +export default useOnramperUrl diff --git a/apps/desktop-wallet/src/features/csvExport/AddressTransactionsCSVExportButton.tsx b/apps/desktop-wallet/src/features/csvExport/AddressTransactionsCSVExportButton.tsx index eeeb8e2929..5e0150a18a 100644 --- a/apps/desktop-wallet/src/features/csvExport/AddressTransactionsCSVExportButton.tsx +++ b/apps/desktop-wallet/src/features/csvExport/AddressTransactionsCSVExportButton.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { FileDown } from 'lucide-react' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/dataPolling/useAddressesDataPolling.ts b/apps/desktop-wallet/src/features/dataPolling/useAddressesDataPolling.ts index c820d0a157..9036b97202 100644 --- a/apps/desktop-wallet/src/features/dataPolling/useAddressesDataPolling.ts +++ b/apps/desktop-wallet/src/features/dataPolling/useAddressesDataPolling.ts @@ -1,32 +1,13 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { TRANSACTIONS_REFRESH_INTERVAL } from '@alephium/shared' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { useQueries } from '@tanstack/react-query' import { addressLatestTransactionQuery } from '@/api/queries/transactionQueries' -import { useAppSelector } from '@/hooks/redux' import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const useAddressesDataPolling = () => { const allAddressHashes = useUnsortedAddressesHashes() - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() useQueries({ queries: allAddressHashes.map((addressHash) => ({ diff --git a/apps/desktop-wallet/src/features/dataPolling/usePendingTxPolling.ts b/apps/desktop-wallet/src/features/dataPolling/usePendingTxPolling.ts index bd284a507e..6d66ccfdef 100644 --- a/apps/desktop-wallet/src/features/dataPolling/usePendingTxPolling.ts +++ b/apps/desktop-wallet/src/features/dataPolling/usePendingTxPolling.ts @@ -1,22 +1,5 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { findTransactionInternalAddresses, isConfirmedTx } from '@alephium/shared' +import { useCurrentlyOnlineNetworkId } from '@alephium/shared-react' import { explorer as e } from '@alephium/web3' import { useEffect } from 'react' @@ -26,11 +9,10 @@ import { sentTransactionStatusChanged } from '@/features/send/sentTransactions/s import { selectSentTransactionByHash } from '@/features/send/sentTransactions/sentTransactionsSelectors' import { useAppDispatch, useAppSelector } from '@/hooks/redux' import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' const usePendingTxPolling = (txHash: e.Transaction['hash']) => { const dispatch = useAppDispatch() - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) + const networkId = useCurrentlyOnlineNetworkId() const sentTx = useAppSelector((s) => selectSentTransactionByHash(s, txHash)) const allAddressHashes = useUnsortedAddressesHashes() diff --git a/apps/desktop-wallet/src/features/hiddenTokens/HiddenTokensBalancesLists.tsx b/apps/desktop-wallet/src/features/hiddenTokens/HiddenTokensBalancesLists.tsx new file mode 100644 index 0000000000..478c2b034d --- /dev/null +++ b/apps/desktop-wallet/src/features/hiddenTokens/HiddenTokensBalancesLists.tsx @@ -0,0 +1,132 @@ +import { ChevronUp, EyeOff } from 'lucide-react' +import { ReactNode, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import useFetchAddressHiddenTokens from '@/api/apiDataHooks/address/useFetchAddressHiddenTokens' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' +import Button from '@/components/Button' +import Table from '@/components/Table' +import { + AddressFTBalancesRow, + AddressNSTBalancesRow +} from '@/features/assetsLists/tokenBalanceRow/AddressTokenBalancesRow' +import { AddressTokenBalancesRowProps, TokenBalancesRowBaseProps } from '@/features/assetsLists/tokenBalanceRow/types' +import { + WalletFTBalancesRow, + WalletNSTBalancesRow +} from '@/features/assetsLists/tokenBalanceRow/WalletTokenBalancesRow' +import { AddressModalBaseProp } from '@/features/modals/modalTypes' +import { useAppSelector } from '@/hooks/redux' +import { isFT, isNFT } from '@/types/tokens' + +export const HiddenWalletTokensBalancesListSection = () => { + const hiddenTokenIds = useAppSelector((s) => s.hiddenTokens.hiddenTokensIds) + + return ( + + + + ) +} + +export const HiddenAddressTokensBalancesListSection = ({ addressHash }: AddressModalBaseProp) => { + const { data: hiddenTokenIds } = useFetchAddressHiddenTokens({ addressHash }) + + return ( + + + + ) +} + +interface HiddenTokensBalancesListSectionProps { + count: number + children: ReactNode +} + +const HiddenTokensBalancesListSection = ({ count, children }: HiddenTokensBalancesListSectionProps) => { + const { t } = useTranslation() + + const [showHiddenTokensBalancesList, setShowHiddenTokensBalancesList] = useState(false) + + const toggleHiddenTokensBalancesList = () => setShowHiddenTokensBalancesList((prev) => !prev) + + if (count === 0) return null + + return ( + <> + + {showHiddenTokensBalancesList ? t('Close hidden assets list') : t('nb_of_hidden_assets', { count })} + + {showHiddenTokensBalancesList && children} + + ) +} + +const HiddenWalletTokensBalancesList = () => { + const hiddenTokenIds = useAppSelector((s) => s.hiddenTokens.hiddenTokensIds) + + return ( + + {hiddenTokenIds.map((tokenId) => ( + + ))} + + ) +} + +const HiddenAddressTokensBalancesList = ({ addressHash }: AddressModalBaseProp) => { + const { data: hiddenTokenIds } = useFetchAddressHiddenTokens({ addressHash }) + + if (!hiddenTokenIds || hiddenTokenIds.length === 0) return null + + return ( + + {hiddenTokenIds.map((tokenId) => ( + + ))} + + ) +} + +const HiddenAddressTokenBalancesRow = ({ addressHash, tokenId }: AddressTokenBalancesRowProps) => { + const { data: token } = useFetchToken(tokenId) + + if (!token) return null + + return isFT(token) ? ( + + ) : !isNFT(token) ? ( + + ) : null +} + +const HiddenWalletTokenBalancesRow = ({ tokenId }: TokenBalancesRowBaseProps) => { + const { data: token } = useFetchToken(tokenId) + + if (!token) return null + + return isFT(token) ? ( + + ) : !isNFT(token) ? ( + + ) : null +} + +const ButtonStyled = styled(Button)` + margin: var(--spacing-4) auto; +` + +const TableStyled = styled(Table)` + border: 1px solid ${({ theme }) => theme.border.primary}; + border-radius: var(--radius-big); + padding-left: var(--spacing-3); + padding-right: var(--spacing-3); + background-color: ${({ theme }) => theme.bg.primary}; +` diff --git a/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensActions.ts b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensActions.ts new file mode 100644 index 0000000000..e4fe7f17b0 --- /dev/null +++ b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensActions.ts @@ -0,0 +1,7 @@ +import { createAction } from '@reduxjs/toolkit' + +import { TokenId } from '@/types/tokens' + +export const hideToken = createAction('hiddenTokens/hide') +export const unhideToken = createAction('hiddenTokens/unhide') +export const hiddenTokensLoadedFromStorage = createAction('hiddenTokens/hiddenTokensLoadedFromStorage') diff --git a/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensPersistentStorage.ts b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensPersistentStorage.ts new file mode 100644 index 0000000000..afd09403c4 --- /dev/null +++ b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensPersistentStorage.ts @@ -0,0 +1,4 @@ +import { PersistentArrayStorage } from '@/storage/persistentArrayStorage' +import { TokenId } from '@/types/tokens' + +export const hiddenTokensStorage = new PersistentArrayStorage('hiddenTokens') diff --git a/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSelectors.ts b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSelectors.ts new file mode 100644 index 0000000000..447c1bfe06 --- /dev/null +++ b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSelectors.ts @@ -0,0 +1,10 @@ +import { createSelector } from '@reduxjs/toolkit' + +import { RootState } from '@/storage/store' +import { TokenId } from '@/types/tokens' + +export const selectIsTokenHidden = createSelector( + (state: RootState) => state.hiddenTokens.hiddenTokensIds, + (_state: RootState, tokenId: TokenId) => tokenId, + (hiddenTokenIds, tokenId) => hiddenTokenIds.includes(tokenId) +) diff --git a/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSlice.ts b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSlice.ts new file mode 100644 index 0000000000..68355c6129 --- /dev/null +++ b/apps/desktop-wallet/src/features/hiddenTokens/hiddenTokensSlice.ts @@ -0,0 +1,67 @@ +import { appReset } from '@alephium/shared' +import { createListenerMiddleware, createSlice, isAnyOf } from '@reduxjs/toolkit' + +import { hiddenTokensLoadedFromStorage, hideToken, unhideToken } from '@/features/hiddenTokens/hiddenTokensActions' +import { hiddenTokensStorage } from '@/features/hiddenTokens/hiddenTokensPersistentStorage' +import { RootState } from '@/storage/store' +import { walletDeleted } from '@/storage/wallets/walletActions' +import { TokenId } from '@/types/tokens' + +const sliceName = 'hiddenTokens' + +export interface hiddenTokensState { + hiddenTokensIds: Array + loadedFromStorage: boolean +} + +const initialState: hiddenTokensState = { + hiddenTokensIds: [], + loadedFromStorage: false +} + +const resetState = () => initialState + +const hiddenTokensSlice = createSlice({ + name: sliceName, + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(hiddenTokensLoadedFromStorage, (state, action) => { + state.hiddenTokensIds = action.payload + state.loadedFromStorage = true + }) + .addCase(hideToken, (state, action) => { + if (!state.hiddenTokensIds.includes(action.payload)) { + state.hiddenTokensIds.push(action.payload) + } + }) + .addCase(unhideToken, (state, action) => { + state.hiddenTokensIds = state.hiddenTokensIds.filter((id) => id !== action.payload) + }) + .addCase(appReset, resetState) + .addCase(walletDeleted, resetState) + } +}) + +export default hiddenTokensSlice + +export const hiddenTokensListenerMiddleware = createListenerMiddleware() + +// When the settings change, store them in persistent storage +hiddenTokensListenerMiddleware.startListening({ + matcher: isAnyOf(hideToken, unhideToken, walletDeleted, appReset), + effect: (_, { getState }) => { + const state = getState() as RootState + const walletId = state.activeWallet.id + const hiddenTokensState = state[sliceName] + + if (hiddenTokensState.loadedFromStorage && walletId) { + try { + hiddenTokensStorage.store(walletId, hiddenTokensState.hiddenTokensIds) + } catch (error) { + console.error(error) + } + } + } +}) diff --git a/apps/desktop-wallet/src/features/historicChart/ChartLengthBadges.tsx b/apps/desktop-wallet/src/features/historicChart/ChartLengthBadges.tsx deleted file mode 100644 index 12c0e12c40..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/ChartLengthBadges.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { memo } from 'react' -import styled from 'styled-components' - -import Button from '@/components/Button' -import SkeletonLoader from '@/components/SkeletonLoader' -import { ChartLength, chartLengths } from '@/features/historicChart/historicChartTypes' -import { chartLengthChanged } from '@/features/historicChart/historicWorthChartActions' -import useHistoricData from '@/features/historicChart/useHistoricData' -import { useAppDispatch, useAppSelector } from '@/hooks/redux' - -const ChartLengthBadges = memo(() => { - const dispatch = useAppDispatch() - const chartLength = useAppSelector((s) => s.historicWorthChart.chartLength) - const { hasHistoricBalances, isLoading: isLoadingHistoricData } = useHistoricData() - - const handleChartLengthButtonClick = (length: ChartLength) => dispatch(chartLengthChanged(length)) - - return ( - - {chartLengths.map((length) => - isLoadingHistoricData ? ( - - ) : ( - hasHistoricBalances && ( - handleChartLengthButtonClick(length)} - > - {length} - - ) - ) - )} - - ) -}) - -export default ChartLengthBadges - -const ChartLengthBadgesStyled = styled.div` - margin-top: 20px; - display: flex; - gap: 10px; -` - -const ButtonStyled = styled(Button)<{ isActive: boolean }>` - color: ${({ theme }) => theme.font.primary}; - opacity: ${({ isActive }) => (isActive ? 1 : 0.4)}; - border-color: ${({ theme }) => theme.font.primary}; - padding: 3px; - height: auto; - min-width: 32px; - border-radius: var(--radius-small); - - &:hover { - border-color: ${({ theme }) => theme.font.tertiary}; - } -` diff --git a/apps/desktop-wallet/src/features/historicChart/FiatDeltaPercentage.tsx b/apps/desktop-wallet/src/features/historicChart/FiatDeltaPercentage.tsx deleted file mode 100644 index ce27d13111..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/FiatDeltaPercentage.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import styled from 'styled-components' - -import DeltaPercentage from '@/components/DeltaPercentage' -import SkeletonLoader from '@/components/SkeletonLoader' -import useHistoricData from '@/features/historicChart/useHistoricData' - -interface FiatDeltaPercentageProps { - worthInBeginningOfChart?: number - hoveredDataPointWorth: number -} - -const FiatDeltaPercentage = ({ worthInBeginningOfChart, hoveredDataPointWorth }: FiatDeltaPercentageProps) => { - const { isLoading: isLoadingHistoricData } = useHistoricData() - - if (isLoadingHistoricData) return - - if (worthInBeginningOfChart === undefined) return null - - return ( - - - - ) -} - -export default FiatDeltaPercentage - -const FiatDeltaPercentageStyled = styled.div` - font-size: 18px; - margin-top: 5px; -` diff --git a/apps/desktop-wallet/src/features/historicChart/HistoricWorthChart.tsx b/apps/desktop-wallet/src/features/historicChart/HistoricWorthChart.tsx deleted file mode 100644 index b1a14a5f04..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/HistoricWorthChart.tsx +++ /dev/null @@ -1,202 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash, CHART_DATE_FORMAT, toHumanReadableAmount } from '@alephium/shared' -import dayjs, { Dayjs } from 'dayjs' -import { motion } from 'framer-motion' -import { memo, useEffect, useState } from 'react' -import Chart from 'react-apexcharts' -import styled, { css, useTheme } from 'styled-components' - -import useFetchWalletWorthAlph from '@/api/apiDataHooks/wallet/useFetchWalletWorthAlph' -import { ChartLength, DataPoint, LatestAmountPerAddress } from '@/features/historicChart/historicChartTypes' -import { getChartOptions, getFilteredChartData } from '@/features/historicChart/historicChartUtils' -import useHistoricData from '@/features/historicChart/useHistoricData' -import { useAppSelector } from '@/hooks/redux' -import { useUnsortedAddressesHashes } from '@/hooks/useUnsortedAddresses' - -interface HistoricWorthChartProps { - onDataPointHover: (dataPoint?: DataPoint) => void - onWorthInBeginningOfChartChange: (worthInBeginningOfChart?: DataPoint['worth']) => void - chartVisible?: boolean - chartInitiallyHidden?: boolean - addressHash?: AddressHash -} - -export const historicWorthChartHeight = 100 - -const chartAnimationVariants = { - shown: { height: historicWorthChartHeight, opacity: 1 }, - hidden: { height: 0, opacity: 0 } -} - -const now = dayjs() -const startingDates: Record = { - '1d': now.subtract(1, 'day'), - '1w': now.subtract(1, 'week'), - '1m': now.subtract(1, 'month'), - '1y': now.subtract(1, 'year') -} - -const HistoricWorthChart = memo( - ({ - chartVisible, - chartInitiallyHidden, - addressHash, - onDataPointHover, - onWorthInBeginningOfChartChange - }: HistoricWorthChartProps) => { - const theme = useTheme() - const discreetMode = useAppSelector((s) => s.settings.discreetMode) - const length = useAppSelector((s) => s.historicWorthChart.chartLength) - const allAddressesHashes = useUnsortedAddressesHashes() - const { alphBalanceHistoryPerAddress, alphPriceHistory, isLoading: isLoadingHistoricData } = useHistoricData() - const { data: latestWorth } = useFetchWalletWorthAlph() - - const [chartData, setChartData] = useState([]) - - const startingDate = startingDates[length].format(CHART_DATE_FORMAT) - const isDataAvailable = !isLoadingHistoricData && !!alphPriceHistory - const firstItem = chartData.at(0) - - useEffect(() => { - onWorthInBeginningOfChartChange(firstItem?.worth) - }, [firstItem?.worth, onWorthInBeginningOfChartChange]) - - useEffect(() => { - if (!isDataAvailable) { - setChartData([]) - return - } - - const computeChartDataPoints = (): DataPoint[] => { - const addressesLatestAmount: LatestAmountPerAddress = {} - - const dataPoints = alphPriceHistory.map(({ date, value }) => { - let totalAmountPerDate = BigInt(0) - const addresses = addressHash ? [addressHash] : allAddressesHashes - - addresses.forEach((hash) => { - const addressAlphBalanceHistory = alphBalanceHistoryPerAddress[hash] - const amountOnDate = addressAlphBalanceHistory?.[date] - - if (amountOnDate !== undefined) { - const amount = BigInt(amountOnDate) - totalAmountPerDate += amount - addressesLatestAmount[hash] = amount - } else { - totalAmountPerDate += addressesLatestAmount[hash] ?? BigInt(0) - } - }) - - return { - date, - worth: value * parseFloat(toHumanReadableAmount(totalAmountPerDate)) - } - }) - - if (latestWorth !== undefined) dataPoints.push({ date: dayjs().format(CHART_DATE_FORMAT), worth: latestWorth }) - - return dataPoints - } - - const trimInitialZeroDataPoints = (data: DataPoint[]) => data.slice(data.findIndex((point) => point.worth !== 0)) - - let dataPoints = computeChartDataPoints() - dataPoints = trimInitialZeroDataPoints(dataPoints) - - setChartData(getFilteredChartData(dataPoints, startingDate)) - }, [ - addressHash, - allAddressesHashes, - alphBalanceHistoryPerAddress, - alphPriceHistory, - isDataAvailable, - latestWorth, - startingDate - ]) - - const shouldHideChart = !isDataAvailable || chartData.length < 2 || !firstItem || latestWorth === undefined - - const xAxisDatesData = chartData.map(({ date }) => date) - const yAxisWorthData = chartData.map(({ worth }) => worth) - - const worthHasGoneUp = (firstItem?.worth ?? 0) < latestWorth - const chartColor = worthHasGoneUp ? theme.global.valid : theme.global.alert - - const chartOptions = getChartOptions(chartColor, xAxisDatesData, { - mouseMove(e, chart, options) { - onDataPointHover(options.dataPointIndex === -1 ? undefined : chartData[options.dataPointIndex]) - }, - mouseLeave() { - onDataPointHover(undefined) - } - }) - - return ( - - - - - - - - ) - } -) - -export default HistoricWorthChart - -const ChartOuterContainer = styled(motion.div)>` - display: flex; - align-items: center; - right: 0; - left: 0; - margin: var(--spacing-4) 0; - - ${({ chartInitiallyHidden }) => - chartInitiallyHidden && - css` - height: 0; - `} - - overflow: hidden; -` - -const ChartInnerContainer = styled(motion.div)` - height: 100%; - width: 100%; -` - -const ChartWrapper = styled.div` - width: 100%; - height: ${historicWorthChartHeight}px; - opacity: 0.3; - transition: opacity 0.2s ease-out; - - filter: saturate(0); - - &:hover { - opacity: 1; - filter: saturate(1); - } -` diff --git a/apps/desktop-wallet/src/features/historicChart/historicChartTypes.ts b/apps/desktop-wallet/src/features/historicChart/historicChartTypes.ts deleted file mode 100644 index bc0530f40b..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/historicChartTypes.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' - -export type LatestAmountPerAddress = Record - -export type DataPoint = { - date: string - worth: number -} - -export const chartLengths = ['1d', '1w', '1m', '1y'] as const - -type ChartLengthType = typeof chartLengths -export type ChartLength = ChartLengthType[number] diff --git a/apps/desktop-wallet/src/features/historicChart/historicChartUtils.ts b/apps/desktop-wallet/src/features/historicChart/historicChartUtils.ts deleted file mode 100644 index e35623c9d8..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/historicChartUtils.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { colord } from 'colord' - -import { DataPoint } from '@/features/historicChart/historicChartTypes' - -export const getFilteredChartData = (chartData: DataPoint[], startingDate: string) => { - const startingPoint = chartData.findIndex((point) => point.date === startingDate) - return startingPoint > 0 ? chartData.slice(startingPoint) : chartData -} - -export const getChartOptions = ( - chartColor: string, - xAxisData: string[], - events: ApexChart['events'] -): ApexCharts.ApexOptions => ({ - chart: { - id: 'alephium-chart', - toolbar: { - show: false - }, - zoom: { - enabled: false - }, - sparkline: { - enabled: true - }, - events, - animations: { - enabled: false, - easing: 'easeout', - speed: 500, - dynamicAnimation: { - enabled: false - } - } - }, - xaxis: { - type: 'datetime', - categories: xAxisData, - axisTicks: { - show: false - }, - axisBorder: { - show: false - }, - tooltip: { - enabled: false - }, - labels: { - show: false - }, - crosshairs: { - show: false - } - }, - yaxis: { - show: false - }, - grid: { - show: false, - padding: { - left: 0, - right: 0 - } - }, - stroke: { - curve: 'smooth', - colors: [chartColor], - width: 2 - }, - tooltip: { - custom: () => null, - fixed: { - enabled: true - } - }, - markers: { - colors: [chartColor], - strokeColors: [chartColor], - hover: { - size: 4 - } - }, - dataLabels: { - enabled: false - }, - fill: { - type: 'gradient', - gradient: { - shadeIntensity: 1, - type: 'vertical', - colorStops: [ - [ - { - offset: 0, - color: colord(chartColor).alpha(0.3).toHex(), - opacity: 1 - }, - { - offset: 100, - color: colord(chartColor).alpha(0).toHex(), - opacity: 1 - } - ] - ] - } - } -}) diff --git a/apps/desktop-wallet/src/features/historicChart/historicWorthChartActions.ts b/apps/desktop-wallet/src/features/historicChart/historicWorthChartActions.ts deleted file mode 100644 index 8e46be8d8c..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/historicWorthChartActions.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { createAction } from '@reduxjs/toolkit' - -import { ChartLength } from '@/features/historicChart/historicChartTypes' - -export const chartLengthChanged = createAction('historicWorthChart/chartLengthChanged') diff --git a/apps/desktop-wallet/src/features/historicChart/historicWorthChartSlice.ts b/apps/desktop-wallet/src/features/historicChart/historicWorthChartSlice.ts deleted file mode 100644 index 8b5a8e834a..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/historicWorthChartSlice.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { createSlice } from '@reduxjs/toolkit' - -import { ChartLength } from '@/features/historicChart/historicChartTypes' -import { chartLengthChanged } from '@/features/historicChart/historicWorthChartActions' - -interface HistoricWorthChartState { - chartLength: ChartLength -} - -const initialState: HistoricWorthChartState = { - chartLength: '1m' -} - -const historicWorthChartSlice = createSlice({ - name: 'historicWorthChart', - initialState, - reducers: {}, - extraReducers(builder) { - builder.addCase(chartLengthChanged, (state, { payload: chartLength }) => { - state.chartLength = chartLength - }) - } -}) - -export default historicWorthChartSlice diff --git a/apps/desktop-wallet/src/features/historicChart/useHistoricData.tsx b/apps/desktop-wallet/src/features/historicChart/useHistoricData.tsx deleted file mode 100644 index 2386ebcb32..0000000000 --- a/apps/desktop-wallet/src/features/historicChart/useHistoricData.tsx +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash, CHART_DATE_FORMAT, ONE_DAY_MS, throttledClient, TokenHistoricalPrice } from '@alephium/shared' -import { ALPH } from '@alephium/token-list' -import { explorer as e } from '@alephium/web3' -import { skipToken, useQueries, useQuery, UseQueryResult } from '@tanstack/react-query' -import dayjs from 'dayjs' - -import { combineIsLoading } from '@/api/apiDataHooks/apiDataHooksUtils' -import { getQueryConfig } from '@/api/apiDataHooks/utils/getQueryConfig' -import { useAppSelector } from '@/hooks/redux' -import { selectCurrentlyOnlineNetworkId } from '@/storage/network/networkSelectors' - -const DAILY = e.IntervalType.Daily - -type Timestamp = string -type Amount = string - -const useHistoricData = () => { - const allAddressHashes = useAppSelector((s) => s.addresses.ids as AddressHash[]) - const currency = useAppSelector((s) => s.settings.fiatCurrency).toLowerCase() - const networkId = useAppSelector(selectCurrentlyOnlineNetworkId) - - const { data: alphPriceHistory, isLoading: isLoadingAlphPriceHistory } = useQuery({ - queryKey: ['history', 'price', ALPH.symbol, { currency }], - // We don't want to delete the price history if the user stays on a page without a chart for too long - ...getQueryConfig({ staleTime: ONE_DAY_MS, gcTime: Infinity }), - queryFn: - networkId !== undefined - ? () => - throttledClient.explorer.market - .getMarketPricesSymbolCharts(ALPH.symbol, { currency }) - .then((rawHistory) => { - const today = dayjs().format(CHART_DATE_FORMAT) - const history = [] as TokenHistoricalPrice[] - - if (rawHistory.timestamps && rawHistory.prices) { - for (let index = 0; index < rawHistory.timestamps.length; index++) { - const timestamp = rawHistory.timestamps[index] - const price = rawHistory.prices[index] - - const itemDate = dayjs(timestamp).format(CHART_DATE_FORMAT) - const prevItemDate = - index > 1 ? dayjs(rawHistory.timestamps[index - 1]).format(CHART_DATE_FORMAT) : undefined - - if (itemDate !== prevItemDate && itemDate !== today) { - history.push({ - date: itemDate, - value: price - }) - } - } - } - - return history - }) - : skipToken - }) - - const { - data: alphBalanceHistoryPerAddress, - isLoading: isLoadingHistoricalAlphBalances, - hasHistoricBalances - } = useQueries({ - queries: - networkId !== undefined - ? allAddressHashes.map((hash) => ({ - queryKey: ['address', hash, 'history', 'addressBalance', DAILY, ALPH.symbol, { networkId }], - // We don't want to delete the balance history if the user stays on a page without a chart for too long - ...getQueryConfig({ staleTime: ONE_DAY_MS, gcTime: Infinity, networkId }), - queryFn: async () => { - const now = dayjs() - const thisMoment = now.valueOf() - const oneYearAgo = now.subtract(365, 'days').valueOf() - - const { amountHistory } = await throttledClient.explorer.addresses.getAddressesAddressAmountHistory( - hash, - { - fromTs: oneYearAgo, - toTs: thisMoment, - 'interval-type': DAILY - } - ) - - return { - address: hash, - amountHistory - } - } - })) - : [], - combine - }) - - return { - alphBalanceHistoryPerAddress, - isLoadingHistoricalAlphBalances, - isLoadingAlphPriceHistory, - alphPriceHistory, - isLoading: isLoadingAlphPriceHistory || isLoadingHistoricalAlphBalances, - hasHistoricBalances - } -} - -export default useHistoricData - -const combine = ( - results: UseQueryResult<{ - address: AddressHash - amountHistory: e.AmountHistory['amountHistory'] - }>[] -) => ({ - data: results.reduce( - (historyPerAddress, { data }) => { - if (data) { - historyPerAddress[data.address] = !data.amountHistory - ? undefined - : data.amountHistory.reduce( - (amountPerDate, [timestamp, amount]) => { - const date = dayjs(timestamp).format(CHART_DATE_FORMAT) - amountPerDate[date] = amount - - return amountPerDate - }, - {} as Record - ) - } - - return historyPerAddress - }, - {} as Record | undefined> - ), - ...combineIsLoading(results), - hasHistoricBalances: results.some(({ data }) => data?.amountHistory?.length) -}) diff --git a/apps/desktop-wallet/src/features/ledger/ConnectLedgerInstructionsPage.tsx b/apps/desktop-wallet/src/features/ledger/ConnectLedgerInstructionsPage.tsx index c26e52e4ad..d0ee98f9f3 100644 --- a/apps/desktop-wallet/src/features/ledger/ConnectLedgerInstructionsPage.tsx +++ b/apps/desktop-wallet/src/features/ledger/ConnectLedgerInstructionsPage.tsx @@ -1,29 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { getHumanReadableError } from '@alephium/shared' import { useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' -import { fadeInSlowly } from '@/animations' import ActionLink from '@/components/ActionLink' -import AppHeader from '@/components/AppHeader' import Button from '@/components/Button' import InfoBox from '@/components/InfoBox' import { FloatingPanel, FooterActionsContainer } from '@/components/PageComponents/PageContainers' @@ -62,7 +42,7 @@ const ConnectLedgerInstructionsPage = () => { } return ( - + navigate('/')}>{t('Connect your Ledger')}
    @@ -88,8 +68,6 @@ const ConnectLedgerInstructionsPage = () => { - - ) } diff --git a/apps/desktop-wallet/src/features/ledger/ConnectWithLedgerButton.tsx b/apps/desktop-wallet/src/features/ledger/ConnectWithLedgerButton.tsx index b18421c8a6..d94e733fd2 100644 --- a/apps/desktop-wallet/src/features/ledger/ConnectWithLedgerButton.tsx +++ b/apps/desktop-wallet/src/features/ledger/ConnectWithLedgerButton.tsx @@ -1,23 +1,6 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' +import styled from 'styled-components' import Button from '@/components/Button' import { ReactComponent as LedgerLogo } from '@/images/ledger.svg' @@ -31,11 +14,17 @@ const ConnectWithLedgerButton = () => { } return ( - ) } export default ConnectWithLedgerButton + +const LedgerLogoStyled = styled(LedgerLogo)` + path { + fill: ${({ theme }) => theme.font.primary}; + } +` diff --git a/apps/desktop-wallet/src/features/ledger/LedgerAddressDiscoverySnackbar.tsx b/apps/desktop-wallet/src/features/ledger/LedgerAddressDiscoverySnackbar.tsx index 6b9acc536e..d709cc1c3a 100644 --- a/apps/desktop-wallet/src/features/ledger/LedgerAddressDiscoverySnackbar.tsx +++ b/apps/desktop-wallet/src/features/ledger/LedgerAddressDiscoverySnackbar.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { X } from 'lucide-react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -48,10 +30,10 @@ const LedgerAddressDiscoverySnackbar = () => { {t('Welcome to your Ledger!') + ' 👋'}
    {t('Would you like to scan for active addresses?')}
    - - diff --git a/apps/desktop-wallet/src/features/ledger/discovery.ts b/apps/desktop-wallet/src/features/ledger/discovery.ts index 314180052a..32cac4c135 100644 --- a/apps/desktop-wallet/src/features/ledger/discovery.ts +++ b/apps/desktop-wallet/src/features/ledger/discovery.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { NonSensitiveAddressDataWithGroup } from '@alephium/keyring' import { findNextAvailableAddressIndex, throttledClient } from '@alephium/shared' import { TOTAL_NUMBER_OF_GROUPS } from '@alephium/web3' diff --git a/apps/desktop-wallet/src/features/ledger/ledgerActions.ts b/apps/desktop-wallet/src/features/ledger/ledgerActions.ts index 4acf9fde0a..67fef1c47f 100644 --- a/apps/desktop-wallet/src/features/ledger/ledgerActions.ts +++ b/apps/desktop-wallet/src/features/ledger/ledgerActions.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createAction } from '@reduxjs/toolkit' export const newLedgerDeviceConnected = createAction('ledger/newLedgerDeviceConnected') diff --git a/apps/desktop-wallet/src/features/ledger/ledgerSlice.ts b/apps/desktop-wallet/src/features/ledger/ledgerSlice.ts index a83578eeb8..4f576179e9 100644 --- a/apps/desktop-wallet/src/features/ledger/ledgerSlice.ts +++ b/apps/desktop-wallet/src/features/ledger/ledgerSlice.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createSlice } from '@reduxjs/toolkit' import { newLedgerDeviceConnected, userWasAskedToDiscoverAddresses } from './ledgerActions' diff --git a/apps/desktop-wallet/src/features/ledger/useInitializeAppWithLedgerData.tsx b/apps/desktop-wallet/src/features/ledger/useInitializeAppWithLedgerData.tsx index caf4d41f65..5e83bfe511 100644 --- a/apps/desktop-wallet/src/features/ledger/useInitializeAppWithLedgerData.tsx +++ b/apps/desktop-wallet/src/features/ledger/useInitializeAppWithLedgerData.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { NonSensitiveAddressData } from '@alephium/keyring' import { useNavigate } from 'react-router-dom' diff --git a/apps/desktop-wallet/src/features/ledger/useLedger.ts b/apps/desktop-wallet/src/features/ledger/useLedger.ts index c6df81fd21..aca663738b 100644 --- a/apps/desktop-wallet/src/features/ledger/useLedger.ts +++ b/apps/desktop-wallet/src/features/ledger/useLedger.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useCallback } from 'react' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/ledger/utils.ts b/apps/desktop-wallet/src/features/ledger/utils.ts index 5e942ce81b..d0a52671cd 100644 --- a/apps/desktop-wallet/src/features/ledger/utils.ts +++ b/apps/desktop-wallet/src/features/ledger/utils.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { GenerateAddressProps, NonSensitiveAddressDataWithGroup } from '@alephium/keyring' import { AlephiumApp as AlephiumLedgerApp } from '@alephium/ledger-app' import { AddressHash, AddressMetadata, findNextAvailableAddressIndex } from '@alephium/shared' diff --git a/apps/desktop-wallet/src/features/localization/i18n.ts b/apps/desktop-wallet/src/features/localization/i18n.ts new file mode 100644 index 0000000000..7456f044e4 --- /dev/null +++ b/apps/desktop-wallet/src/features/localization/i18n.ts @@ -0,0 +1,43 @@ +import i18next from 'i18next' +import { initReactI18next } from 'react-i18next' + +import bg from '../../../locales/bg-BG/translation.json' +import de from '../../../locales/de-DE/translation.json' +import el from '../../../locales/el-GR/translation.json' +import en from '../../../locales/en-US/translation.json' +import es from '../../../locales/es-ES/translation.json' +import fr from '../../../locales/fr-FR/translation.json' +import id from '../../../locales/id-ID/translation.json' +import it from '../../../locales/it-IT/translation.json' +import pt from '../../../locales/pt-PT/translation.json' +import ru from '../../../locales/ru-RU/translation.json' +import th from '../../../locales/th-TH/translation.json' +import tr from '../../../locales/tr-TR/translation.json' +import vi from '../../../locales/vi-VN/translation.json' +import zh from '../../../locales/zh-CN/translation.json' + +i18next.use(initReactI18next).init({ + resources: { + 'en-US': { translation: en }, + 'bg-BG': { translation: bg }, + 'es-ES': { translation: es }, + 'de-DE': { translation: de }, + 'id-ID': { translation: id }, + 'it-IT': { translation: it }, + 'fr-FR': { translation: fr }, + 'pt-PT': { translation: pt }, + 'ru-RU': { translation: ru }, + 'tr-TR': { translation: tr }, + 'vi-VN': { translation: vi }, + 'el-GR': { translation: el }, + 'zh-CN': { translation: zh }, + 'th-TH': { translation: th } + }, + lng: 'en-US', + fallbackLng: 'en-US', + interpolation: { + escapeValue: false + } +}) + +export default i18next diff --git a/apps/desktop-wallet/src/features/localization/i18next.d.ts b/apps/desktop-wallet/src/features/localization/i18next.d.ts new file mode 100644 index 0000000000..90c8a92171 --- /dev/null +++ b/apps/desktop-wallet/src/features/localization/i18next.d.ts @@ -0,0 +1,15 @@ +import 'i18next' + +import en from '../../../locales/en-US/translation.json' + +type EnglishTranslationKeys = typeof en + +export type TranslationKey = keyof EnglishTranslationKeys + +declare module 'i18next' { + interface CustomTypeOptions { + resources: { + translation: EnglishTranslationKeys + } + } +} diff --git a/apps/desktop-wallet/src/features/localization/languages.ts b/apps/desktop-wallet/src/features/localization/languages.ts new file mode 100644 index 0000000000..5e6b6c6ebd --- /dev/null +++ b/apps/desktop-wallet/src/features/localization/languages.ts @@ -0,0 +1,34 @@ +import { SelectOption } from '@/components/Inputs/Select' + +export type Language = + | 'en-US' + | 'fr-FR' + | 'de-DE' + | 'vi-VN' + | 'pt-PT' + | 'ru-RU' + | 'bg-BG' + | 'es-ES' + | 'id-ID' + | 'tr-TR' + | 'it-IT' + | 'el-GR' + | 'zh-CN' + | 'th-TH' + +export const languageOptions: SelectOption[] = [ + { label: 'English', value: 'en-US' }, + { label: 'Bahasa Indonesia', value: 'id-ID' }, + { label: 'Български', value: 'bg-BG' }, + { label: 'Deutsch', value: 'de-DE' }, + { label: 'Español', value: 'es-ES' }, + { label: 'Français', value: 'fr-FR' }, + { label: 'Italiano', value: 'it-IT' }, + { label: 'Português', value: 'pt-PT' }, + { label: 'Русский', value: 'ru-RU' }, + { label: 'Türkçe', value: 'tr-TR' }, + { label: 'Tiếng Việt', value: 'vi-VN' }, + { label: 'Ελληνικά', value: 'el-GR' }, + { label: 'ไทย', value: 'th-TH' }, + { label: '简体中文', value: 'zh-CN' } +] diff --git a/apps/desktop-wallet/src/features/localization/localizationActions.ts b/apps/desktop-wallet/src/features/localization/localizationActions.ts new file mode 100644 index 0000000000..1dd11dc72a --- /dev/null +++ b/apps/desktop-wallet/src/features/localization/localizationActions.ts @@ -0,0 +1,13 @@ +import { createAction } from '@reduxjs/toolkit' + +import { Language } from '@/features/localization/languages' + +export const languageChangeStarted = createAction('localization/languageChangeStarted') + +export const languageChangeFinished = createAction('localization/languageChangeFinished') + +export const systemLanguageMatchSucceeded = createAction('localization/systemLanguageMatchSucceeded') + +export const systemLanguageMatchFailed = createAction('localization/systemLanguageMatchFailed') + +export const languageChanged = createAction('localization/languageChanged') diff --git a/apps/desktop-wallet/src/features/modals/modalActions.ts b/apps/desktop-wallet/src/features/modals/modalActions.ts index 3815da4ffd..248214f487 100644 --- a/apps/desktop-wallet/src/features/modals/modalActions.ts +++ b/apps/desktop-wallet/src/features/modals/modalActions.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createAction } from '@reduxjs/toolkit' import { ModalInstance, OpenModalParams } from '@/features/modals/modalTypes' diff --git a/apps/desktop-wallet/src/features/modals/modalAdapters.ts b/apps/desktop-wallet/src/features/modals/modalAdapters.ts index 4f2d2cf689..54314b0348 100644 --- a/apps/desktop-wallet/src/features/modals/modalAdapters.ts +++ b/apps/desktop-wallet/src/features/modals/modalAdapters.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createEntityAdapter } from '@reduxjs/toolkit' import { ModalInstance } from '@/features/modals/modalTypes' diff --git a/apps/desktop-wallet/src/features/modals/modalSelectors.ts b/apps/desktop-wallet/src/features/modals/modalSelectors.ts index e314ba48ad..efa5e3d259 100644 --- a/apps/desktop-wallet/src/features/modals/modalSelectors.ts +++ b/apps/desktop-wallet/src/features/modals/modalSelectors.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createSelector } from '@reduxjs/toolkit' import { modalAdapter } from '@/features/modals/modalAdapters' diff --git a/apps/desktop-wallet/src/features/modals/modalSlice.ts b/apps/desktop-wallet/src/features/modals/modalSlice.ts index f347ba43ab..719d13b166 100644 --- a/apps/desktop-wallet/src/features/modals/modalSlice.ts +++ b/apps/desktop-wallet/src/features/modals/modalSlice.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { createSlice, isAnyOf } from '@reduxjs/toolkit' diff --git a/apps/desktop-wallet/src/features/modals/modalTypes.ts b/apps/desktop-wallet/src/features/modals/modalTypes.ts index d15e263248..a37cef5fa9 100644 --- a/apps/desktop-wallet/src/features/modals/modalTypes.ts +++ b/apps/desktop-wallet/src/features/modals/modalTypes.ts @@ -1,23 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { AddressHash } from '@alephium/shared' +import { AddressHash, WalletConnectSessionProposalModalProps } from '@alephium/shared' +import { BuyModalProps } from '@/features/buy/BuyModal' +import { WalletPassphraseDisclaimerModalProps } from '@/features/passphrase/WalletPassphraseDisclaimerModal' import { CallContractSendModalProps } from '@/features/send/sendModals/callContract/CallContractSendModal' import { DeployContractSendModalProps } from '@/features/send/sendModals/deployContract/DeployContractSendModal' import { ConfirmLockTimeModalProps } from '@/features/send/sendModals/transfer/ConfirmLockTimeModal' @@ -26,7 +10,6 @@ import { WalletUnlockModalProps } from '@/features/switch-wallet/WalletUnlockMod import { TransactionDetailsModalProps } from '@/features/transactionsDisplay/transactionDetailsModal/TransactionDetailsModal' import { SignMessageModalProps } from '@/features/walletConnect/SignMessageModal' import { SignUnsignedTxModalProps } from '@/features/walletConnect/SignUnsignedTxModal' -import { WalletConnectSessionProposalModalProps } from '@/features/walletConnect/WalletConnectSessionProposalModal' import { AddressSweepModalProps } from '@/modals/AddressSweepModal' import { ConfirmModalProps } from '@/modals/ConfirmModal' import { ConsolidateUTXOsModalProps } from '@/modals/ConsolidateUTXOsModal' @@ -35,6 +18,7 @@ import { NewAddressModalProps } from '@/modals/NewAddressModal' import { NFTDetailsModalProps } from '@/modals/NFTDetailsModal' import { SettingsModalProps } from '@/modals/SettingsModal' import { DisablePasswordRequirementModalProps } from '@/modals/SettingsModal/DisablePasswordRequirementModal' +import { TokenDetailsModalProps } from '@/modals/tokenDetails/tokeDetailsTypes' import { WalletRemovalModalProps } from '@/modals/WalletRemovalModal' const ModalNames = { @@ -67,7 +51,10 @@ const ModalNames = { DisablePasswordRequirementModal: 'DisablePasswordRequirementModal', AddressSweepModal: 'AddressSweepModal', WalletRemovalModal: 'WalletRemovalModal', - DeleteAddressesModal: 'DeleteAddressesModal' + DeleteAddressesModal: 'DeleteAddressesModal', + BuyModal: 'BuyModal', + WalletPassphraseDisclaimerModal: 'WalletPassphraseDisclaimerModal', + TokenDetailsModal: 'TokenDetailsModal' } as const export type ModalName = keyof typeof ModalNames @@ -186,6 +173,18 @@ export type OpenModalParams = | { name: typeof ModalNames.DeleteAddressesModal } + | { + name: typeof ModalNames.BuyModal + props: BuyModalProps + } + | { + name: typeof ModalNames.WalletPassphraseDisclaimerModal + props: WalletPassphraseDisclaimerModalProps + } + | { + name: typeof ModalNames.TokenDetailsModal + props: TokenDetailsModalProps + } export type ModalInstance = { id: number diff --git a/apps/desktop-wallet/src/features/modals/withModal.tsx b/apps/desktop-wallet/src/features/modals/withModal.tsx new file mode 100644 index 0000000000..98aaf73dae --- /dev/null +++ b/apps/desktop-wallet/src/features/modals/withModal.tsx @@ -0,0 +1,16 @@ +import { ComponentType, memo } from 'react' + +import { ModalBaseProp } from '@/features/modals/modalTypes' + +const withModal =

    (Component: ComponentType) => { + const WrappedComponent = (props: ModalBaseProp & P) => { + if (!props.id) { + throw new Error("The 'id' prop is required for all modals") + } + return + } + + return memo(WrappedComponent) +} + +export default withModal diff --git a/apps/desktop-wallet/src/features/passphrase/UsePassphraseButton.tsx b/apps/desktop-wallet/src/features/passphrase/UsePassphraseButton.tsx new file mode 100644 index 0000000000..76ce08c2cd --- /dev/null +++ b/apps/desktop-wallet/src/features/passphrase/UsePassphraseButton.tsx @@ -0,0 +1,29 @@ +import { Lock, X } from 'lucide-react' +import { useTranslation } from 'react-i18next' + +import Button, { ButtonProps } from '@/components/Button' +import { openModal } from '@/features/modals/modalActions' +import { WalletPassphraseDisclaimerModalProps } from '@/features/passphrase/WalletPassphraseDisclaimerModal' +import { useAppDispatch } from '@/hooks/redux' + +interface UsePassphraseButtonProps extends ButtonProps, Pick { + passphraseConsent: boolean +} + +const UsePassphraseButton = ({ passphraseConsent, onConsentChange, ...props }: UsePassphraseButtonProps) => { + const { t } = useTranslation() + const dispatch = useAppDispatch() + + const onUsePassphraseClick = () => + passphraseConsent + ? onConsentChange(false) + : dispatch(openModal({ name: 'WalletPassphraseDisclaimerModal', props: { onConsentChange } })) + + return ( + + ) +} + +export default UsePassphraseButton diff --git a/apps/desktop-wallet/src/features/passphrase/WalletPassphraseDisclaimerModal.tsx b/apps/desktop-wallet/src/features/passphrase/WalletPassphraseDisclaimerModal.tsx new file mode 100644 index 0000000000..6d884a7606 --- /dev/null +++ b/apps/desktop-wallet/src/features/passphrase/WalletPassphraseDisclaimerModal.tsx @@ -0,0 +1,75 @@ +import { t } from 'i18next' +import { memo, useState } from 'react' +import { Trans } from 'react-i18next' +import styled from 'styled-components' + +import ActionLink from '@/components/ActionLink' +import FooterButton from '@/components/Buttons/FooterButton' +import InfoBox from '@/components/InfoBox' +import { closeModal } from '@/features/modals/modalActions' +import { ModalBaseProp } from '@/features/modals/modalTypes' +import { useAppDispatch } from '@/hooks/redux' +import CenteredModal from '@/modals/CenteredModal' +import { links } from '@/utils/links' +import { openInWebBrowser } from '@/utils/misc' + +export interface WalletPassphraseDisclaimerModalProps { + onConsentChange: (consent: boolean) => void +} + +const WalletPassphraseDisclaimerModal = memo( + ({ id, onConsentChange }: WalletPassphraseDisclaimerModalProps & ModalBaseProp) => { + const dispatch = useAppDispatch() + const [isConsentActive, setIsConsentActive] = useState(false) + + const handleClose = () => { + onConsentChange(isConsentActive) + dispatch(closeModal({ id })) + } + + return ( + + +

    + + This is an advanced feature! +
    + Use it only if you know what you are doing. +
    + Please, read our openInWebBrowser(links.passphrase)}>documentation + to learn about it. +
    +

    + + + setIsConsentActive(!isConsentActive)} + /> + + + + + {t('Continue')} + + + ) + } +) + +export default WalletPassphraseDisclaimerModal + +const WarningEmphasis = styled.strong` + color: ${({ theme }) => theme.global.alert}; +` + +const ConsentCheckbox = styled.div` + display: flex; + align-items: center; + justify-content: center; + gap: 5px; + margin-bottom: 16px; + text-align: left; +` diff --git a/apps/desktop-wallet/src/features/passphrase/WalletPassphraseForm.tsx b/apps/desktop-wallet/src/features/passphrase/WalletPassphraseForm.tsx new file mode 100644 index 0000000000..48c6c64cc3 --- /dev/null +++ b/apps/desktop-wallet/src/features/passphrase/WalletPassphraseForm.tsx @@ -0,0 +1,59 @@ +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import Input from '@/components/Inputs/Input' + +interface WalletPassphraseFormProps { + onPassphraseConfirmed: (passphrase: string) => void + className?: string +} + +const WalletPassphraseForm = ({ onPassphraseConfirmed, className }: WalletPassphraseFormProps) => { + const { t } = useTranslation() + const [value, setValue] = useState('') + const [confirmValue, setConfirmValue] = useState('') + + const passphraseIsNotUsed = !value && !confirmValue + const showConfirmError = confirmValue.length >= value.length && value !== confirmValue + + useEffect(() => { + const isPassphraseConfirmed = value === confirmValue || passphraseIsNotUsed + onPassphraseConfirmed(isPassphraseConfirmed ? confirmValue : '') + }, [onPassphraseConfirmed, confirmValue, value, passphraseIsNotUsed]) + + useEffect(() => { + if (!value && !!confirmValue) setConfirmValue('') + }, [confirmValue, value]) + + return ( + + setValue(e.target.value)} + /> + setConfirmValue(e.target.value)} + error={showConfirmError && t("Passphrases don't match")} + /> + + ) +} + +export default WalletPassphraseForm + +const Container = styled.div` + width: 100%; + display: flex; + flex-direction: column; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid ${({ theme }) => theme.border.primary}; +` diff --git a/apps/desktop-wallet/src/features/passphrase/usePassphrase.ts b/apps/desktop-wallet/src/features/passphrase/usePassphrase.ts new file mode 100644 index 0000000000..068a6a7149 --- /dev/null +++ b/apps/desktop-wallet/src/features/passphrase/usePassphrase.ts @@ -0,0 +1,23 @@ +import { useCallback, useState } from 'react' + +const usePassphrase = () => { + const [passphrase, setPassphrase] = useState('') + const [passphraseConsent, setPassphraseConsent] = useState(false) + + const isPassphraseSubmitEnabled = (passphraseConsent && !!passphrase) || (!passphraseConsent && !passphrase) + + const handleUsePassphrasePress = useCallback((consent: boolean) => { + setPassphraseConsent(consent) + setPassphrase('') + }, []) + + return { + passphrase, + setPassphrase, + isPassphraseSubmitEnabled, + passphraseConsent, + handleUsePassphrasePress + } +} + +export default usePassphrase diff --git a/apps/desktop-wallet/src/features/refreshData/RefreshButton.tsx b/apps/desktop-wallet/src/features/refreshData/RefreshButton.tsx index 1f2821cd48..d73e82e9c4 100644 --- a/apps/desktop-wallet/src/features/refreshData/RefreshButton.tsx +++ b/apps/desktop-wallet/src/features/refreshData/RefreshButton.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { RefreshCw } from 'lucide-react' import { memo } from 'react' import { useTranslation } from 'react-i18next' @@ -44,7 +26,7 @@ const RefreshBtn = memo(({ onClick, isLoading }: RefreshBtnProps) => { { data-tooltip-id="default" data-tooltip-content={t('Refresh data')} isLoading={isLoading} + squared /> ) }) diff --git a/apps/desktop-wallet/src/features/refreshData/useRefreshBalances.ts b/apps/desktop-wallet/src/features/refreshData/useRefreshBalances.ts index 8e6feacb80..7f73d241ca 100644 --- a/apps/desktop-wallet/src/features/refreshData/useRefreshBalances.ts +++ b/apps/desktop-wallet/src/features/refreshData/useRefreshBalances.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useIsFetching } from '@tanstack/react-query' import { useCallback } from 'react' diff --git a/apps/desktop-wallet/src/features/send/AddressInputs.tsx b/apps/desktop-wallet/src/features/send/AddressInputs.tsx index f0e63299fc..19f90c46eb 100644 --- a/apps/desktop-wallet/src/features/send/AddressInputs.tsx +++ b/apps/desktop-wallet/src/features/send/AddressInputs.tsx @@ -1,28 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { AlbumIcon, ContactIcon } from 'lucide-react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import styled, { useTheme } from 'styled-components' +import styled from 'styled-components' -import Box from '@/components/Box' import Button from '@/components/Button' import HashEllipsed from '@/components/HashEllipsed' import AddressInput from '@/components/Inputs/AddressInput' @@ -32,7 +13,7 @@ import SelectOptionItemContent from '@/components/Inputs/SelectOptionItemContent import Truncate from '@/components/Truncate' import InputsSection from '@/features/send/InputsSection' import { useAppSelector } from '@/hooks/redux' -import { useFetchSortedAddressesHashes } from '@/hooks/useAddresses' +import { useFetchAddressesHashesSortedByLastUse } from '@/hooks/useAddresses' import AddressSelectModal from '@/modals/AddressSelectModal' import { useMoveFocusOnPreviousModal } from '@/modals/ModalContainer' import ModalPortal from '@/modals/ModalPortal' @@ -60,8 +41,7 @@ const AddressInputs = ({ const { t } = useTranslation() const moveFocusOnPreviousModal = useMoveFocusOnPreviousModal() const contacts = useAppSelector(selectAllContacts) - const { data: allAddressHashes } = useFetchSortedAddressesHashes() - const theme = useTheme() + const { data: allAddressHashes } = useFetchAddressesHashesSortedByLastUse() const [isContactSelectModalOpen, setIsContactSelectModalOpen] = useState(false) const [isAddressSelectModalOpen, setIsAddressSelectModalOpen] = useState(false) @@ -92,53 +72,32 @@ const AddressInputs = ({ return ( - - - - + + {toAddress && onToAddressChange && ( - + onToAddressChange(e.target.value.trim())} + placeholder={t('The address which will receive the assets.')} + heightSize="big" + noMargin /> - - @@ -158,7 +117,6 @@ const AddressInputs = ({ {contact.label}} SecondaryContent={} - isSelected={contact.value === toAddress?.value} /> )} /> @@ -196,13 +154,6 @@ const HashEllipsedStyled = styled(HashEllipsed)` max-width: 150px; ` -const BoxStyled = styled(Box)` - display: flex; - align-items: center; - gap: 10px; - height: var(--inputHeight); -` - const AddressToInput = styled(AddressInput)` margin: 0; ` @@ -210,4 +161,5 @@ const AddressToInput = styled(AddressInput)` const DestinationActions = styled.div` display: flex; gap: 5px; + margin-top: var(--spacing-2); ` diff --git a/apps/desktop-wallet/src/features/send/AlphAmountInfoBox.tsx b/apps/desktop-wallet/src/features/send/AlphAmountInfoBox.tsx index 7a0f037d88..8a7d71b18f 100644 --- a/apps/desktop-wallet/src/features/send/AlphAmountInfoBox.tsx +++ b/apps/desktop-wallet/src/features/send/AlphAmountInfoBox.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { formatAmountForDisplay } from '@alephium/shared' import { useTranslation } from 'react-i18next' import styled from 'styled-components' diff --git a/apps/desktop-wallet/src/features/send/CheckAddressesBox.tsx b/apps/desktop-wallet/src/features/send/CheckAddressesBox.tsx index 7dde55d0f3..e23adcc3c2 100644 --- a/apps/desktop-wallet/src/features/send/CheckAddressesBox.tsx +++ b/apps/desktop-wallet/src/features/send/CheckAddressesBox.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -24,7 +6,6 @@ import styled from 'styled-components' import ActionLink from '@/components/ActionLink' import AddressBadge from '@/components/AddressBadge' import Box from '@/components/Box' -import HorizontalDivider from '@/components/Dividers/HorizontalDivider' import HashEllipsed from '@/components/HashEllipsed' import Truncate from '@/components/Truncate' import { useAppSelector } from '@/hooks/redux' @@ -53,24 +34,21 @@ const CheckAddressesBox = ({ fromAddress, toAddressHash, className }: CheckAddre {toAddressHash && ( - <> - - - {t('To')} - - {contact ? ( - - {contact.name} - - - ) : ( - openInWebBrowser(`${explorerUrl}/addresses/${toAddressHash}`)}> - - - )} - - - + + {t('To')} + + {contact ? ( + <> + {contact.name} + + + ) : ( + openInWebBrowser(`${explorerUrl}/addresses/${toAddressHash}`)}> + + + )} + + )} ) @@ -82,7 +60,7 @@ const AddressRow = styled.div` display: flex; align-items: center; justify-content: space-between; - padding: 18px 15px; + padding: 18px 0; gap: 20px; ` @@ -94,6 +72,7 @@ const AddressLabel = styled.div` const AddressLabelHash = styled.div` display: flex; gap: 10px; + min-width: 0; ` const ContactName = styled(Truncate)` diff --git a/apps/desktop-wallet/src/features/send/CheckAmountsBox.tsx b/apps/desktop-wallet/src/features/send/CheckAmountsBox.tsx index 8190ca46f9..8bcd59e22f 100644 --- a/apps/desktop-wallet/src/features/send/CheckAmountsBox.tsx +++ b/apps/desktop-wallet/src/features/send/CheckAmountsBox.tsx @@ -1,48 +1,29 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AssetAmount, calculateAmountWorth, toHumanReadableAmount } from '@alephium/shared' import { ALPH } from '@alephium/token-list' import { isNumber } from 'lodash' import { Info } from 'lucide-react' -import { Fragment } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { useFetchTokenPrice } from '@/api/apiDataHooks/market/useFetchTokenPrices' -import useFetchToken, { isFT, isNFT } from '@/api/apiDataHooks/token/useFetchToken' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' import ActionLink from '@/components/ActionLink' -import Amount from '@/components/Amount' +import Amount, { AmountBaseProps } from '@/components/Amount' import AssetLogo from '@/components/AssetLogo' import Box from '@/components/Box' -import HorizontalDivider from '@/components/Dividers/HorizontalDivider' import { openModal } from '@/features/modals/modalActions' import { getTransactionAssetAmounts } from '@/features/send/sendUtils' import { useAppDispatch } from '@/hooks/redux' +import { isFT, isNFT } from '@/types/tokens' import { links } from '@/utils/links' import { openInWebBrowser } from '@/utils/misc' -interface CheckAmountsBoxProps { +interface CheckAmountsBoxProps extends AmountBaseProps { assetAmounts: AssetAmount[] className?: string } -const CheckAmountsBox = ({ assetAmounts, className }: CheckAmountsBoxProps) => { +const CheckAmountsBox = ({ assetAmounts, className, ...props }: CheckAmountsBoxProps) => { const userSpecifiedAlphAmount = assetAmounts.find((asset) => asset.id === ALPH.id)?.amount const { attoAlphAmount, tokens, extraAlphForDust } = getTransactionAssetAmounts(assetAmounts) @@ -50,30 +31,35 @@ const CheckAmountsBox = ({ assetAmounts, className }: CheckAmountsBoxProps) => { const assets = userSpecifiedAlphAmount ? [alphAsset, ...tokens] : [...tokens, alphAsset] return ( - - {assets.map((asset, index) => ( - - {index > 0 && } - - + + {assets.map((asset) => ( + ))} - + ) } export default CheckAmountsBox -interface AssetAmountRowProps { +interface AssetAmountRowProps extends AmountBaseProps { tokenId: string amount: string extraAlphForDust: bigint } -const AssetAmountRow = ({ tokenId, amount, extraAlphForDust }: AssetAmountRowProps) => { +const AssetAmountRow = ({ tokenId, amount, extraAlphForDust, ...props }: AssetAmountRowProps) => { const { t } = useTranslation() const { data: token } = useFetchToken(tokenId) const dispatch = useAppDispatch() + if (!token) return null + const handleRowClick = () => { if (isNFT(token)) dispatch(openModal({ name: 'NFTDetailsModal', props: { nftId: tokenId } })) } @@ -81,7 +67,7 @@ const AssetAmountRow = ({ tokenId, amount, extraAlphForDust }: AssetAmountRowPro return ( - + {(isFT(token) || isNFT(token)) && {token.name}} @@ -99,7 +85,7 @@ const AssetAmountRow = ({ tokenId, amount, extraAlphForDust }: AssetAmountRowPro {!isNFT(token) && ( - + {isFT(token) && } )} @@ -126,18 +112,28 @@ const FiatAmountStyled = styled(Amount)` font-weight: var(--font-weight-medium); ` +const CheckAmountsBoxStyled = styled(Box)` + display: flex; + flex-direction: column; +` + const AssetAmountRowStyled = styled.div` display: flex; - padding: 18px 15px; align-items: center; justify-content: space-between; gap: 15px; + border-radius: var(--radius-big); + padding: var(--spacing-3) 0; + + &:not(:last-child) { + border-bottom: 1px solid ${({ theme }) => theme.border.secondary}; + } cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')}; ` const TokenName = styled.span` - font-size: 14px; + font-size: 13px; font-weight: var(--fontWeight-medium); ` diff --git a/apps/desktop-wallet/src/features/send/CheckFeeLockTimeBox.tsx b/apps/desktop-wallet/src/features/send/CheckFeeLockTimeBox.tsx index 59a96f0827..0b2920abc9 100644 --- a/apps/desktop-wallet/src/features/send/CheckFeeLockTimeBox.tsx +++ b/apps/desktop-wallet/src/features/send/CheckFeeLockTimeBox.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ALPH } from '@alephium/token-list' import dayjs from 'dayjs' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/send/CheckModalContent.tsx b/apps/desktop-wallet/src/features/send/CheckModalContent.tsx index 4ec0fd6c1d..11f0a32d3d 100644 --- a/apps/desktop-wallet/src/features/send/CheckModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/CheckModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' export default styled.div` diff --git a/apps/desktop-wallet/src/features/send/CheckWorthBox.tsx b/apps/desktop-wallet/src/features/send/CheckWorthBox.tsx index bafbed7459..233b4e687e 100644 --- a/apps/desktop-wallet/src/features/send/CheckWorthBox.tsx +++ b/apps/desktop-wallet/src/features/send/CheckWorthBox.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AssetAmount, calculateAmountWorth } from '@alephium/shared' import { ALPH } from '@alephium/token-list' import { isNumber } from 'lodash' diff --git a/apps/desktop-wallet/src/features/send/GasSettings.tsx b/apps/desktop-wallet/src/features/send/GasSettings.tsx index 162a16aba1..972aa28fc6 100644 --- a/apps/desktop-wallet/src/features/send/GasSettings.tsx +++ b/apps/desktop-wallet/src/features/send/GasSettings.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { formatAmountForDisplay, fromHumanReadableAmount, diff --git a/apps/desktop-wallet/src/features/send/InfoRow.tsx b/apps/desktop-wallet/src/features/send/InfoRow.tsx index 3115947217..34fcca4dff 100644 --- a/apps/desktop-wallet/src/features/send/InfoRow.tsx +++ b/apps/desktop-wallet/src/features/send/InfoRow.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import styled from 'styled-components' interface InfoRowProps { @@ -33,7 +15,7 @@ const InfoRow: FC = ({ label, className, children }) => ( export default styled(InfoRow)` display: flex; justify-content: space-between; - padding: 18px 15px; + padding: 18px 0; ` const Label = styled.div` diff --git a/apps/desktop-wallet/src/features/send/InputsSection.tsx b/apps/desktop-wallet/src/features/send/InputsSection.tsx index c5ddb47061..ed13aa8a20 100644 --- a/apps/desktop-wallet/src/features/send/InputsSection.tsx +++ b/apps/desktop-wallet/src/features/send/InputsSection.tsx @@ -1,55 +1,42 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ReactNode } from 'react' import styled from 'styled-components' interface InputsSectionProps { - title: string + title?: string subtitle?: string HeaderActions?: ReactNode className?: string } const InputsSection: FC = ({ title, className, HeaderActions, subtitle, children }) => ( -
    -
    - {title} - {HeaderActions} -
    + + {(title || HeaderActions) && ( +
    + {title && {title}} + {HeaderActions} +
    + )} {subtitle && {subtitle}} {children} -
    + ) export default InputsSection +const InputsSectionStyled = styled.div` + gap: 10px; +` + const Title = styled.div` - font-size: 16px; - font-weight: var(--fontWeight-semiBold); + color: ${({ theme }) => theme.font.secondary}; + font-size: 14px; ` const Header = styled.div` - margin-left: 10px; display: flex; align-items: center; justify-content: space-between; - height: 40px; + height: 30px; ` const Subtitle = styled.div` diff --git a/apps/desktop-wallet/src/features/send/SelectOptionAddressToken.tsx b/apps/desktop-wallet/src/features/send/SelectOptionAddressToken.tsx index f3b8100e8d..d69603b024 100644 --- a/apps/desktop-wallet/src/features/send/SelectOptionAddressToken.tsx +++ b/apps/desktop-wallet/src/features/send/SelectOptionAddressToken.tsx @@ -1,26 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import useFetchAddressSingleTokenBalances from '@/api/apiDataHooks/address/useFetchAddressSingleTokenBalances' -import useFetchToken, { isNFT } from '@/api/apiDataHooks/token/useFetchToken' +import useFetchToken from '@/api/apiDataHooks/token/useFetchToken' import SelectOptionToken, { SelectOptionTokenBaseProps } from '@/components/Inputs/SelectOptionToken' +import { isNFT } from '@/types/tokens' interface SelectOptionAddressTokenProps extends SelectOptionTokenBaseProps { addressHash: AddressHash @@ -28,10 +11,11 @@ interface SelectOptionAddressTokenProps extends SelectOptionTokenBaseProps { const SelectOptionAddressToken = ({ tokenId, addressHash, ...props }: SelectOptionAddressTokenProps) => { const { data: token, isLoading: isLoadingToken } = useFetchToken(tokenId) + const isNft = token && isNFT(token) const { data: tokenBalances, isLoading: isLoadingTokenBalances } = useFetchAddressSingleTokenBalances({ addressHash, tokenId, - skip: isLoadingToken || isNFT(token) + skip: isLoadingToken || isNft }) const amount = tokenBalances?.totalBalance ? BigInt(tokenBalances.totalBalance) : undefined @@ -40,7 +24,7 @@ const SelectOptionAddressToken = ({ tokenId, addressHash, ...props }: SelectOpti diff --git a/apps/desktop-wallet/src/features/send/SendModal.tsx b/apps/desktop-wallet/src/features/send/SendModal.tsx index ba887aaf61..c046a33d4f 100644 --- a/apps/desktop-wallet/src/features/send/SendModal.tsx +++ b/apps/desktop-wallet/src/features/send/SendModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { getHumanReadableError, WALLETCONNECT_ERRORS } from '@alephium/shared' import { node } from '@alephium/web3' import { colord } from 'colord' @@ -66,7 +48,8 @@ import { TxData, UnsignedTx } from '@/features/send/sendTypes' -import StepsProgress, { Step } from '@/features/send/StepsProgress' +import { Step } from '@/features/send/StepsProgress' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useWalletConnectContext } from '@/features/walletConnect/walletConnectContext' import { useAppDispatch, useAppSelector } from '@/hooks/redux' import CenteredModal, { ScrollableModalContent } from '@/modals/CenteredModal' @@ -100,7 +83,7 @@ function SendModal({ }: ModalBaseProp & SendModalProps) { const { t } = useTranslation() const dispatch = useAppDispatch() - const settings = useAppSelector((s) => s.settings) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) const posthog = usePostHog() const { sendAnalytics } = useAnalytics() const { sendUserRejectedResponse, sendSuccessResponse, sendFailureResponse } = useWalletConnectContext() @@ -117,6 +100,7 @@ function SendModal({ const [unsignedTxId, setUnsignedTxId] = useState('') const [contractAddress, setContractAddress] = useState('') const [unsignedTransaction, setUnsignedTransaction] = useState() + const [buildExecuteScriptTxResult, setBuildExecuteScriptTxResult] = useState() const [isTransactionBuildTriggered, setIsTransactionBuildTriggered] = useState(false) const isRequestToApproveContractCall = initialStep === 'info-check' @@ -139,9 +123,11 @@ function SendModal({ setUnsignedTxId, setContractAddress, isSweeping, - consolidationRequired + consolidationRequired, + buildExecuteScriptTxResult, + setBuildExecuteScriptTxResult }), - [consolidationRequired, isSweeping, sweepUnsignedTxs, unsignedTransaction, unsignedTxId] + [buildExecuteScriptTxResult, consolidationRequired, isSweeping, sweepUnsignedTxs, unsignedTransaction, unsignedTxId] ) const handleSendExtended = useCallback(async () => { @@ -250,7 +236,7 @@ function SendModal({ name: 'ConsolidateUTXOsModal', props: { fee: fees, - onConsolidateClick: settings.passwordRequirement ? confirmPassword : handleSendExtended + onConsolidateClick: passwordRequirement ? confirmPassword : handleSendExtended } }) ) @@ -283,7 +269,7 @@ function SendModal({ type, txContext, dispatch, - settings.passwordRequirement, + passwordRequirement, handleSendExtended, sendAnalytics, t, @@ -331,10 +317,9 @@ function SendModal({ dynamicContent onBack={onBackCallback} focusMode - noPadding disableBack={isRequestToApproveContractCall && step !== 'password-check'} + hasFooterButtons > - {step === 'addresses' && (type === 'transfer' ? ( @@ -343,64 +328,58 @@ function SendModal({ ) : ( ))} - {step === 'build-tx' && ( - - {type === 'transfer' ? ( - - ) : type === 'call-contract' ? ( - - ) : ( - - )} - - )} - {step === 'info-check' && !!transactionData && !!fees && ( - - {type === 'transfer' ? ( - - ) : type === 'call-contract' ? ( - - ) : ( - - )} - - )} - {step === 'password-check' && settings.passwordRequirement && ( - - - - {t('You can disable this confirmation step from the wallet settings.')} - - - + {step === 'build-tx' && + (type === 'transfer' ? ( + + ) : type === 'call-contract' ? ( + + ) : ( + + ))} + {step === 'info-check' && + !!transactionData && + !!fees && + (type === 'transfer' ? ( + + ) : type === 'call-contract' ? ( + + ) : ( + + ))} + {step === 'password-check' && passwordRequirement && ( + + + {t('You can disable this confirmation step from the wallet settings.')} + + )} {step === 'tx-sent' && ( diff --git a/apps/desktop-wallet/src/features/send/StepsProgress.tsx b/apps/desktop-wallet/src/features/send/StepsProgress.tsx index fb406845aa..8793fdc8a1 100644 --- a/apps/desktop-wallet/src/features/send/StepsProgress.tsx +++ b/apps/desktop-wallet/src/features/send/StepsProgress.tsx @@ -1,29 +1,12 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { Fragment } from 'react' import { useTranslation } from 'react-i18next' import styled, { useTheme } from 'styled-components' import DotIcon from '@/components/DotIcon' +import { TranslationKey } from '@/features/localization/i18next' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useAppSelector } from '@/hooks/redux' -import { TranslationKey } from '@/types/i18next' interface StepsProgressProps { currentStep: Step @@ -72,11 +55,11 @@ const StepsProgress = ({ currentStep, isContract, className }: StepsProgressProp const useStepsUI = (currentStep: Step) => { const theme = useTheme() - const settings = useAppSelector((s) => s.settings) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) const nextColor = colord(theme.font.tertiary).alpha(0.3).toHex() - const steps: Step[] = !settings.passwordRequirement + const steps: Step[] = !passwordRequirement ? ['addresses', 'build-tx', 'info-check', 'tx-sent'] : ['addresses', 'build-tx', 'info-check', 'password-check', 'tx-sent'] diff --git a/apps/desktop-wallet/src/features/send/TokensAmountInputs.tsx b/apps/desktop-wallet/src/features/send/TokensAmountInputs.tsx index b0beb22e6b..1be3123e54 100644 --- a/apps/desktop-wallet/src/features/send/TokensAmountInputs.tsx +++ b/apps/desktop-wallet/src/features/send/TokensAmountInputs.tsx @@ -1,25 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash, fromHumanReadableAmount, getNumberOfDecimals, toHumanReadableAmount } from '@alephium/shared' import { ALPH } from '@alephium/token-list' import { MIN_UTXO_SET_AMOUNT } from '@alephium/web3' -import { MoreVertical, Plus } from 'lucide-react' +import { Plus } from 'lucide-react' import { useCallback, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled, { useTheme } from 'styled-components' @@ -32,12 +14,12 @@ import useFetchWalletNftsSearchStrings from '@/api/apiDataHooks/wallet/useFetchW import ActionLink from '@/components/ActionLink' import Amount from '@/components/Amount' import AssetLogo from '@/components/AssetLogo' -import Box from '@/components/Box' +import Button from '@/components/Button' import DeleteButton from '@/components/Buttons/DeleteButton' -import HorizontalDivider from '@/components/Dividers/HorizontalDivider' import { inputDefaultStyle, InputProps } from '@/components/Inputs' import Input from '@/components/Inputs/Input' -import { SelectContainer, SelectOption, SelectOptionsModal } from '@/components/Inputs/Select' +import { SelectOption, SelectOptionsModal, SelectOutterContainer } from '@/components/Inputs/Select' +import SelectMoreIcon from '@/components/Inputs/SelectMoreIcon' import SelectOptionTokenName from '@/components/Inputs/SelectOptionTokenName' import Truncate from '@/components/Truncate' import InputsSection from '@/features/send/InputsSection' @@ -90,6 +72,8 @@ const TokensAmountInputs = ({ const remainingAvailableAssetsOptions = allTokensOptions.filter((option) => !selectedAssetIds.includes(option.value)) const disabled = remainingAvailableAssetsOptions.length === 0 const canAddMultipleAssets = allowMultiple && assetAmounts.length < allTokensOptions.length + const shouldDisplayAssetSelectModal = + assetAmounts.length === 0 || (isAssetSelectModalOpen && !!selectedOption && !disabled) const handleOpenAddressTokensSelectModal = useCallback((tokenRowIndex: number) => { setSelectedTokenRowIndex(tokenRowIndex) @@ -192,23 +176,21 @@ const TokensAmountInputs = ({ return ( <> - + {assetAmounts.map(({ id, amountInput = '' }, index) => { const tokenBalances = tokensBalances.find((token) => token.id === id) - if (!tokenBalances) return - const ft = listedFts.find((token) => token.id === id) ?? unlistedFts.find((token) => token.id === id) // TODO: If ALPH, subtract dust for each other token, possibly by querying the node `/addresses/{address}/utxos` const availableHumanReadableAmount = toHumanReadableAmount( - BigInt(tokenBalances.availableBalance ?? 0), + BigInt(tokenBalances?.availableBalance ?? 0), ft?.decimals ?? 0 ) return ( - + handleOpenAddressTokensSelectModal(index)} onKeyDown={(e) => onEnterOrSpace(e, () => handleOpenAddressTokensSelectModal(index))} @@ -224,35 +206,31 @@ const TokensAmountInputs = ({ - {!disabled && ( - - - - )} + {!disabled && } {!nftIds.includes(id) && ( - <> - - - - handleTokenAmountChange(index, e.target.value)} - onClick={() => setSelectedTokenRowIndex(index)} - onMouseDown={() => setSelectedTokenRowIndex(index)} - onKeyDown={(e) => onEnterOrSpace(e, () => setSelectedTokenRowIndex(index))} - type="number" - min={id === ALPH.id ? minAmountInAlph : 0} - max={availableHumanReadableAmount} - aria-label={t('Amount')} - label={`${t('Amount')} ${ft ? `(${ft.symbol})` : ''}`} - error={errors[index]} - /> - - - + + handleTokenAmountChange(index, e.target.value)} + onClick={() => setSelectedTokenRowIndex(index)} + onMouseDown={() => setSelectedTokenRowIndex(index)} + onKeyDown={(e) => onEnterOrSpace(e, () => setSelectedTokenRowIndex(index))} + type="number" + min={id === ALPH.id ? minAmountInAlph : 0} + max={availableHumanReadableAmount} + aria-label={t('Amount')} + placeholder={`${t('Amount')} ${ft ? `(${ft.symbol})` : ''}`} + error={errors[index]} + autoFocus + size={18} + /> + + + + {tokenBalances && ( - {t('Available').toLowerCase()} - - handleTokenAmountChange(index, availableHumanReadableAmount)}> - {t('Use max amount')} - - - - + )} + {t('Available').toLowerCase()} + + handleTokenAmountChange(index, availableHumanReadableAmount)}> + {t('Use max amount')} + + + )} {assetAmounts.length > 1 && handleRemoveAssetClick(index)} />} - + ) })} - {isAssetSelectModalOpen && selectedOption && remainingAvailableAssetsOptions.length > 0 && ( + {shouldDisplayAssetSelectModal && ( 5} /> )} {canAddMultipleAssets && ( - + )} @@ -331,7 +309,6 @@ const useAddressTokensSelectOptions = (addressHash: AddressHash) => { const AddAssetSection = styled.div` display: flex; - justify-content: center; margin: 13px 0; ` @@ -341,9 +318,13 @@ const AssetAmounts = styled.div` } ` -const BoxStyled = styled(Box)` +const SelectContainer = styled.div` padding: 5px; position: relative; + display: flex; + flex-direction: column; + border: 1px solid ${({ theme }) => theme.border.primary}; + border-radius: var(--radius-medium); &:hover { ${DeleteButton} { @@ -352,7 +333,7 @@ const BoxStyled = styled(Box)` } ` -const AssetSelect = styled(SelectContainer)` +const AssetSelect = styled(SelectOutterContainer)` margin: 0; ` @@ -385,12 +366,10 @@ const AssetAmountInput = styled(Input)` } ` -const HorizontalDividerStyled = styled(HorizontalDivider)` - margin: 5px 0; -` - -const AssetAmountRow = styled.div` +const AssetAmountContainer = styled.div` position: relative; + display: flex; + flex-direction: column; ` const AvailableAmountColumn = styled.div` @@ -409,9 +388,3 @@ const AvailableAmountColumn = styled.div` const AvailableAmount = styled.div` color: ${({ theme }) => theme.font.secondary}; ` - -const SelectVerticalDots = styled.div` - flex: 1; - display: flex; - justify-content: flex-end; -` diff --git a/apps/desktop-wallet/src/features/send/sendModals/callContract/AddressesTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/callContract/AddressesTxModalContent.tsx index 54630f57c6..a60262ff86 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/callContract/AddressesTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/callContract/AddressesTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -24,8 +6,7 @@ import { InputFieldsColumn } from '@/components/InputFieldsColumn' import AddressInputs from '@/features/send/AddressInputs' import { CallContractTxModalData } from '@/features/send/sendTypes' import { useAppSelector } from '@/hooks/redux' -import { useFetchSortedAddressesHashes } from '@/hooks/useAddresses' -import { ModalContent } from '@/modals/CenteredModal' +import { useFetchAddressesHashesSortedByLastUse } from '@/hooks/useAddresses' import { selectAddressByHash } from '@/storage/addresses/addressesSelectors' interface CallContractAddressesTxModalContentProps { @@ -40,7 +21,7 @@ const CallContractAddressesTxModalContent = ({ onCancel }: CallContractAddressesTxModalContentProps) => { const { t } = useTranslation() - const { data: allAddressHashes } = useFetchSortedAddressesHashes() + const { data: allAddressHashes } = useFetchAddressesHashesSortedByLastUse() const [fromAddressHash, setFromAddressHash] = useState(data.fromAddress.hash) const fromAddress = useAppSelector((s) => selectAddressByHash(s, fromAddressHash)) @@ -51,7 +32,7 @@ const CallContractAddressesTxModalContent = ({ } return ( - + <> onSubmit({ fromAddress })}>{t('Continue')} - + ) } diff --git a/apps/desktop-wallet/src/features/send/sendModals/callContract/BuildTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/callContract/BuildTxModalContent.tsx index e2439a35f1..2e9e6f8f65 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/callContract/BuildTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/callContract/BuildTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { fromHumanReadableAmount } from '@alephium/shared' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/send/sendModals/callContract/CallContractSendModal.tsx b/apps/desktop-wallet/src/features/send/sendModals/callContract/CallContractSendModal.tsx index 6def2ad44c..cfe56132ef 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/callContract/CallContractSendModal.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/callContract/CallContractSendModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { fromHumanReadableAmount, throttledClient } from '@alephium/shared' import { SignExecuteScriptTxResult } from '@alephium/web3' import { PostHog } from 'posthog-js' @@ -51,6 +33,7 @@ export const buildCallContractTransaction = async (txData: CallContractTxData, c gasAmount: txData.gasAmount, gasPrice: txData.gasPrice ? fromHumanReadableAmount(txData.gasPrice).toString() : undefined }) + ctx.setBuildExecuteScriptTxResult(response) ctx.setUnsignedTransaction(response) ctx.setUnsignedTxId(response.txId) ctx.setFees(BigInt(response.gasAmount) * BigInt(response.gasPrice)) @@ -100,14 +83,15 @@ export const getCallContractWalletConnectResult = ( context: TxContext, signature: string ): SignExecuteScriptTxResult => { - if (!context.unsignedTransaction) throw Error('No unsignedTransaction available') + if (!context.buildExecuteScriptTxResult) throw Error('No buildExecuteScriptTxResult available') return { - groupIndex: context.unsignedTransaction.fromGroup, - unsignedTx: context.unsignedTransaction.unsignedTx, + groupIndex: context.buildExecuteScriptTxResult.fromGroup, + unsignedTx: context.buildExecuteScriptTxResult.unsignedTx, txId: context.unsignedTxId, signature, - gasAmount: context.unsignedTransaction.gasAmount, - gasPrice: BigInt(context.unsignedTransaction.gasPrice) + gasAmount: context.buildExecuteScriptTxResult.gasAmount, + gasPrice: BigInt(context.buildExecuteScriptTxResult.gasPrice), + simulatedOutputs: context.buildExecuteScriptTxResult.simulatedOutputs } } diff --git a/apps/desktop-wallet/src/features/send/sendModals/callContract/CheckTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/callContract/CheckTxModalContent.tsx index 903ebb1b86..34f27b75ec 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/callContract/CheckTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/callContract/CheckTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import FooterButton from '@/components/Buttons/FooterButton' @@ -26,11 +8,12 @@ import CheckFeeLockTimeBox from '@/features/send/CheckFeeLockTimeBox' import CheckModalContent from '@/features/send/CheckModalContent' import CheckWorthBox from '@/features/send/CheckWorthBox' import { CallContractTxData, CheckTxProps } from '@/features/send/sendTypes' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useAppSelector } from '@/hooks/redux' const CallContractCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProps) => { const { t } = useTranslation() - const settings = useAppSelector((s) => s.settings) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) return ( <> @@ -41,8 +24,8 @@ const CallContractCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProps< - - {t(settings.passwordRequirement ? 'Confirm' : 'Send')} + + {t(passwordRequirement ? 'Confirm' : 'Send')} ) diff --git a/apps/desktop-wallet/src/features/send/sendModals/deployContract/AddressesTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/deployContract/AddressesTxModalContent.tsx index 80f8a7fce2..a9e03611d6 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/deployContract/AddressesTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/deployContract/AddressesTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -25,7 +7,6 @@ import AddressInputs from '@/features/send/AddressInputs' import { DeployContractTxModalData } from '@/features/send/sendTypes' import { useAppSelector } from '@/hooks/redux' import { useFetchAddressesHashesWithBalance } from '@/hooks/useAddresses' -import { ModalContent } from '@/modals/CenteredModal' import { selectAddressByHash } from '@/storage/addresses/addressesSelectors' interface DeployContractAddressesTxModalContentProps { @@ -51,7 +32,7 @@ const DeployContractAddressesTxModalContent = ({ } return ( - + <> onSubmit({ fromAddress })}>{t('Continue')} - + ) } diff --git a/apps/desktop-wallet/src/features/send/sendModals/deployContract/BuildTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/deployContract/BuildTxModalContent.tsx index 9b4dcfcee5..bece5fe18c 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/deployContract/BuildTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/deployContract/BuildTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { ALPH } from '@alephium/token-list' import { useState } from 'react' import { useTranslation } from 'react-i18next' diff --git a/apps/desktop-wallet/src/features/send/sendModals/deployContract/CheckTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/deployContract/CheckTxModalContent.tsx index 44f7e11fec..b9d2453c57 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/deployContract/CheckTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/deployContract/CheckTxModalContent.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import Box from '@/components/Box' @@ -28,11 +10,12 @@ import CheckModalContent from '@/features/send/CheckModalContent' import CheckWorthBox from '@/features/send/CheckWorthBox' import InfoRow from '@/features/send/InfoRow' import { CheckTxProps, DeployContractTxData } from '@/features/send/sendTypes' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useAppSelector } from '@/hooks/redux' const DeployContractCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProps) => { const { t } = useTranslation() - const settings = useAppSelector((s) => s.settings) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) return ( <> @@ -52,8 +35,8 @@ const DeployContractCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProp - - {t(settings.passwordRequirement ? 'Confirm' : 'Send')} + + {t(passwordRequirement ? 'Confirm' : 'Send')} ) diff --git a/apps/desktop-wallet/src/features/send/sendModals/deployContract/DeployContractSendModal.tsx b/apps/desktop-wallet/src/features/send/sendModals/deployContract/DeployContractSendModal.tsx index f1115cfc3d..4b5ea7f5cc 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/deployContract/DeployContractSendModal.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/deployContract/DeployContractSendModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { throttledClient } from '@alephium/shared' import { binToHex, contractIdFromAddress, SignDeployContractTxResult } from '@alephium/web3' import { PostHog } from 'posthog-js' diff --git a/apps/desktop-wallet/src/features/send/sendModals/transfer/AddressesTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/transfer/AddressesTxModalContent.tsx index a8e9826bd0..0e363a771a 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/transfer/AddressesTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/transfer/AddressesTxModalContent.tsx @@ -1,31 +1,12 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import FooterButton from '@/components/Buttons/FooterButton' import { InputFieldsColumn } from '@/components/InputFieldsColumn' import AddressInputs from '@/features/send/AddressInputs' import { TransferAddressesTxModalOnSubmitData, TransferTxModalData } from '@/features/send/sendTypes' import { useAppSelector } from '@/hooks/redux' import { useFetchAddressesHashesWithBalance } from '@/hooks/useAddresses' -import { ModalContent } from '@/modals/CenteredModal' +import { ModalFooterButton, ModalFooterButtons } from '@/modals/CenteredModal' import { selectAddressByHash } from '@/storage/addresses/addressesSelectors' import { isAddressValid, requiredErrorMessage } from '@/utils/form-validation' @@ -37,7 +18,7 @@ interface TransferAddressesTxModalContentProps { const TransferAddressesTxModalContent = ({ data, onSubmit, onCancel }: TransferAddressesTxModalContentProps) => { const { t } = useTranslation() - const { data: fromAddresses } = useFetchAddressesHashesWithBalance() + const { data: fromAddresses } = useFetchAddressesHashesWithBalance(data.tokenId) const [fromAddressHash, setFromAddressHash] = useState(data.fromAddress.hash) const [toAddress, setToAddress] = useStateWithError(data?.toAddress ?? '') @@ -64,7 +45,7 @@ const TransferAddressesTxModalContent = ({ data, onSubmit, onCancel }: TransferA const isSubmitButtonActive = toAddress.value && !toAddress.error return ( - + <> - - onSubmit({ - fromAddress, - toAddress: toAddress.value - }) - } - disabled={!isSubmitButtonActive} - > - {t('Continue')} - - + + + onSubmit({ + fromAddress, + toAddress: toAddress.value, + tokenId: data.tokenId + }) + } + disabled={!isSubmitButtonActive} + > + {t('Continue')} + + + ) } diff --git a/apps/desktop-wallet/src/features/send/sendModals/transfer/BuildTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/transfer/BuildTxModalContent.tsx index 8602a4169e..5960d8f65b 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/transfer/BuildTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/transfer/BuildTxModalContent.tsx @@ -1,29 +1,9 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - -import { ALPH } from '@alephium/token-list' import dayjs from 'dayjs' import { useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import useFetchAddressBalances from '@/api/apiDataHooks/address/useFetchAddressBalances' -import FooterButton from '@/components/Buttons/FooterButton' import { InputFieldsColumn } from '@/components/InputFieldsColumn' import Input from '@/components/Inputs/Input' import ToggleSection from '@/components/ToggleSection' @@ -33,6 +13,7 @@ import { shouldBuildSweepTransactions } from '@/features/send/sendUtils' import TokensAmountInputs from '@/features/send/TokensAmountInputs' import useAreAmountsWithinAddressAvailableBalances from '@/features/send/useAreAmountsWithinAddressAvailableBalances' import useGasSettings from '@/hooks/useGasSettings' +import { ModalFooterButton, ModalFooterButtons } from '@/modals/CenteredModal' import { AssetAmountInputType } from '@/types/assets' export interface TransferBuildTxModalContentProps { @@ -40,8 +21,6 @@ export interface TransferBuildTxModalContentProps { onSubmit: (data: TransferTxData) => void } -const defaultAssetAmounts = [{ id: ALPH.id }] - const TransferBuildTxModalContent = ({ data, onSubmit }: TransferBuildTxModalContentProps) => { const { t } = useTranslation() const { @@ -55,11 +34,15 @@ const TransferBuildTxModalContent = ({ data, onSubmit }: TransferBuildTxModalCon } = useGasSettings(data?.gasAmount?.toString(), data?.gasPrice) const [lockTime, setLockTime] = useState(data.lockTime) - const [assetAmounts, setAssetAmounts] = useState(data.assetAmounts || defaultAssetAmounts) const { fromAddress, toAddress } = data const { data: tokensBalances } = useFetchAddressBalances({ addressHash: fromAddress.hash }) + + const [assetAmounts, setAssetAmounts] = useState( + data.assetAmounts ?? (data.tokenId ? [{ id: data.tokenId }] : []) + ) + const allAssetAmountsAreWithinAvailableBalance = useAreAmountsWithinAddressAvailableBalances( fromAddress.hash, assetAmounts ?? [] @@ -128,22 +111,24 @@ const TransferBuildTxModalContent = ({ data, onSubmit }: TransferBuildTxModalCon onGasPriceChange={handleGasPriceChange} /> - - onSubmit({ - fromAddress, - toAddress, - assetAmounts, - gasAmount: gasAmount ? parseInt(gasAmount) : undefined, - gasPrice, - lockTime, - shouldSweep - }) - } - disabled={!isSubmitButtonActive} - > - {t('Check')} - + + + onSubmit({ + fromAddress, + toAddress, + assetAmounts, + gasAmount: gasAmount ? parseInt(gasAmount) : undefined, + gasPrice, + lockTime, + shouldSweep + }) + } + disabled={!isSubmitButtonActive} + > + {t('Check')} + + ) } diff --git a/apps/desktop-wallet/src/features/send/sendModals/transfer/CheckTxModalContent.tsx b/apps/desktop-wallet/src/features/send/sendModals/transfer/CheckTxModalContent.tsx index 376d876b66..c476b521cf 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/transfer/CheckTxModalContent.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/transfer/CheckTxModalContent.tsx @@ -1,24 +1,6 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' +import styled from 'styled-components' -import FooterButton from '@/components/Buttons/FooterButton' import { openModal } from '@/features/modals/modalActions' import CheckAddressesBox from '@/features/send/CheckAddressesBox' import CheckAmountsBox from '@/features/send/CheckAmountsBox' @@ -26,11 +8,13 @@ import CheckFeeLocktimeBox from '@/features/send/CheckFeeLockTimeBox' import CheckModalContent from '@/features/send/CheckModalContent' import CheckWorthBox from '@/features/send/CheckWorthBox' import { CheckTxProps, TransferTxData } from '@/features/send/sendTypes' +import { selectEffectivePasswordRequirement } from '@/features/settings/settingsSelectors' import { useAppDispatch, useAppSelector } from '@/hooks/redux' +import { ModalFooterButton, ModalFooterButtons } from '@/modals/CenteredModal' const TransferCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProps) => { const { t } = useTranslation() - const settings = useAppSelector((s) => s.settings) + const passwordRequirement = useAppSelector(selectEffectivePasswordRequirement) const dispatch = useAppDispatch() const handleButtonPress = () => { @@ -42,14 +26,22 @@ const TransferCheckTxModalContent = ({ data, fees, onSubmit }: CheckTxProps - + - {t(settings.passwordRequirement ? 'Confirm' : 'Send')} + + + {t(passwordRequirement ? 'Confirm' : 'Send')} + ) } export default TransferCheckTxModalContent + +const CheckAmountsBoxStyled = styled(CheckAmountsBox)` + background-color: ${({ theme }) => theme.bg.secondary}; + padding: 0 var(--spacing-2); +` diff --git a/apps/desktop-wallet/src/features/send/sendModals/transfer/ConfirmLockTimeModal.tsx b/apps/desktop-wallet/src/features/send/sendModals/transfer/ConfirmLockTimeModal.tsx index b78f100895..2ee576f30a 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/transfer/ConfirmLockTimeModal.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/transfer/ConfirmLockTimeModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import dayjs from 'dayjs' import { LockIcon } from 'lucide-react' import { memo } from 'react' diff --git a/apps/desktop-wallet/src/features/send/sendModals/transfer/TransferSendModal.tsx b/apps/desktop-wallet/src/features/send/sendModals/transfer/TransferSendModal.tsx index 2bd785b272..07362af13b 100644 --- a/apps/desktop-wallet/src/features/send/sendModals/transfer/TransferSendModal.tsx +++ b/apps/desktop-wallet/src/features/send/sendModals/transfer/TransferSendModal.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { fromHumanReadableAmount, throttledClient } from '@alephium/shared' import { SignTransferTxResult } from '@alephium/web3' import { PostHog } from 'posthog-js' diff --git a/apps/desktop-wallet/src/features/send/sendTypes.ts b/apps/desktop-wallet/src/features/send/sendTypes.ts index 81ffcf115e..b5525b3813 100644 --- a/apps/desktop-wallet/src/features/send/sendTypes.ts +++ b/apps/desktop-wallet/src/features/send/sendTypes.ts @@ -1,25 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AssetAmount } from '@alephium/shared' import { node } from '@alephium/web3' import { Address } from '@/types/addresses' +import { TokenId } from '@/types/tokens' export interface TransferTxData { fromAddress: Address @@ -29,6 +12,7 @@ export interface TransferTxData { gasAmount?: number gasPrice?: string lockTime?: Date + tokenId?: TokenId } export interface CallContractTxData { @@ -56,7 +40,10 @@ export type TransferTxModalData = PartialTxData export type CallContractTxModalData = PartialTxData export type DeployContractTxModalData = PartialTxData -export type TransferAddressesTxModalOnSubmitData = PartialTxData +export type TransferAddressesTxModalOnSubmitData = PartialTxData< + TransferTxData, + 'fromAddress' | 'toAddress' | 'tokenId' +> export type AddressesTxModalData = | TransferAddressesTxModalOnSubmitData @@ -100,4 +87,6 @@ export type TxContext = { setContractAddress: (contractAddress: string) => void isSweeping: boolean consolidationRequired: boolean + buildExecuteScriptTxResult: node.BuildExecuteScriptTxResult | undefined + setBuildExecuteScriptTxResult: (tx: node.BuildExecuteScriptTxResult | undefined) => void } diff --git a/apps/desktop-wallet/src/features/send/sendUtils.ts b/apps/desktop-wallet/src/features/send/sendUtils.ts index 85837dd2cd..c71c04127b 100644 --- a/apps/desktop-wallet/src/features/send/sendUtils.ts +++ b/apps/desktop-wallet/src/features/send/sendUtils.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AssetAmount } from '@alephium/shared' import { ALPH } from '@alephium/token-list' import { DUST_AMOUNT, MIN_UTXO_SET_AMOUNT } from '@alephium/web3' diff --git a/apps/desktop-wallet/src/features/send/sentTransactions/SentTransactionSnackbarPopup.tsx b/apps/desktop-wallet/src/features/send/sentTransactions/SentTransactionSnackbarPopup.tsx index 37d78c4ef2..b52446513e 100644 --- a/apps/desktop-wallet/src/features/send/sentTransactions/SentTransactionSnackbarPopup.tsx +++ b/apps/desktop-wallet/src/features/send/sentTransactions/SentTransactionSnackbarPopup.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useInterval } from '@alephium/shared-react' import { explorer as e } from '@alephium/web3' import { colord } from 'colord' @@ -75,7 +57,7 @@ const SentTransactionSnackbarPopup = memo(({ txHash }: SentTransactionSnackbarPo {sentTx.status !== 'sent' && } - diff --git a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsActions.ts b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsActions.ts index bd66f05026..76d1218e9d 100644 --- a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsActions.ts +++ b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsActions.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createAction } from '@reduxjs/toolkit' import { SentTransaction } from '@/types/transactions' diff --git a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsAdapter.ts b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsAdapter.ts index d39973d79e..e789a4ceeb 100644 --- a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsAdapter.ts +++ b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsAdapter.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createEntityAdapter } from '@reduxjs/toolkit' import { SentTransaction } from '@/types/transactions' diff --git a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSelectors.ts b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSelectors.ts index 24775c8319..92aafc9056 100644 --- a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSelectors.ts +++ b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSelectors.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import { createSelector } from '@reduxjs/toolkit' diff --git a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSlice.ts b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSlice.ts index b58949682d..4a286686b5 100644 --- a/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSlice.ts +++ b/apps/desktop-wallet/src/features/send/sentTransactions/sentTransactionsSlice.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createSlice, EntityState, isAnyOf } from '@reduxjs/toolkit' import { sentTransactionStatusChanged } from '@/features/send/sentTransactions/sentTransactionsActions' diff --git a/apps/desktop-wallet/src/features/send/useAreAmountsWithinAddressAvailableBalances.ts b/apps/desktop-wallet/src/features/send/useAreAmountsWithinAddressAvailableBalances.ts index bf73b6d85e..ad39a331d2 100644 --- a/apps/desktop-wallet/src/features/send/useAreAmountsWithinAddressAvailableBalances.ts +++ b/apps/desktop-wallet/src/features/send/useAreAmountsWithinAddressAvailableBalances.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { AddressHash } from '@alephium/shared' import useFetchAddressBalances from '@/api/apiDataHooks/address/useFetchAddressBalances' diff --git a/apps/desktop-wallet/src/features/settings/RegionSettings.tsx b/apps/desktop-wallet/src/features/settings/RegionSettings.tsx index d21c50c966..ddd2328e68 100644 --- a/apps/desktop-wallet/src/features/settings/RegionSettings.tsx +++ b/apps/desktop-wallet/src/features/settings/RegionSettings.tsx @@ -1,28 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useTranslation } from 'react-i18next' import KeyValueInput from '@/components/Inputs/InlineLabelValueInput' import Select from '@/components/Inputs/Select' import useAnalytics from '@/features/analytics/useAnalytics' +import useRegionOptions from '@/features/settings/regionSettings/useRegionOptions' import { numberFormatRegionChanged } from '@/features/settings/settingsActions' -import useRegionOptions from '@/features/settings/useRegionOptions' import { useAppDispatch, useAppSelector } from '@/hooks/redux' const RegionSettings = () => { @@ -42,6 +24,7 @@ const RegionSettings = () => { . -*/ - import { upperFirst } from 'lodash' import { useMemo } from 'react' -import { Language } from '@/features/settings/settingsTypes' import { useAppSelector } from '@/hooks/redux' import regionsLocales from './regions.json' @@ -32,6 +13,11 @@ const useRegionOptions = () => { export default useRegionOptions +const getRegionsOptions = (languageLocale: string) => + regionsLocales + .map((regionLocale) => getRegionOption(regionLocale, languageLocale)) + .sort((a, b) => a.label.localeCompare(b.label)) + // Inspired by https://github.com/LedgerHQ/ledger-live/blob/065dda3/apps/ledger-live-desktop/src/renderer/screens/settings/sections/General/RegionSelect.tsx const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Locale) => { const [language, region = ''] = regionLocale.split('-') @@ -50,8 +36,3 @@ const getRegionOption = (regionLocale: string, languageLocale: string | Intl.Loc label } } - -const getRegionsOptions = (languageLocale: Language) => - regionsLocales - .map((regionLocale) => getRegionOption(regionLocale, languageLocale)) - .sort((a, b) => a.label.localeCompare(b.label)) diff --git a/apps/desktop-wallet/src/features/settings/settingsActions.ts b/apps/desktop-wallet/src/features/settings/settingsActions.ts index cc92dea776..9183087406 100644 --- a/apps/desktop-wallet/src/features/settings/settingsActions.ts +++ b/apps/desktop-wallet/src/features/settings/settingsActions.ts @@ -1,34 +1,8 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { createAction } from '@reduxjs/toolkit' -import { Language, Settings } from '@/features/settings/settingsTypes' +import { Settings } from '@/features/settings/settingsTypes' import { ThemeType } from '@/features/theme/themeTypes' -export const languageChangeStarted = createAction('settings/languageChangeStarted') - -export const languageChangeFinished = createAction('settings/languageChangeFinished') - -export const systemLanguageMatchSucceeded = createAction('settings/systemLanguageMatchSucceeded') - -export const systemLanguageMatchFailed = createAction('settings/systemLanguageMatchFailed') - export const systemRegionMatchSucceeded = createAction('settings/systemRegionMatchSucceeded') export const systemRegionMatchFailed = createAction('settings/systemRegionMatchFailed') @@ -43,8 +17,6 @@ export const passwordRequirementToggled = createAction('settings/passwordRequire export const devToolsToggled = createAction('settings/devToolsToggled') -export const languageChanged = createAction('settings/languageChanged') - export const numberFormatRegionChanged = createAction( 'settings/numberFormatRegionChanged' ) @@ -58,3 +30,7 @@ export const analyticsToggled = createAction(' export const localStorageGeneralSettingsMigrated = createAction( 'settings/localStorageGeneralSettingsMigrated' ) + +export const addressOrderPreferenceChanged = createAction( + 'settings/addressOrderPreferenceChanged' +) diff --git a/apps/desktop-wallet/src/features/settings/settingsConstants.ts b/apps/desktop-wallet/src/features/settings/settingsConstants.ts index b0601ec7f0..6c4c18dc5f 100644 --- a/apps/desktop-wallet/src/features/settings/settingsConstants.ts +++ b/apps/desktop-wallet/src/features/settings/settingsConstants.ts @@ -1,41 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { CURRENCIES, Currency, defaultNetworkSettings } from '@alephium/shared' import { SelectOption } from '@/components/Inputs/Select' -import { Language, Settings } from '@/features/settings/settingsTypes' - -export const languageOptions: SelectOption[] = [ - { label: 'English', value: 'en-US' }, - { label: 'Български', value: 'bg-BG' }, - { label: 'Deutsch', value: 'de-DE' }, - { label: 'Español', value: 'es-ES' }, - { label: 'Français', value: 'fr-FR' }, - { label: 'Bahasa Indonesia', value: 'id-ID' }, - { label: 'Italiano', value: 'it-IT' }, - { label: 'Português', value: 'pt-PT' }, - { label: 'Русский', value: 'ru-RU' }, - { label: 'Türkçe', value: 'tr-TR' }, - { label: 'Tiếng Việt', value: 'vi-VN' }, - { label: 'Ελληνικά', value: 'el-GR' }, - { label: '简体中文', value: 'zh-CN' } -] +import { Settings } from '@/features/settings/settingsTypes' export const fiatCurrencyOptions: SelectOption[] = Object.values(CURRENCIES).map((currency) => ({ label: currency.ticker, @@ -51,6 +17,11 @@ export const LockTimes = { ONE_HOUR: 60, TWO_HOURS: 120 } +export enum AddressOrder { + LastUse = 'lastUse', + AlphBalance = 'alphBalance', + Label = 'label' +} export const locktimeInMinutes = Object.values(LockTimes) @@ -64,7 +35,8 @@ export const defaultSettings: Settings = { devTools: false, analytics: true, fiatCurrency: 'USD', - region: undefined + region: undefined, + addressOrderPreference: AddressOrder.LastUse }, network: defaultNetworkSettings } diff --git a/apps/desktop-wallet/src/features/settings/settingsPersistentStorage.ts b/apps/desktop-wallet/src/features/settings/settingsPersistentStorage.ts index 2173f4749c..4fa294e15c 100644 --- a/apps/desktop-wallet/src/features/settings/settingsPersistentStorage.ts +++ b/apps/desktop-wallet/src/features/settings/settingsPersistentStorage.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { merge } from 'lodash' import posthog from 'posthog-js' diff --git a/apps/desktop-wallet/src/features/settings/settingsSelectors.ts b/apps/desktop-wallet/src/features/settings/settingsSelectors.ts new file mode 100644 index 0000000000..999b089e10 --- /dev/null +++ b/apps/desktop-wallet/src/features/settings/settingsSelectors.ts @@ -0,0 +1,9 @@ +import { createSelector } from '@reduxjs/toolkit' + +import { RootState } from '@/storage/store' + +// If the wallet is using Ledger, we don't want to use a password +export const selectEffectivePasswordRequirement = createSelector( + [(state: RootState) => state.activeWallet.isLedger, (state: RootState) => state.settings.passwordRequirement], + (isLedger, passwordRequirement) => (isLedger ? false : passwordRequirement) +) diff --git a/apps/desktop-wallet/src/features/settings/settingsSlice.ts b/apps/desktop-wallet/src/features/settings/settingsSlice.ts index 0d6eb24421..9997c6d60d 100644 --- a/apps/desktop-wallet/src/features/settings/settingsSlice.ts +++ b/apps/desktop-wallet/src/features/settings/settingsSlice.ts @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import 'dayjs/locale/bg' import 'dayjs/locale/de' import 'dayjs/locale/es' @@ -28,24 +10,29 @@ import 'dayjs/locale/tr' import 'dayjs/locale/vi' import 'dayjs/locale/el' import 'dayjs/locale/zh-cn' +import 'dayjs/locale/th' import { fiatCurrencyChanged } from '@alephium/shared' import { createListenerMiddleware, createSlice, isAnyOf } from '@reduxjs/toolkit' import dayjs from 'dayjs' import posthog from 'posthog-js' +import i18next from '@/features/localization/i18n' import { - analyticsToggled, - devToolsToggled, - discreetModeToggled, languageChanged, languageChangeFinished, languageChangeStarted, + systemLanguageMatchFailed, + systemLanguageMatchSucceeded +} from '@/features/localization/localizationActions' +import { + addressOrderPreferenceChanged, + analyticsToggled, + devToolsToggled, + discreetModeToggled, localStorageGeneralSettingsMigrated, numberFormatRegionChanged, passwordRequirementToggled, - systemLanguageMatchFailed, - systemLanguageMatchSucceeded, systemRegionMatchFailed, systemRegionMatchSucceeded, themeSettingsChanged, @@ -54,7 +41,6 @@ import { } from '@/features/settings/settingsActions' import SettingsStorage from '@/features/settings/settingsPersistentStorage' import { GeneralSettings } from '@/features/settings/settingsTypes' -import i18next from '@/i18n' import { RootState } from '@/storage/store' const initialState = SettingsStorage.load('general') as GeneralSettings @@ -96,6 +82,9 @@ const settingsSlice = createSlice({ .addCase(fiatCurrencyChanged, (state, action) => { state.fiatCurrency = action.payload }) + .addCase(addressOrderPreferenceChanged, (state, action) => { + state.addressOrderPreference = action.payload + }) builder .addMatcher(isAnyOf(numberFormatRegionChanged, systemRegionMatchSucceeded), (state, action) => { @@ -125,6 +114,7 @@ settingsListenerMiddleware.startListening({ numberFormatRegionChanged, walletLockTimeChanged, analyticsToggled, + addressOrderPreferenceChanged, fiatCurrencyChanged ), effect: (_, { getState }) => { diff --git a/apps/desktop-wallet/src/features/settings/settingsTypes.ts b/apps/desktop-wallet/src/features/settings/settingsTypes.ts index 34419c4265..7e026d8098 100644 --- a/apps/desktop-wallet/src/features/settings/settingsTypes.ts +++ b/apps/desktop-wallet/src/features/settings/settingsTypes.ts @@ -1,23 +1,7 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { Currency, NetworkSettings } from '@alephium/shared' +import { Language } from '@/features/localization/languages' +import { AddressOrder } from '@/features/settings/settingsConstants' import { ThemeSettings } from '@/features/theme/themeTypes' export interface GeneralSettings { @@ -30,24 +14,10 @@ export interface GeneralSettings { analytics: boolean fiatCurrency: Currency region: string | undefined + addressOrderPreference: AddressOrder } export interface Settings { general: GeneralSettings network: NetworkSettings } - -export type Language = - | 'en-US' - | 'fr-FR' - | 'de-DE' - | 'vi-VN' - | 'pt-PT' - | 'ru-RU' - | 'bg-BG' - | 'es-ES' - | 'id-ID' - | 'tr-TR' - | 'it-IT' - | 'el-GR' - | 'zh-CN' diff --git a/apps/desktop-wallet/src/features/snackbar/SnackbarBox.tsx b/apps/desktop-wallet/src/features/snackbar/SnackbarBox.tsx index 2bc28fcac1..2e31510017 100644 --- a/apps/desktop-wallet/src/features/snackbar/SnackbarBox.tsx +++ b/apps/desktop-wallet/src/features/snackbar/SnackbarBox.tsx @@ -1,42 +1,66 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { colord } from 'colord' import { motion } from 'framer-motion' +import { ReactNode } from 'react' import styled, { css } from 'styled-components' -const SnackbarBox = styled(motion.div)` - margin: var(--spacing-3); - min-width: 200px; - padding: var(--spacing-4) var(--spacing-3); +interface SnackbarBoxProps { + children: ReactNode + className?: string +} + +const SnackbarBox = ({ children, ...props }: SnackbarBoxProps) => ( + + + {children} + +) + +export default SnackbarBox + +const SnackbarBoxContent = styled(motion.div)` + font-size: 13px; color: ${({ theme }) => theme.font.primary}; - border-radius: var(--radius-big); - max-width: 800px; word-wrap: break-word; overflow-y: auto; + font-weight: var(--fontWeight-semiBold); + pointer-events: all; + margin-top: -10px; + background-color: ${({ theme }) => theme.bg.highlight}; + z-index: 1; + padding: 10px 20px; + border-radius: var(--radius-medium); +` + +const BlurredBackground = styled.div` + position: absolute; + top: -30px; + left: 0; + right: 0; + bottom: 0; + border-radius: 0 0 100% 100%; + filter: blur(30px); + pointer-events: none; + transform: scaleX(1.2); + z-index: 0; + background: linear-gradient(to bottom, ${({ theme }) => theme.bg.background2} 80%, transparent 100%); +` + +const SnackBarBoxContainer = styled(motion.div)` + position: relative; + display: flex; + justify-content: center; + align-items: center; + min-width: 200px; + max-width: 1200px; + width: 80%; + height: 80px; &.alert { ${({ theme }) => getSnackbarStyling(theme.global.alert)} } &.info { - ${({ theme }) => - theme.name === 'light' ? getSnackbarStyling(theme.bg.contrast) : getSnackbarStyling(theme.bg.background2)} + ${({ theme }) => getSnackbarStyling(theme.global.accent)} } &.success { @@ -44,10 +68,14 @@ const SnackbarBox = styled(motion.div)` } ` -export default SnackbarBox - const getSnackbarStyling = (color: string) => css` - background-color: ${color}; - border: 1px solid ${colord(color).lighten(0.1).toHex()}; - color: rgba(255, 255, 255, 0.8); + ${SnackbarBoxContent} { + font-size: 14px; + background-color: ${({ theme }) => colord(color).alpha(0.1).toHex()}; + border: 1px solid + ${({ theme }) => + theme.name === 'light' ? colord(color).alpha(0.1).toHex() : colord(color).alpha(0.2).lighten(0.1).toHex()}; + color: ${({ theme }) => + theme.name === 'light' ? colord(color).toHex() : colord(color).alpha(1).lighten(0.1).toHex()}; + } ` diff --git a/apps/desktop-wallet/src/features/switch-wallet/WalletSelect.tsx b/apps/desktop-wallet/src/features/switch-wallet/WalletSelect.tsx index c117bfaec0..4816cdc261 100644 --- a/apps/desktop-wallet/src/features/switch-wallet/WalletSelect.tsx +++ b/apps/desktop-wallet/src/features/switch-wallet/WalletSelect.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -55,6 +37,7 @@ const WalletSelect = () => { return ( w.value === selectedWallet)} - onSelect={setSelectedWallet} - title={t('Select a wallet')} - id="wallet" - /> - setPassword(e.target.value)} - value={password} - id="password" - autoFocus - /> - - - - - + + + + + + + + + {pendingDappConnectionUrl + ? t('Connect to dApp') + : isAwaitingSessionRequestApproval + ? t('Received dApp request') + : t('Welcome back.')} + + + + setPassword(e.target.value)} + value={password} + id="password" + autoFocus + heightSize="big" + /> + {passphraseConsent && } + + - - - - + + + + + + + ) } @@ -144,31 +129,43 @@ const UnlockPanel = ({ onNewWalletLinkClick }: UnlockPanelProps) => { export default UnlockPanel const SectionStyled = styled(Section)` - min-width: 328px; + min-width: 280px; ` -const ButtonsSection = styled(SectionStyled)` +const MainAction = styled(SectionStyled)` margin-top: 30px; - gap: 20px; + gap: 10px; +` + +const BottomActions = styled.div` + position: absolute; + bottom: 20px; + right: 0; + left: 0; + justify-content: center; + display: flex; + gap: 10px; ` -const SecondaryButtonsSection = styled.div` +const BrandContainer = styled.div` display: flex; - flex-direction: column; - gap: 5px; - width: 100%; + gap: 14px; align-items: center; + margin-bottom: var(--spacing-7); ` -const WalletPassphraseStyled = styled(WalletPassphrase)` - margin: 16px 0; - width: 100%; - position: fixed; - bottom: 5px; - right: 20px; +const AlephiumLogoContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding: 26px; + width: 80px; + height: 80px; + border-radius: 80px; + background-color: ${({ theme }) => theme.bg.contrast}; ` -const ParagraphStyled = styled(Paragraph)` - font-weight: var(--fontWeight-semiBold); - font-size: 16px; +const Title = styled.div` + font-size: 23px; + margin: var(--spacing-2); ` diff --git a/apps/desktop-wallet/src/pages/HomePage/index.tsx b/apps/desktop-wallet/src/pages/HomePage/index.tsx index 094be53e02..e93d731c28 100644 --- a/apps/desktop-wallet/src/pages/HomePage/index.tsx +++ b/apps/desktop-wallet/src/pages/HomePage/index.tsx @@ -1,28 +1,10 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { useState } from 'react' import { useTranslation } from 'react-i18next' +import styled from 'styled-components' -import { fadeInSlowly } from '@/animations' -import AppHeader from '@/components/AppHeader' import { FloatingPanel } from '@/components/PageComponents/PageContainers' import PanelTitle from '@/components/PageComponents/PanelTitle' +import SideBarSettingsButton from '@/components/PageComponents/SideBarSettingsButton' import { useAppSelector } from '@/hooks/redux' import NewWalletActions from '@/pages/HomePage/NewWalletActions' import UnlockPanel from '@/pages/HomePage/UnlockPanel' @@ -35,30 +17,32 @@ const HomePage = () => { const [showNewWalletActions, setShowNewWalletActions] = useState(false) return ( - - - {showNewWalletActions ? ( - <> - - {t('New wallet')} - - setShowNewWalletActions(false)} /> - - ) : hasAtLeastOneWallet ? ( - setShowNewWalletActions(true)} /> - ) : ( - <> - - {t('Welcome.')} - - - - )} - - - + + {showNewWalletActions ? ( + + {t('New wallet')} + setShowNewWalletActions(false)} /> + + ) : hasAtLeastOneWallet ? ( + setShowNewWalletActions(true)} /> + ) : ( + + + {t('Welcome.')} + + + + )} + ) } export default HomePage + +const SettingsButtonStyled = styled(SideBarSettingsButton)` + position: absolute; + bottom: var(--spacing-2); + left: var(--spacing-2); + width: auto; +` diff --git a/apps/desktop-wallet/src/pages/LockedWalletLayout.tsx b/apps/desktop-wallet/src/pages/LockedWalletLayout.tsx index 7c08459abf..9acdc1de71 100644 --- a/apps/desktop-wallet/src/pages/LockedWalletLayout.tsx +++ b/apps/desktop-wallet/src/pages/LockedWalletLayout.tsx @@ -1,41 +1,19 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion, MotionProps } from 'framer-motion' +import { ReactNode } from 'react' import styled from 'styled-components' -import SideBar from '@/components/PageComponents/SideBar' +import AppHeader from '@/components/AppHeader' import Scrollbar from '@/components/Scrollbar' -import { ReactComponent as AlephiumLogotype } from '@/images/logotype.svg' interface LockedWalletLayoutProps extends MotionProps { className?: string - animateSideBar?: boolean + children: ReactNode } -const LockedWalletLayout: FC = ({ children, animateSideBar, ...props }) => ( +const LockedWalletLayout = ({ children, ...props }: LockedWalletLayoutProps) => ( - - - - - + {children} @@ -48,15 +26,6 @@ export default styled(LockedWalletLayout)` background-color: ${({ theme }) => theme.bg.background1}; ` -const Logo = styled.div` - padding: 5px; -` - -const AlephiumLogotypeStyled = styled(AlephiumLogotype)` - fill: ${({ theme }) => theme.font.primary}; - color: ${({ theme }) => theme.font.primary}; -` - const CenteredContainer = styled.div` display: flex; align-items: center; diff --git a/apps/desktop-wallet/src/pages/NewWallet/CheckWordsIntroPage.tsx b/apps/desktop-wallet/src/pages/NewWallet/CheckWordsIntroPage.tsx index 213980527b..d33d5af99f 100644 --- a/apps/desktop-wallet/src/pages/NewWallet/CheckWordsIntroPage.tsx +++ b/apps/desktop-wallet/src/pages/NewWallet/CheckWordsIntroPage.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { motion } from 'framer-motion' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -27,7 +9,6 @@ import { PanelContentContainer, Section } from '@/components/PageComponents/PageContainers' -import PanelTitle from '@/components/PageComponents/PanelTitle' import Paragraph from '@/components/Paragraph' import { useStepsContext } from '@/contexts/steps' import useAnalytics from '@/features/analytics/useAnalytics' @@ -51,9 +32,6 @@ const CheckWordsIntroPage = () => { return ( - - {t('Security Check')} -
    @@ -70,14 +48,19 @@ const CheckWordsIntroPage = () => { - {t('Alright! Time to check if you got your words right!')} - - {t('Select the words in the right order.')} {t('Ready?')} + + {t('Alright! Time to check if you got your words right!')} {t('Select the words in the right order.')}{' '} + {t('Ready?')}
    - + +
    ) @@ -93,7 +76,7 @@ const LockContainer = styled.div` flex-direction: column; justify-content: center; align-items: center; - padding-bottom: 60px; + padding-bottom: 30px; ` const Lock = styled(motion.div)` diff --git a/apps/desktop-wallet/src/pages/NewWallet/CheckWordsPage.tsx b/apps/desktop-wallet/src/pages/NewWallet/CheckWordsPage.tsx index 3ab5791800..5bceb636ad 100644 --- a/apps/desktop-wallet/src/pages/NewWallet/CheckWordsPage.tsx +++ b/apps/desktop-wallet/src/pages/NewWallet/CheckWordsPage.tsx @@ -1,343 +1,115 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { dangerouslyConvertUint8ArrayMnemonicToString } from '@alephium/keyring' -import { colord } from 'colord' -import { motion, PanInfo } from 'framer-motion' -import { throttle } from 'lodash' -import { AlertTriangle, ThumbsUp } from 'lucide-react' +import { shuffle } from 'lodash' import { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import styled from 'styled-components' -import Box from '@/components/Box' import Button from '@/components/Button' -import InfoBox from '@/components/InfoBox' import { FloatingPanel, FooterActionsContainer, PanelContentContainer, Section } from '@/components/PageComponents/PageContainers' -import PanelTitle from '@/components/PageComponents/PanelTitle' import Paragraph from '@/components/Paragraph' import { useStepsContext } from '@/contexts/steps' import { useWalletContext } from '@/contexts/wallet' import useAnalytics from '@/features/analytics/useAnalytics' -interface WordKey { - word: string - key: string // Used to build layout and ensure anims are working when duplicates exist -} - const CheckWordsPage = () => { const { t } = useTranslation() const { onButtonBack, onButtonNext } = useStepsContext() const { mnemonic } = useWalletContext() const { sendAnalytics } = useAnalytics() - const [wordList, setWordList] = useState([]) - const [selectedElements, setSelectedElements] = useState<{ [wordKey: string]: Element | null }>({}) - const [selectedWords, setSelectedWords] = useState([]) - const [isValid, setIsValid] = useState(false) - - // === Drag interaction === - const [isDragging, setIsDragging] = useState(false) - const [closestWordKey, setClosestWordKey] = useState('') + const [mnemonicWords, setMnemonicWords] = useState([]) + const [currentWordIndex, setCurrentWordIndex] = useState(0) + const [options, setOptions] = useState([]) + const [isError, setIsError] = useState(false) useEffect(() => { if (!mnemonic) return - setWordList( - getAlphabeticallyOrderedList(dangerouslyConvertUint8ArrayMnemonicToString(mnemonic).split(' ')).map( - (wordString, i) => ({ - word: wordString, - key: `${wordString}-${i}` - }) - ) - ) - - setSelectedElements( - dangerouslyConvertUint8ArrayMnemonicToString(mnemonic) - .split(' ') - .reduce((p, c) => ({ ...p, [c]: null }), {}) - ) + const words = dangerouslyConvertUint8ArrayMnemonicToString(mnemonic).split(' ') + setMnemonicWords(words) + setCurrentWordIndex(0) + setOptions(generateOptions(words, 0)) }, [mnemonic]) - useEffect(() => { - if (!mnemonic) return - - setIsValid( - selectedWords.map(({ word }) => word).join(' ') === dangerouslyConvertUint8ArrayMnemonicToString(mnemonic) - ) - }, [mnemonic, selectedWords]) - - // === Actions === - // =============== - const handleSelectedWordRemove = (w: WordKey) => { - if (isDragging) { - setIsDragging(false) - return - } - setSelectedWords(selectedWords.filter((word) => w.key !== word.key)) - - // Remove from element list - selectedElements[w.key] = null - } - - const handleSelectedWordDrag = throttle( - ( - event: MouseEvent | TouchEvent | PointerEvent, - info: PanInfo, - word: WordKey, - currentSelectedElements: typeof selectedElements - ) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { [word.key]: _currentElement, ...otherElements } = currentSelectedElements - const closestElement = Object.values(otherElements).reduce( - (p, c, i) => { - // Distance - let returnedObject - - if (c) { - const rect = c.getBoundingClientRect() - const distance = Math.hypot(rect.x - info.point.x, rect.y - info.point.y) - - if (p.distance === 0) { - returnedObject = { - wordKey: Object.keys(otherElements)[i], - element: c, - distance: distance - } - } else if (distance < p.distance) { - returnedObject = { - wordKey: Object.keys(otherElements)[i], - element: c, - distance: distance - } - } else { - returnedObject = p - } - } else { - returnedObject = p - } - - return returnedObject - }, - { - wordKey: '', - element: null as Element | null, - distance: 0 - } - ) - - setClosestWordKey(closestElement.wordKey) - }, - 300 - ) - - const handleSelectedWordDragEnd = (word: WordKey, newNeighbourWordKey: string) => { - // Find neighbour index - if (closestWordKey) { - const currentIndex = selectedWords.findIndex((w) => w.key === word.key) - let newIndex = selectedWords.findIndex((w) => w.key === newNeighbourWordKey) - if (currentIndex < newIndex) { - newIndex -= 1 + const handleOptionClick = (selectedWord: string) => { + const correctWord = mnemonicWords[currentWordIndex] + if (selectedWord === correctWord) { + if (currentWordIndex + 1 < mnemonicWords.length) { + setCurrentWordIndex(currentWordIndex + 1) + setOptions(generateOptions(mnemonicWords, currentWordIndex + 1)) + setIsError(false) + } else { + sendAnalytics({ event: 'Creating wallet: Verifying words: Completed' }) + onButtonNext() } - - const filteredWords = selectedWords.filter((w) => w.key !== word.key) - setSelectedWords([...filteredWords.slice(0, newIndex), word, ...filteredWords.slice(newIndex)]) - setClosestWordKey('') + } else { + setIsError(true) } } - // === Renders - - const renderRemainingWords = () => - wordList - .filter((w) => !selectedWords?.includes(w)) - .map((w) => ( - setSelectedWords([...selectedWords, w])} key={w.key} layoutId={w.key}> - {w.word} - - )) - - const renderSelectedWords = () => - selectedWords?.map((w) => ( - handleSelectedWordRemove(w)} - key={w.key} - layoutId={w.key} - drag - ref={(element) => { - if (selectedElements && element) selectedElements[w.key] = element - }} - onDragStart={() => setIsDragging(true)} - onDrag={(e, info) => handleSelectedWordDrag(e, info, w, selectedElements)} - onDragEnd={() => handleSelectedWordDragEnd(w, closestWordKey)} - > - {isDragging && closestWordKey === w.key && } - {w.word} - - )) - - const handleNextButtonPress = () => { - sendAnalytics({ event: 'Creating wallet: Verifying words: Clicked next' }) - cleanup() - onButtonNext() - } - const handleBackButtonPress = () => { sendAnalytics({ event: 'Creating wallet: Verifying words: Clicked back' }) - cleanup() onButtonBack() } - const cleanup = () => { - setWordList([]) - setSelectedElements({}) - setSelectedWords([]) - } - return ( - - - {t('Security Check')} - + -
    - {t('Select the words in the right order.')} - - {renderSelectedWords()} - - {renderRemainingWords()} +
    + + }} + /> + + {isError && {t('Incorrect word. Please try again.')}} + + {options.map((option, index) => ( + handleOptionClick(option)} squared> + {option} + + ))} +
    - {selectedWords.length === wordList.length ? ( - !isValid ? ( - - ) : ( - - ) - ) : null} - {selectedWords.length === wordList.length && ( - - - - - )} + + + ) } -const getAlphabeticallyOrderedList = (arr: string[]) => arr.slice().sort() +const generateOptions = (words: string[], index: number): string[] => { + const correctWord = words[index] + const otherWords = words.filter((w, i) => i !== index) + const randomWords = shuffle(otherWords).slice(0, 2) + return shuffle([correctWord, ...randomWords]) +} export default CheckWordsPage -const RemainingWordList = styled.div` +const OptionsContainer = styled.div` display: flex; flex-wrap: wrap; margin: var(--spacing-4) 0; - flex: 1; - align-items: flex-start; - justify-content: flex-start; - align-content: flex-start; -` - -const SelectedWord = styled(motion.button)` - padding: 6px var(--spacing-2); - border-radius: 5px; - background-color: ${({ theme }) => theme.global.accent}; - color: ${({ theme }) => theme.font.contrastPrimary}; - font-weight: var(--fontWeight-medium); - text-align: center; - margin-bottom: var(--spacing-2); - position: relative; - cursor: pointer; - - &:not(:last-child) { - margin-right: var(--spacing-2); - } - - &:hover { - background-color: ${({ theme }) => colord(theme.global.accent).alpha(0.8).toRgbString()}; - } - - &:focus-visible { - box-shadow: 0 0 0 3px ${({ theme }) => colord(theme.global.accent).darken(20).toRgbString()}; - } + justify-content: center; ` -const DragCursor = styled(motion.div)` - position: absolute; - left: -7px; - top: 0; - bottom: 0; - width: 4px; - background-color: ${({ theme }) => theme.global.accent}; +const OptionButton = styled(Button)` + margin: var(--spacing-2); + font-family: monospace; ` -const SelectedWordList = styled(Box)` - width: 100%; - padding: var(--spacing-4); - min-height: 200px; - margin-bottom: var(--spacing-4); - - display: flex; - flex-wrap: wrap; - align-items: flex-start; - justify-content: flex-start; - align-content: flex-start; - - &.valid { - ${SelectedWord} { - background-color: ${({ theme }) => theme.global.valid}; - } - } - - &.error { - ${SelectedWord} { - background-color: ${({ theme }) => theme.global.alert}; - } - } -` - -const RemainingWord = styled(SelectedWord)` - background-color: ${({ theme }) => theme.global.accent}; - background-color: ${({ theme }) => colord(theme.global.accent).alpha(0.1).toRgbString()}; - color: ${({ theme }) => theme.global.accent}; - - &:hover { - background-color: ${({ theme }) => colord(theme.global.accent).alpha(0.3).toRgbString()}; - } +const ErrorText = styled.p` + color: ${({ theme }) => theme.global.alert}; + margin-top: var(--spacing-2); ` diff --git a/apps/desktop-wallet/src/pages/NewWallet/CreateWalletPage.tsx b/apps/desktop-wallet/src/pages/NewWallet/CreateWalletPage.tsx index 36bc0a5540..a8edfcf765 100644 --- a/apps/desktop-wallet/src/pages/NewWallet/CreateWalletPage.tsx +++ b/apps/desktop-wallet/src/pages/NewWallet/CreateWalletPage.tsx @@ -1,21 +1,3 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { encryptMnemonic } from '@alephium/keyring' import { getHumanReadableError } from '@alephium/shared' import { AlertCircle } from 'lucide-react' @@ -147,6 +129,7 @@ const CreateWalletPage = ({ isRestoring = false }: { isRestoring?: boolean }) => onChange={(e) => onUpdateWalletName(e.target.value)} error={walletNameError} isValid={walletName.length > 0 && walletNameError.length === 0} + heightSize="big" /> onChange={(e) => onUpdatePassword(e.target.value)} error={passwordError} isValid={!passwordError && password.length > 0} + heightSize="big" /> error={passwordCheck && password !== passwordCheck ? t('Passwords are different') : ''} isValid={password.length > 0 && password === passwordCheck} disabled={!password || passwordError.length > 0} + heightSize="big" />
    - - diff --git a/apps/desktop-wallet/src/pages/NewWallet/ImportWordsPage.tsx b/apps/desktop-wallet/src/pages/NewWallet/ImportWordsPage.tsx index 7a17657546..001ecae959 100644 --- a/apps/desktop-wallet/src/pages/NewWallet/ImportWordsPage.tsx +++ b/apps/desktop-wallet/src/pages/NewWallet/ImportWordsPage.tsx @@ -1,34 +1,16 @@ -/* -Copyright 2018 - 2024 The Alephium Authors -This file is part of the alephium project. - -The library is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with the library. If not, see . -*/ - import { keyring } from '@alephium/keyring' import { bip39Words } from '@alephium/shared' -import Tagify, { BaseTagData, ChangeEventData, TagData } from '@yaireo/tagify' -import { useEffect, useRef, useState } from 'react' +import { colord } from 'colord' +import { AnimatePresence, motion } from 'framer-motion' +import { useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import styled from 'styled-components' import Button from '@/components/Button' -import TextAreaTags from '@/components/Inputs/TextAreaTags' import { FloatingPanel, FooterActionsContainer, - PanelContentContainer, - Section + PanelContentContainer } from '@/components/PageComponents/PageContainers' import PanelTitle from '@/components/PageComponents/PanelTitle' import Paragraph from '@/components/Paragraph' @@ -42,74 +24,220 @@ const ImportWordsPage = () => { const { setMnemonic } = useWalletContext() const { sendAnalytics } = useAnalytics() - const [phrase, setPhrase] = useState<{ value: string }[]>([]) + const [phrase, setPhrase] = useState('') + const [error, setError] = useState(null) const allowedWords = useRef(bip39Words) - const defaultPlaceholder = t('Type your recovery phrase') - const [customPlaceholder, setCustomPlaceholder] = useState(defaultPlaceholder) - const tagifyRef = useRef | undefined>() - - // Alephium's node code uses 12 as the minimal mnemomic length. - const isPhraseLongEnough = phrase.length >= 12 - - const handlePhraseChange = (event: CustomEvent>) => { - // Split words where spaces are - const newPhrase = event.detail.value && JSON.parse(event.detail.value) - setPhrase(newPhrase || []) - setCustomPlaceholder( - newPhrase.length > 0 ? t('{{ amount }} words entered', { amount: newPhrase.length }) : defaultPlaceholder - ) + + // Alephium's node code uses 12 as the minimal mnemonic length. + const isPhraseLongEnough = phrase.trim().split(/\s+/).length >= 12 + + const handlePhraseChange = (e: React.ChangeEvent) => { + const sanitizedValue = sanitizePhrase(e.target.value) + setPhrase(sanitizedValue) + setError(null) // Reset error when typing } - useEffect(() => { - if (tagifyRef.current) { - tagifyRef.current.DOM.input.setAttribute('data-placeholder', customPlaceholder) + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault() + if (phrase.trim().length > 0 && !phrase.endsWith(' ')) { + setPhrase((prev) => `${prev} `) // Add a space only if not already at the end + } + } + + // Allow only letters and spaces + const isLetter = /^[a-zA-Z]$/ + if (!isLetter.test(e.key) && e.key !== ' ' && e.key !== 'Backspace') { + e.preventDefault() + } + + // Prevent multiple consecutive spaces + if (e.key === ' ' && phrase.endsWith(' ')) { + e.preventDefault() } - }, [customPlaceholder]) + } const handleNextButtonPress = () => { - if (!isPhraseLongEnough) return + const words = phrase.trim().split(/\s+/) + const invalidWords = words.filter((word) => !allowedWords.current.includes(word)) + + if (invalidWords.length > 0) { + setError(t('Invalid mnemonic. Please double check your words.')) + return + } + + if (words.length < 12) return sendAnalytics({ event: 'Importing wallet: Entering words: Clicked next' }) try { - setMnemonic(keyring.importMnemonicString(phrase.map((word) => word.value).join(' '))) + setMnemonic(keyring.importMnemonicString(words.join(' '))) onButtonNext() } catch (error) { + setError(t('Invalid mnemonic. Please double check your words.')) sendAnalytics({ type: 'error', error, message: 'Could not import mnemonic string', isSensitive: true }) } finally { - setPhrase([]) + setPhrase('') } } + const words = phrase.length > 0 && phrase[0] !== '' ? phrase.trim().split(/\s+/) : [] + const firstColumnWords = words.slice(0, 12) + const secondColumnWords = words.slice(12, 24) + return ( - - {t('Secret recovery phrase')} - -
    - -
    - - {!isPhraseLongEnough - ? t("Make sure to store the words in a secure location! They are your wallet's secret recovery phrase.") - : t("All good? Let's continue!")} - -
    - - - - -
    + + + {t('Secret recovery phrase')} + + + {t("Make sure to store the words in a secure location! They are your wallet's secret recovery phrase.")} + + +