Skip to content

Commit

Permalink
Merge pull request #65 from alleslabs/feat/instantiate-button
Browse files Browse the repository at this point in the history
Feat/instantiate button
  • Loading branch information
poomthiti committed Jan 11, 2023
2 parents da6d5ba + a786ee0 commit 974c931
Show file tree
Hide file tree
Showing 17 changed files with 281 additions and 65 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#65](https://github.com/alleslabs/celatone-frontend/pull/60) Create instantiate button component
- [#64](https://github.com/alleslabs/celatone-frontend/pull/64) Add contract not exist page
- [#63](https://github.com/alleslabs/celatone-frontend/pull/63) Add code id explorer link and code table row navigation
- [#67](https://github.com/alleslabs/celatone-frontend/pull/67) Add Public Codes shortcut to sidebar and add Quick Actions section
Expand Down
8 changes: 1 addition & 7 deletions src/lib/components/Copier.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,8 @@ export const Copier = ({ value, ml = "8px" }: CopierProps) => {
isOpen={hasCopied}
label="Copied!"
placement="top"
bg="primary.dark"
color="text.main"
fontWeight={400}
fontSize="14px"
p="8px 16px"
borderRadius="8px"
arrowSize={8}
mb="4px"
bg="primary.dark"
>
<CopyIcon
display="flex"
Expand Down
10 changes: 9 additions & 1 deletion src/lib/components/InputWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { SearchIcon } from "@chakra-ui/icons";
import type { InputProps } from "@chakra-ui/react";
import { Input, InputGroup, InputRightElement } from "@chakra-ui/react";
import type { ChangeEvent } from "react";

interface InputWithIconProps {
placeholder: string;
value: string;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
size?: InputProps["size"];
}

const InputWithIcon = ({
placeholder,
value,
size,
onChange,
}: InputWithIconProps) => {
return (
<InputGroup>
<Input placeholder={placeholder} value={value} onChange={onChange} />
<Input
placeholder={placeholder}
value={value}
onChange={onChange}
size={size}
/>
<InputRightElement>
<SearchIcon color="input.main" />
</InputRightElement>
Expand Down
37 changes: 37 additions & 0 deletions src/lib/components/PermissionChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { chakra, Tag } from "@chakra-ui/react";
import { useWallet } from "@cosmos-kit/react";
import type { CSSProperties } from "react";

import type { HumanAddr, PermissionAddresses } from "lib/types";
import { InstantiatePermission } from "lib/types";

const StyledTag = chakra(Tag, {
baseStyle: {
borderRadius: "16px",
fontSize: "12px",
fontWeight: 400,
color: "text.main",
},
});

interface PermissionChipProps {
instantiatePermission: InstantiatePermission;
permissionAddresses: PermissionAddresses;
}

export const PermissionChip = ({
instantiatePermission,
permissionAddresses,
}: PermissionChipProps) => {
const { address } = useWallet();

const isAllowed =
permissionAddresses.includes(address as HumanAddr) ||
instantiatePermission === InstantiatePermission.EVERYBODY;

const tagBgColor: CSSProperties["backgroundColor"] = isAllowed
? "rgba(161, 230, 143, 0.5)"
: "rgba(173, 173, 173, 0.6)";

return <StyledTag bgColor={tagBgColor}>{instantiatePermission}</StyledTag>;
};
94 changes: 94 additions & 0 deletions src/lib/components/button/InstantiateButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { ButtonProps } from "@chakra-ui/react";
import { Button, chakra, Icon, Tooltip } from "@chakra-ui/react";
import { useWallet } from "@cosmos-kit/react";
import { useRouter } from "next/router";
import { MdHowToVote, MdPerson } from "react-icons/md";

import type { HumanAddr, PermissionAddresses } from "lib/types";
import { InstantiatePermission } from "lib/types";

interface InstantiateButtonProps extends ButtonProps {
instantiatePermission: InstantiatePermission;
permissionAddresses: PermissionAddresses;
codeId: number;
}

const StyledIcon = chakra(Icon, {
baseStyle: {
boxSize: "4",
display: "flex",
alignItems: "center",
},
});

const getInstantiateButtonProps = (
isAllowed: boolean,
isDisabled: boolean
): {
tooltipLabel: string;
variant: string;
icon: JSX.Element | undefined;
} => {
if (isAllowed) {
return {
tooltipLabel: isDisabled
? "You need to connect wallet to instantiate"
: "You can instantiate without opening proposal",
variant: "outline-primary",
icon: <StyledIcon as={MdPerson} />,
};
}
return {
tooltipLabel: isDisabled
? ""
: "Instantiate through proposal only, Coming soon",
variant: "outline-gray",
icon: isDisabled ? undefined : <StyledIcon as={MdHowToVote} />,
};
};

export const InstantiateButton = ({
instantiatePermission = InstantiatePermission.UNKNOWN,
permissionAddresses = [],
codeId,
...buttonProps
}: InstantiateButtonProps) => {
const router = useRouter();
const { address } = useWallet();
const goToInstantiate = () =>
router.push({ pathname: "/instantiate", query: { "code-id": codeId } });

const isAllowed =
permissionAddresses.includes(address as HumanAddr) ||
instantiatePermission === InstantiatePermission.EVERYBODY;
const isDisabled =
instantiatePermission === InstantiatePermission.UNKNOWN || !address;

const { tooltipLabel, variant, icon } = getInstantiateButtonProps(
isAllowed,
isDisabled
);

return (
<Tooltip
hasArrow
label={tooltipLabel}
placement="top"
arrowSize={8}
bg="primary.dark"
>
<Button
// Change to isDisabled when create proposal flow is done
disabled={!isAllowed}
// disabled={isDisabled}
variant={variant}
leftIcon={icon}
size="sm"
onClick={isAllowed ? goToInstantiate : () => null}
{...buttonProps}
>
Instantiate
</Button>
</Tooltip>
);
};
8 changes: 7 additions & 1 deletion src/lib/components/table/EditableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,13 @@ export const EditableCell = ({
)}
</Flex>
{!!tooltip && (
<Tooltip hasArrow label={tooltip} bg="primary.dark" placement="top">
<Tooltip
hasArrow
label={tooltip}
placement="top"
bg="primary.dark"
arrowSize={8}
>
<InfoIcon color="gray.600" boxSize="14px" cursor="pointer" />
</Tooltip>
)}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/data/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const getCodeListByUserQueryDocument = graphql(`
account {
uploader: address
}
access_config_permission
access_config_addresses
}
}
`);
Expand All @@ -25,6 +27,8 @@ export const getCodeListByIDsQueryDocument = graphql(`
account {
uploader: address
}
access_config_permission
access_config_addresses
}
}
`);
Expand Down
12 changes: 6 additions & 6 deletions src/lib/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import * as types from "./graphql";
import { TypedDocumentNode as DocumentNode } from "@graphql-typed-document-node/core";

const documents = {
"\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n":
"\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n":
types.GetCodeListByUserQueryDocument,
"\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n":
"\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n":
types.GetCodeListByIDsQueryDocument,
"\n query getInstantiatedCountByUserQueryDocument($walletAddr: String!) {\n contracts_aggregate(\n where: { transaction: { account: { address: { _eq: $walletAddr } } } }\n ) {\n aggregate {\n count\n }\n }\n }\n":
types.GetInstantiatedCountByUserQueryDocumentDocument,
Expand All @@ -22,11 +22,11 @@ const documents = {
};

export function graphql(
source: "\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n"
): typeof documents["\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n"];
source: "\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n"
): typeof documents["\n query getCodeListByUserQuery($walletAddr: String!) {\n codes(\n where: { account: { address: { _eq: $walletAddr } } }\n limit: 500\n offset: 0\n order_by: { id: desc }\n ) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n"];
export function graphql(
source: "\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n"
): typeof documents["\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n }\n }\n"];
source: "\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n"
): typeof documents["\n query getCodeListByIDsQuery($ids: [Int!]!) {\n codes(where: { id: { _in: $ids } }) {\n id\n instantiated: contract_instantiated\n account {\n uploader: address\n }\n access_config_permission\n access_config_addresses\n }\n }\n"];
export function graphql(
source: "\n query getInstantiatedCountByUserQueryDocument($walletAddr: String!) {\n contracts_aggregate(\n where: { transaction: { account: { address: { _eq: $walletAddr } } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"
): typeof documents["\n query getInstantiatedCountByUserQueryDocument($walletAddr: String!) {\n contracts_aggregate(\n where: { transaction: { account: { address: { _eq: $walletAddr } } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"];
Expand Down
20 changes: 20 additions & 0 deletions src/lib/gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6256,6 +6256,8 @@ export type GetCodeListByUserQueryQuery = {
codes: Array<{
__typename?: "codes";
id: number;
access_config_permission: string;
access_config_addresses: any;
instantiated: number;
account: { __typename?: "accounts"; uploader: string };
}>;
Expand All @@ -6270,6 +6272,8 @@ export type GetCodeListByIDsQueryQuery = {
codes: Array<{
__typename?: "codes";
id: number;
access_config_permission: string;
access_config_addresses: any;
instantiated: number;
account: { __typename?: "accounts"; uploader: string };
}>;
Expand Down Expand Up @@ -6493,6 +6497,14 @@ export const GetCodeListByUserQueryDocument = {
],
},
},
{
kind: "Field",
name: { kind: "Name", value: "access_config_permission" },
},
{
kind: "Field",
name: { kind: "Name", value: "access_config_addresses" },
},
],
},
},
Expand Down Expand Up @@ -6587,6 +6599,14 @@ export const GetCodeListByIDsQueryDocument = {
],
},
},
{
kind: "Field",
name: { kind: "Name", value: "access_config_permission" },
},
{
kind: "Field",
name: { kind: "Name", value: "access_config_addresses" },
},
],
},
},
Expand Down
17 changes: 7 additions & 10 deletions src/lib/pages/code/component/CTASection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Flex, Button, chakra, Icon } from "@chakra-ui/react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
import { MdCheck } from "react-icons/md";

import { InstantiateButton } from "lib/components/button/InstantiateButton";
import { RemoveCode } from "lib/components/modal/code/RemoveCode";
import { SaveOrEditCodeModal } from "lib/components/modal/code/SaveOrEditCode";
import { useCodeStore } from "lib/hooks";
Expand All @@ -16,21 +16,18 @@ const StyledIcon = chakra(Icon, {

export const CTASection = observer(
({ id, ...codeInfo }: Omit<CodeInfo, "contracts">) => {
const router = useRouter();
const { isCodeIdExist } = useCodeStore();
const isSaved = isCodeIdExist(id);

return (
<Flex gap={4}>
{isSaved && <SaveOrEditCodeModal mode="edit" id={id} {...codeInfo} />}
<Button
variant="outline-primary"
onClick={() =>
router.push({ pathname: "/instantiate", query: { "code-id": id } })
}
>
Instantiate
</Button>
<InstantiateButton
instantiatePermission={codeInfo.instantiatePermission}
permissionAddresses={codeInfo.permissionAddresses}
codeId={id}
size="md"
/>
{isSaved ? (
<RemoveCode
codeId={id}
Expand Down
28 changes: 18 additions & 10 deletions src/lib/pages/code/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@ import { BackButton } from "lib/components/button/BackButton";
import { ExplorerLink } from "lib/components/ExplorerLink";
import PageContainer from "lib/components/PageContainer";
import { useCodeStore } from "lib/hooks";
import { useCodeData } from "lib/model/code";
import { InstantiatePermission } from "lib/types";
import { getFirstQueryParam } from "lib/utils";

import { CodeInfoSection } from "./component/CodeInfoSection";
import { CTASection } from "./component/CTASection";

const CodeDetails = observer(() => {
const router = useRouter();
const codeId = getFirstQueryParam(router.query.codeId);
/**
* @todos Maybe use data from data hook instead
* @todos Handle incorrect codeIdParam and render not found or error page.
*/
const { getCodeLocalInfo } = useCodeStore();
const codeInfo = getCodeLocalInfo(Number(codeId));
const codeIdParam = getFirstQueryParam(router.query.codeId);
const codeId = Number(codeIdParam);

const { getCodeLocalInfo } = useCodeStore();
const localCodeInfo = getCodeLocalInfo(codeId);
const codeDetails = useCodeData(codeId);
/**
* @todos Wireup page with data hook and component functionality/logic
*/
Expand All @@ -31,20 +35,24 @@ const CodeDetails = observer(() => {
<Flex align="center" justify="space-between" mt={6}>
<Flex direction="column" gap={1}>
<Heading as="h5" variant="h5">
{codeInfo?.description ?? codeId}
{localCodeInfo?.description ?? codeId}
</Heading>
<Flex gap={2}>
<Text fontWeight={500} color="text.dark" variant="body2">
Code ID
</Text>
<ExplorerLink type="code_id" value={codeId} />
<ExplorerLink type="code_id" value={codeId.toString()} />
</Flex>
</Flex>
{/* TODO: Check uploader with data hook */}
{/* TODO: check default uploader case */}
<CTASection
id={Number(codeId)}
uploader={codeInfo?.uploader ?? ""}
description={codeInfo?.description ?? ""}
id={codeId}
uploader={codeDetails?.uploader ?? localCodeInfo?.uploader ?? ""}
description={localCodeInfo?.description}
instantiatePermission={
codeDetails?.instantiatePermission ?? InstantiatePermission.UNKNOWN
}
permissionAddresses={codeDetails?.permissionAddresses ?? []}
/>
</Flex>
<Divider borderColor="divider.main" my={12} />
Expand Down
Loading

1 comment on commit 974c931

@vercel
Copy link

@vercel vercel bot commented on 974c931 Jan 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.