diff options
author | crupest <crupest@outlook.com> | 2021-01-03 19:38:05 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-03 19:38:05 +0800 |
commit | 418d631528fdb581a384068719e9af5dbaa66740 (patch) | |
tree | 42e2c0396a16cb5fbaaae665a238a8a031bef7f0 /FrontEnd/src/app/views/timeline-common | |
parent | 4b3ae3edd9e8aceac5ff26ef137d2a8d686fe305 (diff) | |
parent | 8af803cb0da57af1355ad28cd056cb5dcf6d6915 (diff) | |
download | timeline-418d631528fdb581a384068719e9af5dbaa66740.tar.gz timeline-418d631528fdb581a384068719e9af5dbaa66740.tar.bz2 timeline-418d631528fdb581a384068719e9af5dbaa66740.zip |
Merge pull request #197 from crupest/front-dev
Front: Highlight and bookmark timeline and new home page.
Diffstat (limited to 'FrontEnd/src/app/views/timeline-common')
7 files changed, 160 insertions, 47 deletions
diff --git a/FrontEnd/src/app/views/timeline-common/CollapseButton.tsx b/FrontEnd/src/app/views/timeline-common/CollapseButton.tsx index 3c52150f..da54f3fd 100644 --- a/FrontEnd/src/app/views/timeline-common/CollapseButton.tsx +++ b/FrontEnd/src/app/views/timeline-common/CollapseButton.tsx @@ -1,8 +1,5 @@ import React from "react"; import clsx from "clsx"; -import Svg from "react-inlinesvg"; -import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg"; -import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg"; const CollapseButton: React.FC<{ collapse: boolean; @@ -11,10 +8,13 @@ const CollapseButton: React.FC<{ style?: React.CSSProperties; }> = ({ collapse, onClick, className, style }) => { return ( - <Svg - src={collapse ? arrowsAngleExpandIcon : arrowsAngleContractIcon} + <i onClick={onClick} - className={clsx("text-primary icon-button", className)} + className={clsx( + collapse ? "bi-arrows-angle-expand" : "bi-arrows-angle-contract", + "text-primary icon-button", + className + )} style={style} /> ); diff --git a/FrontEnd/src/app/views/timeline-common/InfoCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/InfoCardTemplate.tsx deleted file mode 100644 index a8de20aa..00000000 --- a/FrontEnd/src/app/views/timeline-common/InfoCardTemplate.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from "react"; -import clsx from "clsx"; - -import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI"; -import SyncStatusBadge from "../timeline-common/SyncStatusBadge"; -import CollapseButton from "../timeline-common/CollapseButton"; - -const InfoCardTemplate: React.FC< - Pick< - TimelineCardComponentProps<"">, - "collapse" | "toggleCollapse" | "syncStatus" | "className" - > & { children: React.ReactElement[] } -> = ({ collapse, toggleCollapse, syncStatus, className, children }) => { - return ( - <div className={clsx("cru-card p-2 clearfix", className)}> - <div className="float-right d-flex align-items-center"> - <SyncStatusBadge status={syncStatus} className="mr-2" /> - <CollapseButton collapse={collapse} onClick={toggleCollapse} /> - </div> - - <div style={{ display: collapse ? "none" : "block" }}>{children}</div> - </div> - ); -}; - -export default InfoCardTemplate; diff --git a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx new file mode 100644 index 00000000..ece1942f --- /dev/null +++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx @@ -0,0 +1,105 @@ +import React from "react"; +import clsx from "clsx"; +import { useTranslation } from "react-i18next"; +import { Dropdown, Button } from "react-bootstrap"; + +import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline"; + +import { TimelineCardComponentProps } from "../timeline-common/TimelinePageTemplateUI"; +import SyncStatusBadge from "../timeline-common/SyncStatusBadge"; +import CollapseButton from "../timeline-common/CollapseButton"; + +export interface TimelineCardTemplateProps + extends Omit<TimelineCardComponentProps<"">, "onManage" | "onMember"> { + infoArea: React.ReactElement; + manageArea: + | { type: "member"; onMember: () => void } + | { + type: "manage"; + items: ( + | { + type: "button"; + text: string; + color?: string; + onClick: () => void; + } + | { type: "divider" } + )[]; + }; +} + +function TimelineCardTemplate({ + timeline, + collapse, + infoArea, + manageArea, + onBookmark, + onHighlight, + toggleCollapse, + syncStatus, + className, +}: TimelineCardTemplateProps): React.ReactElement | null { + const { t } = useTranslation(); + + return ( + <div className={clsx("cru-card p-2 clearfix", className)}> + <div className="float-right d-flex align-items-center"> + <SyncStatusBadge status={syncStatus} className="mr-2" /> + <CollapseButton collapse={collapse} onClick={toggleCollapse} /> + </div> + <div style={{ display: collapse ? "none" : "block" }}> + {infoArea} + <p className="mb-0">{timeline.description}</p> + <small className="mt-1 d-block"> + {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} + </small> + <div className="text-right mt-2"> + {onHighlight != null ? ( + <i + className="bi-star icon-button text-yellow mr-3" + onClick={onHighlight} + /> + ) : null} + {onBookmark != null ? ( + <i + className="bi-bookmark icon-button text-yellow mr-3" + onClick={onBookmark} + /> + ) : null} + {manageArea.type === "manage" ? ( + <Dropdown className="d-inline-block"> + <Dropdown.Toggle variant="outline-primary"> + {t("timeline.manage")} + </Dropdown.Toggle> + <Dropdown.Menu> + {manageArea.items.map((item, index) => { + if (item.type === "divider") { + return <Dropdown.Divider key={index} />; + } else { + return ( + <Dropdown.Item + key={index} + onClick={item.onClick} + className={ + item.color != null ? "text-" + item.color : undefined + } + > + {t(item.text)} + </Dropdown.Item> + ); + } + })} + </Dropdown.Menu> + </Dropdown> + ) : ( + <Button variant="outline-primary" onClick={manageArea.onMember}> + {t("timeline.memberButton")} + </Button> + )} + </div> + </div> + </div> + ); +} + +export default TimelineCardTemplate; diff --git a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx index 408c49a1..233c81bd 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx @@ -2,9 +2,6 @@ import React from "react"; import clsx from "clsx"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; -import chevronDownIcon from "bootstrap-icons/icons/chevron-down.svg"; -import trashIcon from "bootstrap-icons/icons/trash.svg"; import { Modal, Button } from "react-bootstrap"; import { useAvatar } from "@/services/user"; @@ -98,9 +95,8 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { <small className="text-dark">{props.post.author.nickname}</small> </span> {more != null ? ( - <Svg - src={chevronDownIcon} - className="text-info icon-button" + <i + className="bi-chevron-down text-info icon-button" onClick={(e) => { more.toggle(); e.stopPropagation(); @@ -139,9 +135,8 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { className="position-absolute position-lt w-100 h-100 mask d-flex justify-content-center align-items-center" onClick={more.toggle} > - <Svg - src={trashIcon} - className="text-danger icon-button large" + <i + className="bi-trash text-danger icon-button large" onClick={(e) => { setDeleteDialog(true); e.stopPropagation(); diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx index 6c57e91d..7f5c8206 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx @@ -11,6 +11,8 @@ import { usePostList, useTimelineInfo, } from "@/services/timeline"; +import { getHttpBookmarkClient } from "@/http/bookmark"; +import { getHttpHighlightClient } from "@/http/highlight"; import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; @@ -116,6 +118,38 @@ export default function TimelinePageTemplate<TManageItem>( ? onManage : undefined, onMember: () => setDialog("member"), + onBookmark: + user != null + ? () => { + void getHttpBookmarkClient() + .put(name, user.token) + .then(() => { + pushAlert({ + message: { + type: "i18n", + key: "timeline.addBookmarkSuccess", + }, + type: "success", + }); + }); + } + : undefined, + onHighlight: + user != null && user.hasHighlightTimelineAdministrationPermission + ? () => { + void getHttpHighlightClient() + .put(name, user.token) + .then(() => { + pushAlert({ + message: { + type: "i18n", + key: "timeline.addHighlightSuccess", + }, + type: "success", + }); + }); + } + : undefined, }; if (type === "cache") { diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx index f60383dd..20ec6e43 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplateUI.tsx @@ -15,6 +15,8 @@ export interface TimelineCardComponentProps<TManageItems> { timeline: TimelineInfo; onManage?: (item: TManageItems | "property") => void; onMember: () => void; + onBookmark?: () => void; + onHighlight?: () => void; className?: string; collapse: boolean; syncStatus: TimelineSyncStatus; @@ -28,6 +30,8 @@ export interface TimelinePageTemplateUIProps<TManageItems> { posts?: TimelinePostInfoEx[]; onManage?: (item: TManageItems | "property") => void; onMember: () => void; + onBookmark?: () => void; + onHighlight?: () => void; onPost?: TimelinePostSendCallback; } | I18nText; @@ -153,6 +157,8 @@ export default function TimelinePageTemplateUI<TManageItems>( timeline={data.timeline} onManage={data.onManage} onMember={data.onMember} + onBookmark={data.onBookmark} + onHighlight={data.onHighlight} syncStatus={syncStatus} collapse={cardCollapse} toggleCollapse={toggleCardCollapse} diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx index dfa2f879..207bf6af 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx @@ -1,10 +1,7 @@ import React from "react"; import clsx from "clsx"; import { useTranslation } from "react-i18next"; -import Svg from "react-inlinesvg"; import { Button, Spinner, Row, Col, Form } from "react-bootstrap"; -import textIcon from "bootstrap-icons/icons/card-text.svg"; -import imageIcon from "bootstrap-icons/icons/image.svg"; import { UiLogicError } from "@/common"; @@ -212,10 +209,12 @@ const TimelinePostEdit: React.FC<TimelinePostEditProps> = (props) => { return ( <> <div className="d-block text-center mt-1 mb-2"> - <Svg + <i onLoad={notifyHeightChange} - src={kind === "text" ? imageIcon : textIcon} - className="icon-button" + className={clsx( + kind === "text" ? "bi-image" : "bi-card-text", + "icon-button" + )} onClick={toggleKind} /> </div> |