mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
web: simplify sync status
This commit is contained in:
@@ -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."
|
||||
}
|
||||
];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user