Skip to content

Commit

Permalink
Merge pull request #1499 from zeitgeistpm/tm-settings-modal
Browse files Browse the repository at this point in the history
Settings modal
  • Loading branch information
TvrtkoM authored Jul 19, 2023
2 parents b461928 + 8cccac3 commit c656428
Show file tree
Hide file tree
Showing 7 changed files with 415 additions and 330 deletions.
29 changes: 19 additions & 10 deletions components/account/AccountButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
DesktopOnboardingModal,
MobileOnboardingModal,
} from "./OnboardingModal";
import SettingsModal from "components/settings/SettingsModal";
import CopyIcon from "../ui/CopyIcon";

const BalanceRow = ({
Expand Down Expand Up @@ -99,6 +100,7 @@ const AccountButton: FC<{
const [hovering, setHovering] = useState<boolean>(false);
const [showOnboarding, setShowOnboarding] = useState(false);
const [showGetZtgModal, setShowGetZtgModal] = useState(false);
const [showSettingsModal, setShowSettingsModal] = useState(false);

const { data: activeBalance } = useZtgBalance(activeAccount?.address);
const { data: polkadotBalance } = useBalance(activeAccount?.address, {
Expand Down Expand Up @@ -381,16 +383,17 @@ const AccountButton: FC<{
</Menu.Item>
<Menu.Item>
{({ active }) => (
<Link href="/settings">
<div className="flex items-center px-6 mb-3 hover:bg-slate-100">
<Settings />
<button
className={`group flex w-full items-center rounded-md px-2 py-2 text-sm font-semibold`}
>
Settings
</button>
</div>
</Link>
<div
className="flex items-center px-6 mb-3 cursor-pointer hover:bg-slate-100"
onClick={() => setShowSettingsModal(true)}
>
<Settings />
<button
className={`group flex w-full items-center rounded-md px-2 py-2 text-sm font-semibold`}
>
Settings
</button>
</div>
)}
</Menu.Item>
<Menu.Item>
Expand All @@ -417,6 +420,12 @@ const AccountButton: FC<{
</Menu>
</div>
)}
<SettingsModal
open={showSettingsModal}
onClose={() => {
setShowSettingsModal(false);
}}
/>
{isMobileDevice ? (
<Modal open={showOnboarding} onClose={() => setShowOnboarding(false)}>
<MobileOnboardingModal />
Expand Down
173 changes: 173 additions & 0 deletions components/settings/AccountSettingsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import React from "react";
import { useForm } from "react-hook-form";
import { isRpcSdk } from "@zeitgeistpm/sdk-next";
import FormTransactionButton from "components/ui/FormTransactionButton";
import { identityRootKey } from "lib/hooks/queries/useIdentity";
import { useExtrinsic } from "lib/hooks/useExtrinsic";
import { useSdkv2 } from "lib/hooks/useSdkv2";
import { queryClient } from "lib/query-client";
import { useNotifications } from "lib/state/notifications";
import { useWallet } from "lib/state/wallet";
import { UserIdentity } from "lib/types/user-identity";
import { useChainConstants } from "lib/hooks/queries/useChainConstants";

export type AcccountSettingsFormProps = {
identity: UserIdentity;
};

const AcccountSettingsForm: React.FC<AcccountSettingsFormProps> = ({
identity,
}) => {
const {
register,
reset,
formState: { isValid, errors, isDirty },
watch,
} = useForm<{
displayName: string;
discord: string;
twitter: string;
}>({
defaultValues: {
displayName: identity.displayName ?? "",
discord: identity.discord ?? "",
twitter: identity.twitter ?? "",
},
mode: "all",
reValidateMode: "onChange",
});

const wallet = useWallet();
const address = wallet.activeAccount?.address;
const [sdk, id] = useSdkv2();

const notificationStore = useNotifications();

const discordHandle = watch("discord");
const twitterHandle = watch("twitter");
const displayName = watch("displayName");

const { data: constants } = useChainConstants();

const indetityCost =
constants?.identity?.basicDeposit ??
0 + (constants?.identity?.fieldDeposit ?? 0);

const isCleared =
!identity?.displayName && !identity?.discord && !identity?.twitter;

const { send: updateIdentity, isLoading: isUpdating } = useExtrinsic(
() => {
if (isRpcSdk(sdk)) {
return sdk.api.tx.identity.setIdentity({
additional: [[{ Raw: "discord" }, { Raw: discordHandle }]],
display: { Raw: displayName },
twitter: { Raw: twitterHandle },
});
}
},
{
onSuccess: () => {
queryClient.invalidateQueries([id, identityRootKey, address]);
notificationStore.pushNotification("Successfully set Identity", {
type: "Success",
});
reset({ displayName, discord: discordHandle, twitter: twitterHandle });
},
},
);

const { send: clearIdentity, isLoading: isClearing } = useExtrinsic(
() => {
if (isRpcSdk(sdk)) {
return sdk.api.tx.identity.clearIdentity();
}
},
{
onSuccess: () => {
queryClient.invalidateQueries([id, identityRootKey, address]);
notificationStore.pushNotification("Successfully cleared Identity", {
type: "Success",
});
reset({
displayName: "",
discord: "",
twitter: "",
});
},
},
);
return (
<form
className="flex flex-col"
onSubmit={(e) => {
e.preventDefault();
if (!isValid) return;
updateIdentity();
}}
>
<label htmlFor="displayName" className="font-bold mb-2">
Display Name
</label>
<input
type="text"
id="displayName"
{...register("displayName", { required: true })}
className={
"rounded-md items-center h-14 px-3 outline-none border-1 border-transparent bg-anti-flash-white " +
(errors?.displayName ? "border-vermilion" : "")
}
/>

<label htmlFor="discord" className="font-bold mt-5 mb-2">
Discord
</label>

<input
type="text"
id="discord"
{...register("discord", { required: true })}
className={
"rounded-md items-center h-14 px-3 outline-none border-1 border-transparent bg-anti-flash-white " +
(errors?.discord ? "border-vermilion" : "")
}
/>

<label htmlFor="twitter" className="font-bold mt-5 mb-2">
Twitter
</label>

<input
type="text"
id="twitter"
{...register("twitter", { required: true })}
className={
"rounded-md items-center h-14 px-3 outline-none border-1 border-transparent bg-anti-flash-white mb-5 " +
(errors?.twitter ? "border-vermilion" : "")
}
/>

<div className="rounded-lg p-5 mb-5 bg-provincial-pink text-sm">
Setting an identity requires a deposit of up to {indetityCost}{" "}
{constants?.tokenSymbol}. This deposit can be retrieved by clearing your
identity.
</div>

<FormTransactionButton
disabled={!isDirty || !isValid || isUpdating || isClearing}
>
Set Identity
</FormTransactionButton>
<button
type="button"
className="mt-2 text-sm text-sky-600 focus:outline-none"
disabled={isUpdating || isClearing || isCleared}
onClick={() => clearIdentity()}
>
Clear Identity
</button>
</form>
);
};

export default AcccountSettingsForm;
120 changes: 120 additions & 0 deletions components/settings/OtherSettingsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import AddressInput, { AddressOption } from "components/ui/AddressInput";
import FormTransactionButton from "components/ui/FormTransactionButton";
import { isRpcSdk } from "@zeitgeistpm/sdk-next";
import { useSdkv2 } from "lib/hooks/useSdkv2";
import { useWallet } from "lib/state/wallet";
import { isValidPolkadotAddress } from "lib/util";

export type OtherSettingsFormProps = {};

const OtherSettingsForm: React.FC<OtherSettingsFormProps> = ({}) => {
const [sdk] = useSdkv2();
const wallet = useWallet();

const proxyConfig = wallet.getProxyFor(wallet.activeAccount?.address);

const {
register,
control,
trigger,
handleSubmit,
reset,
formState: { isValid, errors, isDirty },
watch,
} = useForm<{
proxyAddress: AddressOption | null;
enableProxy: boolean;
}>({
defaultValues: {
proxyAddress: proxyConfig
? { label: proxyConfig.address, value: proxyConfig.address }
: null,
enableProxy: proxyConfig?.enabled ?? false,
},
mode: "all",
reValidateMode: "onChange",
});

const proxyEnabled = watch("enableProxy");

useEffect(() => {
trigger("proxyAddress");
}, [proxyEnabled]);

return (
<form
className="flex flex-col"
onSubmit={handleSubmit((data) => {
if (!wallet.activeAccount?.address) {
return;
}
wallet.setProxyFor(wallet.activeAccount.address, {
address: data.proxyAddress?.value ?? "",
enabled: data.enableProxy,
});
reset(data);
})}
>
<label className="font-bold mb-2">Proxy Account</label>
<div className="flex flex-row p-2 mb-2">
<input
type="checkbox"
{...register("enableProxy")}
id="enableProxy"
className="cursor-pointer accent-ztg-blue"
/>
<label className="ml-2 cursor-pointer" htmlFor="enableProxy">
Enable Proxy Execution
</label>
</div>
<Controller
name="proxyAddress"
rules={{
validate: async (v, { enableProxy }) => {
if (!enableProxy) {
return true;
}
if (enableProxy && !v?.value) {
return "Enter an address to proxy";
}
if (v && !isValidPolkadotAddress(v.value)) {
return "Invalid address";
}
if (v && isRpcSdk(sdk) && v.value) {
const proxies = await sdk.api.query.proxy.proxies(v.value);
const proxyMatch = proxies?.[0]?.find((p) => {
return p.delegate.toString() === wallet.activeAccount?.address;
});
if (!Boolean(proxyMatch)) {
return "You are not a proxy for this account.";
}
}
},
deps: ["enableProxy"],
}}
render={({ field: { value, onChange } }) => {
return (
<AddressInput
onChange={onChange}
value={value}
disabled={!proxyEnabled}
error={!isValid ? errors.proxyAddress?.message : undefined}
/>
);
}}
control={control}
/>
<FormTransactionButton
disabled={!isValid || !isDirty}
className="mt-5"
disableFeeCheck={true}
>
Save
</FormTransactionButton>
</form>
);
};

export default OtherSettingsForm;
Loading

0 comments on commit c656428

Please sign in to comment.