import React from 'react'; import { useTranslation } from 'react-i18next'; import { of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ExcludeKey } from '../utilities/type'; import { pushAlert } from '../common/alert-service'; import { useUser, userInfoService, UserNotExistError } from '../data/user'; import { timelineService, usePostList, useTimelineInfo, } from '../data/timeline'; import { TimelineDeleteCallback } from './Timeline'; import { TimelineMemberDialog } from './TimelineMember'; import TimelinePropertyChangeDialog from './TimelinePropertyChangeDialog'; import { TimelinePageTemplateUIProps } from './TimelinePageTemplateUI'; import { TimelinePostSendCallback } from './TimelinePostEdit'; import { UiLogicError } from '../common'; export interface TimelinePageTemplateProps { name: string; onManage: (item: TManageItem) => void; UiComponent: React.ComponentType< ExcludeKey, 'CardComponent'> >; dataVersion?: number; notFoundI18nKey: string; } export default function TimelinePageTemplate( props: TimelinePageTemplateProps ): React.ReactElement | null { const { t } = useTranslation(); const { name } = props; const service = timelineService; const user = useUser(); const [dialog, setDialog] = React.useState( null ); const timelineState = useTimelineInfo(name); const timeline = timelineState?.timeline; const postListState = usePostList(name); const error: string | undefined = (() => { if (timelineState != null) { const { type, timeline } = timelineState; if (type === 'offline' && timeline == null) return 'Network Error'; if (type === 'synced' && timeline == null) return t(props.notFoundI18nKey); } return undefined; })(); const closeDialog = React.useCallback((): void => { setDialog(null); }, []); let dialogElement: React.ReactElement | undefined; if (dialog === 'property') { if (timeline == null) { throw new UiLogicError( 'Timeline is null but attempt to open change property dialog.' ); } dialogElement = ( { return service.changeTimelineProperty(name, req).toPromise().then(); }} /> ); } else if (dialog === 'member') { if (timeline == null) { throw new UiLogicError( 'Timeline is null but attempt to open change property dialog.' ); } dialogElement = ( { return userInfoService .getUserInfo(u) .pipe( catchError((e) => { if (e instanceof UserNotExistError) { return of(null); } else { throw e; } }) ) .toPromise(); }, onAddUser: (u) => { return service.addMember(name, u.username).toPromise().then(); }, onRemoveUser: (u) => { service.removeMember(name, u); }, } : null } /> ); } const { UiComponent } = props; const onDelete: TimelineDeleteCallback = React.useCallback( (index, id) => { service.deletePost(name, id).subscribe(null, () => { pushAlert({ type: 'danger', message: t('timeline.deletePostFailed'), }); }); }, [service, name, t] ); const onPost: TimelinePostSendCallback = React.useCallback( (req) => { return service.createPost(name, req).toPromise().then(); }, [service, name] ); const onManageProp = props.onManage; const onManage = React.useCallback( (item: 'property' | TManageItem) => { if (item === 'property') { setDialog(item); } else { onManageProp(item); } }, [onManageProp] ); const onMember = React.useCallback(() => { setDialog('member'); }, []); return ( <> {dialogElement} ); }