Skip to content

Commit

Permalink
Add contract nft selector to gallery editor
Browse files Browse the repository at this point in the history
  • Loading branch information
jakzaizzat committed Apr 30, 2024
1 parent 23cf5ec commit b8d1465
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { graphql, useFragment } from 'react-relay';
import { GalleryRefreshControl } from '~/components/GalleryRefreshControl';
import { useSyncTokensActions } from '~/contexts/SyncTokensContext';
import { NftSelectorContractPickerGridFragment$key } from '~/generated/NftSelectorContractPickerGridFragment.graphql';
import { SelectedItemMultiMode } from '~/screens/GalleryScreen/GalleryEditorNftSelector';
import { NftSelectorLoadingSkeleton } from '~/screens/NftSelectorScreen/NftSelectorLoadingSkeleton';
import { NftSelectorPickerSingularAsset } from '~/screens/NftSelectorScreen/NftSelectorPickerSingularAsset';

Expand All @@ -14,9 +15,19 @@ type Props = {
tokenRefs: NftSelectorContractPickerGridFragment$key;
onSelect: (tokenId: string) => void;
style?: ViewProps['style'];

isMultiselectMode?: boolean;
selectedTokens?: SelectedItemMultiMode[];
};

export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect, style }: Props) {
export function NftSelectorContractPickerGrid({
isCreator,
isMultiselectMode,
tokenRefs,
onSelect,
selectedTokens,
style,
}: Props) {
const tokens = useFragment(
graphql`
fragment NftSelectorContractPickerGridFragment on Token @relay(plural: true) {
Expand All @@ -43,6 +54,11 @@ export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect,
syncCreatedTokensForExistingContract(contractId);
}, [syncCreatedTokensForExistingContract, contractId]);

const isSelected = useMemo(() => {
const selectedTokenIds = selectedTokens?.map((token) => token.id) ?? [];
return (tokenId: string) => selectedTokenIds.includes(tokenId);
}, [selectedTokens]);

const renderItem = useCallback<ListRenderItem<typeof tokens>>(
({ item: row }) => {
return (
Expand All @@ -53,6 +69,8 @@ export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect,
key={token.dbid}
onPress={onSelect}
tokenRef={token}
isMultiselectMode={isMultiselectMode}
isSelected={isSelected(token.dbid)}
/>
);
})}
Expand All @@ -63,7 +81,7 @@ export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect,
</View>
);
},
[onSelect]
[isMultiselectMode, isSelected, onSelect]
);

const rows = useMemo(() => {
Expand All @@ -83,6 +101,7 @@ export function NftSelectorContractPickerGrid({ isCreator, tokenRefs, onSelect,
renderItem={renderItem}
data={rows}
estimatedItemSize={100}
extraData={[isMultiselectMode, selectedTokens]}
refreshControl={
isCreator ? (
<GalleryRefreshControl
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useCallback } from 'react';
import { View } from 'react-native';
import { contexts } from 'shared/analytics/constants';
import { MultiSelectIcon } from 'src/icons/MultiSelectIcon';

import { AnimatedRefreshIcon } from '~/components/AnimatedRefreshIcon';
import { GalleryTouchableOpacity } from '~/components/GalleryTouchableOpacity';
import { IconContainer } from '~/components/IconContainer';
import { BaseM } from '~/components/Text';

type Props = {
contractName: string;
isMultiselectMode: boolean;
setIsMultiselectMode: (value: boolean) => void;
onSelectedAllPress: () => void;
hasSelectedItems: boolean;
};

export function NftSelectorContractToolbar({
contractName,
isMultiselectMode,
setIsMultiselectMode,
onSelectedAllPress,
hasSelectedItems,
}: Props) {
const handleMultiselectPress = useCallback(() => {
setIsMultiselectMode?.(!isMultiselectMode);
}, [isMultiselectMode, setIsMultiselectMode]);

return (
<View className="flex-row justify-between px-4 pt-4">
<BaseM weight="Bold">{contractName}</BaseM>

<View className="flex-row space-x-2 items-center">
<GalleryTouchableOpacity
onPress={onSelectedAllPress}
eventElementId={null}
eventName={null}
eventContext={null}
>
<BaseM weight="Bold" classNameOverride="text-shadow">
{hasSelectedItems ? 'Deselect All' : 'Select All'}
</BaseM>
</GalleryTouchableOpacity>
<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}
onSync={() => {}}
isSyncing={false}
eventElementId="NftSelectorSelectorRefreshButton"
eventName="NftSelectorSelectorRefreshButton pressed"
/>
</View>
</View>
);
}
1 change: 1 addition & 0 deletions apps/mobile/src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type RootStackNavigatorParamList = {
galleryId: string;
};
NftSelectorContractGalleryEditor: {
galleryId: string;
contractAddress: string;
ownerFilter?: 'Collected' | 'Created';
};
Expand Down
26 changes: 15 additions & 11 deletions apps/mobile/src/screens/GalleryScreen/GalleryEditorNftSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { Suspense, useCallback, useMemo, useState } from 'react';
import { Text, View } from 'react-native';
import { View } from 'react-native';

import { Button } from '~/components/Button';
import { NftSelectorHeader } from '~/components/NftSelector/NftSelectorHeader';
Expand Down Expand Up @@ -105,12 +105,13 @@ export function GalleryEditorNftSelector() {
navigation.navigate({
name: 'NftSelectorContractGalleryEditor',
params: {
galleryId: route.params.galleryId,
contractAddress,
},
});
}
},
[isMultiselectMode, navigation]
[isMultiselectMode, navigation, route.params.galleryId]
);

const handleAddSelectedTokens = useCallback(() => {
Expand Down Expand Up @@ -142,17 +143,20 @@ export function GalleryEditorNftSelector() {
<NftSelectorHeader
title="Select item to add"
rightButton={
<Button
onPress={handleAddSelectedTokens}
eventElementId={null}
eventName={null}
eventContext={null}
text="add"
size="xs"
/>
isMultiselectMode && selectedTokens.length > 0 ? (
<Button
onPress={handleAddSelectedTokens}
eventElementId={null}
eventName={null}
eventContext={null}
text="add"
size="xs"
fontWeight="Bold"
textTransform="capitalize"
/>
) : null
}
/>
<Text>{JSON.stringify(selectedTokens)}</Text>
<NftSelectorToolbar
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { useCallback } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { graphql, useLazyLoadQuery } from 'react-relay';

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

import { SelectedItemMultiMode } from './GalleryEditorNftSelector';

export function GalleryEditorNftSelectorContractScreen() {
const query = useLazyLoadQuery<GalleryEditorNftSelectorContractScreenQuery>(
graphql`
query GalleryEditorNftSelectorContractScreenQuery {
...NftSelectorContractFragment
viewer {
... on Viewer {
user {
tokens {
__typename
dbid
definition {
contract {
dbid
name
contractAddress {
address
}
}
}
...NftSelectorContractPickerGridFragment
}
}
}
}
}
`,
{}
Expand All @@ -20,22 +45,126 @@ export function GalleryEditorNftSelectorContractScreen() {
const route =
useRoute<RouteProp<RootStackNavigatorParamList, 'NftSelectorContractGalleryEditor'>>();

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

const contractAddress = route.params.contractAddress;
const isCreator = route.params.ownerFilter === 'Created';

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]);

const contractName = nonNullableTokens[0]?.definition?.contract?.name ?? '';
const contractId = nonNullableTokens[0]?.definition?.contract?.dbid ?? '';

const handleSelectNft = useCallback(
(tokenId: string) => {
navigation.navigate('PostComposer', {
tokenId,
});
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,
});
}
},
[navigation]
[navigation, isMultiselectMode, route.params.galleryId]
);

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 handleSelectedAllPress = useCallback(() => {
setSelectedTokens((prevTokens) => {
if (prevTokens.length > 0) {
return [];
} else {
return nonNullableTokens.map((token) => {
return {
id: token.dbid,
};
});
}
});
}, [nonNullableTokens]);

return (
<NftSelectorContract
contractAddress={route.params.contractAddress}
onSelectNft={handleSelectNft}
queryRef={query}
isCreator={route.params.ownerFilter === 'Created'}
isFullScreen
/>
<NftSelectorContractWrapper isFullscreen>
<NftSelectorContractHeader
title="Select items to add"
isCreator={isCreator}
contractId={contractId}
rightButton={
selectedTokens.length > 0 ? (
<Button
onPress={handleAddSelectedTokens}
eventElementId={null}
eventName={null}
eventContext={null}
text="add"
size="xs"
fontWeight="Bold"
textTransform="capitalize"
/>
) : null
}
/>
<NftSelectorContractToolbar
contractName={contractName}
isMultiselectMode={isMultiselectMode}
setIsMultiselectMode={setIsMultiselectMode}
onSelectedAllPress={handleSelectedAllPress}
hasSelectedItems={selectedTokens.length > 0}
/>
<NftSelectorContractPickerGrid
isCreator={isCreator}
tokenRefs={nonNullableTokens}
onSelect={handleSelectNft}
isMultiselectMode={isMultiselectMode}
selectedTokens={selectedTokens}
/>
</NftSelectorContractWrapper>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,10 @@ function TokenGroup({
}, [tokens]);

const isSelected = useMemo(() => {
if (isSingleView) {
return selectedTokens.some((token) => {
return token.id === firstToken?.dbid;
});
} else {
return selectedTokens.some((token) => {
return token.id === contractAddress;
});
}
}, [selectedTokens, contractAddress, firstToken, isSingleView]);
return selectedTokens.some((token) => {
return token.id === firstToken?.dbid;
});
}, [firstToken?.dbid, selectedTokens]);

if (!firstToken) {
return null;
Expand Down
Loading

0 comments on commit b8d1465

Please sign in to comment.