Skip to content

Commit b06448e

Browse files
committed
Disable fields for non owner accounts
1 parent 45de813 commit b06448e

File tree

6 files changed

+93
-21
lines changed

6 files changed

+93
-21
lines changed

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/ContractEditModulesPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const ContractEditModulesPage: React.FC<
9696
installedModules={installedModules}
9797
refetchModules={() => installedModulesQuery.refetch()}
9898
contract={contract}
99-
ownerAccount={isOwner ? account : undefined}
99+
isOwnerAccount={isOwner}
100100
/>
101101
</div>
102102
);

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/InstalledModulesTable.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { ScrollShadow } from "@/components/ui/ScrollShadow/ScrollShadow";
44
import { Alert, AlertTitle } from "@/components/ui/alert";
55
import { CircleSlash } from "lucide-react";
66
import type { ContractOptions } from "thirdweb";
7-
import type { Account } from "thirdweb/wallets";
87
import { ModuleCard } from "./module-card";
98

109
export const InstalledModulesTable = (props: {
@@ -14,9 +13,9 @@ export const InstalledModulesTable = (props: {
1413
isPending: boolean;
1514
};
1615
refetchModules: () => void;
17-
ownerAccount?: Account;
16+
isOwnerAccount: boolean;
1817
}) => {
19-
const { installedModules, ownerAccount } = props;
18+
const { installedModules, isOwnerAccount } = props;
2019

2120
const sectionTitle = (
2221
<h2 className="mb-3 font-bold text-2xl tracking-tight">
@@ -49,7 +48,7 @@ export const InstalledModulesTable = (props: {
4948
moduleAddress={moduleAddress}
5049
contract={props.contract}
5150
onRemoveModule={props.refetchModules}
52-
ownerAccount={ownerAccount}
51+
isOwnerAccount={isOwnerAccount}
5352
/>
5453
))}
5554
</div>

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/Transferrable.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export function TransferrableModuleUI(
122122
<Switch
123123
{...restField}
124124
checked={field.value}
125+
disabled={!props.isOwnerAccount}
125126
className="!m-0"
126127
onCheckedChange={(v) => {
127128
field.onChange(v);
@@ -167,7 +168,11 @@ export function TransferrableModuleUI(
167168
render={({ field }) => (
168169
<FormItem className="grow">
169170
<FormControl>
170-
<Input placeholder="0x..." {...field} />
171+
<Input
172+
placeholder="0x..."
173+
{...field}
174+
disabled={!props.isOwnerAccount}
175+
/>
171176
</FormControl>
172177
<FormMessage />
173178
</FormItem>
@@ -180,6 +185,7 @@ export function TransferrableModuleUI(
180185
onClick={() => {
181186
formFields.remove(index);
182187
}}
188+
disabled={!props.isOwnerAccount}
183189
>
184190
<Trash2Icon className="size-4" />
185191
</Button>
@@ -206,6 +212,7 @@ export function TransferrableModuleUI(
206212
});
207213
}}
208214
className="gap-2"
215+
disabled={!props.isOwnerAccount}
209216
>
210217
<PlusIcon className="size-3" />
211218
Add Address

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/module-card.stories.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { useMutation } from "@tanstack/react-query";
3-
import { Toaster, toast } from "sonner";
3+
import { Toaster } from "sonner";
44
import { BadgeContainer, mobileViewport } from "stories/utils";
55
import { ThirdwebProvider } from "thirdweb/react";
66
import { ModuleCardUI } from "./module-card";
@@ -32,8 +32,11 @@ function Component() {
3232
mutationFn: async () => {
3333
await new Promise((resolve) => setTimeout(resolve, 1000));
3434
},
35-
onSuccess() {
36-
toast.success("Module removed successfully");
35+
});
36+
37+
const updateMutation = useMutation({
38+
mutationFn: async () => {
39+
await new Promise((resolve) => setTimeout(resolve, 1000));
3740
},
3841
});
3942

@@ -48,18 +51,31 @@ function Component() {
4851
return (
4952
<ThirdwebProvider>
5053
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
51-
<BadgeContainer label="No Update, No Children">
54+
<BadgeContainer label="No Update, No Children, Owner Account">
55+
<ModuleCardUI
56+
contractInfo={contractInfo}
57+
moduleAddress="0x0000000000000000000000000000000000000000"
58+
uninstallButton={{
59+
onClick: () => removeMutation.mutateAsync(),
60+
isPending: removeMutation.isPending,
61+
}}
62+
isOwnerAccount={true}
63+
/>
64+
</BadgeContainer>
65+
66+
<BadgeContainer label="No Update, No Children, Not Owner Account">
5267
<ModuleCardUI
5368
contractInfo={contractInfo}
5469
moduleAddress="0x0000000000000000000000000000000000000000"
5570
uninstallButton={{
5671
onClick: () => removeMutation.mutateAsync(),
5772
isPending: removeMutation.isPending,
5873
}}
74+
isOwnerAccount={false}
5975
/>
6076
</BadgeContainer>
6177

62-
<BadgeContainer label="Update Button (disabled), No Children">
78+
<BadgeContainer label="Update Button (disabled), No Children, Owner">
6379
<ModuleCardUI
6480
contractInfo={contractInfo}
6581
moduleAddress="0x0000000000000000000000000000000000000000"
@@ -69,13 +85,14 @@ function Component() {
6985
}}
7086
updateButton={{
7187
isDisabled: true,
72-
isPending: false,
73-
onClick: () => {},
88+
isPending: updateMutation.isPending,
89+
onClick: () => updateMutation.mutateAsync(),
7490
}}
91+
isOwnerAccount={true}
7592
/>
7693
</BadgeContainer>
7794

78-
<BadgeContainer label="Update Button (enabled), Children">
95+
<BadgeContainer label="Update Button (enabled), Children, Owner">
7996
<ModuleCardUI
8097
contractInfo={contractInfo}
8198
moduleAddress="0x0000000000000000000000000000000000000000"
@@ -85,9 +102,10 @@ function Component() {
85102
}}
86103
updateButton={{
87104
isDisabled: false,
88-
isPending: false,
89-
onClick: () => {},
105+
isPending: updateMutation.isPending,
106+
onClick: () => updateMutation.mutateAsync(),
90107
}}
108+
isOwnerAccount={true}
91109
>
92110
<div className="flex h-36 items-center justify-center rounded-lg bg-muted text-muted-foreground text-sm">
93111
CHILDREN

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/module-card.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ import {
2424
waitForReceipt,
2525
} from "thirdweb";
2626
import { uninstallModuleByProxy } from "thirdweb/modules";
27+
import { useActiveAccount } from "thirdweb/react";
2728
import type { Account } from "thirdweb/wallets";
2829
import { useModuleContractInfo } from "./moduleContractInfo";
2930

3031
export function ModuleCard(props: {
3132
moduleAddress: string;
3233
contract: ContractOptions;
3334
onRemoveModule: () => void;
34-
ownerAccount?: Account;
35+
isOwnerAccount: boolean;
3536
}) {
36-
const { contract, moduleAddress, ownerAccount } = props;
37+
const { contract, moduleAddress } = props;
3738
const [isUninstallModalOpen, setIsUninstallModalOpen] = useState(false);
39+
const account = useActiveAccount();
3840

3941
const contractInfo = useModuleContractInfo(
4042
getContract({
@@ -72,11 +74,12 @@ export function ModuleCard(props: {
7274
});
7375

7476
const handleRemove = async () => {
75-
if (!ownerAccount) {
77+
if (!account) {
78+
toast.error("Wallet is not connected");
7679
return;
7780
}
7881

79-
uninstallMutation.mutate(ownerAccount);
82+
uninstallMutation.mutate(account);
8083
};
8184

8285
if (!contractInfo) {
@@ -86,6 +89,7 @@ export function ModuleCard(props: {
8689
return (
8790
<>
8891
<ModuleCardUI
92+
isOwnerAccount={props.isOwnerAccount}
8993
contractInfo={{
9094
name: contractInfo.name,
9195
description: contractInfo.description,
@@ -166,6 +170,7 @@ export type ModuleCardUIProps = {
166170
publisher?: string;
167171
};
168172
moduleAddress: string;
173+
isOwnerAccount: boolean;
169174
};
170175

171176
export function ModuleCardUI(props: ModuleCardUIProps) {
@@ -256,7 +261,9 @@ export function ModuleCardUI(props: ModuleCardUIProps) {
256261
className="min-w-24 gap-2"
257262
onClick={props.updateButton.onClick}
258263
disabled={
259-
props.updateButton.isDisabled || props.updateButton.isPending
264+
props.updateButton.isDisabled ||
265+
props.updateButton.isPending ||
266+
!props.isOwnerAccount
260267
}
261268
>
262269
{props.updateButton.isPending && <Spinner className="size-4" />}
@@ -269,6 +276,7 @@ export function ModuleCardUI(props: ModuleCardUIProps) {
269276
onClick={props.uninstallButton.onClick}
270277
variant="destructive"
271278
className="min-w-24 gap-2"
279+
disabled={!props.isOwnerAccount}
272280
>
273281
{props.uninstallButton.isPending && <Spinner className="size-4" />}
274282
Uninstall

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/transferrable.stories.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { Checkbox } from "@/components/ui/checkbox";
12
import type { Meta, StoryObj } from "@storybook/react";
23
import { useMutation } from "@tanstack/react-query";
4+
import { useState } from "react";
35
import { Toaster, toast } from "sonner";
46
import { BadgeContainer, mobileViewport } from "stories/utils";
57
import { ThirdwebProvider } from "thirdweb/react";
@@ -33,7 +35,24 @@ export const Mobile: Story = {
3335
const testAddress1 = "0x1F846F6DAE38E1C88D71EAA191760B15f38B7A37";
3436
const testAddress2 = "0x83Dd93fA5D8343094f850f90B3fb90088C1bB425";
3537

38+
export function CheckboxWithText() {
39+
return (
40+
<div className="items-top flex space-x-2">
41+
<Checkbox id="terms1" />
42+
<div className="grid gap-1.5 leading-none">
43+
<label
44+
htmlFor="terms1"
45+
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
46+
>
47+
Is Owner
48+
</label>
49+
</div>
50+
</div>
51+
);
52+
}
53+
3654
function Component() {
55+
const [isOwner, setIsOwner] = useState(true);
3756
async function updateStub(values: TransferrableModuleFormValues) {
3857
console.log("submitting", values);
3958
await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -59,6 +78,22 @@ function Component() {
5978
return (
6079
<ThirdwebProvider>
6180
<div className="container flex max-w-[1150px] flex-col gap-10 py-10">
81+
<div className="items-top flex space-x-2">
82+
<Checkbox
83+
id="terms1"
84+
checked={isOwner}
85+
onCheckedChange={(v) => setIsOwner(!!v)}
86+
/>
87+
<div className="grid gap-1.5 leading-none">
88+
<label
89+
htmlFor="terms1"
90+
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
91+
>
92+
Is Owner
93+
</label>
94+
</div>
95+
</div>
96+
6297
<BadgeContainer label="Empty AllowList, Not Restricted">
6398
<TransferrableModuleUI
6499
contractInfo={contractInfo}
@@ -72,6 +107,7 @@ function Component() {
72107
onClick: () => removeMutation.mutateAsync(),
73108
isPending: removeMutation.isPending,
74109
}}
110+
isOwnerAccount={isOwner}
75111
/>
76112
</BadgeContainer>
77113

@@ -88,6 +124,7 @@ function Component() {
88124
onClick: () => removeMutation.mutateAsync(),
89125
isPending: removeMutation.isPending,
90126
}}
127+
isOwnerAccount={isOwner}
91128
/>
92129
</BadgeContainer>
93130

@@ -104,6 +141,7 @@ function Component() {
104141
onClick: () => removeMutation.mutateAsync(),
105142
isPending: removeMutation.isPending,
106143
}}
144+
isOwnerAccount={isOwner}
107145
/>
108146
</BadgeContainer>
109147

@@ -120,6 +158,7 @@ function Component() {
120158
onClick: () => removeMutation.mutateAsync(),
121159
isPending: removeMutation.isPending,
122160
}}
161+
isOwnerAccount={isOwner}
123162
/>
124163
</BadgeContainer>
125164

@@ -136,6 +175,7 @@ function Component() {
136175
onClick: () => removeMutation.mutateAsync(),
137176
isPending: removeMutation.isPending,
138177
}}
178+
isOwnerAccount={isOwner}
139179
/>
140180
</BadgeContainer>
141181

0 commit comments

Comments
 (0)