web: simplify sync status

This commit is contained in:
Abdullah Atta
2023-12-26 15:17:01 +05:00
parent aa952d2ced
commit f116feb6b9
3 changed files with 40 additions and 86 deletions

View File

@@ -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 ? (
<Flex />
) : (
<Flex sx={{ gap: 1 }}>
<Flex sx={{ gap: "small" }}>
{isLoggedIn ? (
<>
{user?.isEmailConfirmed ? (
<Circle size={7} color={"var(--icon-success)"} />
<Circle
size={7}
color={"var(--icon-success)"}
sx={{ p: "small" }}
/>
) : (
<Button
onClick={() => hashNavigate("/email/verify")}
@@ -198,9 +200,7 @@ function statusToInfoText(status: UpdateStatus) {
}
function SyncStatus() {
const syncStatus = useAppStore(
(state) => state.syncStatus
) /* TODO: remove this type coercing */ as unknown as SyncState;
const syncStatus = useAppStore((state) => state.syncStatus);
const lastSynced = useAppStore((state) => state.lastSynced);
const isSyncEnabled = useAppStore((state) => state.isSyncEnabled);
const sync = useAppStore((state) => state.sync);
@@ -222,46 +222,19 @@ function SyncStatus() {
color: "paragraph",
height: "100%"
}}
title={status.tooltip}
title={
(status.text
? status.text({ lastSynced, type: syncStatus.type })
: status.tooltip) +
(syncStatus.progress ? ` (${syncStatus.progress})` : "")
}
data-test-id={`sync-status-${status.key}`}
>
{!syncStatus.progress && (
<status.icon
size={12}
rotate={status.loading}
color={status.iconColor}
/>
)}
<Text
variant="body"
sx={{ ml: status.text ? "3px" : 0, fontSize: "subBody" }}
>
{status.text ? (
<>
{typeof status.text === "string" ? (
status.text
) : (
<status.text lastSynced={lastSynced} type={syncStatus.type} />
)}{" "}
</>
) : null}
</Text>
{syncStatus.progress && (
<Text variant={"subBody"} ml={1}>
{" "}
({syncStatus.progress})
</Text>
)}
<status.icon size={12} rotate={status.loading} color={status.iconColor} />
</Button>
);
}
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 ? (
<TimeAgo live={true} locale="en_short" datetime={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 }) => (
<>
<TimeAgo live={true} locale="en_short" datetime={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."
}
];

View File

@@ -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<AppStore> {
: "disabled"
: "offline",
progress: null,
type: null
type: undefined
};
colors: Color[] = [];
notices: Notice[] = [];
@@ -122,12 +122,6 @@ class AppStore extends BaseStore<AppStore> {
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<AppStore> {
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) {

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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 = {