Skip to content

Commit

Permalink
fix:Optimize NFT attributes filter. (#2568)
Browse files Browse the repository at this point in the history
  • Loading branch information
linleiqin authored Feb 15, 2023
1 parent 884b156 commit e8cad35
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { Dispatch, SetStateAction } from 'react';
import { createContext, useContext } from 'react';

export type NFTAttributesContextValue = {
selectedAttributes: Record<string, string[] | undefined>;
clearFlag: number;
setIsDisabled?: (isDisabled: boolean) => void;
};

export type INFTAttributesContent = {
Expand Down
188 changes: 94 additions & 94 deletions packages/kit/src/views/NFTMarket/Modals/NFTAttributesModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable camelcase */
import type { FC } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { useRoute } from '@react-navigation/core';
import { MotiView } from 'moti';
Expand All @@ -22,6 +22,14 @@ import useModalClose from '@onekeyhq/components/src/Modal/Container/useModalClos
import type { CollectionAttribute } from '@onekeyhq/engine/src/types/nft';

import { NFTAttributesContext, useNFTAttributesContext } from './context';
import {
clearAttributeStatus,
generteAttributeParams,
getAttributesStatus,
isAttributeNameSelected,
isSelectedAttribute,
setAttributesStatus,
} from './refs';

import type { NFTMarketRoutes, NFTMarketRoutesParams } from '../type';
import type { NFTAttributesContextValue } from './context';
Expand All @@ -37,38 +45,34 @@ type SubItemProps = {
};

const SubItem: FC<SubItemProps> = ({ attributeName, value }) => {
// const [selected, setSelected] = useState(false);
const [selected, setSelected] = useState(
getAttributesStatus({
attributeName,
attributesValue: value.attributes_value,
}),
);
const context = useNFTAttributesContext()?.context;
const setContext = useNFTAttributesContext()?.setContext;

const selected = useMemo(() => {
if (context) {
const item = context.selectedAttributes[attributeName];
if (item && item.includes(value.attributes_value)) {
return true;
}
return false;
}
}, [attributeName, context, value.attributes_value]);
useEffect(() => {
const status = getAttributesStatus({
attributeName,
attributesValue: value.attributes_value,
});
setSelected(status);
}, [attributeName, context?.clearFlag, value.attributes_value]);

const onPressHandle = useCallback(() => {
if (setContext) {
setContext((ctx) => {
const { selectedAttributes } = ctx;
const item = selectedAttributes[attributeName] ?? [];
if (item.includes(value.attributes_value)) {
const index = item.findIndex((v) => v === value.attributes_value);
item.splice(index, 1);
} else {
item.push(value.attributes_value);
}
selectedAttributes[attributeName] = item;
return {
selectedAttributes,
};
setSelected((prev) => {
setAttributesStatus({
attributeName,
attributesValue: value.attributes_value,
enable: !prev,
});
}
}, [attributeName, setContext, value.attributes_value]);
if (context && context.setIsDisabled) {
context.setIsDisabled(!isSelectedAttribute());
}
return !prev;
});
}, [attributeName, context, value.attributes_value]);

return (
<TouchableOpacity onPress={onPressHandle}>
Expand Down Expand Up @@ -122,18 +126,9 @@ const ItemList: FC<ItemProps> = ({ attribute }) => {
),
[attribute.attributes_name, attribute.attributes_values],
);
const context = useNFTAttributesContext()?.context;

const defaultCollapsed = useMemo(() => {
if (context?.selectedAttributes) {
const item = context?.selectedAttributes[attribute.attributes_name] ?? [];
return item.length === 0;
}
}, [attribute.attributes_name, context?.selectedAttributes]);

return (
<Collapse
defaultCollapsed={defaultCollapsed}
defaultCollapsed={!isAttributeNameSelected(attribute.attributes_name)}
renderCustomTrigger={(onPress, collapsed) => (
<TouchableOpacity onPress={onPress}>
<Box
Expand Down Expand Up @@ -165,64 +160,74 @@ const NFTAttributesModal: FC = () => {
attributes: routeAttributes,
onAttributeSelected,
} = route.params;
const attributes = collection.attributes as CollectionAttribute[];
const renderItem: ListRenderItem<CollectionAttribute> = useCallback(
({ item }) => <ItemList attribute={item} />,
[],
);

const defaultAtts = useMemo(() => {
const result: Record<string, string[] | undefined> = {};
routeAttributes.forEach((item) => {
result[item.attribute_name] = item.attribute_values;
});
return result;
}, [routeAttributes]);
const [flatListData, updateListData] = useState<CollectionAttribute[]>([]);

const [isDisabled, setIsDisabled] = useState(false);
const [context, setContext] = useState<NFTAttributesContextValue>({
selectedAttributes: defaultAtts,
clearFlag: 0,
setIsDisabled,
});

const isDisabled = useMemo(() => {
const { selectedAttributes } = context;
for (let i = 0; i < attributes.length; i += 1) {
const item = attributes[i];
const { attributes_name } = item;
const items = selectedAttributes[attributes_name];
if (items && items.length > 0) {
return false;
}
}
return true;
}, [attributes, context]);
const loadData = useCallback(async () => {
routeAttributes.forEach(
({ attribute_name: attributeName, attribute_values }) => {
attribute_values.forEach((attributesValue) => {
setAttributesStatus({ attributeName, attributesValue, enable: true });
});
},
);
const data = collection.attributes?.filter(
({ attributes_values }) => attributes_values.length < 100,
) as CollectionAttribute[];
return Promise.resolve(data);
}, [collection.attributes, routeAttributes]);

useEffect(() => {
// const startDate = new Date().getTime();
loadData().then((data) => {
// console.log('time : ', new Date().getTime() - startDate);
updateListData(data);
setIsDisabled(!isSelectedAttribute());
});

return () => {
clearAttributeStatus();
};
}, [loadData, routeAttributes]);

const closeModal = useModalClose();

const onPrimaryActionPress = useCallback(() => {
const result: Array<{
attribute_name: string;
attribute_values: string[];
}> = [];
const { selectedAttributes } = context;

attributes.forEach((item) => {
const { attributes_name } = item;
const items = selectedAttributes[attributes_name];
if (items && items.length > 0) {
result.push({
attribute_name: attributes_name,
attribute_values: items,
});
}
});
const result = generteAttributeParams();
if (onAttributeSelected) {
closeModal();
onAttributeSelected(result);
}
}, [attributes, closeModal, context, onAttributeSelected]);
}, [closeModal, onAttributeSelected]);

const isVerticalLayout = useIsVerticalLayout();

const contextValue = useMemo(() => ({ context, setContext }), [context]);

const renderItem: ListRenderItem<CollectionAttribute> = useCallback(
({ item }) => <ItemList attribute={item} />,
[],
);

const flatListProps = useMemo(
() => ({
contentContainerStyle: {
padding: 0,
paddingTop: isVerticalLayout ? 4 : 12,
},
data: flatListData,
renderItem,
keyExtractor: (item: CollectionAttribute) => item.attributes_name,
}),
[isVerticalLayout, renderItem, flatListData],
);

return (
<NFTAttributesContext.Provider value={contextValue}>
<Modal
Expand All @@ -231,23 +236,18 @@ const NFTAttributesModal: FC = () => {
primaryActionTranslationId="action__apply"
secondaryActionTranslationId="action__clear"
onSecondaryActionPress={() => {
if (setContext) {
setContext({ selectedAttributes: {} });
}
clearAttributeStatus();
setIsDisabled(true);
setContext((ctx) => ({
...ctx,
clearFlag: ctx.clearFlag + 1,
}));
}}
header={intl.formatMessage({ id: 'title__filter' })}
size="md"
height="640px"
flatListProps={{
contentContainerStyle: {
padding: 0,
paddingTop: isVerticalLayout ? 4 : 12,
},
data: attributes,
// @ts-ignore
renderItem,
keyExtractor: (item) => (item as CollectionAttribute).attributes_name,
}}
// @ts-ignore
flatListProps={flatListProps}
/>
</NFTAttributesContext.Provider>
);
Expand Down
81 changes: 81 additions & 0 deletions packages/kit/src/views/NFTMarket/Modals/NFTAttributesModal/refs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// attributes_name--attributes_value
type Refs = Record<string, boolean>;

const selectedAttributes: Refs = {};

function setAttributesStatus({
attributeName,
attributesValue,
enable,
}: {
attributeName: string;
attributesValue: string;
enable: boolean;
}) {
const key = `${attributeName}--${attributesValue}`;
selectedAttributes[key] = enable;
}

function getAttributesStatus({
attributeName,
attributesValue,
}: {
attributeName: string;
attributesValue: string;
}) {
const key = `${attributeName}--${attributesValue}`;
if (selectedAttributes[key]) {
return selectedAttributes[key];
}
return false;
}

function clearAttributeStatus() {
for (const [key] of Object.entries(selectedAttributes)) {
delete selectedAttributes[key];
}
}

function generteAttributeParams() {
const map: Record<string, string[]> = {};
for (const [key, enable] of Object.entries(selectedAttributes)) {
if (enable) {
const attributeName = key.split('--')[0];
const attributesValue = key.split('--')[1];
const items = map[attributeName] ?? [];
items.push(attributesValue);
map[attributeName] = items;
}
}
return Object.entries(map).map(([key, values]) => ({
attribute_name: key,
attribute_values: values,
}));
}

function isSelectedAttribute() {
for (const [, enable] of Object.entries(selectedAttributes)) {
if (enable) {
return true;
}
}
return false;
}

function isAttributeNameSelected(attributeName: string) {
for (const [key, enable] of Object.entries(selectedAttributes)) {
if (key.split('--')[0] === attributeName && enable) {
return true;
}
}
return false;
}

export {
isSelectedAttribute,
setAttributesStatus,
getAttributesStatus,
clearAttributeStatus,
generteAttributeParams,
isAttributeNameSelected,
};

0 comments on commit e8cad35

Please sign in to comment.