|
|
@@ -16,6 +16,8 @@ import loggerFactory from '~/utils/logger';
|
|
|
import FormattedDistanceDate from '../FormattedDistanceDate';
|
|
|
|
|
|
import InfiniteScroll from './InfiniteScroll';
|
|
|
+import { SidebarHeaderReloadButton } from './SidebarHeaderReloadButton';
|
|
|
+import RecentChangesContentSkeleton from './Skeleton/RecentChangesContentSkeleton';
|
|
|
|
|
|
import TagLabelsStyles from '../Page/TagLabels.module.scss';
|
|
|
import styles from './RecentChanges.module.scss';
|
|
|
@@ -23,11 +25,15 @@ import styles from './RecentChanges.module.scss';
|
|
|
|
|
|
const logger = loggerFactory('growi:History');
|
|
|
|
|
|
-type PageItemProps = {
|
|
|
+type PageItemLowerProps = {
|
|
|
page: IPageHasId,
|
|
|
}
|
|
|
|
|
|
-const PageItemLower = memo(({ page }: PageItemProps): JSX.Element => {
|
|
|
+type PageItemProps = PageItemLowerProps & {
|
|
|
+ isSmall: boolean
|
|
|
+}
|
|
|
+
|
|
|
+const PageItemLower = memo(({ page }: PageItemLowerProps): JSX.Element => {
|
|
|
return (
|
|
|
<div className="d-flex justify-content-between grw-recent-changes-item-lower pt-1">
|
|
|
<div className="d-flex">
|
|
|
@@ -44,8 +50,7 @@ const PageItemLower = memo(({ page }: PageItemProps): JSX.Element => {
|
|
|
});
|
|
|
PageItemLower.displayName = 'PageItemLower';
|
|
|
|
|
|
-
|
|
|
-const LargePageItem = memo(({ page }: PageItemProps): JSX.Element => {
|
|
|
+const PageItem = memo(({ page, isSmall }: PageItemProps): JSX.Element => {
|
|
|
const dPagePath = new DevidedPagePath(page.path, false, true);
|
|
|
const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
|
|
|
const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
|
|
|
@@ -75,67 +80,38 @@ const LargePageItem = memo(({ page }: PageItemProps): JSX.Element => {
|
|
|
});
|
|
|
|
|
|
return (
|
|
|
- <li className={`list-group-item ${styles['list-group-item']} py-3 px-0`}>
|
|
|
+ <li className={`list-group-item ${styles['list-group-item']} ${isSmall ? 'py-2' : 'py-3'} px-0`}>
|
|
|
<div className="d-flex w-100">
|
|
|
<UserPicture user={page.lastUpdateUser} size="md" noTooltip />
|
|
|
<div className="flex-grow-1 ml-2">
|
|
|
{ !dPagePath.isRoot && <FormerLink /> }
|
|
|
- <h5 className="my-2">
|
|
|
+ <h5 className={isSmall ? 'my-0 text-truncate' : 'my-2'}>
|
|
|
<PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
|
|
|
{locked}
|
|
|
</h5>
|
|
|
- <div className="grw-tag-labels mt-1 mb-2">
|
|
|
+ {!isSmall && <div className="grw-tag-labels mt-1 mb-2">
|
|
|
{ tagElements }
|
|
|
- </div>
|
|
|
+ </div>}
|
|
|
<PageItemLower page={page} />
|
|
|
</div>
|
|
|
</div>
|
|
|
</li>
|
|
|
);
|
|
|
});
|
|
|
-LargePageItem.displayName = 'LargePageItem';
|
|
|
-
|
|
|
-
|
|
|
-const SmallPageItem = memo(({ page }: PageItemProps): JSX.Element => {
|
|
|
- const dPagePath = new DevidedPagePath(page.path, false, true);
|
|
|
- const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
|
|
|
- const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
|
|
|
- const FormerLink = () => (
|
|
|
- <div className="grw-page-path-text-muted-container small">
|
|
|
- <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />
|
|
|
- </div>
|
|
|
- );
|
|
|
-
|
|
|
- let locked;
|
|
|
- if (page.grant !== 1) {
|
|
|
- locked = <span><i className="icon-lock ml-2" /></span>;
|
|
|
- }
|
|
|
-
|
|
|
- return (
|
|
|
- <li className={`list-group-item ${styles['list-group-item']} py-2 px-0`}>
|
|
|
- <div className="d-flex w-100">
|
|
|
- <UserPicture user={page.lastUpdateUser} size="md" noTooltip />
|
|
|
- <div className="flex-grow-1 ml-2">
|
|
|
- { !dPagePath.isRoot && <FormerLink /> }
|
|
|
- <h5 className="my-0 text-truncate">
|
|
|
- <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
|
|
|
- {locked}
|
|
|
- </h5>
|
|
|
- <PageItemLower page={page} />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </li>
|
|
|
- );
|
|
|
-});
|
|
|
-SmallPageItem.displayName = 'SmallPageItem';
|
|
|
+PageItem.displayName = 'PageItem';
|
|
|
|
|
|
const RecentChanges = (): JSX.Element => {
|
|
|
+
|
|
|
const PER_PAGE = 20;
|
|
|
const { t } = useTranslation();
|
|
|
- const swr = useSWRInifinitexRecentlyUpdated();
|
|
|
+ const swrInifinitexRecentlyUpdated = useSWRInifinitexRecentlyUpdated();
|
|
|
+ const { data: dataRecentlyUpdated, error, mutate: mutateRecentlyUpdated } = swrInifinitexRecentlyUpdated;
|
|
|
+
|
|
|
const [isRecentChangesSidebarSmall, setIsRecentChangesSidebarSmall] = useState(false);
|
|
|
- const isEmpty = swr.data?.[0].length === 0;
|
|
|
- const isReachingEnd = isEmpty || (swr.data && swr.data[swr.data.length - 1]?.length < PER_PAGE);
|
|
|
+ const isEmpty = dataRecentlyUpdated?.[0].length === 0;
|
|
|
+ const isLoading = error == null && dataRecentlyUpdated === undefined;
|
|
|
+ const isReachingEnd = isEmpty || (dataRecentlyUpdated && dataRecentlyUpdated[dataRecentlyUpdated.length - 1]?.length < PER_PAGE);
|
|
|
+
|
|
|
const retrieveSizePreferenceFromLocalStorage = useCallback(() => {
|
|
|
if (window.localStorage.isRecentChangesSidebarSmall === 'true') {
|
|
|
setIsRecentChangesSidebarSmall(true);
|
|
|
@@ -153,12 +129,10 @@ const RecentChanges = (): JSX.Element => {
|
|
|
}, [retrieveSizePreferenceFromLocalStorage]);
|
|
|
|
|
|
return (
|
|
|
- <div data-testid="grw-recent-changes">
|
|
|
- <div className="grw-sidebar-content-header p-3 d-flex">
|
|
|
- <h3 className="mb-0 text-nowrap">{t('Recent Changes')}</h3>
|
|
|
- <button type="button" className="btn btn-sm ml-auto grw-btn-reload" onClick={() => swr.mutate()}>
|
|
|
- <i className="icon icon-reload"></i>
|
|
|
- </button>
|
|
|
+ <div className="px-3" data-testid="grw-recent-changes">
|
|
|
+ <div className="grw-sidebar-content-header py-3 d-flex">
|
|
|
+ <h3 className="mb-0 text-nowrap">{t('Recent Changes')}</h3>
|
|
|
+ <SidebarHeaderReloadButton onClick={() => mutateRecentlyUpdated()}/>
|
|
|
<div className="d-flex align-items-center">
|
|
|
<div className={`grw-recent-changes-resize-button ${styles['grw-recent-changes-resize-button']} custom-control custom-switch ml-1`}>
|
|
|
<input
|
|
|
@@ -173,21 +147,23 @@ const RecentChanges = (): JSX.Element => {
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div className="grw-recent-changes p-3">
|
|
|
- <ul className="list-group list-group-flush">
|
|
|
- <InfiniteScroll
|
|
|
- swrInifiniteResponse={swr}
|
|
|
- isReachingEnd={isReachingEnd}
|
|
|
- >
|
|
|
- {pages => pages.map(page => (
|
|
|
- isRecentChangesSidebarSmall
|
|
|
- ? <SmallPageItem key={page._id} page={page} />
|
|
|
- : <LargePageItem key={page._id} page={page} />
|
|
|
- ))
|
|
|
- }
|
|
|
- </InfiniteScroll>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
+ {
|
|
|
+ isLoading ? <RecentChangesContentSkeleton /> : (
|
|
|
+ <div className="grw-recent-changes py-3">
|
|
|
+ <ul className="list-group list-group-flush">
|
|
|
+ <InfiniteScroll
|
|
|
+ swrInifiniteResponse={swrInifinitexRecentlyUpdated}
|
|
|
+ isReachingEnd={isReachingEnd}
|
|
|
+ >
|
|
|
+ {pages => pages.map(
|
|
|
+ page => <PageItem key={page._id} page={page} isSmall={isRecentChangesSidebarSmall} />,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ </InfiniteScroll>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
</div>
|
|
|
);
|
|
|
|