Просмотр исходного кода

improve IPageWithMeta and PageDeleteModal

Yuki Takei 4 лет назад
Родитель
Сommit
47da21759a

+ 8 - 6
packages/app/src/components/DescendantsPageList.tsx

@@ -2,7 +2,9 @@ import React, { useCallback, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import { toastSuccess } from '~/client/util/apiNotification';
 import {
-  IPageHasId, IPageWithMeta,
+  IDataWithMeta,
+  IPageHasId,
+  IPageInfoForOperation,
 } from '~/interfaces/page';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { OnDeletedFunction } from '~/interfaces/ui';
@@ -22,8 +24,8 @@ type SubstanceProps = {
   onPagesDeleted?: OnDeletedFunction,
 }
 
-const convertToIPageWithEmptyMeta = (page: IPageHasId): IPageWithMeta => {
-  return { pageData: page };
+const convertToIDataWithMeta = (page: IPageHasId): IDataWithMeta<IPageHasId> => {
+  return { data: page };
 };
 
 export const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element => {
@@ -39,7 +41,7 @@ export const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element
   const pageIds = pagingResult?.items?.map(page => page._id);
   const { injectTo } = useSWRxPageInfoForList(pageIds, true);
 
-  let pageWithMetas: IPageWithMeta[] = [];
+  let pageWithMetas: IDataWithMeta<IPageHasId, IPageInfoForOperation>[] = [];
 
   // for mutation
   const { advance: advancePt } = usePageTreeTermManager();
@@ -47,9 +49,9 @@ export const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element
   // initial data
   if (pagingResult != null) {
     // convert without meta at first
-    pageWithMetas = pagingResult.items.map(page => convertToIPageWithEmptyMeta(page));
+    const dataWithMetas = pagingResult.items.map(page => convertToIDataWithMeta(page));
     // inject data for listing
-    pageWithMetas = injectTo(pageWithMetas);
+    pageWithMetas = injectTo(dataWithMetas);
   }
 
   const pageDeletedHandler: OnDeletedFunction = useCallback((...args) => {

+ 1 - 1
packages/app/src/components/Navbar/GlobalSearch.tsx

@@ -36,7 +36,7 @@ const GlobalSearch: FC<Props> = (props: Props) => {
   const gotoPage = useCallback((data: IPageWithMeta<IPageSearchMeta>[]) => {
     assert(data.length > 0);
 
-    const page = data[0].pageData; // should be single page selected
+    const page = data[0].data; // should be single page selected
 
     // navigate to page
     if (page != null) {

+ 2 - 2
packages/app/src/components/Navbar/SubNavButtons.tsx

@@ -121,12 +121,12 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
     }
 
     const pageToDelete: IPageToDeleteWithMeta = {
-      pageData: {
+      data: {
         _id: pageId,
         revision: revisionId,
         path,
       },
-      pageMeta: pageInfo,
+      meta: pageInfo,
     };
 
     onClickDeleteMenuItem(pageToDelete);

+ 16 - 14
packages/app/src/components/PageDeleteModal.tsx

@@ -9,7 +9,7 @@ import { apiv3Post } from '~/client/util/apiv3-client';
 import { usePageDeleteModal } from '~/stores/modal';
 
 import {
-  IDeleteSinglePageApiv1Result, IDeleteManyPageApiv3Result, isIPageInfoForOperation, IPageWithAnyMeta,
+  IDeleteSinglePageApiv1Result, IDeleteManyPageApiv3Result, isIPageInfoForOperation, IPageToDeleteWithMeta, IDataWithMeta, IPageInfoForOperation,
 } from '~/interfaces/page';
 
 import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
@@ -37,23 +37,25 @@ const PageDeleteModal: FC = () => {
 
   const isOpened = deleteModalData?.isOpened ?? false;
 
-  // const notOperatablePages: IPageWithAnyMeta[] = (deleteModalData?.pages ?? [])
-  //   .filter(p => !isIPageInfoForOperation(p.pageMeta));
-  // const notOperatablePageIds = notOperatablePages.map(p => p.pageData._id);
+  const notOperatablePages: IPageToDeleteWithMeta[] = (deleteModalData?.pages ?? [])
+    .filter(p => !isIPageInfoForOperation(p.meta));
+  const notOperatablePageIds = notOperatablePages.map(p => p.data._id);
 
-  // const { injectTo } = useSWRxPageInfoForList(notOperatablePageIds);
-  // const injectedPages = injectTo(deleteModalData?.pages);
+  const { injectTo } = useSWRxPageInfoForList(notOperatablePageIds);
 
   const isAbleToDeleteCompletely = useMemo(() => {
-    // if (deleteModalData != null && deleteModalData.pages != null && deleteModalData.pages.length > 0) {
-    //   return deleteModalData.pages.every(page => page.pageMeta?.isAbleToDeleteCompletely);
-    // }
+    let injectedPages: IDataWithMeta<unknown, IPageInfoForOperation>[];
+    if (deleteModalData?.pages != null && notOperatablePageIds.length > 0) {
+      injectedPages = injectTo(deleteModalData?.pages);
+
+      return injectedPages.every(pageWithMeta => pageWithMeta.meta?.isAbleToDeleteCompletely);
+    }
     return true;
-  }, [deleteModalData]);
+  }, [deleteModalData?.pages, injectTo, notOperatablePageIds.length]);
 
   const forceDeleteCompletelyMode = useMemo(() => {
     if (deleteModalData != null && deleteModalData.pages != null && deleteModalData.pages.length > 0) {
-      return deleteModalData.pages.every(page => isTrashPage(page.pageData?.path ?? ''));
+      return deleteModalData.pages.every(pageWithMeta => isTrashPage(pageWithMeta.data?.path ?? ''));
     }
     return false;
   }, [deleteModalData]);
@@ -90,7 +92,7 @@ const PageDeleteModal: FC = () => {
         const isCompletely = isDeleteCompletely === true ? true : undefined;
 
         const pageIdToRevisionIdMap = {};
-        deleteModalData.pages.forEach((p) => { pageIdToRevisionIdMap[p.pageData._id] = p.pageData.revision });
+        deleteModalData.pages.forEach((p) => { pageIdToRevisionIdMap[p.data._id] = p.data.revision as string });
 
         const { data } = await apiv3Post<IDeleteManyPageApiv3Result>('/pages/delete', {
           pageIdToRevisionIdMap,
@@ -115,7 +117,7 @@ const PageDeleteModal: FC = () => {
         const recursively = isDeleteRecursively === true ? true : undefined;
         const completely = forceDeleteCompletelyMode || isDeleteCompletely ? true : undefined;
 
-        const page = deleteModalData.pages[0].pageData;
+        const page = deleteModalData.pages[0].data;
 
         const { path, isRecursively, isCompletely } = await apiPost('/pages.remove', {
           page_id: page._id,
@@ -187,7 +189,7 @@ const PageDeleteModal: FC = () => {
 
   const renderPagePathsToDelete = () => {
     if (deleteModalData != null && deleteModalData.pages != null) {
-      return deleteModalData.pages.map(page => <div key={page.pageData._id}><code>{ page.pageData.path }</code></div>);
+      return deleteModalData.pages.map(page => <div key={page.data._id}><code>{ page.data.path }</code></div>);
     }
     return <></>;
   };

+ 1 - 1
packages/app/src/components/PageList/PageList.tsx

@@ -29,7 +29,7 @@ const PageList = (props: Props): JSX.Element => {
 
   const pageList = pages.map(page => (
     <PageListItemL
-      key={page.pageData._id}
+      key={page.data._id}
       page={page}
       isEnableActions={isEnableActions}
       onPageDeleted={onPagesDeleted}

+ 5 - 5
packages/app/src/components/PageList/PageListItemL.tsx

@@ -20,7 +20,7 @@ import {
   usePageRenameModal, usePageDuplicateModal, usePageDeleteModal, usePutBackPageModal,
 } from '~/stores/modal';
 import {
-  IPageInfoAll, IPageWithMeta, isIPageInfoForEntity, isIPageInfoForListing,
+  IPageInfoAll, IPageInfoForListing, IPageWithMeta, isIPageInfoForEntity, isIPageInfoForListing,
 } from '~/interfaces/page';
 import { IPageSearchMeta, isIPageSearchMeta } from '~/interfaces/search';
 import { OnDeletedFunction } from '~/interfaces/ui';
@@ -30,7 +30,7 @@ import { ForceHideMenuItems, PageItemControl } from '../Common/Dropdown/PageItem
 import PagePathHierarchicalLink from '../PagePathHierarchicalLink';
 
 type Props = {
-  page: IPageWithMeta | IPageWithMeta<IPageInfoAll & IPageSearchMeta>,
+  page: IPageWithMeta | IPageWithMeta<IPageSearchMeta> | IPageWithMeta<IPageInfoForListing & IPageSearchMeta>,
   isSelected?: boolean, // is item selected(focused)
   isEnableActions?: boolean,
   forceHideMenuItems?: ForceHideMenuItems,
@@ -43,7 +43,7 @@ type Props = {
 const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (props: Props, ref): JSX.Element => {
   const {
     // todo: refactoring variable name to clear what changed
-    page: { pageData, pageMeta }, isSelected, isEnableActions,
+    page: { data: pageData, meta: pageMeta }, isSelected, isEnableActions,
     forceHideMenuItems,
     showPageUpdatedTime,
     onClickItem, onCheckboxChanged, onPageDeleted,
@@ -119,7 +119,7 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
 
 
   const deleteMenuItemClickHandler = useCallback((_id: string, pageInfo: IPageInfoAll | undefined) => {
-    const pageToDelete = { pageData, pageInfo };
+    const pageToDelete = { data: pageData, meta: pageInfo };
 
     // open modal
     openDeleteModal([pageToDelete], { onDeleted: onPageDeleted });
@@ -207,7 +207,7 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
               <div className="item-control ml-auto">
                 <PageItemControl
                   pageId={pageData._id}
-                  pageInfo={pageMeta}
+                  pageInfo={isIPageInfoForListing(pageMeta) ? pageMeta : undefined}
                   isEnableActions={isEnableActions}
                   forceHideMenuItems={forceHideMenuItems}
                   onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}

+ 1 - 1
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -103,7 +103,7 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
 
   const { t } = useTranslation();
 
-  const page = pageWithMeta?.pageData;
+  const page = pageWithMeta?.data;
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openRenameModal } = usePageRenameModal();
   const { open: openDeleteModal } = usePageDeleteModal();

+ 17 - 15
packages/app/src/components/SearchPage/SearchResultList.tsx

@@ -3,7 +3,9 @@ import React, {
   ForwardRefRenderFunction, useCallback, useImperativeHandle, useRef,
 } from 'react';
 import { ISelectable, ISelectableAll } from '~/client/interfaces/selectable-all';
-import { IPageWithMeta, isIPageInfoForListing } from '~/interfaces/page';
+import {
+  IPageInfoForListing, IPageWithMeta, isIPageInfoForListing,
+} from '~/interfaces/page';
 import { IPageSearchMeta } from '~/interfaces/search';
 import { useIsGuestUser } from '~/stores/context';
 import { useSWRxPageInfoForList } from '~/stores/page';
@@ -30,8 +32,8 @@ const SearchResultListSubstance: ForwardRefRenderFunction<ISelectableAll, Props>
   } = props;
 
   const pageIdsWithNoSnippet = pages
-    .filter(page => (page.pageMeta?.elasticSearchResult?.snippet.length ?? 0) === 0)
-    .map(page => page.pageData._id);
+    .filter(page => (page.meta?.elasticSearchResult?.snippet.length ?? 0) === 0)
+    .map(page => page.data._id);
 
   const { data: isGuestUser } = useIsGuestUser();
   const { data: idToPageInfo } = useSWRxPageInfoForList(pageIdsWithNoSnippet, true);
@@ -60,16 +62,16 @@ const SearchResultListSubstance: ForwardRefRenderFunction<ISelectableAll, Props>
 
   const clickItemHandler = useCallback((pageId: string) => {
     if (onPageSelected != null) {
-      const selectedPage = pages.find(page => page.pageData._id === pageId);
+      const selectedPage = pages.find(page => page.data._id === pageId);
       onPageSelected(selectedPage);
     }
   }, [onPageSelected, pages]);
 
-  let injectedPage;
+  let injectedPages: (IPageWithMeta<IPageSearchMeta> | IPageWithMeta<IPageInfoForListing & IPageSearchMeta>)[] | undefined;
   // inject data to list
   if (idToPageInfo != null) {
-    injectedPage = pages.map((page) => {
-      const pageInfo = idToPageInfo[page.pageData._id];
+    injectedPages = pages.map((page) => {
+      const pageInfo = idToPageInfo[page.data._id];
 
       if (!isIPageInfoForListing(pageInfo)) {
         // return as is
@@ -77,26 +79,26 @@ const SearchResultListSubstance: ForwardRefRenderFunction<ISelectableAll, Props>
       }
 
       return {
-        pageData: page.pageData,
-        pageMeta: {
-          ...page.pageMeta,
-          revisionShortBody: pageInfo.revisionShortBody,
+        data: page.data,
+        meta: {
+          ...page.meta,
+          ...pageInfo,
         },
-      };
+      } as IPageWithMeta<IPageInfoForListing & IPageSearchMeta>;
     });
   }
 
   return (
     <ul data-testid="search-result-list" className="page-list-ul list-group list-group-flush">
-      { (injectedPage ?? pages).map((page, i) => {
+      { (injectedPages ?? pages).map((page, i) => {
         return (
           <PageListItemL
-            key={page.pageData._id}
+            key={page.data._id}
             // eslint-disable-next-line no-return-assign
             ref={c => itemsRef.current[i] = c}
             page={page}
             isEnableActions={!isGuestUser}
-            isSelected={page.pageData._id === selectedPageId}
+            isSelected={page.data._id === selectedPageId}
             forceHideMenuItems={forceHideMenuItems}
             onClickItem={clickItemHandler}
             onCheckboxChanged={props.onCheckboxChanged}

+ 3 - 3
packages/app/src/components/SearchPage2/SearchPageBase.tsx

@@ -68,7 +68,7 @@ const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturn
       }
 
       if (pages != null) {
-        pages.forEach(page => selectedPageIdsByCheckboxes.add(page.pageData._id));
+        pages.forEach(page => selectedPageIdsByCheckboxes.add(page.data._id));
       }
     },
     deselectAll: () => {
@@ -182,7 +182,7 @@ const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturn
                       ref={searchResultListRef}
                       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                       pages={pages!}
-                      selectedPageId={selectedPageWithMeta?.pageData._id}
+                      selectedPageId={selectedPageWithMeta?.data._id}
                       forceHideMenuItems={forceHideMenuItems}
                       onPageSelected={page => setSelectedPageWithMeta(page)}
                       onCheckboxChanged={checkboxChangedHandler}
@@ -249,7 +249,7 @@ export const usePageDeleteModalForBulkDeletion = (
     }
 
     const selectedPages = data.data
-      .filter(pageWithMeta => selectedPageIds.has(pageWithMeta.pageData._id));
+      .filter(pageWithMeta => selectedPageIds.has(pageWithMeta.data._id));
 
     openDeleteModal(selectedPages, {
       onDeleted: (...args) => {

+ 1 - 1
packages/app/src/components/SearchTypeahead.tsx

@@ -190,7 +190,7 @@ const SearchTypeahead: ForwardRefRenderFunction<IFocusable, Props> = (props: Pro
   }
 
   const renderMenuItemChildren = (option: IPageWithMeta<IPageSearchMeta>) => {
-    const { pageData } = option;
+    const { data: pageData } = option;
     return (
       <span>
         <UserPicture user={pageData.lastUpdateUser} size="sm" noLink />

+ 2 - 2
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -303,12 +303,12 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
     }
 
     const pageToDelete: IPageToDeleteWithMeta = {
-      pageData: {
+      data: {
         _id: page._id,
         revision: page.revision as string,
         path: page.path,
       },
-      pageMeta: pageInfo,
+      meta: pageInfo,
     };
 
     if (onClickDeleteMenuItem != null) {

+ 6 - 12
packages/app/src/interfaces/page.ts

@@ -97,20 +97,14 @@ export const isIPageInfoForListing = (pageInfo: any | undefined): pageInfo is IP
 //   return <IPageInfoTypeResolver<T>>pageInfo;
 // };
 
-export type IPageWithMeta<M = IPageInfoAll> = {
-  pageData: IPageHasId,
-  pageMeta?: M,
-};
+export type IDataWithMeta<D = unknown, M = unknown> = {
+  data: D,
+  meta?: M,
+}
 
-export type IPageWithAnyMeta = {
-  pageData: IPageHasId,
-  pageMeta?: unknown,
-};
+export type IPageWithMeta<M = IPageInfoAll> = IDataWithMeta<IPageHasId, M>;
 
-export type IPageToDeleteWithMeta<M = IPageInfoAll> = {
-  pageData: HasObjectId & { path: string, revision: string },
-  pageMeta?: M,
-};
+export type IPageToDeleteWithMeta = IDataWithMeta<HasObjectId & (IPage | { path: string, revision: string }), IPageInfoForOperation | unknown>;
 
 export type IDeleteSinglePageApiv1Result = {
   ok: boolean

+ 3 - 2
packages/app/src/interfaces/search.ts

@@ -1,4 +1,4 @@
-import { IPageInfoAll, IPageWithMeta } from './page';
+import { IPageWithMeta } from './page';
 
 export type IPageSearchMeta = {
   bookmarkCount?: number,
@@ -9,7 +9,8 @@ export type IPageSearchMeta = {
   };
 }
 
-export const isIPageSearchMeta = (meta: IPageInfoAll | (IPageInfoAll & IPageSearchMeta) | undefined): meta is IPageInfoAll & IPageSearchMeta => {
+// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
+export const isIPageSearchMeta = (meta: any): meta is IPageSearchMeta => {
   return meta != null && 'elasticSearchResult' in meta;
 };
 

+ 1 - 1
packages/app/src/server/routes/apiv3/page.js

@@ -370,7 +370,7 @@ module.exports = (crowi) => {
         return res.apiv3Err(`Page '${pageId}' is not found or forbidden`);
       }
 
-      return res.apiv3(pageWithMeta.pageMeta);
+      return res.apiv3(pageWithMeta.meta);
     }
     catch (err) {
       logger.error('get-page-info', err);

+ 6 - 6
packages/app/src/server/service/page.ts

@@ -237,8 +237,8 @@ class PageService {
 
     if (isSharedPage) {
       return {
-        pageData: page,
-        pageMeta: {
+        data: page,
+        meta: {
           isEmpty: page.isEmpty,
           isMovable: false,
           isDeletable: false,
@@ -261,8 +261,8 @@ class PageService {
 
     if (isGuestUser) {
       return {
-        pageData: page,
-        pageMeta: metadataForGuest,
+        data: page,
+        meta: metadataForGuest,
       };
     }
 
@@ -273,8 +273,8 @@ class PageService {
     const subscription = await Subscription.findByUserIdAndTargetId(user._id, pageId);
 
     return {
-      pageData: page,
-      pageMeta: {
+      data: page,
+      meta: {
         ...metadataForGuest,
         isAbleToDeleteCompletely,
         isBookmarked,

+ 13 - 6
packages/app/src/server/service/search.ts

@@ -1,7 +1,8 @@
 import xss from 'xss';
 
 import { SearchDelegatorName } from '~/interfaces/named-query';
-import { IFormattedSearchResult, ISearchResult, ISearchResultMeta } from '~/interfaces/search';
+import { IPageWithMeta } from '~/interfaces/page';
+import { IFormattedSearchResult, IPageSearchMeta, ISearchResult } from '~/interfaces/search';
 import loggerFactory from '~/utils/logger';
 
 import NamedQuery from '../models/named-query';
@@ -17,6 +18,8 @@ import { serializeUserSecurely } from '../models/serializers/user-serializer';
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:service:search');
 
+const nonNullable = <T>(value: T): value is NonNullable<T> => value != null;
+
 // options for filtering xss
 const filterXssOptions = {
   whiteList: {
@@ -350,10 +353,9 @@ class SearchService implements SearchQueryParser, SearchResolver {
    */
   async formatSearchResult(searchResult: ISearchResult<any>, delegatorName): Promise<IFormattedSearchResult> {
     if (!this.checkIsFormattable(searchResult, delegatorName)) {
-      const data = searchResult.data.map((page) => {
+      const data: IPageWithMeta<IPageSearchMeta>[] = searchResult.data.map((page) => {
         return {
-          pageData: page,
-          pageMeta: {},
+          data: page,
         };
       });
 
@@ -378,11 +380,15 @@ class SearchService implements SearchQueryParser, SearchResolver {
     result.meta = searchResult.meta;
 
     // set search result page data
-    result.data = searchResult.data.map((data) => {
+    const pages: (IPageWithMeta<IPageSearchMeta> | null)[] = searchResult.data.map((data) => {
       const pageData = findPageResult.pages.find((pageData) => {
         return pageData.id === data._id;
       });
 
+      if (pageData == null) {
+        return null;
+      }
+
       // add tags and seenUserCount to pageData
       pageData._doc.tags = data._source.tag_names;
       pageData._doc.seenUserCount = (pageData.seenUsers && pageData.seenUsers.length) || 0;
@@ -417,9 +423,10 @@ class SearchService implements SearchQueryParser, SearchResolver {
         elasticSearchResult,
       };
 
-      return { pageData, pageMeta };
+      return { data: pageData, meta: pageMeta };
     });
 
+    result.data = pages.filter(nonNullable);
     return result;
   }
 

+ 4 - 17
packages/app/src/stores/modal.tsx

@@ -1,10 +1,7 @@
 import { SWRResponse } from 'swr';
 import { useStaticSWR } from './use-static-swr';
 import { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
-import {
-  IPageInfoAll, IPageToDeleteWithMeta, IPageWithMeta,
-} from '~/interfaces/page';
-import { IPageSearchMeta } from '~/interfaces/search';
+import { IPageToDeleteWithMeta } from '~/interfaces/page';
 
 
 /*
@@ -31,29 +28,19 @@ export const usePageCreateModal = (status?: CreateModalStatus): SWRResponse<Crea
   };
 };
 
-/*
-* PageDeleteModal
-*/
-// export type IPageForPageDeleteModal = {
-//   pageId: string,
-//   revisionId?: string,
-//   path: string
-//   isAbleToDeleteCompletely?: boolean,
-// }
-
 export type IDeleteModalOption = {
   onDeleted?: OnDeletedFunction,
 }
 
 type DeleteModalStatus = {
   isOpened: boolean,
-  pages?: (IPageWithMeta<IPageInfoAll | IPageSearchMeta> | IPageToDeleteWithMeta)[],
+  pages?: IPageToDeleteWithMeta[],
   opts?: IDeleteModalOption,
 }
 
 type DeleteModalStatusUtils = {
   open(
-    pages?: (IPageWithMeta<IPageInfoAll | IPageSearchMeta> | IPageToDeleteWithMeta)[],
+    pages?: IPageToDeleteWithMeta[],
     opts?: IDeleteModalOption,
   ): Promise<DeleteModalStatus | undefined>,
   close(): Promise<DeleteModalStatus | undefined>,
@@ -69,7 +56,7 @@ export const usePageDeleteModal = (status?: DeleteModalStatus): SWRResponse<Dele
   return {
     ...swrResponse,
     open: (
-        pages?: (IPageWithMeta<IPageInfoAll | IPageSearchMeta> | IPageToDeleteWithMeta)[],
+        pages?: IPageToDeleteWithMeta[],
         opts?: IDeleteModalOption,
     ) => swrResponse.mutate({
       isOpened: true, pages, opts,

+ 12 - 11
packages/app/src/stores/page.tsx

@@ -2,9 +2,10 @@ import useSWR, { SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
 import { apiv3Get } from '~/client/util/apiv3-client';
+import { HasObjectId } from '~/interfaces/has-object-id';
 
 import {
-  IPageInfo, IPageHasId, IPageInfoForOperation, IPageInfoForListing, IPageWithMeta, IPageWithAnyMeta,
+  IPageInfo, IPageHasId, IPageInfoForOperation, IPageInfoForListing, IDataWithMeta,
 } from '~/interfaces/page';
 import { IPagingResult } from '~/interfaces/paging-result';
 import { apiGet } from '../client/util/apiv1-client';
@@ -92,38 +93,38 @@ export const useSWRxPageInfo = (
 };
 
 type PageInfoInjector = {
-  injectTo: (pages: (IPageHasId | IPageWithAnyMeta)[]) => IPageWithMeta[],
+  injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => IDataWithMeta<D, IPageInfoForOperation>[],
 }
 
-const isIPageWithMeta = (page: IPageHasId | IPageWithAnyMeta): page is IPageWithAnyMeta => {
-  return 'pageData' in page;
+const isIDataWithMeta = (item: HasObjectId | IDataWithMeta): item is IDataWithMeta => {
+  return 'data' in item;
 };
 
 export const useSWRxPageInfoForList = (
     pageIds: string[] | null | undefined,
     attachShortBody = false,
-): SWRResponse<Record<string, IPageInfo | IPageInfoForListing>, Error> & PageInfoInjector => {
+): SWRResponse<Record<string, IPageInfoForListing>, Error> & PageInfoInjector => {
 
   const shouldFetch = pageIds != null && pageIds.length > 0;
 
-  const swrResult = useSWRImmutable<Record<string, IPageInfo | IPageInfoForListing>>(
+  const swrResult = useSWRImmutable<Record<string, IPageInfoForListing>>(
     shouldFetch ? ['/page-listing/info', pageIds, attachShortBody] : null,
     (endpoint, pageIds, attachShortBody) => apiv3Get(endpoint, { pageIds, attachShortBody }).then(response => response.data),
   );
 
   return {
     ...swrResult,
-    injectTo: (pages: (IPageHasId | IPageWithAnyMeta)[]) => {
+    injectTo: <D extends HasObjectId>(pages: (D | IDataWithMeta<D>)[]) => {
       return pages.map((item) => {
-        const page: IPageHasId = isIPageWithMeta(item) ? item.pageData : item;
-        const orgPageMeta = isIPageWithMeta(item) ? item.pageMeta : undefined;
+        const page = isIDataWithMeta(item) ? item.data : item;
+        const orgPageMeta = isIDataWithMeta(item) ? item.meta : undefined;
 
         // get an applicable IPageInfo
         const applicablePageInfo = (swrResult.data ?? {})[page._id];
 
         return {
-          pageData: page,
-          pageMeta: applicablePageInfo ?? orgPageMeta,
+          data: page,
+          meta: applicablePageInfo ?? orgPageMeta,
         };
       });
     },