Skip to content

Commit

Permalink
Able to add multiple token into gallery
Browse files Browse the repository at this point in the history
  • Loading branch information
jakzaizzat committed Apr 29, 2024
1 parent 35e47fc commit 1f69a5b
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import clsx from 'clsx';
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
import { CheckIcon } from 'src/icons/CheckIcon';

type Props = {
selected: boolean;
};

export function NftSelectorSelectionIndicator({ selected }: Props) {
const animateStyle = useAnimatedStyle(() => {
return {
opacity: withTiming(selected ? 1 : 0),
};
});

return (
<Animated.View
className={clsx(
'absolute top-1 right-1 h-3 w-3 rounded-full border border-white shadow flex items-center justify-center',
selected ? 'bg-activeBlue' : 'bg-white/30'
)}
>
{selected ? (
<Animated.View style={animateStyle}>
<CheckIcon />
</Animated.View>
) : null}
</Animated.View>
);
}
19 changes: 19 additions & 0 deletions apps/mobile/src/components/NftSelector/NftSelectorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react';
import { View, ViewProps } from 'react-native';
import { contexts } from 'shared/analytics/constants';
import { chains } from 'shared/utils/chains';
import { MultiSelectIcon } from 'src/icons/MultiSelectIcon';
import { SlidersIcon } from 'src/icons/SlidersIcon';
import { getChainIconComponent } from 'src/utils/getChainIconComponent';

Expand Down Expand Up @@ -41,6 +42,8 @@ type Props = {
setNetworkFilter: (value: NetworkChoice) => void;
sortView: NftSelectorSortView;
setSortView: (value: NftSelectorSortView) => void;
isMultiselectMode?: boolean;
setIsMultiselectMode?: (value: boolean) => void;
isSyncing: boolean;
isSyncingCreatedTokens: boolean;
handleSync: () => void;
Expand All @@ -56,6 +59,8 @@ export function NftSelectorToolbar({
setNetworkFilter,
sortView,
setSortView,
isMultiselectMode,
setIsMultiselectMode,
isSyncing,
isSyncingCreatedTokens,
handleSync,
Expand Down Expand Up @@ -92,6 +97,10 @@ export function NftSelectorToolbar({
[setNetworkFilter]
);

const handleMultiselectPress = useCallback(() => {
setIsMultiselectMode?.(!isMultiselectMode);
}, [isMultiselectMode, setIsMultiselectMode]);

return (
<View className="flex flex-col space-y-4" style={style}>
<View className="px-4">
Expand All @@ -116,6 +125,16 @@ export function NftSelectorToolbar({
/>

<View className="flex flex-row space-x-1">
<IconContainer
size="sm"
onPress={handleMultiselectPress}
icon={<MultiSelectIcon />}
eventElementId="NftSelectorSelectorSettingsButton"
eventName="NftSelectorSelectorSettingsButton pressed"
eventContext={contexts.Posts}
color={isMultiselectMode ? 'active' : 'default'}
/>

<AnimatedRefreshIcon
onSync={handleSync}
isSyncing={ownershipTypeFilter === 'Collected' ? isSyncing : isSyncingCreatedTokens}
Expand Down
10 changes: 10 additions & 0 deletions apps/mobile/src/icons/MultiSelectIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Svg, { Path,SvgProps } from 'react-native-svg';

export function MultiSelectIcon(props: SvgProps) {
return (
<Svg width={16} height={16} fill="none" {...props}>
<Path stroke="#000" d="M13.667 2.332H5v8.667h8.667V2.332Z" />
<Path stroke="#000" d="M11 11v2.667H2.334V5H5" />
</Svg>
);
}
5 changes: 5 additions & 0 deletions apps/mobile/src/navigation/RootStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { RootStackNavigatorParamList } from '~/navigation/types';
import { Debugger } from '~/screens/Debugger';
import { DesignSystemButtonsScreen } from '~/screens/DesignSystemButtonsScreen';
import { GalleryEditorNftSelector } from '~/screens/GalleryScreen/GalleryEditorNftSelector';
import { GalleryEditorNftSelectorContractScreen } from '~/screens/GalleryScreen/GalleryEditorNftSelectorContractScreen';
import { GalleryEditorScreen } from '~/screens/GalleryScreen/GalleryEditorScreen';
import { TwitterSuggestionListScreen } from '~/screens/HomeScreen/TwitterSuggestionListScreen';
import { UserSuggestionListScreen } from '~/screens/HomeScreen/UserSuggestionListScreen';
Expand Down Expand Up @@ -105,6 +106,10 @@ export function RootStackNavigator({ navigationContainerRef }: Props) {

<Stack.Screen name="GalleryEditor" component={GalleryEditorScreen} />
<Stack.Screen name="NftSelectorGalleryEditor" component={GalleryEditorNftSelector} />
<Stack.Screen
name="NftSelectorContractGalleryEditor"
component={GalleryEditorNftSelectorContractScreen}
/>
</Stack.Navigator>
<View className="flex">
<PortalHost name="bottomSheetPortal" />
Expand Down
4 changes: 4 additions & 0 deletions apps/mobile/src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export type RootStackNavigatorParamList = {
NftSelectorGalleryEditor: {
galleryId: string;
};
NftSelectorContractGalleryEditor: {
contractAddress: string;
ownerFilter?: 'Collected' | 'Created';
};
};

export type ScreenWithNftSelector = 'ProfilePicture' | 'Post' | 'Community' | 'Onboarding';
Expand Down
128 changes: 114 additions & 14 deletions apps/mobile/src/screens/GalleryScreen/GalleryEditorNftSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { Suspense, useCallback, useMemo } from 'react';
import { View } from 'react-native';
import { noop } from 'shared/utils/noop';
import { Suspense, useCallback, useMemo, useState } from 'react';
import { Text, View } from 'react-native';

import { Button } from '~/components/Button';
import { NftSelectorHeader } from '~/components/NftSelector/NftSelectorHeader';
import { NftSelectorToolbar } from '~/components/NftSelector/NftSelectorToolbar';
import { NftSelectorWrapper } from '~/components/NftSelector/NftSelectorWrapper';
import { useNftSelector } from '~/components/NftSelector/useNftSelector';
import { NftSelectorPickerGridTokenGridFragment$data } from '~/generated/NftSelectorPickerGridTokenGridFragment.graphql';
import { RootStackNavigatorParamList, RootStackNavigatorProp } from '~/navigation/types';
import { NftSelectorLoadingSkeleton } from '~/screens/NftSelectorScreen/NftSelectorLoadingSkeleton';
import { NftSelectorPickerGrid } from '~/screens/NftSelectorScreen/NftSelectorPickerGrid';

export type SelectedItemMultiMode = {
id: string;
contractAddress?: string;
};

export function GalleryEditorNftSelector() {
const {
searchQuery,
Expand All @@ -29,20 +35,97 @@ export function GalleryEditorNftSelector() {

const route = useRoute<RouteProp<RootStackNavigatorParamList, 'NftSelectorGalleryEditor'>>();

const [isMultiselectMode, setIsMultiselectMode] = useState(false);

const [selectedTokens, setSelectedTokens] = useState<SelectedItemMultiMode[]>([]);

const handleSelectNft = useCallback(
(tokenId: string) => {
navigation.navigate({
name: 'GalleryEditor',
params: {
galleryId: route.params.galleryId,
stagedTokens: [tokenId],
},
merge: true,
});
if (isMultiselectMode) {
setSelectedTokens((prevTokens) => {
if (
prevTokens.some((token) => {
return token.id === tokenId;
})
) {
return [
...prevTokens.filter((token) => {
return token.id !== tokenId;
}),
];
} else {
return [
...prevTokens,
{
id: tokenId,
},
];
}
});
} else {
navigation.navigate({
name: 'GalleryEditor',
params: {
galleryId: route.params.galleryId,
stagedTokens: [tokenId],
},
merge: true,
});
}
},
[isMultiselectMode, navigation, route.params.galleryId]
);

const handleSelectNftGroup = useCallback(
(contractAddress: string, tokens: NftSelectorPickerGridTokenGridFragment$data[number][]) => {
if (isMultiselectMode) {
const formattedTokens = tokens.map((token) => {
return {
id: token.dbid,
contractAddress,
};
});

setSelectedTokens((prevTokens) => {
if (
prevTokens.some((token) => {
return token.contractAddress === contractAddress;
})
) {
return [
...prevTokens.filter((token) => {
return token.contractAddress !== contractAddress;
}),
];
} else {
return [...prevTokens, ...formattedTokens];
}
});
} else {
navigation.navigate({
name: 'NftSelectorContractGalleryEditor',
params: {
contractAddress,
},
});
}
},
[navigation, route.params.galleryId]
[isMultiselectMode, navigation]
);

const handleAddSelectedTokens = useCallback(() => {
const formattedTokens = selectedTokens.map((token) => token.id);

navigation.navigate({
name: 'GalleryEditor',
params: {
galleryId: route.params.galleryId,
stagedTokens: formattedTokens,
},
merge: true,
});
}, [navigation, route.params.galleryId, selectedTokens]);

const searchCriteria = useMemo(
() => ({
searchQuery,
Expand All @@ -56,7 +139,20 @@ export function GalleryEditorNftSelector() {
return (
<NftSelectorWrapper ownershipTypeFilter={ownershipTypeFilter} isFullscreen>
<View className="gap-8">
<NftSelectorHeader title="Select item to add" />
<NftSelectorHeader
title="Select item to add"
rightButton={
<Button
onPress={handleAddSelectedTokens}
eventElementId={null}
eventName={null}
eventContext={null}
text="add"
size="xs"
/>
}
/>
<Text>{JSON.stringify(selectedTokens)}</Text>
<NftSelectorToolbar
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
Expand All @@ -68,6 +164,8 @@ export function GalleryEditorNftSelector() {
setSortView={setSortView}
isSyncing={isSyncing}
isSyncingCreatedTokens={isSyncingCreatedTokens}
isMultiselectMode={isMultiselectMode}
setIsMultiselectMode={setIsMultiselectMode}
handleSync={sync}
/>
</View>
Expand All @@ -77,7 +175,9 @@ export function GalleryEditorNftSelector() {
searchCriteria={searchCriteria}
onRefresh={sync}
onSelect={handleSelectNft}
onSelectNftGroup={noop}
onSelectNftGroup={handleSelectNftGroup}
isMultiselectMode={isMultiselectMode}
selectedTokens={selectedTokens}
/>
</Suspense>
</View>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { useCallback } from 'react';
import { graphql, useLazyLoadQuery } from 'react-relay';

import { NftSelectorContract } from '~/components/NftSelector/NftSelectorContract/NftSelectorContract';
import { GalleryEditorNftSelectorContractScreenQuery } from '~/generated/GalleryEditorNftSelectorContractScreenQuery.graphql';
import { RootStackNavigatorParamList, RootStackNavigatorProp } from '~/navigation/types';

export function GalleryEditorNftSelectorContractScreen() {
const query = useLazyLoadQuery<GalleryEditorNftSelectorContractScreenQuery>(
graphql`
query GalleryEditorNftSelectorContractScreenQuery {
...NftSelectorContractFragment
}
`,
{}
);

const navigation = useNavigation<RootStackNavigatorProp>();
const route =
useRoute<RouteProp<RootStackNavigatorParamList, 'NftSelectorContractGalleryEditor'>>();

const handleSelectNft = useCallback(
(tokenId: string) => {
navigation.navigate('PostComposer', {
tokenId,
});
},
[navigation]
);

return (
<NftSelectorContract
contractAddress={route.params.contractAddress}
onSelectNft={handleSelectNft}
queryRef={query}
isCreator={route.params.ownerFilter === 'Created'}
isFullScreen
/>
);
}
Loading

0 comments on commit 1f69a5b

Please sign in to comment.