From 122185acacd9606554c985713e4658cc62e8e6a6 Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Wed, 22 Jan 2025 13:14:03 +0100 Subject: [PATCH] Update onboarding component to actually sync --- src/views/onboarding/component.tsx | 146 +++++++++++++++++++++++------ src/views/onboarding/view.tsx | 15 +-- 2 files changed, 126 insertions(+), 35 deletions(-) diff --git a/src/views/onboarding/component.tsx b/src/views/onboarding/component.tsx index 8300b05..a35ee50 100644 --- a/src/views/onboarding/component.tsx +++ b/src/views/onboarding/component.tsx @@ -1,4 +1,7 @@ import { useState, useEffect } from "react"; +import SyncManager from "src/sync-manager"; +import { usePlugin } from "src/views/hooks"; +import { DEFAULT_SETTINGS } from "src/settings/settings"; const STEPS = [ "welcome", @@ -6,24 +9,33 @@ const STEPS = [ "token", "folders", "sync", - "interface", "first_sync", - "done", ] as const; type Step = (typeof STEPS)[number]; type StepData = { - repo?: { owner: string; repo: string; branch: string }; - token?: { token: string }; - folders?: { repoFolder: string; vaultFolder: string }; - sync?: { + repo: { owner: string; repo: string; branch: string }; + token: { token: string }; + folders: { repoFolder: string; vaultFolder: string }; + sync: { mode: "manual" | "interval"; syncOnStart: boolean; onConflict: "ignore" | "ask" | "overwrite"; }; }; +const DEFAULT_STEP_DATA: StepData = { + repo: { owner: "", repo: "", branch: "" }, + token: { token: "" }, + folders: { repoFolder: "", vaultFolder: "" }, + sync: { + mode: "manual", + syncOnStart: false, + onConflict: "overwrite", + }, +}; + const WelcomeStepComponent = () => { return (
{ }; const RepoStepComponent = ({ - values = { owner: "", repo: "", branch: "" }, + values, onChange, }: { - values?: { owner: string; repo: string; branch: string }; + values: { owner: string; repo: string; branch: string }; onChange: (values: { owner: string; repo: string; branch: string }) => void; }) => { return ( @@ -117,10 +129,10 @@ const RepoStepComponent = ({ }; const TokenStepComponent = ({ - values = { token: "" }, + values, onChange, }: { - values?: { token: string }; + values: { token: string }; onChange: (values: { token: string }) => void; }) => { return ( @@ -174,10 +186,10 @@ const TokenStepComponent = ({ }; const FoldersStepComponent = ({ - values = { repoFolder: "", vaultFolder: "" }, + values, onChange, }: { - values?: { repoFolder: string; vaultFolder: string }; + values: { repoFolder: string; vaultFolder: string }; onChange: (values: { repoFolder: string; vaultFolder: string }) => void; }) => { return ( @@ -226,10 +238,10 @@ const FoldersStepComponent = ({ }; const SyncSettingsStepComponent = ({ - values = { mode: "manual", syncOnStart: false, onConflict: "overwrite" }, + values, onChange, }: { - values?: { + values: { mode: "manual" | "interval"; syncOnStart: boolean; onConflict: "ignore" | "ask" | "overwrite"; @@ -301,11 +313,71 @@ const SyncSettingsStepComponent = ({ }; const FirstSyncStepComponent = ({ - setIsValid, + stepData, + onClose, }: { - setIsValid: (valid: boolean) => void; + stepData: StepData; + onClose: () => Promise; }) => { - setIsValid(true); + const [syncing, setSyncing] = useState(true); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(false); + + const plugin = usePlugin(); + if (!plugin) { + // Unlikely to happen, makes TS happy though + throw new Error("Plugin is not initialized"); + } + useEffect(() => { + (async () => { + const settings = { + ...DEFAULT_SETTINGS, + githubToken: stepData.token.token, + githubOwner: stepData.repo.owner, + githubRepo: stepData.repo.repo, + githubBranch: stepData.repo.branch, + repoContentDir: stepData.folders.repoFolder, + localContentDir: stepData.folders.vaultFolder, + uploadStrategy: stepData.sync.mode, + syncOnStartup: stepData.sync.syncOnStart, + conflictHandling: stepData.sync.onConflict, + }; + // We don't want to save the settings yet, so we create a new sync manager + // used only for the first sync. + // After that's sucessful we'll save the settings. + const syncManager = new SyncManager( + plugin.app.vault, + settings, + // There can't be any conflicts at this point so we don't need to handle them + async (_) => { + return []; + }, + ); + try { + await syncManager.firstSync(); + } catch (e) { + setError(e.message); + setSuccess(false); + setSyncing(false); + return; + } + plugin.settings = settings; + await plugin.saveSettings(); + setSyncing(false); + setSuccess(true); + })(); + }, [stepData]); + + const loadingBarStyle = () => { + if (error) { + return { width: 0 }; + } else if (success) { + return { width: "100%" }; + } else { + return {}; + } + }; + return (

- Now we can sync your vault with your repository. This might take a few - minutes depending on the size of your vault. + Now we can try and sync your vault with your repository. This might take + a few minutes depending on the number of files.

- Syncing vault... + {syncing ? "Syncing..." : null} + {error ? "Syncing failed" : null} + {success ? "Syncing complete" : null}
-
-
+
+
-
Show failure or success message here
+ {error ?
{error}
: null} + {success ?
Everything is set up and ready to go.
: null}
+ {success ? ( + + ) : null}
); }; -const OnBoardingComponent = () => { +const OnBoardingComponent = ({ onClose }: { onClose: () => Promise }) => { // It starts as true because the first step is just a welcome message // so the button is already enabled. const [isValid, setIsValid] = useState(true); const [step, setStep] = useState("welcome"); - const [stepData, setStepData] = useState({}); + const [stepData, setStepData] = useState(DEFAULT_STEP_DATA); const updateStepData = (step: Step, data: StepData[keyof StepData]) => { setStepData((stepData) => { @@ -419,9 +511,7 @@ const OnBoardingComponent = () => { /> ); case "first_sync": - return ; - // case "done": - // return ; + return ; } }; return ( diff --git a/src/views/onboarding/view.tsx b/src/views/onboarding/view.tsx index 20edde1..a84a8fc 100644 --- a/src/views/onboarding/view.tsx +++ b/src/views/onboarding/view.tsx @@ -16,20 +16,21 @@ export class OnboardingDialog extends Modal { // Make the dialog look like the settings modal this.modalEl.addClass("mod-settings"); this.modalEl.addClass("mod-sidebar-layout"); - // Make the dialog slightly smaller than Obsidian settings - this.modalEl.setCssProps({ - width: "60vw", - height: "50vh", - }); this.root.render( - + { + this.plugin.settings.firstStart = false; + await this.plugin.saveSettings(); + this.close(); + }} + /> , ); } - onClose() { + async onClose() { const { contentEl } = this; this.root?.unmount(); contentEl.empty();