From bfc2cec7d43eb8eaa46dd3f91084932381257059 Mon Sep 17 00:00:00 2001
From: kj_sh604
Date: Sun, 15 Mar 2026 16:19:35 -0400
Subject: refactor: excalidraw-app/

---
 excalidraw-app/collab/CollabError.tsx | 54 +++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 excalidraw-app/collab/CollabError.tsx

(limited to 'excalidraw-app/collab/CollabError.tsx')

diff --git a/excalidraw-app/collab/CollabError.tsx b/excalidraw-app/collab/CollabError.tsx
new file mode 100644
index 0000000..76828cf
--- /dev/null
+++ b/excalidraw-app/collab/CollabError.tsx
@@ -0,0 +1,54 @@
+import { Tooltip } from "@excalidraw/excalidraw/components/Tooltip";
+import { warning } from "@excalidraw/excalidraw/components/icons";
+import clsx from "clsx";
+import { useEffect, useRef, useState } from "react";
+import { atom } from "../app-jotai";
+
+import "./CollabError.scss";
+
+type ErrorIndicator = {
+  message: string | null;
+  /** used to rerun the useEffect responsible for animation */
+  nonce: number;
+};
+
+export const collabErrorIndicatorAtom = atom<ErrorIndicator>({
+  message: null,
+  nonce: 0,
+});
+
+const CollabError = ({ collabError }: { collabError: ErrorIndicator }) => {
+  const [isAnimating, setIsAnimating] = useState(false);
+  const clearAnimationRef = useRef<string | number>(0);
+
+  useEffect(() => {
+    setIsAnimating(true);
+    clearAnimationRef.current = window.setTimeout(() => {
+      setIsAnimating(false);
+    }, 1000);
+
+    return () => {
+      window.clearTimeout(clearAnimationRef.current);
+    };
+  }, [collabError.message, collabError.nonce]);
+
+  if (!collabError.message) {
+    return null;
+  }
+
+  return (
+    <Tooltip label={collabError.message} long={true}>
+      <div
+        className={clsx("collab-errors-button", {
+          "collab-errors-button-shake": isAnimating,
+        })}
+      >
+        {warning}
+      </div>
+    </Tooltip>
+  );
+};
+
+CollabError.displayName = "CollabError";
+
+export default CollabError;
-- 
cgit v1.2.3

