فهرست منبع

feat: show network stats for collaboration

Aakansha Doshi 4 سال پیش
والد
کامیت
8157c84d11
6فایلهای تغییر یافته به همراه83 افزوده شده و 1 حذف شده
  1. 2 0
      src/appState.ts
  2. 23 0
      src/components/App.tsx
  3. 16 0
      src/components/Stats.tsx
  4. 4 1
      src/locales/en.json
  5. 37 0
      src/networkStats.ts
  6. 1 0
      src/types.ts

+ 2 - 0
src/appState.ts

@@ -73,6 +73,7 @@ export const getDefaultAppState = (): Omit<
     zenModeEnabled: false,
     zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
     viewModeEnabled: false,
+    networkSpeed: "calculating...",
   };
 };
 
@@ -153,6 +154,7 @@ const APP_STATE_STORAGE_CONF = (<
   zenModeEnabled: { browser: true, export: false },
   zoom: { browser: true, export: false },
   viewModeEnabled: { browser: false, export: false },
+  networkSpeed: { browser: false, export: false },
 });
 
 const _clearAppStateForStorage = <ExportType extends "export" | "browser">(

+ 23 - 0
src/components/App.tsx

@@ -1,3 +1,4 @@
+// @ts-nocheck
 import { Point, simplify } from "points-on-curve";
 import React from "react";
 import { RoughCanvas } from "roughjs/bin/canvas";
@@ -182,6 +183,7 @@ import LayerUI from "./LayerUI";
 import { Stats } from "./Stats";
 import { Toast } from "./Toast";
 import { actionToggleViewMode } from "../actions/actionToggleViewMode";
+import { getNetworkSpeed } from "../networkStats";
 
 const { history } = createHistory();
 
@@ -461,6 +463,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
             setAppState={this.setAppState}
             elements={this.scene.getElements()}
             onClose={this.toggleStats}
+            isCollaborating={this.props.isCollaborating}
           />
         )}
         {this.state.toastMessage !== null && (
@@ -845,6 +848,21 @@ class App extends React.Component<ExcalidrawProps, AppState> {
       this.addEventListeners();
     }
 
+    if (
+      prevState.showStats !== this.state.showStats ||
+      prevProps.isCollaborating !== this.props.isCollaborating
+    ) {
+      if (this.state.showStats && this.props.isCollaborating) {
+        this.calculateNetStats();
+        navigator.connection.addEventListener("change", this.calculateNetStats);
+      } else {
+        navigator.connection.removeEventListener(
+          "change",
+          this.calculateNetStats,
+        );
+      }
+    }
+
     document
       .querySelector(".excalidraw")
       ?.classList.toggle("Appearance_dark", this.state.appearance === "dark");
@@ -970,6 +988,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     }
   }
 
+  private calculateNetStats = async () => {
+    const speed = await getNetworkSpeed();
+    const networkSpeed = speed === -1 ? "Error!" : speed;
+    this.setState({ networkSpeed });
+  };
   // Copy/paste
 
   private onCut = withBatchedUpdates((event: ClipboardEvent) => {

+ 16 - 0
src/components/Stats.tsx

@@ -30,6 +30,7 @@ export const Stats = (props: {
   setAppState: React.Component<any, AppState>["setState"];
   elements: readonly NonDeletedExcalidrawElement[];
   onClose: () => void;
+  isCollaborating: boolean;
 }) => {
   const isMobile = useIsMobile();
   const [storageSizes, setStorageSizes] = useState<StorageSizes>({
@@ -192,6 +193,21 @@ export const Stats = (props: {
                 {hash}
               </td>
             </tr>
+            {props.isCollaborating ? (
+              <>
+                <tr>
+                  <th colSpan={2}>{t("stats.collaboration")}</th>
+                </tr>
+                <tr>
+                  <td>{t("stats.collaborators")}</td>
+                  <td>{props.appState.collaborators.size}</td>
+                </tr>
+                <tr>
+                  <td>{t("stats.networkSpeed")}</td>
+                  <td>{props.appState.networkSpeed}</td>
+                </tr>
+              </>
+            ) : null}
           </tbody>
         </table>
       </Island>

+ 4 - 1
src/locales/en.json

@@ -238,7 +238,10 @@
     "version": "Version",
     "versionCopy": "Click to copy",
     "versionNotAvailable": "Version not available",
-    "width": "Width"
+    "width": "Width",
+    "collaboration": "Collaboration",
+    "networkSpeed": "Network Speed",
+    "collaborators": "Collaborators"
   },
   "toast": {
     "copyStyles": "Copied styles.",

+ 37 - 0
src/networkStats.ts

@@ -0,0 +1,37 @@
+const IMAGE_URL =
+  "https://user-images.githubusercontent.com/11256141/107117897-76fa3880-68a3-11eb-9ec6-c214c7af373b.png";
+const IMAGE_SIZE = 4525154; // in bytes
+const calculateSpeed = (startTime: number, endTime: number) => {
+  const duration = (endTime - startTime) / 1000;
+  const imageSizeInBits = IMAGE_SIZE * 8;
+  let speed = imageSizeInBits / duration;
+  const suffix = ["bps", "kbps", "mbps", "gbps"];
+  let index = 0;
+  while (speed > 1024) {
+    index++;
+    speed = speed / 1024;
+  }
+  return `${speed.toFixed(2)} ${suffix[index]}`;
+};
+
+const processImage = () => {
+  return new Promise((resolve, reject) => {
+    const image = new Image();
+    let endTime: number;
+    image.onload = () => {
+      endTime = new Date().getTime();
+      const speed = calculateSpeed(startTime, endTime);
+      resolve(speed);
+    };
+
+    image.onerror = () => {
+      resolve(-1);
+    };
+
+    const startTime = new Date().getTime();
+    image.src = `${IMAGE_URL}?t=${startTime}`; // start time acts as a cache buster so everytime new url is requested
+  });
+};
+export const getNetworkSpeed = async () => {
+  return await processImage();
+};

+ 1 - 0
src/types.ts

@@ -88,6 +88,7 @@ export type AppState = {
   appearance: "light" | "dark";
   gridSize: number | null;
   viewModeEnabled: boolean;
+  networkSpeed?: string;
 
   /** top-most selected groups (i.e. does not include nested groups) */
   selectedGroupIds: { [groupId: string]: boolean };