Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(web): refactor assets hooks #719

Merged
merged 12 commits into from
Oct 12, 2023
75 changes: 10 additions & 65 deletions web/src/beta/components/fields/URLField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import React, { useCallback, useEffect, useState } from "react";
import Button from "@reearth/beta/components/Button";
import Property from "@reearth/beta/components/fields";
import TextInput from "@reearth/beta/components/fields/common/TextInput";
import useHooks from "@reearth/beta/features/Assets/AssetsQueriesHook/hooks";
import { FILE_FORMATS, IMAGE_FORMATS } from "@reearth/beta/features/Assets/constants";
import { Asset } from "@reearth/beta/features/Assets/types";
import { useManageAssets } from "@reearth/beta/features/Assets/useManageAssets/hooks";
import useHooks from "@reearth/beta/features/Assets/useAssetUploader/hooks";
import ChooseAssetModal from "@reearth/beta/features/Modals/ChooseAssetModal";
import { checkIfFileType } from "@reearth/beta/utils/util";
import { useT } from "@reearth/services/i18n";
Expand Down Expand Up @@ -50,62 +48,19 @@ const URLField: React.FC<Props> = ({ name, description, value, fileType, assetTy
},
[fileType, onChange, setNotification, t],
);

const {
assets,
isLoading,
hasMoreAssets,
searchTerm,
selectedAssets,
selectAsset,
handleGetMoreAssets,
handleFileSelect,
handleSortChange,
handleSearchTerm,
} = useHooks({ workspaceId: currentWorkspace?.id, onAssetSelect: handleChange });

const { localSearchTerm, wrapperRef, onScrollToBottom, handleSearchInputChange, handleSearch } =
useManageAssets({
selectedAssets,
searchTerm,
isLoading,
hasMoreAssets,
onGetMore: handleGetMoreAssets,
onSortChange: handleSortChange,
onSearch: handleSearchTerm,
});

const handleReset = useCallback(() => {
const selectedAsset = assets?.find(a => a.url === currentValue);
if (selectedAsset) {
selectAsset([selectedAsset]);
}
}, [currentValue, assets, selectAsset]);
const { onFileUpload } = useHooks({
workspaceId: currentWorkspace?.id,
onAssetSelect: handleChange,
});

useEffect(() => {
if (value) {
setCurrentValue(value);
}
}, [value]);

useEffect(() => {
handleReset();
}, [handleReset]);

const handleClose = useCallback(() => {
setOpen(false);
handleReset();
}, [handleReset]);

const handleClick = useCallback(() => setOpen(!open), [open]);

const handleSelect = useCallback(
(asset?: Asset) => {
if (!asset) return;
selectAsset(!selectedAssets.includes(asset) ? [asset] : []);
},
[selectedAssets, selectAsset],
);
const handleModalClose = useCallback(() => setOpen(false), []);

return (
<Property name={name} description={description}>
Expand All @@ -122,27 +77,17 @@ const URLField: React.FC<Props> = ({ name, description, value, fileType, assetTy
icon="uploadSimple"
text={t("Upload")}
iconPosition="left"
onClick={handleFileSelect}
onClick={onFileUpload}
/>
</ButtonWrapper>
)}
{open && (
<ChooseAssetModal
open={open}
onModalClose={handleModalClose}
assetType={assetType}
localSearchTerm={localSearchTerm}
selectedAssets={selectedAssets}
wrapperRef={wrapperRef}
assets={assets}
isLoading={isLoading}
hasMoreAssets={hasMoreAssets}
searchTerm={searchTerm}
onClose={handleClose}
handleSearch={handleSearch}
handleSearchInputChange={handleSearchInputChange}
onGetMore={handleGetMoreAssets}
onScrollToBottom={onScrollToBottom}
onSelectAsset={handleSelect}
currentWorkspace={currentWorkspace}
currentValue={currentValue}
onSelect={handleChange}
/>
)}
Expand Down
174 changes: 174 additions & 0 deletions web/src/beta/features/Assets/AssetHooks/hooks.tsx
KaWaite marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { useCallback, useState, useRef, useMemo, useEffect } from "react";

import { Asset, SortType } from "@reearth/beta/features/Assets/types";
import { autoFillPage, onScrollToBottom } from "@reearth/beta/utils/infinite-scroll";
import { useAssetsFetcher } from "@reearth/services/api";
import { Maybe, AssetSortType as GQLSortType } from "@reearth/services/gql";
import { useT } from "@reearth/services/i18n";

const assetsPerPage = 20;

const enumTypeMapper: Partial<Record<GQLSortType, string>> = {
[GQLSortType.Date]: "date",
[GQLSortType.Name]: "name",
[GQLSortType.Size]: "size",
};

function toGQLEnum(val?: SortType) {
if (!val) return;
return (Object.keys(enumTypeMapper) as GQLSortType[]).find(k => enumTypeMapper[k] === val);
}
KaWaite marked this conversation as resolved.
Show resolved Hide resolved

function pagination(
sort?: { type?: Maybe<SortType>; reverse?: boolean },
endCursor?: string | null,
) {
const reverseOrder = !sort?.type || sort?.type === "date" ? !sort?.reverse : !!sort?.reverse;

return {
after: !reverseOrder ? endCursor : undefined,
before: reverseOrder ? endCursor : undefined,
first: !reverseOrder ? assetsPerPage : undefined,
last: reverseOrder ? assetsPerPage : undefined,
};
}

export default ({
workspaceId,
}: {
workspaceId?: string;
onAssetSelect?: (inputValue?: string) => void;
}) => {
const [sort, setSort] = useState<{ type?: SortType; reverse?: boolean }>();
const [searchTerm, setSearchTerm] = useState<string>();
const [selectedAssets, selectAsset] = useState<Asset[]>([]);
const isGettingMore = useRef(false);

const { useAssetsQuery, useRemoveAssets } = useAssetsFetcher();

const { assets, hasMoreAssets, loading, isRefetching, endCursor, fetchMore } = useAssetsQuery({
teamId: workspaceId ?? "",
pagination: pagination(sort),
sort: toGQLEnum(sort?.type),
keyword: searchTerm,
});

const t = useT();
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [localSearchTerm, setLocalSearchTerm] = useState<string>(searchTerm ?? "");
const openDeleteModal = useCallback(() => setDeleteModalVisible(true), []);
const closeDeleteModal = useCallback(() => setDeleteModalVisible(false), []);
const wrapperRef = useRef<HTMLDivElement>(null);
const sortOptions: { key: string; label: string }[] = useMemo(
() => [
{ key: "date", label: t("Last Uploaded") },
{ key: "date-reverse", label: t("First Uploaded") },
{ key: "name", label: t("A To Z") },
{ key: "name-reverse", label: t("Z To A") },
{ key: "size", label: t("Size Small to Large") },
{ key: "size-reverse", label: t("Size Large to Small") },
],
[t],
);

const iconChoice =
KaWaite marked this conversation as resolved.
Show resolved Hide resolved
sort?.type === "name"
? sort?.reverse
? "filterNameReverse"
: "filterName"
: sort?.type === "size"
? sort?.reverse
? "filterSizeReverse"
: "filterSize"
: sort?.reverse
? "filterTimeReverse"
: "filterTime";

const onGetMoreAssets = useCallback(async () => {
KaWaite marked this conversation as resolved.
Show resolved Hide resolved
if (hasMoreAssets && !isGettingMore.current) {
isGettingMore.current = true;
await fetchMore({
variables: {
pagination: pagination(sort, endCursor),
},
});
isGettingMore.current = false;
}
}, [endCursor, sort, fetchMore, hasMoreAssets, isGettingMore]);

const onSortChange = useCallback(
(type?: string, reverse?: boolean) => {
if (!type && reverse === undefined) return;
setSort({
type: (type as SortType) ?? sort?.type,
reverse: !!reverse,
});
},
[sort],
);

const handleSearchTerm = useCallback((term?: string) => {
setSearchTerm(term);
}, []);

const onRemove = useCallback(async () => {
if (selectedAssets?.length) {
const { status } = await useRemoveAssets(selectedAssets.map(a => a.id));
if (status === "success") {
selectAsset([]);
}
setDeleteModalVisible(false);
}
}, [selectedAssets, useRemoveAssets]);

const onReverse = useCallback(() => {
onSortChange?.(undefined, !sort?.reverse);
}, [onSortChange, sort?.reverse]);

const onSearchInputChange = useCallback(
(value: string) => {
setLocalSearchTerm(value);
},
[setLocalSearchTerm],
);
const onSearch = useCallback(() => {
if (!localSearchTerm || localSearchTerm.length < 1) {
handleSearchTerm?.(undefined);
} else {
handleSearchTerm?.(localSearchTerm);
}
}, [localSearchTerm, handleSearchTerm]);

const isLoading = useMemo(() => {
return loading ?? isRefetching;
}, [isRefetching, loading]);

useEffect(() => {
if (wrapperRef.current && !isLoading && hasMoreAssets)
autoFillPage(wrapperRef, onGetMoreAssets);
}, [onGetMoreAssets, hasMoreAssets, isLoading]);

return {
wrapperRef,
assets,
sortOptions,
iconChoice,
sort,
searchTerm,
localSearchTerm,
isLoading,
hasMoreAssets,
selectedAssets,
deleteModalVisible,
closeDeleteModal,
selectAsset,
onGetMoreAssets,
onSortChange,
onScrollToBottom,
openDeleteModal,
onRemove,
onReverse,
onSearchInputChange,
onSearch,
};
};
Loading
Loading