From f116feb6b92d905cf68b33407bbc23d7fef66a24 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Tue, 26 Dec 2023 15:17:01 +0500 Subject: [PATCH] web: simplify sync status --- apps/web/src/components/status-bar/index.tsx | 108 ++++++------------- apps/web/src/stores/app-store.ts | 12 +-- packages/common/src/hooks/use-time-ago.ts | 6 +- 3 files changed, 40 insertions(+), 86 deletions(-) diff --git a/apps/web/src/components/status-bar/index.tsx b/apps/web/src/components/status-bar/index.tsx index 6df557b19..a029bb10d 100644 --- a/apps/web/src/components/status-bar/index.tsx +++ b/apps/web/src/components/status-bar/index.tsx @@ -25,7 +25,6 @@ import { Loading, Update, SyncError, - Checkmark, Alert, Issue, SyncOff, @@ -33,7 +32,6 @@ import { } from "../icons"; import { useStore as useUserStore } from "../../stores/user-store"; import { useStore as useAppStore } from "../../stores/app-store"; -import TimeAgo from "../time-ago"; import { hardNavigate, hashNavigate } from "../../navigation"; import { useAutoUpdater, UpdateStatus } from "../../hooks/use-auto-updater"; import { @@ -43,7 +41,7 @@ import { import useStatus, { statusToString } from "../../hooks/use-status"; import { ScopedThemeProvider } from "../theme-provider"; import { checkForUpdate, installUpdate } from "../../utils/updater"; -import { toTitleCase } from "@notesnook/common"; +import { getTimeAgo, toTitleCase } from "@notesnook/common"; import { User } from "@notesnook/core/dist/api/user-manager"; function StatusBar() { @@ -68,11 +66,15 @@ function StatusBar() { {isFocusMode ? ( ) : ( - + {isLoggedIn ? ( <> {user?.isEmailConfirmed ? ( - + ) : ( ); } -type SyncState = { - key: SyncStatus; - progress: number; - type: SyncType; -}; -type SyncType = "download" | "upload" | "sync"; type SyncStatus = | "synced" | "syncing" @@ -278,14 +251,11 @@ type SyncStatusFilter = { user: User | undefined, lastSynced: number ) => boolean; - text: - | string - | ((props: { - type?: "download" | "upload" | "sync"; - user?: User; - lastSynced: number; - }) => JSX.Element); - tooltip?: string; + text?: (props: { + type?: "download" | "upload" | "sync"; + lastSynced: number; + }) => string; + tooltip: string; iconColor?: string; loading?: boolean; }; @@ -293,14 +263,13 @@ type SyncStatusFilter = { const syncStatusFilters: SyncStatusFilter[] = [ { key: "synced", - isActive: (syncStatus) => syncStatus === "synced", + isActive: (syncStatus) => + syncStatus === "synced" || syncStatus === "completed", icon: Sync, text: ({ lastSynced }) => - lastSynced ? ( - - ) : ( - <>click to sync - ), + lastSynced + ? `Synced ${getTimeAgo(lastSynced, "en_short", { minInterval: 1000 })}` + : "click to sync", tooltip: "All changes are synced." }, { @@ -308,22 +277,15 @@ const syncStatusFilters: SyncStatusFilter[] = [ isActive: (syncStatus) => syncStatus === "syncing", icon: Sync, loading: true, - text: ({ type }) => <>{toTitleCase(type || "sync")}ing, + text: ({ type }) => `${toTitleCase(type || "sync")}ing`, tooltip: "Syncing your notes..." }, - { - key: "completed", - isActive: (syncStatus) => syncStatus === "completed", - icon: Checkmark, - iconColor: "var(--icon-success)", - text: "" - }, { key: "conflicts", isActive: (syncStatus) => syncStatus === "conflicts", icon: Alert, iconColor: "var(--icon-error)", - text: "Merge conflicts", + text: () => "Merge conflicts", tooltip: "Please resolve all merge conflicts and run the sync again." }, { @@ -331,7 +293,7 @@ const syncStatusFilters: SyncStatusFilter[] = [ isActive: (_syncStatus, user) => !user?.isEmailConfirmed, icon: Alert, iconColor: "var(--icon-error)", - text: "Sync disabled", + text: () => "Sync disabled", tooltip: "Please confirm your email to start syncing." }, { @@ -339,19 +301,17 @@ const syncStatusFilters: SyncStatusFilter[] = [ isActive: (syncStatus) => syncStatus === "failed", icon: SyncError, iconColor: "var(--icon-error)", - text: "Sync failed", + text: () => "Sync failed", tooltip: "Sync failed to completed. Please try again." }, { key: "offline", isActive: (syncStatus) => syncStatus === "offline", icon: SyncOff, - text: ({ lastSynced }) => ( - <> - {" "} - (offline) - - ), + text: ({ lastSynced }) => + `Synced ${getTimeAgo(lastSynced, "en_short", { + minInterval: 1000 + })} (offline)`, tooltip: "You are offline." }, { @@ -359,7 +319,7 @@ const syncStatusFilters: SyncStatusFilter[] = [ iconColor: "var(--icon-disabled)", isActive: (syncStatus) => syncStatus === "disabled", icon: SyncOff, - text: "Sync disabled", + text: () => "Sync disabled", tooltip: "Sync is disabled." } ]; diff --git a/apps/web/src/stores/app-store.ts b/apps/web/src/stores/app-store.ts index ef5fbeb30..89fe2d642 100644 --- a/apps/web/src/stores/app-store.ts +++ b/apps/web/src/stores/app-store.ts @@ -50,7 +50,7 @@ type SyncState = type SyncStatus = { key: SyncState; progress: number | null; - type: "download" | "upload" | null; + type?: "download" | "upload" | "sync"; }; const networkCheck = new NetworkCheck(); let syncStatusTimeout = 0; @@ -72,7 +72,7 @@ class AppStore extends BaseStore { : "disabled" : "offline", progress: null, - type: null + type: undefined }; colors: Color[] = []; notices: Notice[] = []; @@ -122,12 +122,6 @@ class AppStore extends BaseStore { this.updateSyncStatus("failed"); }); - db.eventManager.subscribe(EVENTS.syncCompleted, async () => { - await this.updateLastSynced(); - this.updateSyncStatus("completed", true); - this.refresh(); - }); - onPageVisibilityChanged(async (type, documentHidden) => { if (!documentHidden && type !== "offline") { logger.info("Page visibility changed. Reconnecting SSE..."); @@ -340,7 +334,7 @@ class AppStore extends BaseStore { updateSyncStatus = (key: SyncState, reset = false) => { logger.info(`Sync status updated: ${key}`); this.set((state) => { - state.syncStatus = { key, progress: null, type: null }; + state.syncStatus = { key, progress: null, type: undefined }; }); if (reset) { diff --git a/packages/common/src/hooks/use-time-ago.ts b/packages/common/src/hooks/use-time-ago.ts index 44c6ba446..9ea4a5ec6 100644 --- a/packages/common/src/hooks/use-time-ago.ts +++ b/packages/common/src/hooks/use-time-ago.ts @@ -18,7 +18,7 @@ along with this program. If not, see . */ import { useEffect, useState } from "react"; -import { TDate, format, register } from "timeago.js"; +import { Opts, TDate, format, register } from "timeago.js"; const shortLocale: [string, string][] = [ ["now", "now"], ["%ss", "in %ss"], @@ -55,8 +55,8 @@ const enShortLocale: [string, string][] = [ register("short", (_n, index) => shortLocale[index]); register("en_short", (_n, index) => enShortLocale[index]); -export function getTimeAgo(datetime: TDate, locale = "short") { - return format(datetime, locale); +export function getTimeAgo(datetime: TDate, locale = "short", opts?: Opts) { + return format(datetime, locale, opts); } type TimeAgoOptions = {