-
Notifications
You must be signed in to change notification settings - Fork 6
Added change password section #563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import { CONFIG, getAccountFullQueryOptions, Keys } from "@ecency/sdk"; | ||
| import { deriveHiveKeys, detectHiveKeyDerivation } from "@ecency/wallets"; | ||
| import { AuthorityType, PrivateKey } from "@hiveio/dhive"; | ||
| import { useMutation, UseMutationOptions, useQuery } from "@tanstack/react-query"; | ||
| import * as R from "remeda"; | ||
|
|
||
| interface Payload { | ||
| newKeys: Record<string, PrivateKey>; | ||
| currentPassword: string; | ||
| } | ||
|
|
||
| type ChangePasswordOptions = Pick< | ||
| UseMutationOptions<unknown, Error, Payload>, | ||
| "onSuccess" | "onError" | ||
| >; | ||
|
|
||
| export function useAccountChangePassword(username: string, options?: ChangePasswordOptions) { | ||
| const { data: accountData } = useQuery(getAccountFullQueryOptions(username)); | ||
|
|
||
| return useMutation({ | ||
| mutationKey: ["accounts", "revoke-key", accountData?.name], | ||
| mutationFn: async ({ currentPassword, newKeys }: Payload) => { | ||
| if (!accountData) { | ||
| throw new Error("[SDK][Update password] – cannot update keys for anon user"); | ||
| } | ||
|
|
||
| const currentDerivation = await detectHiveKeyDerivation(username, currentPassword, "owner"); | ||
|
|
||
| let currentKeys: Record<string, string> = {}; | ||
| let currentKey: PrivateKey; | ||
|
|
||
| if (currentDerivation === "bip44") { | ||
| const keys = await deriveHiveKeys(currentPassword); | ||
| currentKeys = { | ||
| owner: keys.ownerPubkey, | ||
| active: keys.activePubkey, | ||
| posting: keys.postingPubkey, | ||
| memo: keys.memoPubkey | ||
| }; | ||
| currentKey = PrivateKey.fromString(keys.owner); | ||
| } else if (currentDerivation === "master-password") { | ||
| currentKeys = { | ||
| owner: PrivateKey.fromLogin(username, currentPassword, "owner").createPublic().toString(), | ||
| active: PrivateKey.fromLogin(username, currentPassword, "active") | ||
| .createPublic() | ||
| .toString(), | ||
| posting: PrivateKey.fromLogin(username, currentPassword, "posting") | ||
| .createPublic() | ||
| .toString(), | ||
| memo: PrivateKey.fromLogin(username, currentPassword, "memo").createPublic().toString() | ||
| }; | ||
| currentKey = PrivateKey.fromLogin(username, currentPassword, "owner"); | ||
| } else { | ||
| currentKeys = { | ||
| owner: PrivateKey.fromString(currentPassword).createPublic().toString() | ||
| }; | ||
| currentKey = PrivateKey.fromString(currentPassword); | ||
| } | ||
|
|
||
| const prepareAuth = (keyName: keyof Keys) => { | ||
| const auth: AuthorityType = R.clone(accountData[keyName]); | ||
|
|
||
| auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]); | ||
| auth.key_auths.filter(([key]) => currentKeys[keyName] === key); | ||
|
|
||
| return auth; | ||
| }; | ||
|
|
||
| return CONFIG.hiveClient.broadcast.updateAccount( | ||
| { | ||
| account: accountData.name, | ||
| json_metadata: accountData.json_metadata, | ||
| owner: prepareAuth("owner"), | ||
| active: prepareAuth("active"), | ||
| posting: prepareAuth("posting"), | ||
| memo_key: accountData.memo_key | ||
| }, | ||
| currentKey | ||
| ); | ||
|
Comment on lines
+69
to
+79
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The mutation updates Proposed fix if memo_key should also be updated return CONFIG.hiveClient.broadcast.updateAccount(
{
account: accountData.name,
json_metadata: accountData.json_metadata,
owner: prepareAuth("owner"),
active: prepareAuth("active"),
posting: prepareAuth("posting"),
- memo_key: accountData.memo_key
+ memo_key: newKeys.memo_key.createPublic().toString()
},
currentKey
);🤖 Prompt for AI Agents |
||
| }, | ||
| ...options | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||||||||||||||||||||
| import { useAccountChangePassword } from "@/api/mutations"; | ||||||||||||||||||||||||||
| import { useClientActiveUser } from "@/api/queries"; | ||||||||||||||||||||||||||
| import { error, success } from "@/features/shared"; | ||||||||||||||||||||||||||
| import { Button, KeyInput, KeyInputImperativeHandle } from "@/features/ui"; | ||||||||||||||||||||||||||
| import { WalletSeedPhrase } from "@/features/wallet"; | ||||||||||||||||||||||||||
| import { getAccountFullQueryOptions } from "@ecency/sdk"; | ||||||||||||||||||||||||||
| import { useHiveKeysQuery } from "@ecency/wallets"; | ||||||||||||||||||||||||||
| import { PrivateKey } from "@hiveio/dhive"; | ||||||||||||||||||||||||||
| import { useQuery } from "@tanstack/react-query"; | ||||||||||||||||||||||||||
| import i18next from "i18next"; | ||||||||||||||||||||||||||
| import { useCallback, useRef, useState } from "react"; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export function ManageKeyChangePassword() { | ||||||||||||||||||||||||||
| const activeUser = useClientActiveUser(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const keyInputRef = useRef<KeyInputImperativeHandle>(null); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const [showSeed, setShowSeed] = useState(false); | ||||||||||||||||||||||||||
| const [currentKey, setCurrentKey] = useState<PrivateKey>(); | ||||||||||||||||||||||||||
| const [rawKey, setRawKey] = useState<string>(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const { data: accountOwnerPublicKeys } = useQuery({ | ||||||||||||||||||||||||||
| ...getAccountFullQueryOptions(activeUser?.username!), | ||||||||||||||||||||||||||
| select: (resp) => resp.owner.key_auths as [string, number][] | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| const { data: keys } = useHiveKeysQuery(activeUser?.username!); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const { mutateAsync: changePassword } = useAccountChangePassword(activeUser?.username!); | ||||||||||||||||||||||||||
|
Comment on lines
+22
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-null assertions on potentially undefined If Suggested approach+ if (!activeUser?.username) {
+ return null; // or a loading/login prompt
+ }
+
const { data: accountOwnerPublicKeys } = useQuery({
- ...getAccountFullQueryOptions(activeUser?.username!),
+ ...getAccountFullQueryOptions(activeUser.username),
select: (resp) => resp.owner.key_auths as [string, number][]
});
- const { data: keys } = useHiveKeysQuery(activeUser?.username!);
+ const { data: keys } = useHiveKeysQuery(activeUser.username);
- const { mutateAsync: changePassword } = useAccountChangePassword(activeUser?.username!);
+ const { mutateAsync: changePassword } = useAccountChangePassword(activeUser.username);🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const handleGenerateNewPassword = useCallback(async () => { | ||||||||||||||||||||||||||
| const { privateKey, raw } = await keyInputRef.current!.handleSign(); | ||||||||||||||||||||||||||
| const publicKey = privateKey.createPublic().toString(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (accountOwnerPublicKeys?.find(([key]) => key === publicKey)) { | ||||||||||||||||||||||||||
| setCurrentKey(privateKey); | ||||||||||||||||||||||||||
| setRawKey(raw); | ||||||||||||||||||||||||||
| setShowSeed(true); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| error(i18next.t("permissions.change-password.invalid-key")); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, [accountOwnerPublicKeys]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const handleChangePassword = useCallback(async () => { | ||||||||||||||||||||||||||
| if (keys && currentKey) { | ||||||||||||||||||||||||||
| await changePassword({ | ||||||||||||||||||||||||||
| newKeys: { | ||||||||||||||||||||||||||
| owner: PrivateKey.fromString(keys.owner), | ||||||||||||||||||||||||||
| active: PrivateKey.fromString(keys.active), | ||||||||||||||||||||||||||
| posting: PrivateKey.fromString(keys.posting), | ||||||||||||||||||||||||||
| memo_key: PrivateKey.fromString(keys.memo) | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
|
Comment on lines
+46
to
+51
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Key name mismatch: The mutation's Proposed fix newKeys: {
owner: PrivateKey.fromString(keys.owner),
active: PrivateKey.fromString(keys.active),
posting: PrivateKey.fromString(keys.posting),
- memo_key: PrivateKey.fromString(keys.memo)
+ memo: PrivateKey.fromString(keys.memo)
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| currentPassword: rawKey! | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| setShowSeed(false); | ||||||||||||||||||||||||||
| setCurrentKey(undefined); | ||||||||||||||||||||||||||
| success(i18next.t("permissions.change-password.success")); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }, [keys, currentKey, activeUser, rawKey]); | ||||||||||||||||||||||||||
|
Comment on lines
+43
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handling for If the mutation fails, the user receives no feedback. Consider wrapping in try/catch or using the mutation's error state. Suggested approach const handleChangePassword = useCallback(async () => {
if (keys && currentKey) {
+ try {
await changePassword({
newKeys: {
owner: PrivateKey.fromString(keys.owner),
active: PrivateKey.fromString(keys.active),
posting: PrivateKey.fromString(keys.posting),
- memo_key: PrivateKey.fromString(keys.memo)
+ memo: PrivateKey.fromString(keys.memo)
},
currentPassword: rawKey!
});
setShowSeed(false);
setCurrentKey(undefined);
success(i18next.t("permissions.change-password.success"));
+ } catch (e) {
+ error(i18next.t("permissions.change-password.error"));
+ }
}
}, [keys, currentKey, activeUser, rawKey]);🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||
| <div className="w-full pt-4"> | ||||||||||||||||||||||||||
| {!showSeed && ( | ||||||||||||||||||||||||||
| <div className="flex flex-col items-start gap-4 w-full"> | ||||||||||||||||||||||||||
| <KeyInput | ||||||||||||||||||||||||||
| keyType="owner" | ||||||||||||||||||||||||||
| placeholder="Current key" | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded English string should use i18n. The placeholder <KeyInput
keyType="owner"
- placeholder="Current key"
+ placeholder={i18next.t("permissions.change-password.current-key-placeholder")}
className="w-full"
ref={keyInputRef}
/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| className="w-full" | ||||||||||||||||||||||||||
| ref={keyInputRef} | ||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||
| <Button onClick={() => handleGenerateNewPassword()} size="sm"> | ||||||||||||||||||||||||||
| {i18next.t("permissions.change-password.generate")} | ||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||
| {showSeed && ( | ||||||||||||||||||||||||||
| <WalletSeedPhrase | ||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||
| showTitle={false} | ||||||||||||||||||||||||||
| username={activeUser?.username!} | ||||||||||||||||||||||||||
| onValidated={() => handleChangePassword()} | ||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,43 +6,81 @@ import { Button, Modal, ModalBody, ModalHeader } from "@/features/ui"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ManageKeysAddKeys } from "./manage-keys-add-keys"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { UilPlus } from "@tooni/iconscout-unicons-react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import clsx from "clsx"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ManageKeyChangePassword } from "./manage-keys-change-password"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function ManageKeys() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [tab, setTab] = useState<"password" | "key">("password"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [showChangePassword, setShowChangePassword] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="grid grid-cols-1 md:grid-cols-2 gap-2 lg:gap-4 rounded-xl p-4 bg-white/80 dark:bg-dark-200/90 text-gray-900 dark:text-white pb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="md:col-span-2 flex justify-between gap-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="text-sm md:text-lg font-bold pb-1"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {i18next.t("permissions.keys.title")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="flex gap-1 mb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={clsx( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "cursor-pointer text-sm font-semibold rounded-full px-2 py-1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab === "password" && "bg-gray-100 dark:bg-gray-100/10 " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setTab("password")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {i18next.t("permissions.change-password.title")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={clsx( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "cursor-pointer text-sm font-semibold rounded-full px-2 py-1", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab === "key" && "bg-gray-100 dark:bg-gray-100/10 " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setTab("key")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {i18next.t("permissions.keys.title")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+21
to
+38
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing keyboard accessibility for tab navigation. The tab elements are clickable divs without keyboard support. Users navigating via keyboard cannot activate these tabs. Add keyboard accessibility <div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "password" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("password")}
+ role="tab"
+ tabIndex={0}
+ onKeyDown={(e) => e.key === "Enter" && setTab("password")}
+ aria-selected={tab === "password"}
>Apply similarly to the "key" tab button. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="text-sm opacity-75">{i18next.t("permissions.keys.hint")}</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="text-sm opacity-75" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __html: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tab === "password" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? i18next.t("permissions.change-password.hint") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : i18next.t("permissions.keys.hint") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| appearance="gray" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="whitespace-nowrap" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon={<UilPlus />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setShowChangePassword(true)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {i18next.t("permissions.keys.add-key")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {tab === "key" && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| appearance="gray" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="whitespace-nowrap" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon={<UilPlus />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setShowChangePassword(true)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {i18next.t("permissions.keys.add-key")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="owner" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="active" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="posting" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="memo" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {tab === "password" && <ManageKeyChangePassword />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {tab === "key" && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="owner" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="active" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="posting" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKey keyName="memo" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal show={showChangePassword} onHide={() => setShowChangePassword(false)} centered={true}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ModalHeader closeButton={true}>{i18next.t("password-update.title")}</ModalHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ModalBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKeysAddKeys onSuccess={() => setShowChangePassword(false)} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ModalBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| show={showChangePassword} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onHide={() => setShowChangePassword(false)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| centered={true} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ModalHeader closeButton={true}>{i18next.t("password-update.title")}</ModalHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ModalBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ManageKeysAddKeys onSuccess={() => setShowChangePassword(false)} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </ModalBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical bug:
filter()return value is discarded, old keys are never removed.Array.prototype.filter()returns a new array and does not mutate the original. The current code pushes the new key but never actually removes the old key since the filter result is thrown away.Proposed fix
const prepareAuth = (keyName: keyof Keys) => { const auth: AuthorityType = R.clone(accountData[keyName]); auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]); - auth.key_auths.filter(([key]) => currentKeys[keyName] === key); + auth.key_auths = auth.key_auths.filter(([key]) => key !== currentKeys[keyName]); return auth; };Note: Also corrected the filter logic—you want to exclude the old key (
!==), not keep only the old key (===).📝 Committable suggestion
🤖 Prompt for AI Agents