From 89806d334fe2c7ef0ea8b3d12c74759e8e3ba860 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 4 Aug 2020 02:31:59 +0800 Subject: ... --- .../src/app/timeline/TimelineInfoCard.tsx | 9 ++- .../ClientApp/src/app/timeline/TimelineItem.tsx | 24 +++--- .../ClientApp/src/app/timeline/TimelineMember.tsx | 13 ++-- .../src/app/timeline/TimelinePageTemplate.tsx | 85 ++++++---------------- .../src/app/timeline/TimelinePageTemplateUI.tsx | 10 ++- 5 files changed, 51 insertions(+), 90 deletions(-) (limited to 'Timeline/ClientApp/src/app/timeline') diff --git a/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx b/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx index c25b2376..ece7d38a 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelineInfoCard.tsx @@ -10,10 +10,11 @@ import { import { useTranslation } from 'react-i18next'; import { fromEvent } from 'rxjs'; -import { useAvatarUrl } from '../data/user'; +import { useAvatar } from '../data/user'; import { timelineVisibilityTooltipTranslationMap } from '../data/timeline'; import { TimelineCardComponentProps } from './TimelinePageTemplateUI'; +import BlobImage from '../common/BlobImage'; export type OrdinaryTimelineManageItem = 'delete'; @@ -26,7 +27,7 @@ const TimelineInfoCard: React.FC = (props) => { const { t } = useTranslation(); - const avatarUrl = useAvatarUrl(props.timeline.owner.username); + const avatar = useAvatar(props.timeline.owner.username); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const containerRef = React.useRef(null!); @@ -60,8 +61,8 @@ const TimelineInfoCard: React.FC = (props) => { {props.timeline.name}
- diff --git a/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx b/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx index 11ac9f08..727de1fe 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelineItem.tsx @@ -16,8 +16,10 @@ import Svg from 'react-inlinesvg'; import chevronDownIcon from 'bootstrap-icons/icons/chevron-down.svg'; import trashIcon from 'bootstrap-icons/icons/trash.svg'; -import { useAvatarUrl } from '../data/user'; -import { TimelinePostInfo, usePostDataUrl } from '../data/timeline'; +import BlobImage from '../common/BlobImage'; + +import { useAvatar } from '../data/user'; +import { TimelinePostInfo } from '../data/timeline'; const TimelinePostDeleteConfirmDialog: React.FC<{ toggle: () => void; @@ -70,13 +72,7 @@ const TimelineItem: React.FC = (props) => { const { more, onResize } = props; - const avatarUrl = useAvatarUrl(props.post.author.username); - - const dataUrl = usePostDataUrl( - props.post.content.type === 'image', - props.post.timelineName, - props.post.id - ); + const avatar = useAvatar(props.post.author.username); const [deleteDialog, setDeleteDialog] = React.useState(false); const toggleDeleteDialog = React.useCallback( @@ -132,7 +128,11 @@ const TimelineItem: React.FC = (props) => { className="float-right float-sm-left mx-2" to={'/users/' + props.post.author.username} > - + {(() => { const { content } = props.post; @@ -140,9 +140,9 @@ const TimelineItem: React.FC = (props) => { return content.text; } else { return ( - ); diff --git a/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx b/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx index 8c637f46..39af412e 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelineMember.tsx @@ -10,9 +10,10 @@ import { Button, } from 'reactstrap'; -import { User, useAvatarUrl } from '../data/user'; +import { User, useAvatar } from '../data/user'; import SearchInput from '../common/SearchInput'; +import BlobImage from '../common/BlobImage'; const TimelineMemberItem: React.FC<{ user: User; @@ -21,13 +22,13 @@ const TimelineMemberItem: React.FC<{ }> = ({ user, owner, onRemove }) => { const { t } = useTranslation(); - const avatarUrl = useAvatarUrl(user.username); + const avatar = useAvatar(user.username); return ( - + {user.nickname} @@ -84,7 +85,7 @@ const TimelineMember: React.FC = (props) => { | { type: 'init' } >({ type: 'init' }); - const userSearchAvatarUrl = useAvatarUrl( + const userSearchAvatar = useAvatar( userSearchState.type === 'user' ? userSearchState.data.username : undefined ); @@ -156,8 +157,8 @@ const TimelineMember: React.FC = (props) => { - diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx index a68d08c6..afa9464a 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelinePageTemplate.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { concat, without } from 'lodash'; import { of } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; +import { catchError } from 'rxjs/operators'; import { ExcludeKey } from '../utilities/type'; import { pushAlert } from '../common/alert-service'; @@ -10,8 +9,8 @@ import { useUser, userInfoService, UserNotExistError } from '../data/user'; import { timelineService, TimelineInfo, - TimelineNotExistError, usePostList, + useTimelineInfo, } from '../data/timeline'; import { TimelineDeleteCallback } from './Timeline'; @@ -51,34 +50,22 @@ export default function TimelinePageTemplate< const [dialog, setDialog] = React.useState( null ); - const [timeline, setTimeline] = React.useState( - undefined - ); - const postListState = usePostList(timeline?.name); - - const [error, setError] = React.useState(undefined); - - React.useEffect(() => { - const subscription = service.getTimeline(name).subscribe( - (ti) => { - setTimeline(ti); - }, - (error) => { - if (error instanceof TimelineNotExistError) { - setError(t(props.notFoundI18nKey)); - } else { - setError( - // TODO: Convert this to a function. - (error as { message?: string })?.message ?? 'Unknown error' - ); - } - } - ); - return () => { - subscription.unsubscribe(); - }; - }, [name, service, user, t, props.dataVersion, props.notFoundI18nKey]); + const timelineState = useTimelineInfo(name); + + const timeline = timelineState?.timeline; + + const postListState = usePostList(name); + + const error: string | undefined = (() => { + if (timelineState != null) { + const { syncState, timeline } = timelineState; + if (syncState === 'offline' && timeline == null) return 'Network Error'; + if (syncState !== 'offline' && timeline == null) + return t(props.notFoundI18nKey); + } + return undefined; + })(); const closeDialog = React.useCallback((): void => { setDialog(null); @@ -102,14 +89,7 @@ export default function TimelinePageTemplate< description: timeline.description, }} onProcess={(req) => { - return service - .changeTimelineProperty(name, req) - .pipe( - map((newTimeline) => { - setTimeline(newTimeline); - }) - ) - .toPromise(); + return service.changeTimelineProperty(name, req).toPromise().then(); }} /> ); @@ -143,33 +123,10 @@ export default function TimelinePageTemplate< .toPromise(); }, onAddUser: (u) => { - return service - .addMember(name, u.username) - .pipe( - map(() => { - setTimeline({ - ...timeline, - members: concat(timeline.members, u), - }); - }) - ) - .toPromise(); + return service.addMember(name, u.username).toPromise().then(); }, onRemoveUser: (u) => { - service.removeMember(name, u).subscribe(() => { - const toDelete = timeline.members.find( - (m) => m.username === u - ); - if (toDelete == null) { - throw new UiLogicError( - 'The member to delete is not in list.' - ); - } - setTimeline({ - ...timeline, - members: without(timeline.members, toDelete), - }); - }); + service.removeMember(name, u); }, } : null @@ -220,7 +177,7 @@ export default function TimelinePageTemplate< <> = ({ state, style, className }) => { @@ -84,7 +86,7 @@ export interface TimelineCardComponentProps { export interface TimelinePageTemplateUIProps { avatarKey?: string | number; timeline?: TimelineInfo; - postListState?: TimelinePostListState; + postListState?: TimelinePostsWithSyncState; CardComponent: React.ComponentType>; onMember: () => void; onManage?: (item: TManageItems | 'property') => void; @@ -197,7 +199,7 @@ export default function TimelinePageTemplateUI( } else { if (timeline != null) { let timelineBody: React.ReactElement; - if (postListState != null && postListState.state !== 'loading') { + if (postListState != null) { if (postListState.state === 'forbid') { timelineBody = (

{t('timeline.messageCantSee')}

-- cgit v1.2.3