From 829d287b7552bbd0c9b254c9036abc395f2fc3f6 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Fri, 5 May 2023 12:58:44 -0300 Subject: [PATCH 01/14] feat: set background gradient on nfa detail page --- ui/src/app.context.tsx | 33 +++++++++++++++++++ ui/src/app.tsx | 25 +++++++------- .../layout/nav-bar/nav-bar.styles.ts | 1 - ui/src/components/layout/page/app-page.tsx | 10 +++++- ui/src/utils/color.ts | 3 +- .../explore/explore-list/nfa-search.styles.ts | 2 +- .../indexed-nfa/fragments/aside.fragment.tsx | 4 +-- ui/src/views/indexed-nfa/indexed-nfa.tsx | 12 +++++++ 8 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 ui/src/app.context.tsx diff --git a/ui/src/app.context.tsx b/ui/src/app.context.tsx new file mode 100644 index 0000000..b84f47e --- /dev/null +++ b/ui/src/app.context.tsx @@ -0,0 +1,33 @@ +import { useState } from 'react'; + +import { createContext } from './utils'; + +export type AppContext = { + backgroundColor: string; + setBackgroundColor: (color: string) => void; +}; + +const [AppProvider, useContext] = createContext({ + name: 'App.Context', + hookName: 'App.useContext', + providerName: 'App.Provider', +}); + +export abstract class App { + static readonly useContext = useContext; + static readonly Provider: React.FC = ({ children }) => { + const [backgroundColor, setBackgroundColor] = useState(''); + + return ( + + {children} + + ); + }; +} + +export namespace App { + export type AppProps = { + children: React.ReactNode; + }; +} diff --git a/ui/src/app.tsx b/ui/src/app.tsx index 634b2b2..d5a93cd 100644 --- a/ui/src/app.tsx +++ b/ui/src/app.tsx @@ -2,6 +2,7 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router-dom'; import { themeGlobals } from '@/theme/globals'; +import { App as AppContext } from './app.context'; import { AppPage, ToastProvider } from './components'; import { ComponentsTest, @@ -17,17 +18,19 @@ export const App: React.FC = () => { <> - - - } /> - } /> - } /> - } /> - {/** TODO remove for release */} - } /> - } /> - - + + + + } /> + } /> + } /> + } /> + {/** TODO remove for release */} + } /> + } /> + + + ); diff --git a/ui/src/components/layout/nav-bar/nav-bar.styles.ts b/ui/src/components/layout/nav-bar/nav-bar.styles.ts index aeb838b..08266c3 100644 --- a/ui/src/components/layout/nav-bar/nav-bar.styles.ts +++ b/ui/src/components/layout/nav-bar/nav-bar.styles.ts @@ -19,7 +19,6 @@ export const NavBarStyles = { content: '""', position: 'absolute', inset: 0, - backgroundColor: alphaColor('black', 0.8), backdropFilter: 'blur(4px)', zIndex: -1, }, diff --git a/ui/src/components/layout/page/app-page.tsx b/ui/src/components/layout/page/app-page.tsx index 0b2587a..ad9e81f 100644 --- a/ui/src/components/layout/page/app-page.tsx +++ b/ui/src/components/layout/page/app-page.tsx @@ -1,3 +1,4 @@ +import { App } from '@/app.context'; import { NavBar } from '@/components'; import { PageStyles as PS } from './page.styles'; @@ -7,8 +8,15 @@ export type AppPageProps = { }; export const AppPage: React.FC = ({ children }: AppPageProps) => { + const { backgroundColor } = App.useContext(); + const background = `linear-gradient(180deg, #${backgroundColor}59 0%, #000000 30%)`; + return ( - + {children} diff --git a/ui/src/utils/color.ts b/ui/src/utils/color.ts index 4422f61..a391255 100644 --- a/ui/src/utils/color.ts +++ b/ui/src/utils/color.ts @@ -10,6 +10,5 @@ export const parseColorToNumber = (color: string): number => { * Converts string number to hex color string. */ export const parseNumberToHexColor = (color: number): string => { - const hexColor = color.toString(16); - return hexColor; + return `${`000000${color.toString(16)}`.slice(-6)}`; }; diff --git a/ui/src/views/explore/explore-list/nfa-search.styles.ts b/ui/src/views/explore/explore-list/nfa-search.styles.ts index 9955ee0..95c9b2b 100644 --- a/ui/src/views/explore/explore-list/nfa-search.styles.ts +++ b/ui/src/views/explore/explore-list/nfa-search.styles.ts @@ -1,4 +1,4 @@ -import { Flex, Icon, Input } from '@/components'; +import { Flex, Icon } from '@/components'; import { styled } from '@/theme'; export const NFASearchFragmentStyles = { diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx index 69f07c0..f0db5d4 100644 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { Link } from 'react-router-dom'; import { Button, Flex, Icon, NFAPreview } from '@/components'; +import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; @@ -10,8 +11,7 @@ const Preview: React.FC = () => { const { nfa } = IndexedNFA.useContext(); const color = useMemo( - // TODO: replace with util function - () => `#${`000000${nfa.color.toString(16)}`.slice(-6)}`, + () => `#${parseNumberToHexColor(nfa.color ?? '')}`, [nfa] ); diff --git a/ui/src/views/indexed-nfa/indexed-nfa.tsx b/ui/src/views/indexed-nfa/indexed-nfa.tsx index 88cb844..007f000 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.tsx +++ b/ui/src/views/indexed-nfa/indexed-nfa.tsx @@ -1,10 +1,13 @@ import { useQuery } from '@apollo/client'; import { ethers } from 'ethers'; +import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; +import { App } from '@/app.context'; import { getNFADocument } from '@/graphclient'; import { NFAMock } from '@/mocks'; import { AppLog } from '@/utils'; +import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFAAsideFragment, @@ -16,8 +19,15 @@ import { IndexedNFAStyles as S } from './indexed-nfa.styles'; export const IndexedNFAView: React.FC = () => { const { id } = useParams<{ id: string }>(); + const { setBackgroundColor } = App.useContext(); const navigate = useNavigate(); + useEffect(() => { + return () => { + setBackgroundColor('000000'); + }; + }, [setBackgroundColor]); + const handleError = (error: unknown): void => { AppLog.errorToast( `It was not possible to find the NFA with id "${id}"`, @@ -33,6 +43,8 @@ export const IndexedNFAView: React.FC = () => { }, onCompleted(data) { if (!data.token) handleError(new Error('Token not found')); + if (data.token?.color) + setBackgroundColor(parseNumberToHexColor(data.token.color)); }, onError(error) { handleError(error); From 84659d639231778be24837e38055fe05267e912d Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Fri, 5 May 2023 19:05:09 -0300 Subject: [PATCH 02/14] refactor: aside component NFA detail page --- .../core/icon/custom/share-icon.tsx | 18 ++ ui/src/components/core/icon/icon-library.tsx | 4 + ui/src/components/index.ts | 1 + .../nfa-icon/index.ts | 0 .../nfa-icon/nfa-icon.styles.ts | 0 ui/src/components/nfa-icon/nfa-icon.tsx | 17 ++ .../ap-form-step/create-ap-form-body.tsx | 10 +- .../views/access-point/nfa-icon/nfa-icon.tsx | 21 -- .../indexed-nfa/fragments/aside.fragment.tsx | 236 ++++++++++++++++-- .../views/indexed-nfa/fragments/tabs/index.ts | 1 + .../indexed-nfa/fragments/tabs/tabs.styles.ts | 48 ++++ .../views/indexed-nfa/fragments/tabs/tabs.tsx | 35 +++ .../views/indexed-nfa/indexed-nfa.styles.ts | 111 ++++++-- 13 files changed, 436 insertions(+), 66 deletions(-) create mode 100644 ui/src/components/core/icon/custom/share-icon.tsx rename ui/src/{views/access-point => components}/nfa-icon/index.ts (100%) rename ui/src/{views/access-point => components}/nfa-icon/nfa-icon.styles.ts (100%) create mode 100644 ui/src/components/nfa-icon/nfa-icon.tsx delete mode 100644 ui/src/views/access-point/nfa-icon/nfa-icon.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/tabs/index.ts create mode 100644 ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts create mode 100644 ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx diff --git a/ui/src/components/core/icon/custom/share-icon.tsx b/ui/src/components/core/icon/custom/share-icon.tsx new file mode 100644 index 0000000..eff3f7e --- /dev/null +++ b/ui/src/components/core/icon/custom/share-icon.tsx @@ -0,0 +1,18 @@ +import { IconStyles as IS } from '../icon.styles'; + +export const Share: React.FC = (props) => ( + + + +); diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index b350ded..b6b32ae 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -7,6 +7,7 @@ import { BsFillSquareFill } from '@react-icons/all-files/bs/BsFillSquareFill'; import { FaBars } from '@react-icons/all-files/fa/FaBars'; import { FaChevronRight } from '@react-icons/all-files/fa/FaChevronRight'; import { FaExternalLinkAlt } from '@react-icons/all-files/fa/FaExternalLinkAlt'; +import { HiOutlineDotsHorizontal } from '@react-icons/all-files/hi/HiOutlineDotsHorizontal'; import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp'; import { IoCheckmarkCircleSharp } from '@react-icons/all-files/io5/IoCheckmarkCircleSharp'; import { IoClose } from '@react-icons/all-files/io5/IoClose'; @@ -24,6 +25,7 @@ import { FleekName, MetamaskIcon, } from './custom'; +import { Share } from './custom/share-icon'; export const IconLibrary = Object.freeze({ back: IoArrowBackCircleSharp, @@ -45,8 +47,10 @@ export const IconLibrary = Object.freeze({ metamask: MetamaskIcon, //remove if not used search: BiSearch, square: BsFillSquareFill, + share: Share, success: AiFillCheckCircle, twitter: AiOutlineTwitter, + 'three-dots': HiOutlineDotsHorizontal, upload: IoCloudUploadSharp, verified: MdVerifiedUser, }); diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index 62f959e..351ed16 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -6,6 +6,7 @@ export * from './spinner'; export * from './toast'; export * from './step'; export * from './nfa-card'; +export * from './nfa-icon'; export * from './nfa-preview'; export * from './card-tag'; export * from './resolved-address'; diff --git a/ui/src/views/access-point/nfa-icon/index.ts b/ui/src/components/nfa-icon/index.ts similarity index 100% rename from ui/src/views/access-point/nfa-icon/index.ts rename to ui/src/components/nfa-icon/index.ts diff --git a/ui/src/views/access-point/nfa-icon/nfa-icon.styles.ts b/ui/src/components/nfa-icon/nfa-icon.styles.ts similarity index 100% rename from ui/src/views/access-point/nfa-icon/nfa-icon.styles.ts rename to ui/src/components/nfa-icon/nfa-icon.styles.ts diff --git a/ui/src/components/nfa-icon/nfa-icon.tsx b/ui/src/components/nfa-icon/nfa-icon.tsx new file mode 100644 index 0000000..e3ca64d --- /dev/null +++ b/ui/src/components/nfa-icon/nfa-icon.tsx @@ -0,0 +1,17 @@ +import { NFAIconStyles as NS } from './nfa-icon.styles'; + +type NFAIconProps = { + image: string; + color: string; +}; + +export const NFAIcon: React.FC = ({ + image, + color, +}: NFAIconProps) => { + return ( + + + + ); +}; diff --git a/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx b/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx index 9cdd56f..41fb535 100644 --- a/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx +++ b/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx @@ -9,6 +9,7 @@ import { CardTag, Flex, Form, + NFAIcon, RowData, Spinner, Stepper, @@ -18,9 +19,9 @@ import { getNFADocument } from '@/graphclient'; import { useAppDispatch } from '@/store'; import { bunnyCDNActions, useBunnyCDNStore } from '@/store/features/bunny-cdn'; import { AppLog } from '@/utils'; +import { parseNumberToHexColor } from '@/utils/color'; import { CreateAccessPoint } from '../create-ap.context'; -import { NFAIconFragment } from '../nfa-icon'; import { useAccessPointFormContext } from './create-ap.form.context'; export const SelectedNFA: React.FC = () => { @@ -28,7 +29,12 @@ export const SelectedNFA: React.FC = () => { return ( } + leftIcon={ + + } label={nfa.name} rightComponent={Selected NFA} /> diff --git a/ui/src/views/access-point/nfa-icon/nfa-icon.tsx b/ui/src/views/access-point/nfa-icon/nfa-icon.tsx deleted file mode 100644 index 3caddb1..0000000 --- a/ui/src/views/access-point/nfa-icon/nfa-icon.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { parseNumberToHexColor } from '@/utils/color'; - -import { NFAIconStyles as NS } from './nfa-icon.styles'; - -type NFAIconProps = { - image: string; - color: number; -}; - -export const NFAIconFragment: React.FC = ({ - image, - color, -}: NFAIconProps) => { - return ( - - - - ); -}; diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx index f0db5d4..dd88a83 100644 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx @@ -1,11 +1,24 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { Link } from 'react-router-dom'; -import { Button, Flex, Icon, NFAPreview } from '@/components'; +import { App } from '@/app.context'; +import { + Button, + Flex, + Icon, + IconName, + NFAIcon, + NFAPreview, + ResolvedAddress, + Text, +} from '@/components'; +import { forwardStyledRef } from '@/theme'; import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; +import { Tab, TabContainer } from './tabs'; +import { AppLog } from '@/utils'; const Preview: React.FC = () => { const { nfa } = IndexedNFA.useContext(); @@ -30,46 +43,221 @@ const Preview: React.FC = () => { ); }; -const CreateAccessPoint: React.FC = () => { +type BadgeProps = { + verified: boolean; +}; + +const Badge: React.FC = ({ verified }: BadgeProps) => { + const text = useMemo( + () => (verified ? 'Verified' : 'Unverified'), + [verified] + ); + + const icon = useMemo(() => (verified ? 'verified' : 'error'), [verified]); + const color = useMemo(() => (verified ? '$green10' : '$red10'), [verified]); + return ( + + + {text} + + ); +}; + +const Header: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + {nfa.name} + {/* TODO remove once subrgraph integration is merged */} + 0.5} /> + + + + + {nfa.owner.id} + + + ); +}; + +type HeaderDataProps = { + label: string; + children: React.ReactNode; +}; + +const HeaderData: React.FC = ({ + label, + children, +}: HeaderDataProps) => ( + + {label} + {children} + +); + +const NFAInfo: React.FC = () => { const { nfa } = IndexedNFA.useContext(); return ( - - - Host NFA Frontend - - {/* TODO: replace with correct text */} + + + {nfa.accessPoints?.length ?? 0} + - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vitae - ante erat. Sed quis finibus diam. - + - - - - {/* TODO: place correct href */} - Learn more - - - - + + {/* TODO: place correct data */} + 12/12/22 + + + ); +}; + +type CustomButtonProps = { + icon: IconName; +} & React.ComponentPropsWithRef; + +const CustomButon = forwardStyledRef( + ({ icon, ...props }, ref) => ( + + ) +); + +const ButtonsFragment: React.FC = () => { + const location = window.location.href; + const handleShareOnClick = (): void => { + navigator.clipboard.writeText(location); + AppLog.successToast('Link copied to clipboard'); + }; + return ( + + + {/* TODO add tooltip to copy link */} + + + ); +}; + +const PropertiesFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const traitsToShow = useMemo(() => { + return [ + [nfa.ENS, 'ENS'], + [nfa.gitRepository.id, 'Repository'], + [10, 'Version'], + [nfa.externalURL, 'Domain'], + ]; + }, [nfa]); + + return ( + + {traitsToShow.map(([value, label], index) => ( + + + {value || '-'} + + {label} + + ))} + + ); +}; + +const OverviewFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + Token ID + {nfa.tokenId} + + + + Network + Mainnet + + + + Standard + ERC_721 + + + + Description + + + {nfa.description} + + + ); +}; + +const TabFragment: React.FC = () => { + const [tabSelected, setTabSelected] = useState(0); + const handleClick = (index: number): void => { + setTabSelected(index); + }; + return ( + <> + + {['Overview', 'Properties'].map((label, index) => ( + + ))} + + {tabSelected === 0 ? : } + ); }; export const IndexedNFAAsideFragment: React.FC = () => { const ref = useRef(null); const [top, setTop] = useState(); + const { nfa } = IndexedNFA.useContext(); + + const { backgroundColor } = App.useContext(); + const background = `linear-gradient(230deg, #${backgroundColor}59 0%, #181818 80%)`; useEffect(() => { setTop(ref.current?.getBoundingClientRect().top); }, [ref]); return ( - + - +
+ + + + ); }; diff --git a/ui/src/views/indexed-nfa/fragments/tabs/index.ts b/ui/src/views/indexed-nfa/fragments/tabs/index.ts new file mode 100644 index 0000000..6dd7856 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/tabs/index.ts @@ -0,0 +1 @@ +export * from './tabs'; diff --git a/ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts b/ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts new file mode 100644 index 0000000..132343e --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts @@ -0,0 +1,48 @@ +import { Flex } from '@/components'; +import { styled } from '@/theme'; + +export const TabsStyles = { + Container: styled(Flex, { + width: '100%', + }), + Tab: { + Container: styled(Flex, { + flexDirection: 'column', + flex: 1, + alignItems: 'center', + cursor: 'pointer', + + variants: { + active: { + true: { + color: 'white', + }, + false: { + color: '$slate8', + }, + }, + }, + }), + Label: styled('span', { + padding: '$2h', + }), + Line: styled('span', { + width: '100%', + borderRadius: '3px', + + variants: { + active: { + true: { + color: 'white', + borderBottom: '3px solid white', + }, + false: { + color: '$slate8', + borderBottom: '2px solid $slate8', + mt: '0.046875rem', + }, + }, + }, + }), + }, +}; diff --git a/ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx b/ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx new file mode 100644 index 0000000..b83b652 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx @@ -0,0 +1,35 @@ +import { forwardStyledRef } from '@/theme'; + +import { TabsStyles as S } from './tabs.styles'; + +type TabProps = { + label: string; + index: number; + onTabClick: (index: number) => void; +} & React.ComponentPropsWithRef; + +export const Tab = forwardStyledRef( + ({ label, index, onTabClick, ...props }, ref) => { + const { active } = props; + const handleClick = (): void => { + onTabClick(index); + }; + + return ( + + {label} + + + ); + } +); + +type TabContainerProps = { + children: React.ReactNode; +}; + +export const TabContainer: React.FC = ({ + children, +}: TabContainerProps) => { + return {children}; +}; diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index 1e1b2c4..1217ca1 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -1,7 +1,7 @@ -import { Skeleton } from '@/components'; +import { Button, Flex, Skeleton, Text } from '@/components'; import { styled } from '@/theme'; -const Spacing = '$5'; +const Spacing = '$6'; export const IndexedNFAStyles = { Grid: styled('div', { @@ -32,34 +32,108 @@ export const IndexedNFAStyles = { gap: Spacing, height: 'fit-content', + borderRadius: '$lg', + padding: Spacing, + maxWidth: '24rem', + '@media (max-width: 580px)': { position: 'static', }, }), + Header: { + Wrapper: styled(Flex, { + flexDirection: 'column', + gap: '$2h', + color: '$slate12', + }), + Container: styled(Flex, { + justifyContent: 'space-between', + alignItems: 'center', + }), + Header: styled('h1', { + fontSize: '2.125rem', + lineHeight: 1.35, + fontWeight: 700, - CreateAccessPoint: { + // maxWidth: '10rem', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }), + Badge: styled('span', { + height: 'fit-content', + width: 'fit-content', + fontSize: '$xs', + fontWeight: '$bold', + padding: '$0h $2', + borderRadius: '$full', + backgroundColor: '#131313', + display: 'flex', + gap: '$1h', + + variants: { + verified: { + true: { + color: '$green10', + }, + false: { + color: '$red10', + }, + }, + }, + }), + }, + Divider: { + Line: styled('span', { + width: '100%', + borderBottom: '1px solid $slate6', + }), + Elipse: styled('span', { + width: '0.375rem', + height: '0.375rem', + backgroundColor: '$slate8', + borderRadius: '100%', + }), + }, + Button: { + Container: styled(Flex, { + gap: '$3', + fontSize: '16px', + + [`${Button}`]: { + borderRadius: '0.375rem', + }, + }), + }, + Overview: { Container: styled('div', { display: 'flex', flexDirection: 'column', - gap: Spacing, - padding: Spacing, - backgroundColor: '$blue1', + backgroundColor: '$slate4', borderRadius: '$lg', + fontSize: '14px', }), - Heading: styled('h2', { - fontSize: '$md', - color: '$slate12', - }), - Text: styled('p', { - fontSize: '$sm', + Row: { + Container: styled(Flex, { + justifyContent: 'space-between', + }), + Label: styled(Text, { + color: '$slate11', + }), + Value: styled(Text, { + fontWeight: '$bold', + }), + }, + Description: styled('p', { color: '$slate11', }), - Extra: styled('a', { + }, + Properties: { + Container: styled('div', { display: 'flex', - alignItems: 'center', - color: '$slate11', - fontSize: '$sm', - gap: '$2', + flexDirection: 'column', + gap: '$1', + padding: '$2h $4', }), }, }, @@ -90,7 +164,7 @@ export const IndexedNFAStyles = { Elipse: styled('span', { width: '0.375rem', height: '0.375rem', - backgroundColor: '$slate4', + backgroundColor: '$slate11', borderRadius: '100%', }), }, @@ -150,7 +224,6 @@ export const IndexedNFAStyles = { }, }, }), - Table: { Container: styled('div', { border: '1px solid $slate6', From 74dbac66cd5925cfccb9f6d06110c5fdef7cd327 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Tue, 9 May 2023 10:52:49 -0300 Subject: [PATCH 03/14] feat: add menu component --- ui/src/assets/Rectangle-199.png | Bin 0 -> 8880 bytes ui/src/assets/Rectangle-200.png | Bin 0 -> 12103 bytes ui/src/assets/Rectangle-201.png | Bin 0 -> 10011 bytes ui/src/components/core/icon/custom/index.ts | 1 + .../core/icon/custom/opensea-icon.tsx | 15 + ui/src/components/core/icon/icon-library.tsx | 2 + ui/src/components/core/index.ts | 1 + ui/src/components/core/menu/index.ts | 1 + ui/src/components/core/menu/menu.styles.ts | 45 +++ ui/src/components/core/menu/menu.tsx | 41 +++ ui/src/constants/env.ts | 1 + .../indexed-nfa/fragments/aside.fragment.tsx | 72 +++- .../indexed-nfa/fragments/main.fragment.tsx | 325 ++++-------------- .../views/indexed-nfa/indexed-nfa.styles.ts | 34 +- .../repo-branch-commit-fields.tsx | 1 + 15 files changed, 282 insertions(+), 257 deletions(-) create mode 100644 ui/src/assets/Rectangle-199.png create mode 100644 ui/src/assets/Rectangle-200.png create mode 100644 ui/src/assets/Rectangle-201.png create mode 100644 ui/src/components/core/icon/custom/opensea-icon.tsx create mode 100644 ui/src/components/core/menu/index.ts create mode 100644 ui/src/components/core/menu/menu.styles.ts create mode 100644 ui/src/components/core/menu/menu.tsx diff --git a/ui/src/assets/Rectangle-199.png b/ui/src/assets/Rectangle-199.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a55255a3d062c678f8eb40020bc883c27d8c71 GIT binary patch literal 8880 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 = (props) => ( + + + +); diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index b6b32ae..22de597 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -24,6 +24,7 @@ import { FleekLogo, FleekName, MetamaskIcon, + OpenseaIcon, } from './custom'; import { Share } from './custom/share-icon'; @@ -45,6 +46,7 @@ export const IconLibrary = Object.freeze({ info: IoInformationCircleSharp, menu: FaBars, metamask: MetamaskIcon, //remove if not used + opensea: OpenseaIcon, search: BiSearch, square: BsFillSquareFill, share: Share, diff --git a/ui/src/components/core/index.ts b/ui/src/components/core/index.ts index 947be14..eb6efae 100644 --- a/ui/src/components/core/index.ts +++ b/ui/src/components/core/index.ts @@ -7,3 +7,4 @@ export * from './separator.styles'; export * from './text'; export * from './switch'; export * from './color-picker'; +export * from './menu'; diff --git a/ui/src/components/core/menu/index.ts b/ui/src/components/core/menu/index.ts new file mode 100644 index 0000000..d96f145 --- /dev/null +++ b/ui/src/components/core/menu/index.ts @@ -0,0 +1 @@ +export * from './menu'; diff --git a/ui/src/components/core/menu/menu.styles.ts b/ui/src/components/core/menu/menu.styles.ts new file mode 100644 index 0000000..179e2c8 --- /dev/null +++ b/ui/src/components/core/menu/menu.styles.ts @@ -0,0 +1,45 @@ +import { Menu } from '@headlessui/react'; + +import { styled } from '@/theme'; + +export const MenuStyles = { + Wrapper: styled('div', { + position: 'relative', + }), + Items: styled(Menu.Items, { + width: '100%', + display: 'flex', + flexDirection: 'column', + position: 'absolute', + border: '1px solid $slate6', + backgroundColor: '$black', + boxSizing: 'border-box', + left: 0, + right: 0, + top: 'calc(100% + $3)', + padding: '$3', + gap: '$2', + borderRadius: '$lg', + zIndex: '$dropdown', + maxHeight: '30vh', + overflow: 'auto', + }), + Item: styled(Menu.Item, { + width: '100%', + position: 'relative', + display: 'flex', + alignItems: 'center', + gap: '$3', + cursor: 'pointer', + padding: '$2 $3', + borderRadius: '$lg', + color: '$slate11', + transition: '$all-200', + fontSize: '$sm', + + '&[data-headlessui-state*="active"]': { + backgroundColor: '$slate2', + color: '$slate12', + }, + }), +}; diff --git a/ui/src/components/core/menu/menu.tsx b/ui/src/components/core/menu/menu.tsx new file mode 100644 index 0000000..a0753fd --- /dev/null +++ b/ui/src/components/core/menu/menu.tsx @@ -0,0 +1,41 @@ +import { Menu as MenuHeadless } from '@headlessui/react'; +import React from 'react'; + +import { forwardStyledRef } from '@/theme'; + +import { MenuStyles as MS } from './menu.styles'; + +export abstract class Menu { + static readonly Root = ({ children }: Menu.MenuProps): JSX.Element => { + return {children}; + }; + + static readonly Items = forwardStyledRef( + ({ children, ...props }, ref): JSX.Element => { + return ( + + {children.map((child, index) => ( + {child} + ))} + + ); + } + ); + + static readonly Button = MenuHeadless.Button; +} + +export namespace Menu { + export type ItemsProps = { + children: React.ReactNode[]; + } & React.ComponentPropsWithRef; + + export type Elements = { + Button: React.FC>; + Items: React.FC; + }; + + export type MenuProps = { + children: React.ReactNode; + } & React.ComponentPropsWithRef; +} diff --git a/ui/src/constants/env.ts b/ui/src/constants/env.ts index 0ec5d8d..b1b1b98 100644 --- a/ui/src/constants/env.ts +++ b/ui/src/constants/env.ts @@ -1,4 +1,5 @@ export const env = Object.freeze({ + environment: import.meta.env.MODE, alchemy: { id: import.meta.env.VITE_ALCHEMY_API_KEY || '', appName: import.meta.env.VITE_ALCHEMY_APP_NAME || '', diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx index dd88a83..900d2b3 100644 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx @@ -7,18 +7,21 @@ import { Flex, Icon, IconName, + Menu, NFAIcon, NFAPreview, ResolvedAddress, Text, } from '@/components'; +import { env } from '@/constants'; +import { FleekERC721 } from '@/integrations/ethereum/contracts'; import { forwardStyledRef } from '@/theme'; +import { AppLog } from '@/utils'; import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; import { Tab, TabContainer } from './tabs'; -import { AppLog } from '@/utils'; const Preview: React.FC = () => { const { nfa } = IndexedNFA.useContext(); @@ -117,29 +120,85 @@ const NFAInfo: React.FC = () => { type CustomButtonProps = { icon: IconName; -} & React.ComponentPropsWithRef; +}; const CustomButon = forwardStyledRef( ({ icon, ...props }, ref) => ( ) ); +type MenuItemProps = { + label: string; + iconName: IconName; + onClick: () => void; +}; + +const MenuItem: React.FC = ({ + label, + iconName, + onClick, +}: MenuItemProps) => { + return ( + + + {label} + + ); +}; + const ButtonsFragment: React.FC = () => { - const location = window.location.href; + const { nfa } = IndexedNFA.useContext(); + const handleShareOnClick = (): void => { + const location = window.location.href; navigator.clipboard.writeText(location); AppLog.successToast('Link copied to clipboard'); }; + + const handleShareOpenSeaOnClick = (): void => { + window.open( + `https://${ + env.environment === 'development' ? 'testnets' : '' + }.opensea.io/assets/${ + env.environment === 'development' ? 'goerli' : 'ethereum' + }/${FleekERC721.address}/${nfa.tokenId}`, + '_blank' + ); + }; + + const handleShareOnTwitterOnClick = (): void => { + window.open(env.twitter.url, '_blank'); //TODO replace with twitter share + }; + return ( - + + + + + + + + + + + + {/* TODO add tooltip to copy link */} @@ -149,6 +208,7 @@ const ButtonsFragment: React.FC = () => { const PropertiesFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); + //TODO replace with real data const traitsToShow = useMemo(() => { return [ [nfa.ENS, 'ENS'], @@ -234,7 +294,7 @@ export const IndexedNFAAsideFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); const { backgroundColor } = App.useContext(); - const background = `linear-gradient(230deg, #${backgroundColor}59 0%, #181818 80%)`; + const background = `linear-gradient(230deg, #${backgroundColor} 0%, #181818 80%)`; useEffect(() => { setTop(ref.current?.getBoundingClientRect().top); diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx index 286273e..9ac9595 100644 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx @@ -1,273 +1,104 @@ -import React, { useMemo } from 'react'; +import React, { useState } from 'react'; -import { Flex, Icon, IconName, ResolvedAddress, Text } from '@/components'; +import Rectangle1 from '@/assets/Rectangle-199.png'; +import Rectangle2 from '@/assets/Rectangle-200.png'; +import Rectangle3 from '@/assets/Rectangle-201.png'; +import { Combobox, Flex, ResolvedAddress, Text } from '@/components'; -import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; -type HeaderDataProps = { +type SortItem = { + value: string; label: string; - children: React.ReactNode; }; -const HeaderData: React.FC = ({ - label, - children, -}: HeaderDataProps) => ( - - {label} - {children} - -); +const orderResults: SortItem[] = [ + { value: 'newest', label: 'Newest' }, + { value: 'oldest', label: 'Oldest' }, +]; const Header: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); + const [selectedValue, setSelectedValue] = useState(orderResults[0]); + const handleSortChange = (item: SortItem | undefined): void => { + //TODO integrate with context and sort + if (item) { + setSelectedValue(item); + } + }; return ( <> - {nfa.name} - - {nfa.owner.id} - - - - - - {/* TODO: place correct data */} - 12/12/22 - - - - - - {nfa.accessPoints?.length ?? 0} - + Hosted NFAs + + {({ Field, Options }) => ( + <> + + {(selected) => selected?.label || 'Select'} + + + {(item) => item.label} + + + )} + - - - ); -}; - -const Description: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - <> - - Description - - - {nfa.description} - - - ); -}; - -type DataWrapperProps = React.PropsWithChildren<{ - label: string | number; -}>; - -const DataWrapper: React.FC = ({ - children, - label, -}: DataWrapperProps) => ( - - {children || '-'} - {label} - -); - -const Traits: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - // TODO: place correct data - const traitsToShow = useMemo(() => { - return [ - [nfa.ENS, 'ENS'], - [nfa.gitRepository.id, 'Repository'], - [10, 'Version'], - [nfa.externalURL, 'Domain'], - [nfa.externalURL, 'Domain 2'], - ]; - }, [nfa]); - - return ( - <> - Traits - - {traitsToShow.map(([value, label]) => ( - - {value} - - ))} - - - ); -}; - -type VerificationBannerProps = { - verified: boolean; -}; - -const VerificationBanner: React.FC = ({ - verified, -}: VerificationBannerProps) => { - const [text, icon] = useMemo<[string, IconName]>(() => { - if (verified) - return ['This Non Fungible Application is Verified.', 'verified']; - return ['This Non Fungible Application is not Verified.', 'error']; - }, [verified]); - - return ( - - {text} - - - ); -}; - -const Verification: React.FC = () => { - return ( - <> - Verification - {/* TODO: Get verified from context */} - 0.5} /> - - {/* TODO: place correct data */} - polygon.eth - polygon/fe - ); }; +const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; // TODO: replace mocks with fetched data -const apMocks = new Array(10).fill(0).map((_, index) => ({ - approved: Math.random() > 0.5, +const apMocks = new Array(20).fill(0).map((_, index) => ({ + thumbnail: + thumbnailMocks[ + Math.floor( + Math.random() * (Math.floor(2) - Math.ceil(0) + 1) + Math.ceil(0) + ) + ], domain: `domain${index}.com`, owner: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049', createdAt: `${Math.floor(Math.random() * 30)}m ago`, })); -const AccessPoints: React.FC = () => { - return ( - <> - Frontends - - - - - - - - - - - - - - - Domain - Owner - Created - - - - - {apMocks.map((item) => ( - - - - - {item.domain} - - {item.owner} - - {item.createdAt} - - - - - ))} - - - - - ); -}; +const AccessPointsListFragment: React.FC = () => { + //TODO add infinite scroll -// TODO: replace mocks with fetched data -const versionsMock = new Array(10).fill(0).map((_, index) => ({ - live: index === 0, - commit: (Math.random() * 0xfffffffff).toString(16), - preview: `test: subgraph matchstick tests for access points and acl refactor (#150 - ) - - * fix: errors from deprecated entities.`, - time: `${Math.floor(Math.random() * 30)}m ago`, -})); - -const Versions: React.FC = () => { return ( - <> - Versions - - - - - - - - - - - - - - - Commit - Preview - Time - - - - - {versionsMock.map((item) => ( - - - - {item.live && 'Live'} - - - {item.commit.slice(0, 6)} - - {item.preview} - - {item.time} - - - - - ))} - - - - + + {apMocks.map((item, index) => ( + + + + + + {item.domain} + + + {item.owner} + + + 220 views + + 2 months ago + + + + ))} + ); }; @@ -275,11 +106,7 @@ export const IndexedNFAMainFragment: React.FC = () => { return (
- - - - - + ); }; diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index 1217ca1..e036515 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -145,8 +145,8 @@ export const IndexedNFAStyles = { flexDirection: 'column', gap: Spacing, }), - Heading: styled('h1', { - fontSize: '2.125rem', + Heading: styled('h2', { + fontSize: '1.625rem', lineHeight: 1.35, fontWeight: 700, }), @@ -156,6 +156,36 @@ export const IndexedNFAStyles = { fontWeight: 700, marginTop: Spacing, }), + AccessPoint: { + List: styled('div', { + display: 'flex', + flexDirection: 'column', + gap: Spacing, + }), + Grid: styled('div', { + display: 'grid', + gridTemplateAreas: '"thumbnail data"', + gap: '$4h', + alignItems: 'center', + gridTemplateColumns: '10rem 1fr', + }), + Thumbnail: styled('div', { + gridArea: 'thumbnail', + }), + Data: { + Container: styled('div', { + gridArea: 'data', + + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + }, + Title: styled('h3', { + color: '$slate12', + fontSize: '$lg', + }), + }, Divider: { Line: styled('span', { width: '100%', diff --git a/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx b/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx index e3ea135..62300fb 100644 --- a/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx +++ b/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx @@ -10,6 +10,7 @@ import { import { AppLog } from '@/utils'; import { Mint } from '@/views/mint/mint.context'; import { useMintFormContext } from '@/views/mint/nfa-step/form-step'; + import { TextStyles } from './repo-branch-commit-fields.styles'; export const RepoBranchCommitFields: React.FC = () => { From 130ef2589c2ae09379ca8efb98990b4368611d90 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Tue, 9 May 2023 17:21:03 -0300 Subject: [PATCH 04/14] style: radial gradient background --- .../indexed-nfa/fragments/aside.fragment.tsx | 25 ++++++++----------- .../indexed-nfa/fragments/main.fragment.tsx | 17 ++++++++----- .../views/indexed-nfa/indexed-nfa.styles.ts | 3 ++- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx index 900d2b3..9802000 100644 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx @@ -16,7 +16,7 @@ import { import { env } from '@/constants'; import { FleekERC721 } from '@/integrations/ethereum/contracts'; import { forwardStyledRef } from '@/theme'; -import { AppLog } from '@/utils'; +import { AppLog, getDate, getRepositoryFromURL } from '@/utils'; import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFA } from '../indexed-nfa.context'; @@ -73,8 +73,7 @@ const Header: React.FC = () => { {nfa.name} - {/* TODO remove once subrgraph integration is merged */} - 0.5} /> + @@ -110,10 +109,7 @@ const NFAInfo: React.FC = () => { - - {/* TODO: place correct data */} - 12/12/22 - + {getDate(nfa.createdAt)} ); }; @@ -208,18 +204,17 @@ const ButtonsFragment: React.FC = () => { const PropertiesFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); - //TODO replace with real data const traitsToShow = useMemo(() => { return [ [nfa.ENS, 'ENS'], - [nfa.gitRepository.id, 'Repository'], + [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], [10, 'Version'], [nfa.externalURL, 'Domain'], ]; }, [nfa]); return ( - + {traitsToShow.map(([value, label], index) => ( { const { nfa } = IndexedNFA.useContext(); return ( - + Token ID {nfa.tokenId} @@ -258,7 +255,7 @@ const OverviewFragment: React.FC = () => { Description - + {nfa.description} @@ -294,7 +291,7 @@ export const IndexedNFAAsideFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); const { backgroundColor } = App.useContext(); - const background = `linear-gradient(230deg, #${backgroundColor} 0%, #181818 80%)`; + const background = `radial-gradient(closest-corner circle at 90% 45%, #${backgroundColor}8c 1% ,#${backgroundColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${backgroundColor} 3%, #${backgroundColor}73 30%, #181818 70%)`; useEffect(() => { setTop(ref.current?.getBoundingClientRect().top); @@ -303,7 +300,7 @@ export const IndexedNFAAsideFragment: React.FC = () => { return (
diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx index b4ecb2c..68b12e4 100644 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx @@ -14,6 +14,7 @@ import { import { getDate, getRepositoryFromURL, getTimeSince } from '@/utils'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; +import { IndexedNFA } from '../indexed-nfa.context'; type SortItem = { value: string; @@ -205,27 +206,31 @@ const apMocks = new Array(20).fill(0).map((_, index) => ({ })); const AccessPointsListFragment: React.FC = () => { - //TODO add infinite scroll + const { + nfa: { accessPoints }, + } = IndexedNFA.useContext(); return ( - {apMocks.map((item, index) => ( + {accessPoints.map((item, index) => ( - + - {item.domain} + {item.id} - {item.owner} + {item.owner.id} 220 views - 2 months ago + + {getTimeSince(item.createdAt)} + diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index e036515..b7e9aef 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -35,6 +35,7 @@ export const IndexedNFAStyles = { borderRadius: '$lg', padding: Spacing, maxWidth: '24rem', + mixBlendMode: 'screen', '@media (max-width: 580px)': { position: 'static', @@ -109,7 +110,7 @@ export const IndexedNFAStyles = { Container: styled('div', { display: 'flex', flexDirection: 'column', - backgroundColor: '$slate4', + backgroundColor: 'rgba(255, 255, 255, 0.06)', borderRadius: '$lg', fontSize: '14px', }), From 36223a762063fb1da1aa72aade379dd1a9c427c2 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Wed, 10 May 2023 12:11:21 -0300 Subject: [PATCH 05/14] chore: nfa icon error image --- ui/src/components/nfa-icon/nfa-icon.tsx | 5 +- .../indexed-nfa/fragments/main.fragment.tsx | 272 +++--------------- 2 files changed, 45 insertions(+), 232 deletions(-) diff --git a/ui/src/components/nfa-icon/nfa-icon.tsx b/ui/src/components/nfa-icon/nfa-icon.tsx index e3ca64d..5c88449 100644 --- a/ui/src/components/nfa-icon/nfa-icon.tsx +++ b/ui/src/components/nfa-icon/nfa-icon.tsx @@ -11,7 +11,10 @@ export const NFAIcon: React.FC = ({ }: NFAIconProps) => { return ( - + (event.currentTarget.style.display = 'none')} + /> ); }; diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx index 68b12e4..698169e 100644 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx @@ -3,18 +3,11 @@ import React, { useState } from 'react'; import Rectangle1 from '@/assets/Rectangle-199.png'; import Rectangle2 from '@/assets/Rectangle-200.png'; import Rectangle3 from '@/assets/Rectangle-201.png'; -import { - Combobox, - Flex, - Icon, - IconName, - ResolvedAddress, - Text, -} from '@/components'; -import { getDate, getRepositoryFromURL, getTimeSince } from '@/utils'; +import { Combobox, Flex, ResolvedAddress, Text } from '@/components'; +import { getTimeSince } from '@/utils'; -import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; import { IndexedNFA } from '../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; type SortItem = { value: string; @@ -63,147 +56,12 @@ const Header: React.FC = () => { )} - {/* - - {nfa.owner.id} - - - - - {getDate(nfa.createdAt)} - - - - - {nfa.accessPoints?.length ?? 0} - - */} - {/* */} ); }; -// const Description: React.FC = () => { -// const { nfa } = IndexedNFA.useContext(); - -// return ( -// <> -// -// Description -// -// -// {nfa.description} -// -// -// ); -// }; - -// type DataWrapperProps = React.PropsWithChildren<{ -// label: string | number; -// }>; - -// const DataWrapper: React.FC = ({ -// children, -// label, -// }: DataWrapperProps) => ( -// -// {children || '-'} -// {label} -// -// ); - -// const Traits: React.FC = () => { -// const { nfa } = IndexedNFA.useContext(); - -// const traitsToShow = useMemo(() => { -// return [ -// [nfa.ENS, 'ENS'], -// [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], -// ['', 'Version'], -// [nfa.externalURL, 'Domain'], -// ]; -// }, [nfa]); - -// return ( -// <> -// Traits -// -// {traitsToShow.map(([value, label]) => ( -// -// {value} -// -// ))} -// -// -// ); -// }; - -// type VerificationBannerProps = { -// verified: boolean; -// }; - -// const VerificationBanner: React.FC = ({ -// verified, -// }: VerificationBannerProps) => { -// const [text, icon] = useMemo<[string, IconName]>(() => { -// if (verified) -// return ['This Non Fungible Application is Verified.', 'verified']; -// return ['This Non Fungible Application is not Verified.', 'error']; -// }, [verified]); - -// return ( -// -// {text} -// -// -// ); -// }; - -// const Verification: React.FC = () => { -// const { nfa } = IndexedNFA.useContext(); - -// return ( -// <> -// Verification -// -// -// -// {nfa.verifier ? ( -// {nfa.verifier?.id} -// ) : ( -// '-' -// )} -// -// -// {getRepositoryFromURL(nfa.gitRepository.id)} -// -// -// -// ); -// }; - +//TODO remove const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; -// TODO: replace mocks with fetched data -const apMocks = new Array(20).fill(0).map((_, index) => ({ - thumbnail: - thumbnailMocks[ - Math.floor( - Math.random() * (Math.floor(2) - Math.ceil(0) + 1) + Math.ceil(0) - ) - ], - domain: `domain${index}.com`, - owner: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049', - createdAt: `${Math.floor(Math.random() * 30)}m ago`, -})); const AccessPointsListFragment: React.FC = () => { const { @@ -212,94 +70,46 @@ const AccessPointsListFragment: React.FC = () => { return ( - {accessPoints.map((item, index) => ( - - - - - - {item.id} - - - {item.owner.id} - - - 220 views - - - {getTimeSince(item.createdAt)} - - - - - ))} + {accessPoints && accessPoints?.length > 0 ? ( + accessPoints.map((item, index) => ( + + + + + + {item.id} + + + {item.owner.id} + + + {/* TODO get from bunny CDN */} + 220 views + + + {getTimeSince(item.createdAt)} + + + + + )) + ) : ( +
No access points found
+ )}
); }; -// const AccessPoints: React.FC = () => { -// const { -// nfa: { accessPoints }, -// } = IndexedNFA.useContext(); - -// return ( -// <> -// Frontends -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// Domain -// Owner -// Created -// -// -// -// -// {accessPoints && accessPoints.length > 0 ? ( -// accessPoints.map((item) => ( -// -// -// -// -// {item.id} -// -// {item.owner.id} -// -// -// {getTimeSince(item.createdAt)} -// -// -// -// -// -// )) -// ) : ( -// -// -// No results -// -// -// )} -// -// -// -// -// ); -// }; export const IndexedNFAMainFragment: React.FC = () => { return ( From 698238c9b92c1d52bbe501354220cc6918d0f6c5 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Wed, 10 May 2023 15:32:46 -0300 Subject: [PATCH 06/14] chore: re-organize fragments indexed nfa view --- .../indexed-nfa/fragments/aside.fragment.tsx | 320 ------------------ .../aside/aside-buttons.fragment.tsx | 96 ++++++ .../fragments/aside/aside-header.fragment.tsx | 44 +++ .../aside/aside-nfa-info.fragment.tsx | 35 ++ .../aside/aside-preview.fragment.tsx | 29 ++ .../fragments/aside/aside-tabs.fragment.tsx | 92 +++++ .../fragments/aside/aside.fragment.tsx | 48 +++ ui/src/views/indexed-nfa/fragments/index.ts | 4 +- .../indexed-nfa/fragments/main.fragment.tsx | 121 ------- .../fragments/main/main-ap-list.fragment.tsx | 61 ++++ .../fragments/main/main-header.fragment.tsx | 56 +++ .../fragments/main/main.fragment.tsx | 12 + .../views/indexed-nfa/indexed-nfa.styles.ts | 151 +-------- ui/src/views/indexed-nfa/indexed-nfa.tsx | 5 - .../indexed-nfa/{fragments => }/tabs/index.ts | 0 .../{fragments => }/tabs/tabs.styles.ts | 0 .../indexed-nfa/{fragments => }/tabs/tabs.tsx | 0 17 files changed, 481 insertions(+), 593 deletions(-) delete mode 100644 ui/src/views/indexed-nfa/fragments/aside.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx delete mode 100644 ui/src/views/indexed-nfa/fragments/main.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx create mode 100644 ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx rename ui/src/views/indexed-nfa/{fragments => }/tabs/index.ts (100%) rename ui/src/views/indexed-nfa/{fragments => }/tabs/tabs.styles.ts (100%) rename ui/src/views/indexed-nfa/{fragments => }/tabs/tabs.tsx (100%) diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx deleted file mode 100644 index 9802000..0000000 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ /dev/null @@ -1,320 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; -import { Link } from 'react-router-dom'; - -import { App } from '@/app.context'; -import { - Button, - Flex, - Icon, - IconName, - Menu, - NFAIcon, - NFAPreview, - ResolvedAddress, - Text, -} from '@/components'; -import { env } from '@/constants'; -import { FleekERC721 } from '@/integrations/ethereum/contracts'; -import { forwardStyledRef } from '@/theme'; -import { AppLog, getDate, getRepositoryFromURL } from '@/utils'; -import { parseNumberToHexColor } from '@/utils/color'; - -import { IndexedNFA } from '../indexed-nfa.context'; -import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; -import { Tab, TabContainer } from './tabs'; - -const Preview: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - const color = useMemo( - () => `#${parseNumberToHexColor(nfa.color ?? '')}`, - [nfa] - ); - - return ( - - ); -}; - -type BadgeProps = { - verified: boolean; -}; - -const Badge: React.FC = ({ verified }: BadgeProps) => { - const text = useMemo( - () => (verified ? 'Verified' : 'Unverified'), - [verified] - ); - - const icon = useMemo(() => (verified ? 'verified' : 'error'), [verified]); - const color = useMemo(() => (verified ? '$green10' : '$red10'), [verified]); - return ( - - - {text} - - ); -}; - -const Header: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - - - {nfa.name} - - - - - - {nfa.owner.id} - - - ); -}; - -type HeaderDataProps = { - label: string; - children: React.ReactNode; -}; - -const HeaderData: React.FC = ({ - label, - children, -}: HeaderDataProps) => ( - - {label} - {children} - -); - -const NFAInfo: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - return ( - - - {nfa.accessPoints?.length ?? 0} - - - - - {getDate(nfa.createdAt)} - - ); -}; - -type CustomButtonProps = { - icon: IconName; -}; - -const CustomButon = forwardStyledRef( - ({ icon, ...props }, ref) => ( - - ) -); - -type MenuItemProps = { - label: string; - iconName: IconName; - onClick: () => void; -}; - -const MenuItem: React.FC = ({ - label, - iconName, - onClick, -}: MenuItemProps) => { - return ( - - - {label} - - ); -}; - -const ButtonsFragment: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - const handleShareOnClick = (): void => { - const location = window.location.href; - navigator.clipboard.writeText(location); - AppLog.successToast('Link copied to clipboard'); - }; - - const handleShareOpenSeaOnClick = (): void => { - window.open( - `https://${ - env.environment === 'development' ? 'testnets' : '' - }.opensea.io/assets/${ - env.environment === 'development' ? 'goerli' : 'ethereum' - }/${FleekERC721.address}/${nfa.tokenId}`, - '_blank' - ); - }; - - const handleShareOnTwitterOnClick = (): void => { - window.open(env.twitter.url, '_blank'); //TODO replace with twitter share - }; - - return ( - - - - - - - - - - - - - - {/* TODO add tooltip to copy link */} - - - ); -}; - -const PropertiesFragment: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - const traitsToShow = useMemo(() => { - return [ - [nfa.ENS, 'ENS'], - [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], - [10, 'Version'], - [nfa.externalURL, 'Domain'], - ]; - }, [nfa]); - - return ( - - {traitsToShow.map(([value, label], index) => ( - - - {value || '-'} - - {label} - - ))} - - ); -}; - -const OverviewFragment: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - - - Token ID - {nfa.tokenId} - - - - Network - Mainnet - - - - Standard - ERC_721 - - - - Description - - - {nfa.description} - - - ); -}; - -const TabFragment: React.FC = () => { - const [tabSelected, setTabSelected] = useState(0); - const handleClick = (index: number): void => { - setTabSelected(index); - }; - return ( - <> - - {['Overview', 'Properties'].map((label, index) => ( - - ))} - - {tabSelected === 0 ? : } - - ); -}; - -export const IndexedNFAAsideFragment: React.FC = () => { - const ref = useRef(null); - const [top, setTop] = useState(); - const { nfa } = IndexedNFA.useContext(); - - const { backgroundColor } = App.useContext(); - const background = `radial-gradient(closest-corner circle at 90% 45%, #${backgroundColor}8c 1% ,#${backgroundColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${backgroundColor} 3%, #${backgroundColor}73 30%, #181818 70%)`; - - useEffect(() => { - setTop(ref.current?.getBoundingClientRect().top); - }, [ref]); - - return ( - - -
- - - - - - ); -}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx new file mode 100644 index 0000000..77520b5 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx @@ -0,0 +1,96 @@ +import { Button, Flex, Icon, IconName, Menu } from '@/components'; +import { env } from '@/constants'; +import { FleekERC721 } from '@/integrations/ethereum/contracts'; +import { forwardStyledRef } from '@/theme'; +import { AppLog } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type CustomButtonProps = { + icon: IconName; +}; + +const CustomButon = forwardStyledRef( + ({ icon, ...props }, ref) => ( + + ) +); + +type MenuItemProps = { + label: string; + iconName: IconName; + onClick: () => void; +}; + +const MenuItem: React.FC = ({ + label, + iconName, + onClick, +}: MenuItemProps) => { + return ( + + + {label} + + ); +}; + +export const ButtonsFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const handleShareOnClick = (): void => { + const location = window.location.href; + navigator.clipboard.writeText(location); + AppLog.successToast('Link copied to clipboard'); + }; + + const handleShareOpenSeaOnClick = (): void => { + window.open( + `https://${ + env.environment === 'development' ? 'testnets' : '' + }.opensea.io/assets/${ + env.environment === 'development' ? 'goerli' : 'ethereum' + }/${FleekERC721.address}/${nfa.tokenId}`, + '_blank' + ); + }; + + const handleShareOnTwitterOnClick = (): void => { + window.open(env.twitter.url, '_blank'); //TODO replace with twitter share + }; + + return ( + + + + + {/* TODO remove span and render as fragment */} + + + + + + + + + + {/* TODO add tooltip to copy link */} + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx new file mode 100644 index 0000000..85ece31 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; + +import { Flex, Icon, NFAIcon, ResolvedAddress } from '@/components'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type BadgeProps = { + verified: boolean; +}; + +const Badge: React.FC = ({ verified }: BadgeProps) => { + const text = useMemo( + () => (verified ? 'Verified' : 'Unverified'), + [verified] + ); + + const icon = useMemo(() => (verified ? 'verified' : 'error'), [verified]); + const color = useMemo(() => (verified ? '$green10' : '$red10'), [verified]); + return ( + + + {text} + + ); +}; + +export const Header: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + {nfa.name} + + + + + + {nfa.owner.id} + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx new file mode 100644 index 0000000..0759cc6 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx @@ -0,0 +1,35 @@ +import { Flex, Text } from '@/components'; +import { getDate } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type HeaderDataProps = { + label: string; + children: React.ReactNode; +}; + +const HeaderData: React.FC = ({ + label, + children, +}: HeaderDataProps) => ( + + {label} + {children} + +); + +export const NFAInfo: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + return ( + + + {nfa.accessPoints?.length ?? 0} + + + + + {getDate(nfa.createdAt)} + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx new file mode 100644 index 0000000..8facad7 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx @@ -0,0 +1,29 @@ +import { useMemo } from 'react'; + +import { NFAPreview } from '@/components'; +import { parseNumberToHexColor } from '@/utils/color'; + +import { IndexedNFA } from '../../indexed-nfa.context'; + +export const Preview: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const color = useMemo( + () => `#${parseNumberToHexColor(nfa.color ?? '')}`, + [nfa] + ); + + return ( + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx new file mode 100644 index 0000000..9e3de9a --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx @@ -0,0 +1,92 @@ +import { useMemo, useState } from 'react'; + +import { Flex } from '@/components'; +import { getRepositoryFromURL } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { Tab, TabContainer } from '../../tabs'; + +const OverviewFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + Token ID + {nfa.tokenId} + + + + Network + Mainnet + + + + Standard + ERC_721 + + + + Description + + + {nfa.description} + + + ); +}; + +const PropertiesFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const traitsToShow = useMemo(() => { + return [ + [nfa.ENS, 'ENS'], + [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], + [nfa.externalURL, 'Domain'], + ]; + }, [nfa]); + + return ( + + {traitsToShow.map(([value, label], index) => ( + + + {value || '-'} + + {label} + + ))} + + ); +}; + +export const TabFragment: React.FC = () => { + const [tabSelected, setTabSelected] = useState(0); + const handleClick = (index: number): void => { + setTabSelected(index); + }; + + return ( + <> + + {['Overview', 'Properties'].map((label, index) => ( + + ))} + + {tabSelected === 0 ? : } + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx new file mode 100644 index 0000000..ae4f766 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx @@ -0,0 +1,48 @@ +import { useEffect, useRef, useState } from 'react'; +import { Link } from 'react-router-dom'; + +import { App } from '@/app.context'; +import { Button } from '@/components'; +import { parseNumberToHexColor } from '@/utils/color'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { ButtonsFragment } from './aside-buttons.fragment'; +import { Header } from './aside-header.fragment'; +import { NFAInfo } from './aside-nfa-info.fragment'; +import { Preview } from './aside-preview.fragment'; +import { TabFragment } from './aside-tabs.fragment'; + +export const IndexedNFAAsideFragment: React.FC = () => { + const ref = useRef(null); + const [top, setTop] = useState(); + const { nfa } = IndexedNFA.useContext(); + + const { backgroundColor } = App.useContext(); + const background = `radial-gradient(closest-corner circle at 90% 45%, #${backgroundColor}8c 1% ,#${backgroundColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${backgroundColor} 3%, #${backgroundColor}73 30%, #181818 70%)`; + + useEffect(() => { + setTop(ref.current?.getBoundingClientRect().top); + }, [ref]); + + return ( + + +
+ + + + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/index.ts b/ui/src/views/indexed-nfa/fragments/index.ts index d90e1fb..e31d087 100644 --- a/ui/src/views/indexed-nfa/fragments/index.ts +++ b/ui/src/views/indexed-nfa/fragments/index.ts @@ -1,3 +1,3 @@ -export * from './aside.fragment'; -export * from './main.fragment'; +export * from './aside/aside.fragment'; +export * from './main/main.fragment'; export * from './skeleton.fragment'; diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx deleted file mode 100644 index 698169e..0000000 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { useState } from 'react'; - -import Rectangle1 from '@/assets/Rectangle-199.png'; -import Rectangle2 from '@/assets/Rectangle-200.png'; -import Rectangle3 from '@/assets/Rectangle-201.png'; -import { Combobox, Flex, ResolvedAddress, Text } from '@/components'; -import { getTimeSince } from '@/utils'; - -import { IndexedNFA } from '../indexed-nfa.context'; -import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; - -type SortItem = { - value: string; - label: string; -}; - -const orderResults: SortItem[] = [ - { value: 'newest', label: 'Newest' }, - { value: 'oldest', label: 'Oldest' }, -]; - -const Header: React.FC = () => { - const [selectedValue, setSelectedValue] = useState(orderResults[0]); - - const handleSortChange = (item: SortItem | undefined): void => { - //TODO integrate with context and sort - if (item) { - setSelectedValue(item); - } - }; - return ( - <> - - Hosted NFAs - - {({ Field, Options }) => ( - <> - - {(selected) => selected?.label || 'Select'} - - - {(item) => item.label} - - - )} - - - - ); -}; - -//TODO remove -const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; - -const AccessPointsListFragment: React.FC = () => { - const { - nfa: { accessPoints }, - } = IndexedNFA.useContext(); - - return ( - - {accessPoints && accessPoints?.length > 0 ? ( - accessPoints.map((item, index) => ( - - - - - - {item.id} - - - {item.owner.id} - - - {/* TODO get from bunny CDN */} - 220 views - - - {getTimeSince(item.createdAt)} - - - - - )) - ) : ( -
No access points found
- )} -
- ); -}; - -export const IndexedNFAMainFragment: React.FC = () => { - return ( - -
- - - ); -}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx new file mode 100644 index 0000000..00b1453 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx @@ -0,0 +1,61 @@ +import Rectangle1 from '@/assets/Rectangle-199.png'; +import Rectangle2 from '@/assets/Rectangle-200.png'; +import Rectangle3 from '@/assets/Rectangle-201.png'; +import { Flex, ResolvedAddress, Text } from '@/components'; +import { getTimeSince } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +//TODO remove +const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; + +export const AccessPointsListFragment: React.FC = () => { + const { + nfa: { accessPoints }, + } = IndexedNFA.useContext(); + + return ( + + {accessPoints && accessPoints?.length > 0 ? ( + accessPoints.map((item, index) => ( + + + + + + {item.id} + + + {item.owner.id} + + + {/* TODO get from bunny CDN */} + 220 views + + + {getTimeSince(item.createdAt)} + + + + + )) + ) : ( + +

No hosted NFAs

+
+ )} +
+ ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx new file mode 100644 index 0000000..c866d75 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx @@ -0,0 +1,56 @@ +import { useState } from 'react'; + +import { Combobox, Flex } from '@/components'; + +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type SortItem = { + value: string; + label: string; +}; + +const orderResults: SortItem[] = [ + { value: 'newest', label: 'Newest' }, + { value: 'oldest', label: 'Oldest' }, +]; + +export const Header: React.FC = () => { + const [selectedValue, setSelectedValue] = useState(orderResults[0]); + + const handleSortChange = (item: SortItem | undefined): void => { + //TODO integrate with context and sort + if (item) { + setSelectedValue(item); + } + }; + return ( + <> + + Hosted NFAs + + {({ Field, Options }) => ( + <> + + {(selected) => selected?.label || 'Select'} + + + {(item) => item.label} + + + )} + + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx new file mode 100644 index 0000000..2cdb4bc --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx @@ -0,0 +1,12 @@ +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { AccessPointsListFragment } from './main-ap-list.fragment'; +import { Header } from './main-header.fragment'; + +export const IndexedNFAMainFragment: React.FC = () => { + return ( + +
+ + + ); +}; diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index b7e9aef..e67d924 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -56,7 +56,6 @@ export const IndexedNFAStyles = { lineHeight: 1.35, fontWeight: 700, - // maxWidth: '10rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', @@ -151,12 +150,6 @@ export const IndexedNFAStyles = { lineHeight: 1.35, fontWeight: 700, }), - SectionHeading: styled('h2', { - fontSize: '$xl', - lineHeight: 1.2, - fontWeight: 700, - marginTop: Spacing, - }), AccessPoint: { List: styled('div', { display: 'flex', @@ -186,6 +179,12 @@ export const IndexedNFAStyles = { color: '$slate12', fontSize: '$lg', }), + NoResults: styled('div', { + display: 'flex', + justifyContent: 'center', + + fontSize: '$lg', + }), }, Divider: { Line: styled('span', { @@ -199,144 +198,6 @@ export const IndexedNFAStyles = { borderRadius: '100%', }), }, - Paragraph: styled('p', { - color: '$slate11', - lineHeight: 1.43, - }), - DataContainer: styled('div', { - display: 'flex', - flexDirection: 'column', - border: '1px solid $slate6', - borderRadius: '$lg', - padding: Spacing, - gap: `$1`, - }), - DataList: styled('div', { - display: 'flex', - flexWrap: 'wrap', - gap: '$5', - }), - VerificationBanner: styled('div', { - position: 'relative', - display: 'flex', - alignItems: 'center', - border: '1px solid $slate6', - borderRadius: '$lg', - padding: '$8 $5', - fontWeight: 700, - overflow: 'hidden', - - '&:after': { - content: '""', - position: 'absolute', - right: '-$5', - top: '-$10', - bottom: '-$10', - left: '84%', - borderRadius: '80% 0 0 80%', - }, - - variants: { - verified: { - true: { - borderColor: '$green11', - color: '$green11', - '&:after': { - backgroundColor: '$green11', - }, - }, - false: { - borderColor: '$red11', - color: '$red11', - '&:after': { - backgroundColor: '$red11', - }, - }, - }, - }, - }), - Table: { - Container: styled('div', { - border: '1px solid $slate6', - borderRadius: '10px', - padding: '0 $5', - - maxHeight: '15.125rem', - overflow: 'auto', - }), - Root: styled('table', { - width: 'calc(100% + 2 * $space$5)', - margin: '0 -$5', - }), - Head: styled('thead', { - position: 'sticky', - top: 0, - backgroundColor: '$black', - - '&:after': { - position: 'absolute', - content: '""', - bottom: 0, - left: 0, - right: 0, - borderBottom: '1px solid $slate6', - }, - }), - Row: styled('tr'), - Data: styled('td', { - padding: '$3', - maxWidth: '10rem', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }), - Body: styled('tbody', { - tr: { - '&:hover': { - backgroundColor: '$slate6', - cursor: 'pointer', - }, - }, - }), - Marker: styled('span', { - display: 'block', - margin: 'auto', - width: '0.5625rem', - height: '0.5625rem', - borderRadius: '$full', - backgroundColor: '$slate6', - - variants: { - variant: { - active: { - backgroundColor: '$green11', - }, - inactive: { - backgroundColor: '$slate8', - }, - }, - text: { - true: { - fontSize: '$xs', - padding: '0 $2', - width: 'fit-content', - height: 'fit-content', - }, - }, - }, - - compoundVariants: [ - { - variant: 'active', - text: true, - css: { - color: '$green11', - backgroundColor: '$green3', - }, - }, - ], - }), - }, }, Skeleton: styled(Skeleton, { diff --git a/ui/src/views/indexed-nfa/indexed-nfa.tsx b/ui/src/views/indexed-nfa/indexed-nfa.tsx index 773af84..51fdd49 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.tsx +++ b/ui/src/views/indexed-nfa/indexed-nfa.tsx @@ -54,11 +54,6 @@ export const IndexedNFAView: React.FC = () => { return ; } - if (!data.token) { - //TODO add 404 page - return
Token not found
; - } - return ( diff --git a/ui/src/views/indexed-nfa/fragments/tabs/index.ts b/ui/src/views/indexed-nfa/tabs/index.ts similarity index 100% rename from ui/src/views/indexed-nfa/fragments/tabs/index.ts rename to ui/src/views/indexed-nfa/tabs/index.ts diff --git a/ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts b/ui/src/views/indexed-nfa/tabs/tabs.styles.ts similarity index 100% rename from ui/src/views/indexed-nfa/fragments/tabs/tabs.styles.ts rename to ui/src/views/indexed-nfa/tabs/tabs.styles.ts diff --git a/ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx b/ui/src/views/indexed-nfa/tabs/tabs.tsx similarity index 100% rename from ui/src/views/indexed-nfa/fragments/tabs/tabs.tsx rename to ui/src/views/indexed-nfa/tabs/tabs.tsx From ca185c0f3665b0d0931fdcb108a0fb7caa7c521a Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Thu, 11 May 2023 11:38:03 -0300 Subject: [PATCH 07/14] feat: infinite scroll for hosted NFAs list --- ui/graphql/queries.graphql | 58 ++++---- ui/src/components/nfa-card/nfa-card.tsx | 3 +- ui/src/providers/apollo-provider.tsx | 4 + ui/src/utils/format.ts | 4 +- .../explore-list/nfa-list.fragment.tsx | 4 +- ui/src/views/explore/explore.context.tsx | 4 +- .../fragments/main/main-ap-list.fragment.tsx | 139 ++++++++++++------ .../fragments/main/main-header.fragment.tsx | 18 ++- .../fragments/main/skeleton.ap-list.tsx | 21 +++ .../fragments/skeleton.fragment.tsx | 6 +- .../views/indexed-nfa/indexed-nfa.context.tsx | 26 +++- 11 files changed, 204 insertions(+), 83 deletions(-) create mode 100644 ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx diff --git a/ui/graphql/queries.graphql b/ui/graphql/queries.graphql index e68bcd3..ae26842 100644 --- a/ui/graphql/queries.graphql +++ b/ui/graphql/queries.graphql @@ -22,50 +22,56 @@ query lastNFAsPaginated( accessPoints { id } - } -} - -query totalTokens { - tokens { - id - } -} - -query getLatestNFAs { - tokens { - id - name + verified } } query getNFADetail($id: ID!) { token(id: $id) { - tokenId - owner { + accessPoints { id } - name description - ENS - externalURL - logo color createdAt - accessPoints { - createdAt - contentVerified - owner { - id - } + ENS + externalURL + gitRepository { + id + } + logo + name + owner { id } verified verifier { id } - gitRepository { + tokenId + } +} + +query getAccessPointsNFA( + $tokenId: String! + $orderBy: AccessPoint_orderBy + $orderDirection: OrderDirection + $pageSize: Int + $skip: Int +) { + accessPoints( + where: { token: $tokenId } + orderDirection: $orderDirection + orderBy: $orderBy + first: $pageSize + skip: $skip + ) { + contentVerified + createdAt + owner { id } + id } } diff --git a/ui/src/components/nfa-card/nfa-card.tsx b/ui/src/components/nfa-card/nfa-card.tsx index 3883dd0..4f95991 100644 --- a/ui/src/components/nfa-card/nfa-card.tsx +++ b/ui/src/components/nfa-card/nfa-card.tsx @@ -61,8 +61,7 @@ export const NFACard: React.FC = forwardStyledRef< }} > {data.name} - {/* TODO: set correct value when it gets available on contract side */} - 0.5} /> + diff --git a/ui/src/providers/apollo-provider.tsx b/ui/src/providers/apollo-provider.tsx index 77d2d5b..5e9e9f0 100644 --- a/ui/src/providers/apollo-provider.tsx +++ b/ui/src/providers/apollo-provider.tsx @@ -46,6 +46,10 @@ const client = new ApolloClient({ keyArgs: ['where', 'orderBy', 'orderDirection'], merge: mergeByKey('id'), }, + accessPoints: { + keyArgs: ['where', 'orderBy', 'orderDirection'], + merge: mergeByKey('id'), + }, }, }, }, diff --git a/ui/src/utils/format.ts b/ui/src/utils/format.ts index d5eeb18..653b946 100644 --- a/ui/src/utils/format.ts +++ b/ui/src/utils/format.ts @@ -13,7 +13,9 @@ export const contractAddress = (address: string): string => { export const getRepositoryFromURL = (url: string): string => { const urlSplitted = url.split('/'); - return `${urlSplitted[3]}/${urlSplitted[4]}`; + return urlSplitted[3] && urlSplitted[4] + ? `${urlSplitted[3]}/${urlSplitted[4]}` + : ''; }; export const getDate = (date: number): string => { diff --git a/ui/src/views/explore/explore-list/nfa-list.fragment.tsx b/ui/src/views/explore/explore-list/nfa-list.fragment.tsx index 22593cf..bfe5df1 100644 --- a/ui/src/views/explore/explore-list/nfa-list.fragment.tsx +++ b/ui/src/views/explore/explore-list/nfa-list.fragment.tsx @@ -46,7 +46,9 @@ export const NFAListFragment: React.FC = () => { skip: pageNumber * pageSize, //skip is for the pagination }, onCompleted: (data) => { - if (data.tokens.length - tokens.length < pageSize) setEndReached(true); + if (data.tokens.length - tokens.length < pageSize) { + setEndReached(true); + } }, }); diff --git a/ui/src/views/explore/explore.context.tsx b/ui/src/views/explore/explore.context.tsx index a02d2e2..ea04e78 100644 --- a/ui/src/views/explore/explore.context.tsx +++ b/ui/src/views/explore/explore.context.tsx @@ -10,7 +10,7 @@ export type ExploreContext = { pageNumber: number; endReached: boolean; setSearch: (search: string) => void; - setOrderBy: (orderBy: string) => void; + setOrderBy: (orderBy: Token_orderBy) => void; setOrderDirection: (orderDirection: OrderDirection) => void; setPageNumber: (pageNumber: number) => void; setEndReached: (isEndReaced: boolean) => void; @@ -29,7 +29,7 @@ export abstract class Explore { children, }: Explore.ProviderProps) => { const [search, setSearch] = useState(''); - const [orderBy, setOrderBy] = useState('tokenId'); + const [orderBy, setOrderBy] = useState('tokenId'); const [orderDirection, setOrderDirection] = useState('desc'); const [pageNumber, setPageNumber] = useState(0); diff --git a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx index 00b1453..7b2f429 100644 --- a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx @@ -1,57 +1,114 @@ +import { useQuery } from '@apollo/client'; +import { ethers } from 'ethers'; +import { useEffect } from 'react'; + import Rectangle1 from '@/assets/Rectangle-199.png'; -import Rectangle2 from '@/assets/Rectangle-200.png'; -import Rectangle3 from '@/assets/Rectangle-201.png'; import { Flex, ResolvedAddress, Text } from '@/components'; -import { getTimeSince } from '@/utils'; +import { + AccessPoint as AccessPointType, + getAccessPointsNFADocument, + Owner, +} from '@/graphclient'; +import { useWindowScrollEnd } from '@/hooks'; +import { AppLog, getTimeSince } from '@/utils'; import { IndexedNFA } from '../../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { SkeletonAccessPointsListFragment } from './skeleton.ap-list'; -//TODO remove -const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; +type AccessPointProps = { + data: Pick & { + owner: Pick; + }; +}; + +const AccessPoint: React.FC = ({ + data, +}: AccessPointProps) => { + const { id: name, owner, createdAt } = data; + return ( + + + + + + {name} + + + {owner.id} + + + {/* TODO get from bunny CDN */} + 220 views + + {getTimeSince(createdAt)} + + + + ); +}; + +const pageSize = 10; //Set this size to test pagination export const AccessPointsListFragment: React.FC = () => { const { - nfa: { accessPoints }, + nfa: { tokenId }, + orderDirection, + pageNumber, + endReached, + setEndReached, + setPageNumber, } = IndexedNFA.useContext(); + const handleError = (error: unknown): void => { + AppLog.errorToast( + 'There was an error trying to get the access points', + error + ); + }; + + const { + loading: isLoading, + data: { accessPoints } = { accessPoints: [] }, + error: queryError, + } = useQuery(getAccessPointsNFADocument, { + skip: tokenId === undefined, + fetchPolicy: 'cache-and-network', + variables: { + tokenId: ethers.utils.hexlify(Number(tokenId)), + orderDirection: orderDirection, + orderBy: 'createdAt', + pageSize, + skip: pageNumber * pageSize, //skip is for the pagination + }, + onCompleted(data) { + if (data.accessPoints.length - accessPoints.length < pageSize) + setEndReached(true); + }, + onError(error) { + handleError(error); + }, + }); + + useEffect(() => { + // Update page number when there are cached tokens + setPageNumber(Math.ceil(accessPoints.length / pageSize)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useWindowScrollEnd(() => { + // debugger; + if (isLoading || endReached || queryError) return; + setPageNumber(pageNumber + 1); + }); + return ( - {accessPoints && accessPoints?.length > 0 ? ( - accessPoints.map((item, index) => ( - - - - - - {item.id} - - - {item.owner.id} - - - {/* TODO get from bunny CDN */} - 220 views - - - {getTimeSince(item.createdAt)} - - - - - )) - ) : ( + {accessPoints.map((item, index) => ( + + ))} + {isLoading && } + {!isLoading && accessPoints.length === 0 && (

No hosted NFAs

diff --git a/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx index c866d75..feb44a8 100644 --- a/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx @@ -1,28 +1,38 @@ import { useState } from 'react'; +import { OrderDirection } from '@/../.graphclient'; import { Combobox, Flex } from '@/components'; +import { AppLog } from '@/utils'; +import { IndexedNFA } from '../../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; type SortItem = { - value: string; + value: OrderDirection; label: string; }; const orderResults: SortItem[] = [ - { value: 'newest', label: 'Newest' }, - { value: 'oldest', label: 'Oldest' }, + { value: 'desc', label: 'Newest' }, + { value: 'asc', label: 'Oldest' }, ]; export const Header: React.FC = () => { + const { setPageNumber, setOrderDirection, setEndReached } = + IndexedNFA.useContext(); const [selectedValue, setSelectedValue] = useState(orderResults[0]); const handleSortChange = (item: SortItem | undefined): void => { - //TODO integrate with context and sort if (item) { setSelectedValue(item); + setPageNumber(0); + setEndReached(false); + setOrderDirection(item.value); + } else { + AppLog.errorToast('Error selecting sort option. Try again'); } }; + return ( <> diff --git a/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx b/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx new file mode 100644 index 0000000..ce64a83 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx @@ -0,0 +1,21 @@ +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +const SkeletonAccessPoint: React.FC = () => ( + + + + + + + + + +); + +export const SkeletonAccessPointsListFragment: React.FC = () => ( + + + + + +); diff --git a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx index e1e05a4..c7379c6 100644 --- a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx @@ -1,4 +1,5 @@ import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; +import { SkeletonAccessPointsListFragment } from './main/skeleton.ap-list'; export const IndexedNFASkeletonFragment: React.FC = () => ( @@ -7,10 +8,7 @@ export const IndexedNFASkeletonFragment: React.FC = () => ( - - - - + ); diff --git a/ui/src/views/indexed-nfa/indexed-nfa.context.tsx b/ui/src/views/indexed-nfa/indexed-nfa.context.tsx index fc99dac..ef044b9 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.context.tsx +++ b/ui/src/views/indexed-nfa/indexed-nfa.context.tsx @@ -1,4 +1,6 @@ -import { Owner, Token } from '@/graphclient'; +import { useState } from 'react'; + +import { OrderDirection, Owner, Token } from '@/graphclient'; import { createContext } from '@/utils'; const [Provider, useContext] = createContext({ @@ -10,7 +12,21 @@ const [Provider, useContext] = createContext({ export const IndexedNFA = { useContext, Provider: ({ children, nfa }: IndexedNFA.ProviderProps): JSX.Element => { - return {children}; + const [orderDirection, setOrderDirection] = + useState('desc'); + const [pageNumber, setPageNumber] = useState(0); + const [endReached, setEndReached] = useState(false); + + const context = { + nfa, + orderDirection, + pageNumber, + endReached, + setOrderDirection, + setPageNumber, + setEndReached, + }; + return {children}; }, }; @@ -19,6 +35,12 @@ export namespace IndexedNFA { nfa: Omit & { owner: Pick; }; + orderDirection: OrderDirection; + pageNumber: number; + endReached: boolean; + setOrderDirection: (orderDirection: OrderDirection) => void; + setPageNumber: (pageNumber: number) => void; + setEndReached: (isEndReaced: boolean) => void; }; export type ProviderProps = { From afba5e4d61eed172d95b069070dd141fe21ddad1 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Thu, 11 May 2023 11:39:09 -0300 Subject: [PATCH 08/14] chore: remove debugger --- .../views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx index 7b2f429..743a1e5 100644 --- a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx @@ -97,7 +97,6 @@ export const AccessPointsListFragment: React.FC = () => { }, []); useWindowScrollEnd(() => { - // debugger; if (isLoading || endReached || queryError) return; setPageNumber(pageNumber + 1); }); From d004890e88741c8acd02c923afbae49c105b08c9 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Thu, 11 May 2023 12:15:42 -0300 Subject: [PATCH 09/14] styles: responsivness on NFA detail page --- ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx | 2 +- ui/src/views/indexed-nfa/indexed-nfa.styles.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx index c7379c6..930d451 100644 --- a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx @@ -2,7 +2,7 @@ import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; import { SkeletonAccessPointsListFragment } from './main/skeleton.ap-list'; export const IndexedNFASkeletonFragment: React.FC = () => ( - + diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index e67d924..e979ea8 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -16,9 +16,10 @@ export const IndexedNFAStyles = { gridTemplateColumns: '20rem 1fr', }, - '@media (max-width: 580px)': { + '@media (max-width: 640px)': { gridTemplateAreas: '"aside" "main"', gridTemplateColumns: '1fr', + justifyItems: 'center', }, }), @@ -37,7 +38,7 @@ export const IndexedNFAStyles = { maxWidth: '24rem', mixBlendMode: 'screen', - '@media (max-width: 580px)': { + '@media (max-width: 640px)': { position: 'static', }, }), @@ -161,7 +162,7 @@ export const IndexedNFAStyles = { gridTemplateAreas: '"thumbnail data"', gap: '$4h', alignItems: 'center', - gridTemplateColumns: '10rem 1fr', + gridTemplateColumns: '7rem 1fr', }), Thumbnail: styled('div', { gridArea: 'thumbnail', From 540eabf4ff1d5fd2b003b3562bb6d94ed78dc138 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Tue, 16 May 2023 12:19:25 -0300 Subject: [PATCH 10/14] style: fix mobile designs --- ui/src/views/indexed-nfa/indexed-nfa.styles.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index e979ea8..f09b688 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -20,6 +20,7 @@ export const IndexedNFAStyles = { gridTemplateAreas: '"aside" "main"', gridTemplateColumns: '1fr', justifyItems: 'center', + padding: '0', }, }), @@ -145,6 +146,7 @@ export const IndexedNFAStyles = { display: 'flex', flexDirection: 'column', gap: Spacing, + width: '100%', }), Heading: styled('h2', { fontSize: '1.625rem', @@ -193,8 +195,8 @@ export const IndexedNFAStyles = { borderBottom: '1px solid $slate6', }), Elipse: styled('span', { - width: '0.375rem', - height: '0.375rem', + minWidth: '0.375rem', + minHeight: '0.375rem', backgroundColor: '$slate11', borderRadius: '100%', }), From 48755af8c1997a68b62f14db24253df5092644c9 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Tue, 16 May 2023 14:44:15 -0300 Subject: [PATCH 11/14] style: fix styles PR review --- .../aside/aside-buttons.fragment.tsx | 41 ++++++++----------- .../fragments/aside/aside-tabs.fragment.tsx | 2 +- .../views/indexed-nfa/indexed-nfa.styles.ts | 2 + 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx index 77520b5..28867f9 100644 --- a/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx @@ -7,6 +7,12 @@ import { AppLog } from '@/utils'; import { IndexedNFA } from '../../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +const openseaLink = `https://${ + env.environment === 'development' ? 'testnets' : '' +}.opensea.io/assets/${ + env.environment === 'development' ? 'goerli' : 'ethereum' +}/${FleekERC721.address}`; + type CustomButtonProps = { icon: IconName; }; @@ -26,19 +32,21 @@ const CustomButon = forwardStyledRef( type MenuItemProps = { label: string; iconName: IconName; - onClick: () => void; + href: string; }; const MenuItem: React.FC = ({ label, iconName, - onClick, + href, }: MenuItemProps) => { return ( - - - {label} - + + + + {label} + + ); }; @@ -51,39 +59,24 @@ export const ButtonsFragment: React.FC = () => { AppLog.successToast('Link copied to clipboard'); }; - const handleShareOpenSeaOnClick = (): void => { - window.open( - `https://${ - env.environment === 'development' ? 'testnets' : '' - }.opensea.io/assets/${ - env.environment === 'development' ? 'goerli' : 'ethereum' - }/${FleekERC721.address}/${nfa.tokenId}`, - '_blank' - ); - }; - - const handleShareOnTwitterOnClick = (): void => { - window.open(env.twitter.url, '_blank'); //TODO replace with twitter share - }; - return ( - + {/* TODO remove span and render as fragment */} diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx index 9e3de9a..87d0b00 100644 --- a/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx @@ -32,7 +32,7 @@ const OverviewFragment: React.FC = () => { Description - + {nfa.description} diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index f09b688..feb521f 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -128,6 +128,8 @@ export const IndexedNFAStyles = { }, Description: styled('p', { color: '$slate11', + overflowY: 'scroll', + pr: '1rem', }), }, Properties: { From 6cf32bedb97beb6d393b811107dfd6de444356a6 Mon Sep 17 00:00:00 2001 From: Camila Sosa Morales Date: Wed, 17 May 2023 16:10:37 -0300 Subject: [PATCH 12/14] feat: UI switch grid list on nfa listing (#261) * feat: display nfas grid or list * feat: add list view * chore: rename component * style: add margin * chore: add skeleton for nfa list * chore: add TODO comment * Update ui/src/views/explore/explore-list/nfa-list/nfa-list.styles.ts Co-authored-by: Felipe Mendes * Update ui/src/views/explore/explore-list/nfa-list/nfa-list.styles.ts Co-authored-by: Felipe Mendes * merge develop * style: responsiveness for explore view * chore: remove old file --------- Co-authored-by: Felipe Mendes --- ui/graphql/queries.graphql | 3 + ui/src/components/core/button/icon-button.tsx | 2 - ui/src/components/core/icon/icon-library.tsx | 4 + .../explore-list/explore-list.fragment.tsx | 4 +- .../explore/explore-list/nfa-list.styles.ts | 24 ----- .../explore/explore-list/nfa-list/index.ts | 1 + .../nfa-list/nfa-grid.fragment.tsx | 37 +++++++ .../nfa-list/nfa-list.fragment.tsx | 53 ++++++++++ .../explore-list/nfa-list/nfa-list.styles.ts | 71 ++++++++++++++ .../nfa-list/nfa-row.fragment.tsx | 54 +++++++++++ .../nfa-list/nfa-row.skeleton.tsx | 26 +++++ .../nfas-container.fragment.tsx} | 38 ++------ .../explore-list/nfa-search.fragment.tsx | 97 ++++++++++++------- .../explore/explore-list/nfa-search.styles.ts | 37 +++++++ ui/src/views/explore/explore.context.tsx | 7 ++ 15 files changed, 368 insertions(+), 90 deletions(-) delete mode 100644 ui/src/views/explore/explore-list/nfa-list.styles.ts create mode 100644 ui/src/views/explore/explore-list/nfa-list/index.ts create mode 100644 ui/src/views/explore/explore-list/nfa-list/nfa-grid.fragment.tsx create mode 100644 ui/src/views/explore/explore-list/nfa-list/nfa-list.fragment.tsx create mode 100644 ui/src/views/explore/explore-list/nfa-list/nfa-list.styles.ts create mode 100644 ui/src/views/explore/explore-list/nfa-list/nfa-row.fragment.tsx create mode 100644 ui/src/views/explore/explore-list/nfa-list/nfa-row.skeleton.tsx rename ui/src/views/explore/explore-list/{nfa-list.fragment.tsx => nfa-list/nfas-container.fragment.tsx} (61%) diff --git a/ui/graphql/queries.graphql b/ui/graphql/queries.graphql index 6bcec26..06644e4 100644 --- a/ui/graphql/queries.graphql +++ b/ui/graphql/queries.graphql @@ -22,6 +22,9 @@ query lastNFAsPaginated( accessPoints { id } + owner { + id + } verified } } diff --git a/ui/src/components/core/button/icon-button.tsx b/ui/src/components/core/button/icon-button.tsx index 32ec420..5eda990 100644 --- a/ui/src/components/core/button/icon-button.tsx +++ b/ui/src/components/core/button/icon-button.tsx @@ -65,7 +65,6 @@ export const IconButton = forwardRef( return props[size as 'sm' | 'md' | 'lg']; }, [size]); - return ( - - + - ( <> {file !== '' ? ( - logo + ) : ( )} - void; }; export const Switch: React.FC = ({ checked, onChange }) => ( - - - {checked ? 'Yes' : 'No'} - - + {checked ? 'Yes' : 'No'} + + ); diff --git a/ui/src/components/nfa-card/nfa-card.styles.ts b/ui/src/components/nfa-card/nfa-card.styles.ts index afa733f..78dc092 100644 --- a/ui/src/components/nfa-card/nfa-card.styles.ts +++ b/ui/src/components/nfa-card/nfa-card.styles.ts @@ -6,6 +6,7 @@ import { Skeleton } from '../layout'; export const NFACardStyles = { Container: styled(Link, { + all: 'unset', display: 'flex', flexDirection: 'column', minWidth: '12.5rem', diff --git a/ui/src/components/step/step.styles.ts b/ui/src/components/step/step.styles.ts index 1a6ed4f..30538e0 100644 --- a/ui/src/components/step/step.styles.ts +++ b/ui/src/components/step/step.styles.ts @@ -1,4 +1,4 @@ -import { Flex } from '@/components'; +import { Flex, Text } from '@/components'; import { styled } from '@/theme'; export const StepStyles = { @@ -23,4 +23,7 @@ export const StepStyles = { justifyContent: 'center', maxWidth: '$106', }), + Text: styled(Text, { + fontSize: '$4xl', + }), }; diff --git a/ui/src/components/step/step.tsx b/ui/src/components/step/step.tsx index bfa6beb..8343f05 100644 --- a/ui/src/components/step/step.tsx +++ b/ui/src/components/step/step.tsx @@ -12,7 +12,7 @@ export const Step: React.FC = ({ children, header }: StepProps) => { -

{header}

+ {header}
{children}
diff --git a/ui/src/components/toast/toast.styles.tsx b/ui/src/components/toast/toast.styles.ts similarity index 53% rename from ui/src/components/toast/toast.styles.tsx rename to ui/src/components/toast/toast.styles.ts index 205da5c..12087e8 100644 --- a/ui/src/components/toast/toast.styles.tsx +++ b/ui/src/components/toast/toast.styles.ts @@ -4,26 +4,26 @@ import { keyframes, styled } from '@/theme'; import { Icon, IconButton } from '../core'; import { Flex } from '../layout'; +import { IconStyles } from '../core/icon/icon.styles'; -export abstract class ToastStyles { - static readonly Provider = ToastLib.Provider; +const KeyFrames = { + hide: keyframes({ + '0%': { opacity: 1 }, + '100%': { opacity: 0 }, + }), + show: keyframes({ + '0%': { opacity: 0 }, + '100%': { opacity: 1 }, + }), +}; - static readonly DismissTimeout = 200; +export const ViewportPadding = '$md'; +export const DismissTimeout = 200; - static readonly ViewportPadding = '$md'; +export const ToastStyles = { + Provider: ToastLib.Provider, - static readonly KeyFrames = { - hide: keyframes({ - '0%': { opacity: 1 }, - '100%': { opacity: 0 }, - }), - show: keyframes({ - '0%': { opacity: 0 }, - '100%': { opacity: 1 }, - }), - }; - - static readonly Root = styled(ToastLib.Root, { + Root: styled(ToastLib.Root, { padding: '$4 $5', borderRadius: '$lg', borderWidth: '$default', @@ -46,23 +46,20 @@ export abstract class ToastStyles { '@media (prefers-reduced-motion: no-preference)': { '&[data-state="open"]': { - animation: `${this.KeyFrames.show} 750ms `, + animation: `${KeyFrames.show} 750ms `, }, '&[data-state="closed"]': { - animation: `${this.KeyFrames.hide} ${this.DismissTimeout}ms ease-in`, + animation: `${KeyFrames.hide} ${DismissTimeout}ms ease-in`, }, }, - }); - - static readonly Body = styled(ToastLib.Description, { + }), + Body: styled(ToastLib.Description, { fontSize: '$md', fontWeight: '$normal', mr: '$5', - }); - - static readonly Close = styled(ToastLib.Close, {}); - - static readonly CloseButton = styled(IconButton, { + }), + Close: ToastLib.Close, + CloseButton: styled(IconButton, { variants: { colorScheme: { error: { @@ -73,11 +70,10 @@ export abstract class ToastStyles { }, }, }, - }); - - static readonly Viewport = styled(ToastLib.Viewport, { - padding: '$14', - + }), + Viewport: styled(ToastLib.Viewport, { + py: '$14', + listStyleType: 'none', position: 'fixed', bottom: 0, left: '50%', @@ -86,17 +82,16 @@ export abstract class ToastStyles { flexDirection: 'column', gap: '$4', zIndex: '$toast', - }); - - static readonly Layout = styled(Flex, { + minWidth: '250px', + }), + Layout: styled(Flex, { flexDirection: 'row', justifyContent: 'space-between', - }); - - static readonly Icon = styled(Icon, { - fontSize: '$5', + alignItems: 'center', + }), + Icon: styled(Icon, { + fontSize: '1.25rem', marginRight: '$2h', - }); - - static readonly Content = styled(Flex, {}); -} + }), + Content: styled(Flex), +}; diff --git a/ui/src/components/toast/toast.tsx b/ui/src/components/toast/toast.tsx index e6e4cb8..32ad9cb 100644 --- a/ui/src/components/toast/toast.tsx +++ b/ui/src/components/toast/toast.tsx @@ -8,7 +8,7 @@ import { } from '@/store'; import { Icon } from '../core'; -import { ToastStyles } from './toast.styles'; +import { DismissTimeout, ToastStyles } from './toast.styles'; type ToastProps = ToastsState.Toast; @@ -29,7 +29,7 @@ const Toast: React.FC = ({ if (onDismiss) onDismiss(); setTimeout(() => { dispatch(toastsActions.dismiss(id)); - }, ToastStyles.DismissTimeout); + }, DismissTimeout); } }, [onDismiss, dispatch, id] @@ -54,6 +54,7 @@ const Toast: React.FC = ({ variant="link" icon={} onClick={onDismiss} + css={{ p: '0' }} /> diff --git a/ui/src/index.tsx b/ui/src/index.tsx index ad6d881..dff3aa4 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -1,4 +1,3 @@ -import './styles.css'; import React from 'react'; import ReactDOM from 'react-dom/client'; diff --git a/ui/src/styles.css b/ui/src/styles.css deleted file mode 100644 index b5c61c9..0000000 --- a/ui/src/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/ui/src/theme/foundations/shadows.ts b/ui/src/theme/foundations/shadows.ts index d81a927..a9bb6cf 100644 --- a/ui/src/theme/foundations/shadows.ts +++ b/ui/src/theme/foundations/shadows.ts @@ -1,4 +1,3 @@ -//TODO: maybe we can use tailwind for this export const shadows = { none: '0 0 #0000', inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)', diff --git a/ui/src/theme/globals.ts b/ui/src/theme/globals.ts index 25df9c7..8686a4c 100644 --- a/ui/src/theme/globals.ts +++ b/ui/src/theme/globals.ts @@ -1,18 +1,25 @@ import { globalCss } from '@stitches/react'; export const themeGlobals = globalCss({ - 'html, body, #root': { + 'html, body': { height: '100%', - padding: 0, + + //TODO add theme colors color: '#ECEDEE', backgroundColor: 'black', fontFamily: 'Manrope', - fontSize: '16px', '@media (max-width: 850px)': { fontSize: '13px', }, }, + + '*': { + margin: '0', + padding: '0', + border: '0', + boxSizing: 'border-box', + }, }); diff --git a/ui/src/theme/themes.ts b/ui/src/theme/themes.ts index b6dfd7d..29fb5bf 100644 --- a/ui/src/theme/themes.ts +++ b/ui/src/theme/themes.ts @@ -52,7 +52,7 @@ const createDripStitches = < }; const { createTheme, ...otherStitches } = createStitches({ - prefix: prefix || ('drip' as string), + prefix: prefix || ('nfa' as string), media: { ...libMedia, ...(media || {}), diff --git a/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx b/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx index 932e393..1e9fa88 100644 --- a/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx +++ b/ui/src/views/access-point/ap-form-step/create-ap-form-body.tsx @@ -26,7 +26,7 @@ import { useAccessPointFormContext } from './create-ap.form.context'; export const SelectedNFA: React.FC = () => { const { nfa } = CreateAccessPoint.useContext(); - + if (!nfa.logo) return null; return ( { const context = useAccessPointFormContextInit(); return ( - + - +
); }; diff --git a/ui/src/views/components-test/components-test.tsx b/ui/src/views/components-test/components-test.tsx index d20a2c1..cf7a85a 100644 --- a/ui/src/views/components-test/components-test.tsx +++ b/ui/src/views/components-test/components-test.tsx @@ -1,4 +1,6 @@ -import { Flex, ResolvedAddress } from '@/components'; +import { useState } from 'react'; + +import { Flex, ResolvedAddress, Switch } from '@/components'; import { ColorPickerTest } from './color-picker'; import { ComboboxTest } from './combobox-test'; @@ -6,9 +8,11 @@ import { SpinnerTest } from './spinner-test'; import { ToastTest } from './toast-test'; export const ComponentsTest: React.FC = () => { + const [checked, setChecked] = useState(false); return ( + {'0x7ed735b7095c05d78df169f991f2b7f1a1f1a049a'} diff --git a/ui/src/views/explore/explore-header/explore-header.styles.ts b/ui/src/views/explore/explore-header/explore-header.styles.ts index 6f2f03e..ddc9e28 100644 --- a/ui/src/views/explore/explore-header/explore-header.styles.ts +++ b/ui/src/views/explore/explore-header/explore-header.styles.ts @@ -11,6 +11,7 @@ export abstract class ExploreHeaderStyles { static readonly Text = styled('h2', { fontSize: '$4xl', maxWidth: '46rem', + fontWeight: 400, }); static readonly GrayText = styled('span', { diff --git a/ui/src/views/explore/explore-list/nfa-list/nfa-row.fragment.tsx b/ui/src/views/explore/explore-list/nfa-list/nfa-row.fragment.tsx index 5465b54..5fc18a5 100644 --- a/ui/src/views/explore/explore-list/nfa-list/nfa-row.fragment.tsx +++ b/ui/src/views/explore/explore-list/nfa-list/nfa-row.fragment.tsx @@ -30,8 +30,7 @@ export const NFARow: React.FC = ({ token }: NFARowProps) => { = ({ return ( - + {/* TODO remove for real image */} + {name} diff --git a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx index 930d451..6b3ed41 100644 --- a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx @@ -3,9 +3,9 @@ import { SkeletonAccessPointsListFragment } from './main/skeleton.ap-list'; export const IndexedNFASkeletonFragment: React.FC = () => ( - - - + diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx index 841aa88..eb0b660 100644 --- a/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx @@ -6,7 +6,7 @@ import { useGithubStore } from '@/store'; import { Mint } from '@/views/mint/mint.context'; import { GithubRepositorySelectionStyles as S } from './github-repository-selection.styles'; -import { RepositoriesList } from './repositories-list'; +import { RepositoriesListFragment } from './repositories-list'; import { UserOrgsCombobox } from './users-orgs-combobox'; export const Loading: React.FC = () => ( @@ -68,7 +68,7 @@ export const GithubRepositoryConnection: React.FC = () => { queryUserAndOrganizations === 'loading' ? ( ) : ( - + )} diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts new file mode 100644 index 0000000..b57446c --- /dev/null +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts @@ -0,0 +1 @@ +export * from './repositories-list.fragment'; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx similarity index 72% rename from ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx rename to ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx index de11b53..7b01e6d 100644 --- a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx @@ -1,16 +1,16 @@ import { useEffect, useMemo } from 'react'; -import { Flex } from '@/components'; import { githubActions, useAppDispatch, useGithubStore } from '@/store'; import { Mint } from '@/views/mint/mint.context'; +import { RepositoiresListStyles as S } from './repositories-list.styles'; import { Repository } from './repository'; type RepositoriesListProps = { searchValue: string; }; -export const RepositoriesList: React.FC = ({ +export const RepositoriesListFragment: React.FC = ({ searchValue, }: RepositoriesListProps) => { const { selectedUserOrg } = Mint.useContext(); @@ -37,15 +37,7 @@ export const RepositoriesList: React.FC = ({ } return ( - + {filteredRepositories.length > 0 ? ( filteredRepositories.map((repo, index, { length }) => ( = ({ )) ) : ( // TODO: update this after designs are done -
- Nothing found. -
+ Nothing found. )} -
+ ); }; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts new file mode 100644 index 0000000..e4cf54e --- /dev/null +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts @@ -0,0 +1,20 @@ +import { Flex } from '@/components'; +import { styled } from '@/theme'; + +export const RepositoiresListStyles = { + Container: styled(Flex, { + height: '$60', + overflowX: 'hidden', + overflowY: 'scroll', + flexDirection: 'column', + pr: '$3h', + }), + Message: styled('div', { + position: 'relative', + cursor: 'default', + userSelect: 'none', + p: '$2 $3h $4 $3h', + color: '$slate11', + textAlign: 'center', + }), +}; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repository.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repository.tsx similarity index 100% rename from ui/src/views/mint/github-step/steps/github-repository-selection/repository.tsx rename to ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repository.tsx diff --git a/ui/src/views/mint/mint.styles.ts b/ui/src/views/mint/mint.styles.ts index f0b0796..75b7426 100644 --- a/ui/src/views/mint/mint.styles.ts +++ b/ui/src/views/mint/mint.styles.ts @@ -5,16 +5,11 @@ export const MintStyles = { Container: styled(Flex, { height: '100%', justifyContent: 'center', + minHeight: '85vh', + alignItems: 'flex-start', '@md': { - //to align on center - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - }, - - '@lg': { - flexDirection: 'row', + alignItems: 'center', }, }), }; diff --git a/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts b/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts index 056a3f3..8b55310 100644 --- a/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts +++ b/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts @@ -10,6 +10,7 @@ export const VerifyNfaStepStyles = { Text: styled(Text, { color: '$slate11', fontSize: '$sm', + lineHeight: '1.25rem', }), VerifyContainer: styled(Card.Text, { p: '$4', diff --git a/ui/src/views/mint/nft-card/nft-card.tsx b/ui/src/views/mint/nft-card/nft-card.tsx index 2280076..bf0c5c4 100644 --- a/ui/src/views/mint/nft-card/nft-card.tsx +++ b/ui/src/views/mint/nft-card/nft-card.tsx @@ -29,7 +29,7 @@ export const NftCard: React.FC = ({ onClick, isLoading, }: NftCardProps) => { - const size = '26.5rem'; + const size = '100%'; const { form: { appName: { @@ -48,7 +48,15 @@ export const NftCard: React.FC = ({ } = useMintFormContext(); return ( - +