diff --git a/src/global/ws/WebsocketConnection.ts b/src/global/ws/WebsocketConnection.ts index 636cb26..42a16c4 100644 --- a/src/global/ws/WebsocketConnection.ts +++ b/src/global/ws/WebsocketConnection.ts @@ -7,7 +7,7 @@ import * as api from "@api"; import { KristAddress, KristBlock, KristTransaction, WSConnectionState, WSIncomingMessage, WSSubscriptionLevel } from "@api/types"; -import { Wallet, WalletMap, findWalletByAddress, syncWallet, syncWalletUpdate } from "@wallets"; +import { Wallet, WalletMap, findWalletByAddress, syncWalletUpdate } from "@wallets"; import WebSocketAsPromised from "websocket-as-promised"; import { WSSubscription } from "./WebsocketSubscription"; diff --git a/src/krist/api/names.ts b/src/krist/api/names.ts index ff93b91..223cb2d 100644 --- a/src/krist/api/names.ts +++ b/src/krist/api/names.ts @@ -9,6 +9,8 @@ import Debug from "debug"; const debug = Debug("kristweb:api-names"); +export type ProgressCallback = () => void; + interface PartialName { name: string; owner: string; @@ -26,7 +28,8 @@ export async function transferNames( decryptedAddresses: ValidDecryptedAddresses, names: PartialName[], - recipient: string + recipient: string, + onProgress?: ProgressCallback ): Promise { for (const name of names) { const { privatekey } = decryptedAddresses[name.owner]; @@ -39,13 +42,16 @@ `/names/${encodeURIComponent(name.name)}/transfer`, { address: recipient, privatekey } ).catch(onError); + + onProgress?.(); } } export async function updateNames( decryptedAddresses: ValidDecryptedAddresses, names: PartialName[], - aRecord?: string | null + aRecord?: string | null, + onProgress?: ProgressCallback ): Promise { for (const name of names) { const { privatekey } = decryptedAddresses[name.owner]; @@ -57,5 +63,7 @@ `/names/${encodeURIComponent(name.name)}/update`, { a: aRecord?.trim() || null, privatekey } ).catch(onError); + + onProgress?.(); } } diff --git a/src/pages/names/mgmt/EditProgress.tsx b/src/pages/names/mgmt/EditProgress.tsx new file mode 100644 index 0000000..a8e4d5f --- /dev/null +++ b/src/pages/names/mgmt/EditProgress.tsx @@ -0,0 +1,55 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { useState } from "react"; +import { Progress } from "antd"; + +import { Trans } from "react-i18next"; +import { TFns } from "@utils/i18n"; + +interface EditProgressHookResponse { + progressBar: JSX.Element; + onProgress: () => void; + initProgress: (total: number) => void; + resetProgress: () => void; +} + +export function useEditProgress( + { t, tKey }: TFns +): EditProgressHookResponse { + const [submitProgress, setSubmitProgress] = useState(0); + const [submitTotal, setSubmitTotal] = useState(1); + + // Increment the progress bar when one of the names has been edited + const onProgress = () => setSubmitProgress(c => c + 1); + + function initProgress(total: number) { + setSubmitProgress(0); + setSubmitTotal(total); + } + + function resetProgress() { + setSubmitProgress(0); + setSubmitTotal(1); + } + + const progressBar = <> + {/* Submitting text */} +
+ + Editing {{ count: submitTotal }} names... + +
+ + {/* Progress bar */} + + ; + + return { progressBar, onProgress, initProgress, resetProgress }; +} diff --git a/src/pages/names/mgmt/NameEditModal.tsx b/src/pages/names/mgmt/NameEditModal.tsx index f8d3c09..aca997e 100644 --- a/src/pages/names/mgmt/NameEditModal.tsx +++ b/src/pages/names/mgmt/NameEditModal.tsx @@ -2,7 +2,7 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { useState, Dispatch, SetStateAction } from "react"; -import { Modal, Form, notification } from "antd"; +import { Modal, notification } from "antd"; import { useTFns } from "@utils/i18n"; @@ -20,6 +20,7 @@ import { handleError } from "./handleErrors"; import { useNameEditForm } from "./NameEditForm"; +import { useEditProgress } from "./EditProgress"; import { showConfirmModal } from "./ConfirmModal"; import { SuccessNotifContent } from "./SuccessNotifContent"; @@ -66,6 +67,9 @@ // Create the form. This is usually not rendered during submission. const { form, formInstance, resetFields } = useNameEditForm({ name, aRecord, mode, submitting, onSubmit, tFns }); + // Progress bar for bulk edits + const { progressBar, onProgress, initProgress, resetProgress } + = useEditProgress(tFns); // Wrap the handleError function const onError = handleError.bind( @@ -107,10 +111,10 @@ if (mode === "transfer") { // Transfer the names - await transferNames(finalAddresses, finalNames, recipient!); + await transferNames(finalAddresses, finalNames, recipient!, onProgress); } else if (mode === "update") { // Update the names - await updateNames(finalAddresses, finalNames, aRecord!); + await updateNames(finalAddresses, finalNames, aRecord!, onProgress); } // Success! Show notification and close modal @@ -171,6 +175,7 @@ // Don't return this promise, so the confirm modal closes immediately const triggerSubmit = () => { + initProgress(count); handleSubmit(filteredNames, recipient, aRecord) .catch(onError) .finally(() => setSubmitting(false)); @@ -194,6 +199,7 @@ if (submitting) return; setVisible(false); resetFields(); + resetProgress(); } return <> @@ -213,7 +219,7 @@ {/* Only render the form if not submitting */} {!submitting && form} - {/* TODO: Display a progress bar here */} + {submitting && progressBar} {/* Give the modals somewhere to find the context from. This is done