From f5dfd52f6efece2f4cad227044ecf4dd66301bbc Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Sat, 26 Aug 2023 21:36:58 +0800
Subject: ...

---
 FrontEnd/src/components/Spinner.tsx | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 FrontEnd/src/components/Spinner.tsx

(limited to 'FrontEnd/src/components/Spinner.tsx')

diff --git a/FrontEnd/src/components/Spinner.tsx b/FrontEnd/src/components/Spinner.tsx
new file mode 100644
index 00000000..ec0c2c35
--- /dev/null
+++ b/FrontEnd/src/components/Spinner.tsx
@@ -0,0 +1,36 @@
+import { CSSProperties } from "react";
+import classnames from "classnames";
+
+import { ThemeColor } from "./common";
+
+import "./Spinner.css";
+
+export interface SpinnerProps {
+  size?: "sm" | "md" | "lg" | number | string;
+  color?: ThemeColor;
+  className?: string;
+  style?: CSSProperties;
+}
+
+export default function Spinner(props: SpinnerProps) {
+  const { size, color, className, style } = props;
+  const calculatedSize =
+    size === "sm"
+      ? "18px"
+      : size === "md"
+      ? "30px"
+      : size === "lg"
+      ? "42px"
+      : typeof size === "number"
+      ? size
+      : size == null
+      ? "20px"
+      : size;
+
+  return (
+    <span
+      className={classnames("cru-spinner", color && `cru-${color}`, className)}
+      style={{ width: calculatedSize, height: calculatedSize, ...style }}
+    />
+  );
+}
-- 
cgit v1.2.3


From 502dd817c79018c84b0a958dd4b2e24781e68ae1 Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Mon, 28 Aug 2023 23:36:38 +0800
Subject: ...

---
 FrontEnd/src/components/ImageCropper.css |  3 +-
 FrontEnd/src/components/Spinner.tsx      | 52 +++++++++++++++++++-------------
 2 files changed, 32 insertions(+), 23 deletions(-)

(limited to 'FrontEnd/src/components/Spinner.tsx')

diff --git a/FrontEnd/src/components/ImageCropper.css b/FrontEnd/src/components/ImageCropper.css
index 9631cf1d..03d2038f 100644
--- a/FrontEnd/src/components/ImageCropper.css
+++ b/FrontEnd/src/components/ImageCropper.css
@@ -1,12 +1,11 @@
 .cru-image-cropper-container {
   position: relative;
   box-sizing: border-box;
+  display: flex;
   user-select: none;
 }
 
 .cru-image-cropper-container img {
-  left: 0;
-  top: 0;
   width: 100%;
   height: 100%;
 }
diff --git a/FrontEnd/src/components/Spinner.tsx b/FrontEnd/src/components/Spinner.tsx
index ec0c2c35..2752a519 100644
--- a/FrontEnd/src/components/Spinner.tsx
+++ b/FrontEnd/src/components/Spinner.tsx
@@ -1,36 +1,46 @@
-import { CSSProperties } from "react";
-import classnames from "classnames";
-
-import { ThemeColor } from "./common";
+import { CSSProperties, ComponentPropsWithoutRef } from "react";
+import classNames from "classnames";
 
 import "./Spinner.css";
 
-export interface SpinnerProps {
+const sizeMap: Record<string, string> = {
+  sm: "18px",
+  md: "30px",
+  lg: "42px",
+};
+
+function calculateSize(size: SpinnerProps["size"]) {
+  if (size == null) {
+    return "1em";
+  }
+  if (typeof size === "number") {
+    return size;
+  }
+  if (size in sizeMap) {
+    return sizeMap[size];
+  }
+  return size;
+}
+
+export interface SpinnerProps extends ComponentPropsWithoutRef<"span"> {
   size?: "sm" | "md" | "lg" | number | string;
-  color?: ThemeColor;
   className?: string;
   style?: CSSProperties;
 }
 
 export default function Spinner(props: SpinnerProps) {
-  const { size, color, className, style } = props;
-  const calculatedSize =
-    size === "sm"
-      ? "18px"
-      : size === "md"
-      ? "30px"
-      : size === "lg"
-      ? "42px"
-      : typeof size === "number"
-      ? size
-      : size == null
-      ? "20px"
-      : size;
+  const { size, className, style, ...otherProps } = props;
+  const calculatedSize = calculateSize(size);
 
   return (
     <span
-      className={classnames("cru-spinner", color && `cru-${color}`, className)}
-      style={{ width: calculatedSize, height: calculatedSize, ...style }}
+      className={classNames("cru-spinner", className)}
+      style={{
+        width: calculatedSize,
+        height: calculatedSize,
+        ...style,
+      }}
+      {...otherProps}
     />
   );
 }
-- 
cgit v1.2.3


From 901fe3d7c032d284da5c9bce24c4aaee9054c7ac Mon Sep 17 00:00:00 2001
From: crupest <crupest@outlook.com>
Date: Wed, 20 Sep 2023 20:16:30 +0800
Subject: Fix lint errors.

---
 FrontEnd/src/components/Spinner.tsx                |  2 +-
 .../components/hooks/useAutoUnsubscribePromise.ts  | 24 ++++++++++++++++++++++
 .../components/hooks/useAutoUnsubscribePromise.tsx | 23 ---------------------
 FrontEnd/src/components/hooks/useWindowLeave.ts    |  2 +-
 .../src/pages/timeline/edit/MarkdownPostEdit.tsx   | 17 +++++++--------
 .../src/pages/timeline/view/MarkdownPostView.tsx   |  3 +--
 FrontEnd/src/services/alert.ts                     |  2 +-
 7 files changed, 35 insertions(+), 38 deletions(-)
 create mode 100644 FrontEnd/src/components/hooks/useAutoUnsubscribePromise.ts
 delete mode 100644 FrontEnd/src/components/hooks/useAutoUnsubscribePromise.tsx

(limited to 'FrontEnd/src/components/Spinner.tsx')

diff --git a/FrontEnd/src/components/Spinner.tsx b/FrontEnd/src/components/Spinner.tsx
index 2752a519..50ccf0b2 100644
--- a/FrontEnd/src/components/Spinner.tsx
+++ b/FrontEnd/src/components/Spinner.tsx
@@ -23,7 +23,7 @@ function calculateSize(size: SpinnerProps["size"]) {
 }
 
 export interface SpinnerProps extends ComponentPropsWithoutRef<"span"> {
-  size?: "sm" | "md" | "lg" | number | string;
+  size?: number | string;
   className?: string;
   style?: CSSProperties;
 }
diff --git a/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.ts b/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.ts
new file mode 100644
index 00000000..01c5a1db
--- /dev/null
+++ b/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.ts
@@ -0,0 +1,24 @@
+import { useEffect, DependencyList } from "react";
+
+export default function useAutoUnsubscribePromise<T>(
+  promiseGenerator: () => Promise<T> | null | undefined,
+  resultHandler: (data: T) => void,
+  dependencies?: DependencyList | undefined,
+) {
+  useEffect(() => {
+    let subscribe = true;
+    const promise = promiseGenerator();
+    if (promise) {
+      void promise.then((data) => {
+        if (subscribe) {
+          resultHandler(data);
+        }
+      });
+
+      return () => {
+        subscribe = false;
+      };
+    }
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [promiseGenerator, resultHandler, ...(dependencies ?? [])]);
+}
diff --git a/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.tsx b/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.tsx
deleted file mode 100644
index 78e21151..00000000
--- a/FrontEnd/src/components/hooks/useAutoUnsubscribePromise.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { useEffect, DependencyList } from "react";
-
-export default function useAutoUnsubscribePromise<T>(
-  promiseGenerator: () => Promise<T> | null | undefined,
-  resultHandler: (data: T) => void,
-  dependencies?: DependencyList | undefined,
-) {
-  useEffect(() => {
-    let subscribe = true;
-    const promise = promiseGenerator();
-    if (promise) {
-      void promise.then((data) => {
-        if (subscribe) {
-          resultHandler(data);
-        }
-      });
-
-      return () => {
-        subscribe = false;
-      };
-    }
-  }, dependencies);
-}
diff --git a/FrontEnd/src/components/hooks/useWindowLeave.ts b/FrontEnd/src/components/hooks/useWindowLeave.ts
index 08777e30..ecd999d4 100644
--- a/FrontEnd/src/components/hooks/useWindowLeave.ts
+++ b/FrontEnd/src/components/hooks/useWindowLeave.ts
@@ -18,5 +18,5 @@ export default function useWindowLeave(
         window.onbeforeunload = null;
       };
     }
-  }, [allow, message]);
+  }, [c, allow, message]);
 }
diff --git a/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx
index 692221fd..36a5572b 100644
--- a/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx
+++ b/FrontEnd/src/pages/timeline/edit/MarkdownPostEdit.tsx
@@ -19,24 +19,21 @@ class MarkedRenderer extends marked.Renderer {
   }
 
   // Custom image parser for indexed image link.
-  image(href: string | null, title: string | null, text: string): string {
-    if (href != null) {
-      const i = parseInt(href);
-      if (!isNaN(i) && i > 0 && i <= this.images.length) {
-        href = this.images[i - 1];
-      }
+  image(href: string, title: string | null, text: string): string {
+    const i = parseInt(href);
+    if (!isNaN(i) && i > 0 && i <= this.images.length) {
+      href = this.images[i - 1];
     }
 
     return super.image(href, title, text);
   }
 }
 
-function generateMarkedOptions(imageUrls: string[]): marked.MarkedOptions {
+function generateMarkedOptions(imageUrls: string[]) {
   return {
-    mangle: false,
-    headerIds: false,
     renderer: new MarkedRenderer(imageUrls),
-  };
+    async: false,
+  } as const;
 }
 
 function renderHtml(text: string, imageUrls: string[]): string {
diff --git a/FrontEnd/src/pages/timeline/view/MarkdownPostView.tsx b/FrontEnd/src/pages/timeline/view/MarkdownPostView.tsx
index caf6eef0..9bb9f980 100644
--- a/FrontEnd/src/pages/timeline/view/MarkdownPostView.tsx
+++ b/FrontEnd/src/pages/timeline/view/MarkdownPostView.tsx
@@ -40,8 +40,7 @@ export default function MarkdownPostView({
   const markdownHtml = useMemo<string | null>(() => {
     if (markdown == null) return null;
     return marked.parse(markdown, {
-      mangle: false,
-      headerIds: false,
+      async: false,
     });
   }, [markdown]);
 
diff --git a/FrontEnd/src/services/alert.ts b/FrontEnd/src/services/alert.ts
index 241deab1..0fa37848 100644
--- a/FrontEnd/src/services/alert.ts
+++ b/FrontEnd/src/services/alert.ts
@@ -1,7 +1,7 @@
 import pull from "lodash/pull";
 
 import { I18nText } from "~src/common";
-import { ThemeColor } from "~src/views/common/common";
+import { ThemeColor } from "~src/components/common";
 
 export interface AlertInfo {
   type?: ThemeColor;
-- 
cgit v1.2.3

