From a337ce43ae91c0c9a1c359dbb91faf75f1375505 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Tue, 22 Dec 2020 00:27:33 +0800
Subject: ...

---
 .../views/timeline-common/TimelineCardTemplate.tsx | 100 +++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx

(limited to 'FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx')

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..a47b3d76
--- /dev/null
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -0,0 +1,100 @@
+import React from "react";
+import clsx from "clsx";
+import { useTranslation } from "react-i18next";
+import { Dropdown, Button } from "react-bootstrap";
+import Svg from "react-inlinesvg";
+import bookmarkIcon from "bootstrap-icons/icons/bookmark.svg";
+
+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,
+  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">
+          {onBookmark != null ? (
+            <Svg
+              src={bookmarkIcon}
+              className="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
+                        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;
-- 
cgit v1.2.3


From e89f8c73fe646e43f66a9f869b56267557c96df5 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Wed, 23 Dec 2020 22:35:11 +0800
Subject: ...

---
 FrontEnd/src/app/services/user.ts                        |  4 ++++
 .../app/views/timeline-common/TimelineCardTemplate.tsx   |  9 +++++++++
 .../app/views/timeline-common/TimelinePageTemplate.tsx   | 16 +++++++++++++++-
 3 files changed, 28 insertions(+), 1 deletion(-)

(limited to 'FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx')

diff --git a/FrontEnd/src/app/services/user.ts b/FrontEnd/src/app/services/user.ts
index 0166bce0..7a60b474 100644
--- a/FrontEnd/src/app/services/user.ts
+++ b/FrontEnd/src/app/services/user.ts
@@ -43,6 +43,10 @@ export class AuthUser implements User {
   get hasAllTimelineAdministrationPermission(): boolean {
     return this.permissions.includes("AllTimelineManagement");
   }
+
+  get hasHighlightTimelineAdministrationPermission(): boolean {
+    return this.permissions.includes("HighlightTimelineManagement");
+  }
 }
 
 export interface LoginCredentials {
diff --git a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
index a47b3d76..ffec1a90 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -3,6 +3,7 @@ import clsx from "clsx";
 import { useTranslation } from "react-i18next";
 import { Dropdown, Button } from "react-bootstrap";
 import Svg from "react-inlinesvg";
+import starIcon from "bootstrap-icons/icons/star.svg";
 import bookmarkIcon from "bootstrap-icons/icons/bookmark.svg";
 
 import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline";
@@ -36,6 +37,7 @@ function TimelineCardTemplate({
   infoArea,
   manageArea,
   onBookmark,
+  onHighlight,
   toggleCollapse,
   syncStatus,
   className,
@@ -55,6 +57,13 @@ function TimelineCardTemplate({
           {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])}
         </small>
         <div className="text-right mt-2">
+          {onHighlight != null ? (
+            <Svg
+              src={starIcon}
+              className="icon-button text-yellow mr-3"
+              onClick={onHighlight}
+            />
+          ) : null}
           {onBookmark != null ? (
             <Svg
               src={bookmarkIcon}
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index 3833cdd9..21720601 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -12,6 +12,7 @@ import {
   useTimelineInfo,
 } from "@/services/timeline";
 import { getHttpBookmarkClient } from "@/http/bookmark";
+import { getHttpHighlightClient } from "@/http/highlight";
 
 import { TimelineMemberDialog } from "./TimelineMember";
 import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog";
@@ -124,7 +125,20 @@ export default function TimelinePageTemplate<TManageItem>(
                     .put(name, user.token)
                     .then(() => {
                       pushAlert({
-                        message: "Succeeded to add bookmark!", //TODO: i18n
+                        message: "Succeeded to add bookmark!", // TODO: i18n
+                        type: "success",
+                      });
+                    });
+                }
+              : undefined,
+          onHighlight:
+            user != null && user.hasHighlightTimelineAdministrationPermission
+              ? () => {
+                  void getHttpHighlightClient()
+                    .put(name, user.token)
+                    .then(() => {
+                      pushAlert({
+                        message: "Succeeded to add highlight!", // TODO: i18n
                         type: "success",
                       });
                     });
-- 
cgit v1.2.3


From d9ac1c1d1d999b0e7719504ff5c3234915de8f06 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Thu, 31 Dec 2020 21:07:52 +0800
Subject: ...

---
 FrontEnd/src/app/index.sass                                 |  7 +++----
 FrontEnd/src/app/views/admin/UserAdmin.tsx                  |  7 ++-----
 FrontEnd/src/app/views/timeline-common/CollapseButton.tsx   | 12 ++++++------
 .../src/app/views/timeline-common/TimelineCardTemplate.tsx  | 13 ++++---------
 FrontEnd/src/app/views/timeline-common/TimelineItem.tsx     | 13 ++++---------
 FrontEnd/src/app/views/timeline-common/TimelinePostEdit.tsx | 11 +++++------
 6 files changed, 24 insertions(+), 39 deletions(-)

(limited to 'FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx')

diff --git a/FrontEnd/src/app/index.sass b/FrontEnd/src/app/index.sass
index ae315715..1a833174 100644
--- a/FrontEnd/src/app/index.sass
+++ b/FrontEnd/src/app/index.sass
@@ -1,4 +1,5 @@
 @import '~bootstrap/scss/bootstrap'
+@import '~bootstrap-icons/font/bootstrap-icons.css'
 
 @import './views/common/common'
 @import './views/common/alert/alert'
@@ -34,12 +35,10 @@ small
     width: 40px
 
 .icon-button
-  width: 1.4em
-  height: 1.4em
+  font-size: 1.4em
   cursor: pointer
   &.large
-    width: 1.6em
-    height: 1.6em
+    font-size: 1.6em
 
 .cursor-pointer
   cursor: pointer
diff --git a/FrontEnd/src/app/views/admin/UserAdmin.tsx b/FrontEnd/src/app/views/admin/UserAdmin.tsx
index e1d2c3da..d66abbec 100644
--- a/FrontEnd/src/app/views/admin/UserAdmin.tsx
+++ b/FrontEnd/src/app/views/admin/UserAdmin.tsx
@@ -1,8 +1,6 @@
 import React, { useState, useEffect } from "react";
 import clsx from "clsx";
 import { ListGroup, Row, Col, Spinner, Button } from "react-bootstrap";
-import InlineSVG from "react-inlinesvg";
-import PencilSquareIcon from "bootstrap-icons/icons/pencil-square.svg";
 
 import OperationDialog, {
   OperationBoolInputInfo,
@@ -220,9 +218,8 @@ const UserItem: React.FC<UserItemProps> = ({ user, on }) => {
 
   return (
     <ListGroup.Item className="admin-user-item">
-      <InlineSVG
-        src={PencilSquareIcon}
-        className="float-right icon-button text-warning"
+      <i
+        className="bi-pencil-square float-right icon-button text-warning"
         onClick={() => setEditMaskVisible(true)}
       />
       <h4 className="text-primary">{user.username}</h4>
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/TimelineCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
index ffec1a90..b42b3aa3 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -2,9 +2,6 @@ import React from "react";
 import clsx from "clsx";
 import { useTranslation } from "react-i18next";
 import { Dropdown, Button } from "react-bootstrap";
-import Svg from "react-inlinesvg";
-import starIcon from "bootstrap-icons/icons/star.svg";
-import bookmarkIcon from "bootstrap-icons/icons/bookmark.svg";
 
 import { timelineVisibilityTooltipTranslationMap } from "@/services/timeline";
 
@@ -58,16 +55,14 @@ function TimelineCardTemplate({
         </small>
         <div className="text-right mt-2">
           {onHighlight != null ? (
-            <Svg
-              src={starIcon}
-              className="icon-button text-yellow mr-3"
+            <i
+              className="bi-star icon-button text-yellow mr-3"
               onClick={onHighlight}
             />
           ) : null}
           {onBookmark != null ? (
-            <Svg
-              src={bookmarkIcon}
-              className="icon-button text-yellow mr-3"
+            <i
+              className="bi-bookmark icon-button text-yellow mr-3"
               onClick={onBookmark}
             />
           ) : null}
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/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>
-- 
cgit v1.2.3


From 00d9ff6051ff591ef44cc44d41afd1184297c1c9 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Thu, 31 Dec 2020 21:10:17 +0800
Subject: ...

---
 FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx')

diff --git a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
index b42b3aa3..ece1942f 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelineCardTemplate.tsx
@@ -78,6 +78,7 @@ function TimelineCardTemplate({
                   } else {
                     return (
                       <Dropdown.Item
+                        key={index}
                         onClick={item.onClick}
                         className={
                           item.color != null ? "text-" + item.color : undefined
-- 
cgit v1.2.3

