From 3f5594c5cd33659b9499fed66c16f6c1a3c94c6e Mon Sep 17 00:00:00 2001 From: Jakz Date: Tue, 19 Mar 2024 13:54:38 +0800 Subject: [PATCH 01/64] nft selector components --- .../components/NftSelector/NftSelector.tsx | 87 +++++++++++ .../NftSelectorContract.tsx | 74 ++++++++++ .../NftSelectorContractHeader.tsx | 44 ++++++ .../NftSelectorContractPickerGrid.tsx | 104 +++++++++++++ .../NftSelectorContractWrapper.tsx | 23 +++ .../NftSelector/NftSelectorHeader.tsx | 34 +++++ .../NftSelector/NftSelectorToolbar.tsx | 138 ++++++++++++++++++ .../NftSelector/NftSelectorWrapper.tsx | 73 +++++++++ .../components/NftSelector/useNftSelector.ts | 43 ++++++ 9 files changed, 620 insertions(+) create mode 100644 apps/mobile/src/components/NftSelector/NftSelector.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractWrapper.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorHeader.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorToolbar.tsx create mode 100644 apps/mobile/src/components/NftSelector/NftSelectorWrapper.tsx create mode 100644 apps/mobile/src/components/NftSelector/useNftSelector.ts diff --git a/apps/mobile/src/components/NftSelector/NftSelector.tsx b/apps/mobile/src/components/NftSelector/NftSelector.tsx new file mode 100644 index 0000000000..84eb26978f --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelector.tsx @@ -0,0 +1,87 @@ +import { useNavigation } from '@react-navigation/native'; +import { Suspense, useCallback } from 'react'; +import { View } from 'react-native'; + +import { useSyncTokensActions } from '~/contexts/SyncTokensContext'; +import { MainTabStackNavigatorProp } from '~/navigation/types'; +import { NftSelectorLoadingSkeleton } from '~/screens/NftSelectorScreen/NftSelectorLoadingSkeleton'; +import { NftSelectorPickerGrid } from '~/screens/NftSelectorScreen/NftSelectorPickerGrid'; + +import { NftSelectorHeader } from './NftSelectorHeader'; +import { NftSelectorToolbar } from './NftSelectorToolbar'; +import { NftSelectorWrapper } from './NftSelectorWrapper'; +import { useNftSelector } from './useNftSelector'; + +export function NftSelector() { + const { + searchQuery, + setSearchQuery, + ownershipTypeFilter, + setFilter, + networkFilter, + setNetworkFilter, + sortView, + setSortView, + sync, + } = useNftSelector(); + + const navigation = useNavigation(); + const { isSyncing, isSyncingCreatedTokens } = useSyncTokensActions(); + + const handleSelectNft = useCallback( + (tokenId: string) => { + navigation.navigate('PostComposer', { + tokenId, + }); + }, + [navigation] + ); + + const handleSelectNftGroup = useCallback( + (contractAddress: string) => { + navigation.navigate('NftSelectorContractScreen', { + contractAddress: contractAddress, + page: 'Post', + ownerFilter: 'Collected', + fullScreen: true, + }); + }, + [navigation] + ); + + return ( + + + + + + + }> + + + + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx new file mode 100644 index 0000000000..b41db2c265 --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx @@ -0,0 +1,74 @@ +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; +import { useCallback, useMemo } from 'react'; +import { graphql, useLazyLoadQuery } from 'react-relay'; + +import { NftSelectorContractQuery } from '~/generated/NftSelectorContractQuery.graphql'; +import { MainTabStackNavigatorParamList, MainTabStackNavigatorProp } from '~/navigation/types'; + +import { NftSelectorContractHeader } from './NftSelectorContractHeader'; +import { NftSelectorContractPickerGrid } from './NftSelectorContractPickerGrid'; +import { NftSelectorContractWrapper } from './NftSelectorContractWrapper'; + +export function NftSelectorContract() { + const query = useLazyLoadQuery( + graphql` + query NftSelectorContractQuery { + viewer { + ... on Viewer { + user { + tokens { + __typename + definition { + contract { + contractAddress { + address + } + } + } + ...NftSelectorContractPickerGridFragment + } + } + } + } + } + `, + {} + ); + + const navigation = useNavigation(); + const route = useRoute>(); + const contractAddress = route.params.contractAddress; + const isCreator = route.params.ownerFilter === 'Created'; + + const handleSelectNft = useCallback( + (tokenId: string) => { + navigation.navigate('PostComposer', { + tokenId, + }); + }, + [navigation] + ); + + const nonNullableTokens = useMemo(() => { + const tokens = []; + + for (const token of query.viewer?.user?.tokens ?? []) { + if (token?.definition?.contract?.contractAddress?.address === contractAddress) { + tokens.push(token); + } + } + + return tokens; + }, [query.viewer?.user?.tokens, contractAddress]); + + return ( + + + + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx new file mode 100644 index 0000000000..78de460a73 --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx @@ -0,0 +1,44 @@ +import { View, ViewProps } from 'react-native'; + +import { BackButton } from '~/components/BackButton'; +import { Typography } from '~/components/Typography'; + +type Props = { + title: string; + rightButton?: React.ReactNode; + style?: ViewProps['style']; +}; + +export function NftSelectorContractHeader({ title, rightButton, style }: Props) { + return ( + + + + + + + {title} + + + {rightButton ? {rightButton} : null} + {/* {isCreator ? ( + + + + ) : null} */} + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx new file mode 100644 index 0000000000..3d2f95b71b --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx @@ -0,0 +1,104 @@ +import { FlashList, ListRenderItem } from '@shopify/flash-list'; +import { useCallback, useMemo } from 'react'; +import { View, ViewProps } from 'react-native'; +import { graphql, useFragment } from 'react-relay'; + +import { GalleryRefreshControl } from '~/components/GalleryRefreshControl'; +import { useSyncTokensActions } from '~/contexts/SyncTokensContext'; +import { NftSelectorContractPickerGridFragment$key } from '~/generated/NftSelectorContractPickerGridFragment.graphql'; +import { NftSelectorLoadingSkeleton } from '~/screens/NftSelectorScreen/NftSelectorLoadingSkeleton'; +import { NftSelectorPickerSingularAsset } from '~/screens/NftSelectorScreen/NftSelectorPickerSingularAsset'; + +type Props = { + isCreator: boolean; + tokenRefs: NftSelectorContractPickerGridFragment$key; + onSelect: (tokenId: string) => void; + style?: ViewProps['style']; +}; + +export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect, style }: Props) { + const tokens = useFragment( + graphql` + fragment NftSelectorContractPickerGridFragment on Token @relay(plural: true) { + dbid + definition { + community { + name + } + contract { + dbid + contractAddress { + address + } + } + } + + ...NftSelectorPickerSingularAssetFragment + } + `, + tokenRefs + ); + + const { isSyncingCreatedTokensForContract, syncCreatedTokensForExistingContract } = + useSyncTokensActions(); + + // [TODO-subcomref] might switch this to community ID + const contractId = tokens[0]?.definition?.contract?.dbid ?? ''; + + const handleSyncTokensForContract = useCallback(async () => { + syncCreatedTokensForExistingContract(contractId); + }, [syncCreatedTokensForExistingContract, contractId]); + + const renderItem = useCallback>( + ({ item: row }) => { + return ( + + {row.map((token) => { + return ( + + ); + })} + + {Array.from({ length: 3 - row.length }).map((_, index) => { + return ; + })} + + ); + }, + [onSelect] + ); + + const rows = useMemo(() => { + const rows = []; + for (let i = 0; i < tokens.length; i += 3) { + rows.push(tokens.slice(i, i + 3)); + } + return rows; + }, [tokens]); + + return ( + + {isSyncingCreatedTokensForContract ? ( + + ) : ( + + ) : undefined + } + /> + )} + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractWrapper.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractWrapper.tsx new file mode 100644 index 0000000000..a540d7d7e3 --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractWrapper.tsx @@ -0,0 +1,23 @@ +import { PropsWithChildren } from 'react'; +import { View } from 'react-native'; + +import { useSafeAreaPadding } from '~/components/SafeAreaViewWithPadding'; + +type Props = { + isFullscreen?: boolean; +} & PropsWithChildren; + +export function NftSelectorContractWrapper({ isFullscreen, children }: Props) { + const { top } = useSafeAreaPadding(); + + return ( + + {children} + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorHeader.tsx b/apps/mobile/src/components/NftSelector/NftSelectorHeader.tsx new file mode 100644 index 0000000000..ae5341233d --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorHeader.tsx @@ -0,0 +1,34 @@ +import { PropsWithChildren } from 'react'; +import { View, ViewProps } from 'react-native'; + +import { BackButton } from '../BackButton'; +import { Typography } from '../Typography'; + +type Props = { + title: string; + rightButton?: React.ReactNode; + style?: ViewProps['style']; +} & PropsWithChildren; + +export function NftSelectorHeader({ rightButton, title, children, style }: Props) { + return ( + + + + + + + {title} + + + + {rightButton && rightButton} + + + {children} + + ); +} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorToolbar.tsx b/apps/mobile/src/components/NftSelector/NftSelectorToolbar.tsx new file mode 100644 index 0000000000..f1eba51ff0 --- /dev/null +++ b/apps/mobile/src/components/NftSelector/NftSelectorToolbar.tsx @@ -0,0 +1,138 @@ +import { useCallback, useMemo, useRef } from 'react'; +import { View, ViewProps } from 'react-native'; +import { contexts } from 'shared/analytics/constants'; +import { chains } from 'shared/utils/chains'; +import { SlidersIcon } from 'src/icons/SlidersIcon'; +import { getChainIconComponent } from 'src/utils/getChainIconComponent'; + +import { SearchIcon } from '~/navigation/MainTabNavigator/SearchIcon'; +import { + NetworkChoice, + NftSelectorFilterBottomSheet, + NftSelectorSortView, +} from '~/screens/NftSelectorScreen/NftSelectorFilterBottomSheet'; + +import { AnimatedRefreshIcon } from '../AnimatedRefreshIcon'; +import { FadedInput } from '../FadedInput'; +import { GalleryBottomSheetModalType } from '../GalleryBottomSheet/GalleryBottomSheetModal'; +import { IconContainer } from '../IconContainer'; +import { Select } from '../Select'; + +const NETWORKS: { + label: string; + id: NetworkChoice; + icon: JSX.Element; + hasCreatorSupport: boolean; +}[] = [ + ...chains.map((chain) => ({ + label: chain.name, + id: chain.name, + icon: getChainIconComponent(chain), + hasCreatorSupport: chain.hasCreatorSupport, + })), +]; + +type Props = { + searchQuery: string; + setSearchQuery: (value: string) => void; + ownershipTypeFilter: 'Collected' | 'Created'; + setFilter: (value: 'Collected' | 'Created') => void; + networkFilter: NetworkChoice; + setNetworkFilter: (value: NetworkChoice) => void; + sortView: NftSelectorSortView; + setSortView: (value: NftSelectorSortView) => void; + isSyncing: boolean; + isSyncingCreatedTokens: boolean; + handleSync: () => void; + style?: ViewProps['style']; +}; + +export function NftSelectorToolbar({ + searchQuery, + setSearchQuery, + ownershipTypeFilter, + setFilter, + networkFilter, + setNetworkFilter, + sortView, + setSortView, + isSyncing, + isSyncingCreatedTokens, + handleSync, + style, +}: Props) { + const filterBottomSheetRef = useRef(null); + + const handleSettingsPress = useCallback(() => { + filterBottomSheetRef.current?.present(); + }, []); + + const decoratedNetworks = useMemo(() => { + return NETWORKS.map((network) => { + return { + ...network, + disabled: ownershipTypeFilter === 'Created' && !network.hasCreatorSupport, + }; + }); + }, [ownershipTypeFilter]); + + const handleNetworkChange = useCallback( + (network: NetworkChoice) => { + setNetworkFilter(network); + }, + [setNetworkFilter] + ); + + return ( + + + } + placeholder="Search pieces" + /> + + + + - - - - - } - eventElementId="NftSelectorSelectorSettingsButton" - eventName="NftSelectorSelectorSettingsButton pressed" - eventContext={contexts.Posts} - /> - - - - - - }> - - - - - - - - - - - ); -} - -function CreatorBottomSheetWrapper({ - isViewingCreatedFilter, -}: { - isViewingCreatedFilter: boolean; -}) { - const experienceQuery = useLazyLoadQuery( - graphql` - query NftSelectorPickerScreenExperienceQuery { - ...useExperienceFragment - } - `, - {} - ); - - const [creatorBetaAnnouncementSeen, setCreatorBetaAnnouncementSeen] = useExperience({ - type: 'CreatorBetaMicroAnnouncementModal', - queryRef: experienceQuery, - }); - const { showBottomSheetModal, hideBottomSheetModal } = useBottomSheetModalActions(); - - useEffect(() => { - if (isViewingCreatedFilter && !creatorBetaAnnouncementSeen) { - showBottomSheetModal({ - content: , - }); - setCreatorBetaAnnouncementSeen({ experienced: true }); - } - }, [ - creatorBetaAnnouncementSeen, - hideBottomSheetModal, - isViewingCreatedFilter, - setCreatorBetaAnnouncementSeen, - showBottomSheetModal, - ]); - - useEffect(() => {}, [hideBottomSheetModal, showBottomSheetModal]); - - return <>; -} - -export function NftSelectorPickerScreen() { - return ( - }> - - - ); -} From f17b171e239477e39671d0ced6867e88ee238fff Mon Sep 17 00:00:00 2001 From: Jakz Date: Tue, 19 Mar 2024 15:14:00 +0800 Subject: [PATCH 07/64] Clean up messed up logic --- .../NftSelectorContractPickerGrid.tsx | 6 -- .../src/navigation/PostStackNavigator.tsx | 4 +- .../NftSelectorPickerGrid.tsx | 80 ++++++------------- .../NftSelectorPickerSingularAsset.tsx | 38 ++------- 4 files changed, 36 insertions(+), 92 deletions(-) diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx index 3d2f95b71b..bf4cd65a62 100644 --- a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid.tsx @@ -22,14 +22,8 @@ export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect, fragment NftSelectorContractPickerGridFragment on Token @relay(plural: true) { dbid definition { - community { - name - } contract { dbid - contractAddress { - address - } } } diff --git a/apps/mobile/src/navigation/PostStackNavigator.tsx b/apps/mobile/src/navigation/PostStackNavigator.tsx index 46518a8c92..ab2a52a97c 100644 --- a/apps/mobile/src/navigation/PostStackNavigator.tsx +++ b/apps/mobile/src/navigation/PostStackNavigator.tsx @@ -1,8 +1,8 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { PostStackNavigatorParamList } from '~/navigation/types'; -import { NftSelectorPickerScreen } from '~/screens/NftSelectorScreen/NftSelectorPickerScreen'; import { PostComposerScreen } from '~/screens/PostScreen/PostComposerScreen'; +import { PostNftSelectorScreen } from '~/screens/PostScreen/PostNftSelectorScreen'; const Stack = createNativeStackNavigator(); @@ -15,7 +15,7 @@ export function PostStackNavigator() { void; + + onSelect: (tokenId: string) => void; + onSelectNftGroup: (contractAddress: string) => void; }; export function NftSelectorPickerGrid({ searchCriteria, - screen, style, onRefresh, + onSelect, + onSelectNftGroup, }: NftSelectorPickerGridProps) { const query = useLazyLoadQuery( graphql` @@ -269,7 +267,8 @@ export function NftSelectorPickerGrid({ tokenRefs={group.tokens} contractAddress={group.address} ownerFilter={searchCriteria.ownerFilter} - screen={screen} + onSelectNft={onSelect} + onSelectGroup={onSelectNftGroup} /> ); })} @@ -281,7 +280,7 @@ export function NftSelectorPickerGrid({ ); }, - [screen, searchCriteria.ownerFilter] + [onSelect, onSelectNftGroup, searchCriteria.ownerFilter] ); const navigation = useNavigation(); @@ -389,10 +388,10 @@ type TokenGridProps = { tokenRefs: NftSelectorPickerGridTokenGridFragment$key; ownerFilter: 'Created' | 'Collected'; contractAddress: string; - screen: ScreenWithNftSelector; + onPress: (contractAddress: string) => void; }; -function TokenGrid({ tokenRefs, contractAddress, screen, style, ownerFilter }: TokenGridProps) { +function TokenGrid({ tokenRefs, contractAddress, style, onPress }: TokenGridProps) { const tokens = useFragment( graphql` fragment NftSelectorPickerGridTokenGridFragment on Token @relay(plural: true) { @@ -404,11 +403,6 @@ function TokenGrid({ tokenRefs, contractAddress, screen, style, ownerFilter }: T tokenRefs ); - const navigation = useNavigation(); - const route = useRoute>(); - - const isFullscreen = route.params.fullScreen; - type Row = { tokens: NftSelectorPickerGridTokenGridFragment$data[number][] }; const rows: Row[] = useMemo(() => { @@ -418,26 +412,8 @@ function TokenGrid({ tokenRefs, contractAddress, screen, style, ownerFilter }: T }, [tokens]); const handlePress = useCallback(() => { - if (screen === 'Onboarding') { - navigation.navigate('Login', { - screen: 'OnboardingNftSelectorContract', - params: { - contractAddress: contractAddress, - page: screen, - ownerFilter: ownerFilter, - fullScreen: isFullscreen, - }, - }); - return; - } - - navigation.navigate('NftSelectorContractScreen', { - contractAddress: contractAddress, - page: screen, - ownerFilter: ownerFilter, - fullScreen: isFullscreen, - }); - }, [contractAddress, isFullscreen, navigation, ownerFilter, screen]); + onPress(contractAddress); + }, [contractAddress, onPress]); return ( void; + onSelectGroup: (contractAddress: string) => void; }; -function TokenGroup({ tokenRefs, contractAddress, style, ownerFilter, screen }: TokenGroupProps) { +function TokenGroup({ + tokenRefs, + contractAddress, + style, + ownerFilter, + onSelectNft, + onSelectGroup, +}: TokenGroupProps) { const tokens = useFragment( graphql` fragment NftSelectorPickerGridOneOrManyFragment on Token @relay(plural: true) { @@ -489,18 +473,6 @@ function TokenGroup({ tokenRefs, contractAddress, style, ownerFilter, screen }: tokenRefs ); - const navigation = useNavigation(); - - const handleSelectNft = useCallback(() => { - if (screen === 'Onboarding') { - navigation.navigate('Login', { - screen: 'OnboardingProfileBio', - }); - return; - } - navigation.pop(); - }, [navigation, screen]); - const [firstToken] = tokens; if (!firstToken) { return null; @@ -509,13 +481,13 @@ function TokenGroup({ tokenRefs, contractAddress, style, ownerFilter, screen }: return ( {tokens.length === 1 ? ( - + ) : ( )} diff --git a/apps/mobile/src/screens/NftSelectorScreen/NftSelectorPickerSingularAsset.tsx b/apps/mobile/src/screens/NftSelectorScreen/NftSelectorPickerSingularAsset.tsx index 96352ad88b..82a0ab17a7 100644 --- a/apps/mobile/src/screens/NftSelectorScreen/NftSelectorPickerSingularAsset.tsx +++ b/apps/mobile/src/screens/NftSelectorScreen/NftSelectorPickerSingularAsset.tsx @@ -1,4 +1,3 @@ -import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import { ResizeMode } from 'expo-av'; import { useCallback, useState } from 'react'; import { ActivityIndicator, View, ViewProps } from 'react-native'; @@ -11,22 +10,21 @@ import { GallerySkeleton } from '~/components/GallerySkeleton'; import { GalleryTouchableOpacity } from '~/components/GalleryTouchableOpacity'; import { NftPreviewAssetToWrapInBoundary } from '~/components/NftPreview/NftPreviewAsset'; import { NftSelectorPickerSingularAssetFragment$key } from '~/generated/NftSelectorPickerSingularAssetFragment.graphql'; -import { MainTabStackNavigatorProp, RootStackNavigatorParamList } from '~/navigation/types'; import { contexts } from '~/shared/analytics/constants'; import colors from '~/shared/theme/colors'; -import { useProfilePicture } from './useProfilePicture'; - type NftSelectorPickerSingularAssetProps = { style?: ViewProps['style']; - onSelect: () => void; tokenRef: NftSelectorPickerSingularAssetFragment$key; + onPress: (tokenId: string) => void; + isLoading?: boolean; }; export function NftSelectorPickerSingularAsset({ style, tokenRef, - onSelect, + onPress, + isLoading, }: NftSelectorPickerSingularAssetProps) { const token = useFragment( graphql` @@ -41,33 +39,13 @@ export function NftSelectorPickerSingularAsset({ tokenRef ); - const route = useRoute>(); - const currentScreen = route.params.page; - - const navigation = useNavigation(); - - const { setProfileImage, isSettingProfileImage } = useProfilePicture(); - const [, setError] = useState(null); const handlePress = useCallback(() => { setError(null); - if (currentScreen === 'ProfilePicture' || currentScreen === 'Onboarding') { - setProfileImage(token.dbid).then(() => { - onSelect(); - }); - } else if (currentScreen === 'Community') { - navigation.navigate('PostComposer', { - tokenId: token.dbid, - redirectTo: 'Community', - }); - } else { - navigation.navigate('PostComposer', { - tokenId: token.dbid, - }); - } - }, [currentScreen, navigation, onSelect, setProfileImage, token.dbid]); + onPress(token.dbid); + }, [onPress, token.dbid]); const [assetLoaded, setAssetLoaded] = useState(false); const handleAssetLoad = useCallback(() => { @@ -77,7 +55,7 @@ export function NftSelectorPickerSingularAsset({ return ( )} - {isSettingProfileImage && ( + {isLoading && ( From 3cc20261710a3096d1e8356f09080947326562d5 Mon Sep 17 00:00:00 2001 From: Jakz Date: Tue, 19 Mar 2024 15:26:32 +0800 Subject: [PATCH 08/64] Remove unused component --- .../components/NftSelector/NftSelector.tsx | 87 ------------------- .../NftSelectorContract.tsx | 74 ---------------- 2 files changed, 161 deletions(-) delete mode 100644 apps/mobile/src/components/NftSelector/NftSelector.tsx delete mode 100644 apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx diff --git a/apps/mobile/src/components/NftSelector/NftSelector.tsx b/apps/mobile/src/components/NftSelector/NftSelector.tsx deleted file mode 100644 index 84eb26978f..0000000000 --- a/apps/mobile/src/components/NftSelector/NftSelector.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useNavigation } from '@react-navigation/native'; -import { Suspense, useCallback } from 'react'; -import { View } from 'react-native'; - -import { useSyncTokensActions } from '~/contexts/SyncTokensContext'; -import { MainTabStackNavigatorProp } from '~/navigation/types'; -import { NftSelectorLoadingSkeleton } from '~/screens/NftSelectorScreen/NftSelectorLoadingSkeleton'; -import { NftSelectorPickerGrid } from '~/screens/NftSelectorScreen/NftSelectorPickerGrid'; - -import { NftSelectorHeader } from './NftSelectorHeader'; -import { NftSelectorToolbar } from './NftSelectorToolbar'; -import { NftSelectorWrapper } from './NftSelectorWrapper'; -import { useNftSelector } from './useNftSelector'; - -export function NftSelector() { - const { - searchQuery, - setSearchQuery, - ownershipTypeFilter, - setFilter, - networkFilter, - setNetworkFilter, - sortView, - setSortView, - sync, - } = useNftSelector(); - - const navigation = useNavigation(); - const { isSyncing, isSyncingCreatedTokens } = useSyncTokensActions(); - - const handleSelectNft = useCallback( - (tokenId: string) => { - navigation.navigate('PostComposer', { - tokenId, - }); - }, - [navigation] - ); - - const handleSelectNftGroup = useCallback( - (contractAddress: string) => { - navigation.navigate('NftSelectorContractScreen', { - contractAddress: contractAddress, - page: 'Post', - ownerFilter: 'Collected', - fullScreen: true, - }); - }, - [navigation] - ); - - return ( - - - - - - - }> - - - - - ); -} diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx deleted file mode 100644 index b41db2c265..0000000000 --- a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContract.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; -import { useCallback, useMemo } from 'react'; -import { graphql, useLazyLoadQuery } from 'react-relay'; - -import { NftSelectorContractQuery } from '~/generated/NftSelectorContractQuery.graphql'; -import { MainTabStackNavigatorParamList, MainTabStackNavigatorProp } from '~/navigation/types'; - -import { NftSelectorContractHeader } from './NftSelectorContractHeader'; -import { NftSelectorContractPickerGrid } from './NftSelectorContractPickerGrid'; -import { NftSelectorContractWrapper } from './NftSelectorContractWrapper'; - -export function NftSelectorContract() { - const query = useLazyLoadQuery( - graphql` - query NftSelectorContractQuery { - viewer { - ... on Viewer { - user { - tokens { - __typename - definition { - contract { - contractAddress { - address - } - } - } - ...NftSelectorContractPickerGridFragment - } - } - } - } - } - `, - {} - ); - - const navigation = useNavigation(); - const route = useRoute>(); - const contractAddress = route.params.contractAddress; - const isCreator = route.params.ownerFilter === 'Created'; - - const handleSelectNft = useCallback( - (tokenId: string) => { - navigation.navigate('PostComposer', { - tokenId, - }); - }, - [navigation] - ); - - const nonNullableTokens = useMemo(() => { - const tokens = []; - - for (const token of query.viewer?.user?.tokens ?? []) { - if (token?.definition?.contract?.contractAddress?.address === contractAddress) { - tokens.push(token); - } - } - - return tokens; - }, [query.viewer?.user?.tokens, contractAddress]); - - return ( - - - - - ); -} From caefb0c5520e04583b39c3d182cdce27065aea3f Mon Sep 17 00:00:00 2001 From: Jakz Date: Tue, 19 Mar 2024 15:32:38 +0800 Subject: [PATCH 09/64] lint --- .../screens/CommunityScreen/CommunityNftSelectorScreen.tsx | 6 +++--- .../src/screens/Onboarding/OnboardingNftSelectorScreeen.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/mobile/src/screens/CommunityScreen/CommunityNftSelectorScreen.tsx b/apps/mobile/src/screens/CommunityScreen/CommunityNftSelectorScreen.tsx index 53a8d6eafb..c179594215 100644 --- a/apps/mobile/src/screens/CommunityScreen/CommunityNftSelectorScreen.tsx +++ b/apps/mobile/src/screens/CommunityScreen/CommunityNftSelectorScreen.tsx @@ -1,12 +1,12 @@ -import { RouteProp,useNavigation, useRoute } from '@react-navigation/native'; +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import { useCallback, useMemo } from 'react'; -import { graphql,useLazyLoadQuery } from 'react-relay'; +import { graphql, useLazyLoadQuery } from 'react-relay'; import { NftSelectorContractHeader } from '~/components/NftSelector/NftSelectorContract/NftSelectorContractHeader'; import { NftSelectorContractPickerGrid } from '~/components/NftSelector/NftSelectorContract/NftSelectorContractPickerGrid'; import { NftSelectorContractWrapper } from '~/components/NftSelector/NftSelectorContract/NftSelectorContractWrapper'; import { CommunityNftSelectorScreenQuery } from '~/generated/CommunityNftSelectorScreenQuery.graphql'; -import { MainTabStackNavigatorParamList,MainTabStackNavigatorProp } from '~/navigation/types'; +import { MainTabStackNavigatorParamList, MainTabStackNavigatorProp } from '~/navigation/types'; export function CommunityNftSelectorScreen() { const query = useLazyLoadQuery( diff --git a/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorScreeen.tsx b/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorScreeen.tsx index d69a985ce7..404a43a7d3 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorScreeen.tsx +++ b/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorScreeen.tsx @@ -1,5 +1,5 @@ import { useNavigation } from '@react-navigation/native'; -import { Suspense,useCallback } from 'react'; +import { Suspense, useCallback } from 'react'; import { View } from 'react-native'; import { contexts } from 'shared/analytics/constants'; import { ChevronRightIcon } from 'src/icons/ChevronRightIcon'; From b4cf07d0189ffde685e3ead1c1d594a93bd70b03 Mon Sep 17 00:00:00 2001 From: Jakz Date: Tue, 19 Mar 2024 15:34:06 +0800 Subject: [PATCH 10/64] Added sync state on single contract screen --- .../NftSelectorContractHeader.tsx | 25 ++++++++++++++++--- .../OnboardingNftSelectorContractScreen.tsx | 14 ++++++----- .../PostNftSelectorContractScreen.tsx | 13 ++++++---- .../PfpSelectorContractScreen.tsx | 13 ++++++---- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx index 78de460a73..65aadd2b1e 100644 --- a/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx +++ b/apps/mobile/src/components/NftSelector/NftSelectorContract/NftSelectorContractHeader.tsx @@ -1,15 +1,34 @@ +import { useCallback } from 'react'; import { View, ViewProps } from 'react-native'; +import { AnimatedRefreshIcon } from '~/components/AnimatedRefreshIcon'; import { BackButton } from '~/components/BackButton'; import { Typography } from '~/components/Typography'; +import { useSyncTokensActions } from '~/contexts/SyncTokensContext'; type Props = { title: string; rightButton?: React.ReactNode; style?: ViewProps['style']; + contractId?: string; + isCreator?: boolean; }; -export function NftSelectorContractHeader({ title, rightButton, style }: Props) { +export function NftSelectorContractHeader({ + title, + rightButton, + style, + contractId, + isCreator, +}: Props) { + const { isSyncingCreatedTokensForContract, syncCreatedTokensForExistingContract } = + useSyncTokensActions(); + + const handleSyncTokensForContract = useCallback(async () => { + if (!contractId) return; + syncCreatedTokensForExistingContract(contractId); + }, [syncCreatedTokensForExistingContract, contractId]); + return ( @@ -29,7 +48,7 @@ export function NftSelectorContractHeader({ title, rightButton, style }: Props) {rightButton ? {rightButton} : null} - {/* {isCreator ? ( + {isCreator && contractId ? ( - ) : null} */} + ) : null} ); } diff --git a/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorContractScreen.tsx b/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorContractScreen.tsx index d75e82cb0f..cf7c0b5f96 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorContractScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/OnboardingNftSelectorContractScreen.tsx @@ -21,6 +21,7 @@ export function OnboardingNftSelectorContractScreen() { __typename definition { contract { + dbid name contractAddress { address @@ -65,14 +66,15 @@ export function OnboardingNftSelectorContractScreen() { return tokens; }, [query.viewer?.user?.tokens, contractAddress]); - const contractName = useMemo( - () => nonNullableTokens[0]?.definition?.contract?.name ?? '', - [nonNullableTokens] - ); - + const contractName = nonNullableTokens[0]?.definition?.contract?.name ?? ''; + const contractId = nonNullableTokens[0]?.definition?.contract?.dbid ?? ''; return ( - + nonNullableTokens[0]?.definition?.contract?.name ?? '', - [nonNullableTokens] - ); + const contractName = nonNullableTokens[0]?.definition?.contract?.name ?? ''; + const contractId = nonNullableTokens[0]?.definition?.contract?.dbid ?? ''; return ( - + nonNullableTokens[0]?.definition?.contract?.name ?? '', - [nonNullableTokens] - ); + const contractName = nonNullableTokens[0]?.definition?.contract?.name ?? ''; + const contractId = nonNullableTokens[0]?.definition?.contract?.dbid ?? ''; return ( - + Date: Wed, 20 Mar 2024 17:47:54 +0800 Subject: [PATCH 11/64] wip --- apps/mobile/package.json | 8 +- .../Tabs/ProfileViewGalleriesTab.tsx | 34 ++++- .../src/navigation/MainTabStackNavigator.tsx | 3 + apps/mobile/src/navigation/types.ts | 2 + .../GalleryScreen/GalleryEditorScreen.tsx | 134 ++++++++++++++++++ yarn.lock | 123 ++++++++++++---- 6 files changed, 268 insertions(+), 36 deletions(-) create mode 100644 apps/mobile/src/screens/GalleryScreen/GalleryEditorScreen.tsx diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 2191685cf2..be68bf4361 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -9,6 +9,7 @@ "@gorhom/bottom-sheet": "4.4.7", "@gorhom/portal": "^1.0.14", "@magic-sdk/react-native-expo": "^16.0.0", + "@mgcrea/react-native-dnd": "^2.2.0", "@privy-io/expo": "^0.6.0", "@react-native-async-storage/async-storage": "1.21.0", "@react-native-clipboard/clipboard": "^1.12.1", @@ -50,11 +51,12 @@ "react": "18.2.0", "react-native": "0.73.4", "react-native-collapsible-tab-view": "^6.1.4", + "react-native-draggable-grid": "^2.1.8", "react-native-fast-image": "^8.6.3", "react-native-fetch-api": "^3.0.0", - "react-native-gesture-handler": "~2.14.0", + "react-native-gesture-handler": "^2.15.0", "react-native-get-random-values": "~1.8.0", - "react-native-haptic-feedback": "^2.0.3", + "react-native-haptic-feedback": "^2.2.0", "react-native-ios-context-menu": "^1.15.3", "react-native-linear-gradient": "^2.6.2", "react-native-markdown-display": "https://github.com/jonasmerlin/react-native-markdown-display.git", @@ -63,7 +65,7 @@ "react-native-pager-view": "6.2.3", "react-native-polyfill-globals": "^3.1.0", "react-native-qrcode-svg": "^6.2.0", - "react-native-reanimated": "~3.6.2", + "react-native-reanimated": "^3.8.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", "react-native-skeleton-placeholder": "^5.2.4", diff --git a/apps/mobile/src/components/ProfileView/Tabs/ProfileViewGalleriesTab.tsx b/apps/mobile/src/components/ProfileView/Tabs/ProfileViewGalleriesTab.tsx index dfae7a5072..9f668347b4 100644 --- a/apps/mobile/src/components/ProfileView/Tabs/ProfileViewGalleriesTab.tsx +++ b/apps/mobile/src/components/ProfileView/Tabs/ProfileViewGalleriesTab.tsx @@ -1,14 +1,18 @@ +import { useNavigation } from '@react-navigation/native'; +import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { ListRenderItem } from '@shopify/flash-list'; import { useCallback, useMemo } from 'react'; import { View } from 'react-native'; import { Tabs } from 'react-native-collapsible-tab-view'; import { useFragment } from 'react-relay'; import { graphql } from 'relay-runtime'; +import { Button } from '~/components/Button'; import { GalleryPreviewCard } from '~/components/ProfileView/GalleryPreviewCard'; import { useListContentStyle } from '~/components/ProfileView/Tabs/useListContentStyle'; import { GalleryPreviewCardFragment$key } from '~/generated/GalleryPreviewCardFragment.graphql'; import { ProfileViewGalleriesTabFragment$key } from '~/generated/ProfileViewGalleriesTabFragment.graphql'; +import { MainTabStackNavigatorParamList } from '~/navigation/types'; import { removeNullValues } from '~/shared/relay/removeNullValues'; type ListItem = { @@ -56,13 +60,29 @@ export function ProfileViewGalleriesTab({ queryRef }: ProfileViewGalleriesTabPro }); }, [user?.featuredGallery?.dbid, user?.galleries]); - const renderItem = useCallback>(({ item }) => { - return ( - - - - ); - }, []); + const navigation = useNavigation>(); + const handleEditGallery = useCallback(() => { + navigation.navigate('GalleryEditor'); + }, [navigation]); + + const renderItem = useCallback>( + ({ item }) => { + return ( + +