diff --git a/src/views/onboarding/component.tsx b/src/views/onboarding/component.tsx
new file mode 100644
index 0000000..9f8f157
--- /dev/null
+++ b/src/views/onboarding/component.tsx
@@ -0,0 +1,458 @@
+import { useState, useEffect } from "react";
+
+type Step =
+ | "welcome"
+ | "repo"
+ | "token"
+ | "folders"
+ | "sync"
+ | "interface"
+ | "first_sync"
+ | "done";
+
+const WelcomeStepComponent = () => {
+ return (
+
+
First setup
+
+ This plugin is still in beta so not all features are available yet. If
+ you find any issues report that.
+
+
+ You'll be guided through some steps to get you started.
+
+
+ );
+};
+
+const RepoStepComponent = ({
+ setIsValid,
+}: {
+ setIsValid: (valid: boolean) => void;
+}) => {
+ const [owner, setOwner] = useState("");
+ const [repo, setRepo] = useState("");
+ const [branch, setBranch] = useState("");
+
+ useEffect(() => {
+ setIsValid(
+ owner.trim() !== "" && repo.trim() !== "" && branch.trim() !== "",
+ );
+ }, [owner, repo, branch]);
+
+ return (
+
+ );
+};
+
+const TokenStepComponent = ({
+ setIsValid,
+}: {
+ setIsValid: (valid: boolean) => void;
+}) => {
+ const [token, setToken] = useState("");
+
+ useEffect(() => {
+ setIsValid(token.trim() !== "");
+ }, [token]);
+
+ return (
+
+
+ Now we need a GitHub Personal Access Token.
+
+
+ The token is used to get, create and update the files between your vault
+ and your GitHub repository. Click{" "}
+ here to
+ create a new token. For more information about the tokens see{" "}
+
+ the official GitHub documentation.
+
+
+
+ The token must have the Contents Read and Write permissions for the same
+ repository you set in the previous step.
+
+
setToken(e.target.value)}
+ />
+
+ );
+};
+
+const FoldersStepComponent = () => {
+ return (
+
+
+ Optionally you can sync only part of your remote repository or your
+ vault.
+
+
+ Leave blank to sync the whole repository and the whole vault.
+
+
+
+
+
+
+ );
+};
+
+const SyncSettingsStepComponent = () => {
+ const [syncMode, setSyncMode] = useState<"manual" | "interval">("manual");
+ const [syncOnStart, setSyncOnStart] = useState(false);
+ const [onConflict, setOnConflict] = useState<"ignore" | "ask" | "overwrite">(
+ "overwrite",
+ );
+
+ return (
+
+
+ You can sync your vault manually or every minute. You can change the
+ interval at a later time.
+
+
setSyncMode(e.target.value)}
+ >
+ Manually
+ On Interval
+
+
Optionally you can sync every time you open Obsidian.
+
setSyncOnStart(!syncOnStart)}
+ >
+
+
+
In case of conflicts you can choose how to handle them.
+
+ This is not yet implemented so remote files will always overwrite local
+ files in case of conflicts.
+
+
setOnConflict(e.target.value)}
+ >
+ Ignore remote file
+ Ask
+ Overwrite local file
+
+
+ );
+};
+
+const FirstSyncStepComponent = ({
+ setIsValid,
+}: {
+ setIsValid: (valid: boolean) => void;
+}) => {
+ setIsValid(true);
+ return (
+
+
+ Now we can sync your vault with your repository. This might take a few
+ minutes depending on the size of your vault.
+
+
+
+ Syncing vault...
+
+
+
+
Show failure or success message here
+
+
+
+ );
+};
+
+const OnBoardingComponent = () => {
+ const [step, setStep] = useState("welcome");
+
+ // 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 previousStep = () => {
+ setStep((step) => {
+ switch (step) {
+ case "welcome":
+ return "welcome";
+ case "repo":
+ // Reset the valid state to true since we come back to welcome
+ setIsValid(true);
+ return "welcome";
+ case "token":
+ return "repo";
+ case "folders":
+ return "token";
+ case "sync":
+ return "folders";
+ case "interface":
+ return "sync";
+ case "first_sync":
+ return "interface";
+ case "done":
+ return "done";
+ }
+ });
+ };
+
+ const nextStep = () => {
+ setStep((step) => {
+ switch (step) {
+ case "welcome":
+ return "repo";
+ case "repo":
+ return "token";
+ case "token":
+ return "folders";
+ case "folders":
+ return "sync";
+ case "sync":
+ return "interface";
+ case "interface":
+ return "first_sync";
+ case "first_sync":
+ return "done";
+ case "done":
+ return "done";
+ }
+ });
+ };
+
+ const currentStepComponent = () => {
+ switch (step) {
+ case "welcome":
+ return ;
+ case "repo":
+ return ;
+ case "token":
+ return ;
+ case "folders":
+ return ;
+ case "sync":
+ return ;
+ case "first_sync":
+ return ;
+ // case "done":
+ // return ;
+ }
+ };
+ return (
+
+
+
+
+
+
+
+ {currentStepComponent()}
+
+
+
+
+
+
+
+ );
+};
+
+export default OnBoardingComponent;
diff --git a/src/views/onboarding/view.tsx b/src/views/onboarding/view.tsx
new file mode 100644
index 0000000..20edde1
--- /dev/null
+++ b/src/views/onboarding/view.tsx
@@ -0,0 +1,37 @@
+import { Modal } from "obsidian";
+import { Root, createRoot } from "react-dom/client";
+import GitHubSyncPlugin from "src/main";
+import { PluginContext } from "src/views/hooks";
+import OnboardingDialogComponent from "./component";
+
+export class OnboardingDialog extends Modal {
+ root: Root | null = null;
+
+ constructor(private plugin: GitHubSyncPlugin) {
+ super(plugin.app);
+ }
+
+ onOpen() {
+ this.root = createRoot(this.modalEl);
+ // 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(
+
+
+ ,
+ );
+ }
+
+ onClose() {
+ const { contentEl } = this;
+ this.root?.unmount();
+ contentEl.empty();
+ }
+}