mirror of
https://github.com/silvanocerza/github-gitless-sync.git
synced 2026-07-22 12:10:28 +00:00
Remove lots of unnecessary code
This commit is contained in:
parent
4085c2f5c5
commit
56a9fb29cd
8 changed files with 22 additions and 536 deletions
|
|
@ -1,14 +0,0 @@
|
|||
import { type Event } from "./types";
|
||||
import SyncManager from "src/sync-manager";
|
||||
|
||||
export default class EventsConsumer {
|
||||
constructor(private readonly syncManager: SyncManager) {}
|
||||
|
||||
async process(event: Event): Promise<void> {
|
||||
if (event.type == "create" || event.type == "modify") {
|
||||
await this.syncManager.uploadFile(event.file);
|
||||
} else if (event.type == "delete") {
|
||||
await this.syncManager.deleteFile(event.filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { Vault, requestUrl, normalizePath } from "obsidian";
|
||||
import MetadataStore, { Metadata } from "../metadata-store";
|
||||
import { requestUrl } from "obsidian";
|
||||
|
||||
export type RepoContent = {
|
||||
files: { [key: string]: GetTreeResponseItem };
|
||||
|
|
@ -132,62 +131,4 @@ export default class GithubClient {
|
|||
});
|
||||
return res.json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or edit a remote file to GitHub.
|
||||
*
|
||||
* @param remoteFilePath Path to remote file
|
||||
* @param fileContent Content of the file
|
||||
* @param sha SHA of the file
|
||||
*/
|
||||
async uploadFile(
|
||||
remoteFilePath: string,
|
||||
fileContent: ArrayBuffer,
|
||||
sha: string | null,
|
||||
) {
|
||||
await requestUrl({
|
||||
url: `https://api.github.com/repos/${this.owner}/${this.repo}/contents/${remoteFilePath}`,
|
||||
method: "PUT",
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify({
|
||||
message: `Edit ${remoteFilePath}`,
|
||||
branch: this.branch,
|
||||
content: Buffer.from(fileContent).toString("base64"),
|
||||
sha: sha,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SHA of a file in the remote repo.
|
||||
*
|
||||
* @param remoteFilePath Path to remote file
|
||||
* @returns sha of the file as string
|
||||
*/
|
||||
async getFileSha(remoteFilePath: string) {
|
||||
const res = await requestUrl({
|
||||
url: `https://api.github.com/repos/${this.owner}/${this.repo}/contents/${remoteFilePath}?ref=${this.branch}`,
|
||||
headers: this.headers(),
|
||||
});
|
||||
return res.json.sha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single file from GitHub.
|
||||
*
|
||||
* @param remoteFilePath Path to remote file
|
||||
* @param sha SHA of the file
|
||||
*/
|
||||
async deleteFile(remoteFilePath: string, sha: string) {
|
||||
await requestUrl({
|
||||
url: `https://api.github.com/repos/${this.owner}/${this.repo}/contents/${remoteFilePath}`,
|
||||
method: "DELETE",
|
||||
headers: this.headers(),
|
||||
body: JSON.stringify({
|
||||
message: `Delete ${remoteFilePath}`,
|
||||
branch: this.branch,
|
||||
sha: sha,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
143
src/main.ts
143
src/main.ts
|
|
@ -1,7 +1,6 @@
|
|||
import { EventRef, Plugin, FileView } from "obsidian";
|
||||
import { GitHubSyncSettings, DEFAULT_SETTINGS } from "./settings/settings";
|
||||
import GitHubSyncSettingsTab from "./settings/tab";
|
||||
import { UploadDialog } from "./views/upload-all-files-dialog/view";
|
||||
import SyncManager from "./sync-manager";
|
||||
|
||||
export default class GitHubSyncPlugin extends Plugin {
|
||||
|
|
@ -9,10 +8,7 @@ export default class GitHubSyncPlugin extends Plugin {
|
|||
syncManager: SyncManager;
|
||||
|
||||
statusBarItem: HTMLElement | null = null;
|
||||
downloadAllRibbonIcon: HTMLElement | null = null;
|
||||
uploadModifiedFilesRibbonIcon: HTMLElement | null = null;
|
||||
uploadCurrentFileRibbonIcon: HTMLElement | null = null;
|
||||
uploadAllFilesRibbonIcon: HTMLElement | null = null;
|
||||
|
||||
activeLeafChangeListener: EventRef | null = null;
|
||||
vaultCreateListener: EventRef | null = null;
|
||||
|
|
@ -56,71 +52,21 @@ export default class GitHubSyncPlugin extends Plugin {
|
|||
this.showStatusBarItem();
|
||||
}
|
||||
|
||||
if (this.settings.showDownloadRibbonButton) {
|
||||
this.showDownloadAllRibbonIcon();
|
||||
}
|
||||
|
||||
if (this.settings.showUploadModifiedFilesRibbonButton) {
|
||||
this.showUploadModifiedFilesRibbonIcon();
|
||||
}
|
||||
|
||||
if (this.settings.showUploadActiveFileRibbonButton) {
|
||||
this.showUploadActiveFileRibbonIcon();
|
||||
}
|
||||
|
||||
if (this.settings.showUploadAllFilesRibbonButton) {
|
||||
this.showUploadAllFilesRibbonIcon();
|
||||
if (this.settings.showSyncRibbonButton) {
|
||||
this.showSyncRibbonIcon();
|
||||
}
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "download-all-files",
|
||||
name: "Download all files to GitHub",
|
||||
repeatable: false,
|
||||
icon: "arrow-down-from-line",
|
||||
callback: async () => {
|
||||
await this.syncManager.downloadAllFiles();
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "upload-modified-files",
|
||||
name: "Upload modified files to GitHub",
|
||||
id: "sync-files",
|
||||
name: "Sync with GitHub",
|
||||
repeatable: false,
|
||||
icon: "refresh-cw",
|
||||
callback: async () => {
|
||||
await this.syncManager.uploadModifiedFiles();
|
||||
await this.syncManager.sync();
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "upload-active-file",
|
||||
name: "Upload active file to GitHub",
|
||||
repeatable: false,
|
||||
icon: "arrow-up",
|
||||
callback: async () => {
|
||||
const activeView = this.app.workspace.getActiveViewOfType(FileView);
|
||||
if (!activeView) {
|
||||
return;
|
||||
}
|
||||
const activeFile = activeView.file;
|
||||
if (!activeFile) {
|
||||
return;
|
||||
}
|
||||
// await this.syncManager.uploadFile(activeFile);
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "upload-all-files",
|
||||
name: "Upload all files to GitHub",
|
||||
repeatable: false,
|
||||
icon: "arrow-up-from-line",
|
||||
callback: async () => await this.openUploadAllFilesDialog(),
|
||||
});
|
||||
}
|
||||
|
||||
async onunload() {
|
||||
|
|
@ -129,16 +75,6 @@ export default class GitHubSyncPlugin extends Plugin {
|
|||
console.log("GitHubSyncPlugin unloaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dialog to upload all file in the tracked folder.
|
||||
* This doesn't take into account the state of the files and uploads
|
||||
* ALL files whether they've been modified or not.
|
||||
*/
|
||||
private async openUploadAllFilesDialog() {
|
||||
new UploadDialog(this).open();
|
||||
this.updateStatusBarItem();
|
||||
}
|
||||
|
||||
showStatusBarItem() {
|
||||
if (this.statusBarItem) {
|
||||
return;
|
||||
|
|
@ -190,34 +126,15 @@ export default class GitHubSyncPlugin extends Plugin {
|
|||
this.statusBarItem.setText(`GitHub: ${state}`);
|
||||
}
|
||||
|
||||
showDownloadAllRibbonIcon() {
|
||||
if (this.downloadAllRibbonIcon) {
|
||||
return;
|
||||
}
|
||||
this.downloadAllRibbonIcon = this.addRibbonIcon(
|
||||
"arrow-down-from-line",
|
||||
"Download all files from GitHub",
|
||||
async () => {
|
||||
await this.syncManager.downloadAllFiles();
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
hideDownloadAllRibbonIcon() {
|
||||
this.downloadAllRibbonIcon?.remove();
|
||||
this.downloadAllRibbonIcon = null;
|
||||
}
|
||||
|
||||
showUploadModifiedFilesRibbonIcon() {
|
||||
showSyncRibbonIcon() {
|
||||
if (this.uploadModifiedFilesRibbonIcon) {
|
||||
return;
|
||||
}
|
||||
this.uploadModifiedFilesRibbonIcon = this.addRibbonIcon(
|
||||
"refresh-cw",
|
||||
"Upload modified files to GitHub",
|
||||
"Sync with GitHub",
|
||||
async () => {
|
||||
await this.syncManager.uploadModifiedFiles();
|
||||
await this.syncManager.sync();
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
);
|
||||
|
|
@ -228,50 +145,6 @@ export default class GitHubSyncPlugin extends Plugin {
|
|||
this.uploadModifiedFilesRibbonIcon = null;
|
||||
}
|
||||
|
||||
showUploadActiveFileRibbonIcon() {
|
||||
if (this.uploadCurrentFileRibbonIcon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.uploadCurrentFileRibbonIcon = this.addRibbonIcon(
|
||||
"arrow-up",
|
||||
"Upload current file to GitHub",
|
||||
async () => {
|
||||
const activeView = this.app.workspace.getActiveViewOfType(FileView);
|
||||
if (!activeView) {
|
||||
return;
|
||||
}
|
||||
const activeFile = activeView.file;
|
||||
if (!activeFile) {
|
||||
return;
|
||||
}
|
||||
// await this.syncManager.uploadFile(activeFile);
|
||||
this.updateStatusBarItem();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
hideUploadActiveFileRibbonIcon() {
|
||||
this.uploadCurrentFileRibbonIcon?.remove();
|
||||
this.uploadCurrentFileRibbonIcon = null;
|
||||
}
|
||||
|
||||
showUploadAllFilesRibbonIcon() {
|
||||
if (this.uploadAllFilesRibbonIcon) {
|
||||
return;
|
||||
}
|
||||
this.uploadAllFilesRibbonIcon = this.addRibbonIcon(
|
||||
"arrow-up-from-line",
|
||||
"Upload all files to GitHub",
|
||||
async () => await this.openUploadAllFilesDialog(),
|
||||
);
|
||||
}
|
||||
|
||||
hideUploadAllFilesRibbonIcon() {
|
||||
this.uploadAllFilesRibbonIcon?.remove();
|
||||
this.uploadAllFilesRibbonIcon = null;
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ export interface GitHubSyncSettings {
|
|||
syncOnStartup: boolean;
|
||||
conflictHandling: "ignore" | "ask" | "overwrite";
|
||||
showStatusBarItem: boolean;
|
||||
showDownloadRibbonButton: boolean;
|
||||
showUploadModifiedFilesRibbonButton: boolean;
|
||||
showUploadActiveFileRibbonButton: boolean;
|
||||
showUploadAllFilesRibbonButton: boolean;
|
||||
showSyncRibbonButton: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: GitHubSyncSettings = {
|
||||
|
|
@ -28,8 +25,5 @@ export const DEFAULT_SETTINGS: GitHubSyncSettings = {
|
|||
syncOnStartup: false,
|
||||
conflictHandling: "ask",
|
||||
showStatusBarItem: true,
|
||||
showDownloadRibbonButton: true,
|
||||
showUploadModifiedFilesRibbonButton: true,
|
||||
showUploadActiveFileRibbonButton: true,
|
||||
showUploadAllFilesRibbonButton: true,
|
||||
showSyncRibbonButton: true,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,73 +218,20 @@ export default class GitHubSyncSettingsTab extends PluginSettingTab {
|
|||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Show download all files button")
|
||||
.setDesc(
|
||||
"Displays a ribbon button to download all files from the remote repository",
|
||||
)
|
||||
.setName("Show sync button")
|
||||
.setDesc("Displays a ribbon button to sync files")
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showDownloadRibbonButton)
|
||||
.setValue(this.plugin.settings.showSyncRibbonButton)
|
||||
.onChange((value) => {
|
||||
this.plugin.settings.showDownloadRibbonButton = value;
|
||||
this.plugin.settings.showSyncRibbonButton = value;
|
||||
this.plugin.saveSettings();
|
||||
if (value) {
|
||||
this.plugin.showDownloadAllRibbonIcon();
|
||||
} else {
|
||||
this.plugin.hideDownloadAllRibbonIcon();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Show upload modified files button")
|
||||
.setDesc("Displays a ribbon button to upload modified files")
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showUploadModifiedFilesRibbonButton)
|
||||
.onChange((value) => {
|
||||
this.plugin.settings.showUploadModifiedFilesRibbonButton = value;
|
||||
this.plugin.saveSettings();
|
||||
if (value) {
|
||||
this.plugin.showUploadModifiedFilesRibbonIcon();
|
||||
this.plugin.showSyncRibbonIcon();
|
||||
} else {
|
||||
this.plugin.hideUploadModifiedFilesRibbonIcon();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Show upload active file button")
|
||||
.setDesc("Displays a ribbon button to upload active file")
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showUploadActiveFileRibbonButton)
|
||||
.onChange((value) => {
|
||||
this.plugin.settings.showUploadActiveFileRibbonButton = value;
|
||||
this.plugin.saveSettings();
|
||||
if (value) {
|
||||
this.plugin.showUploadActiveFileRibbonIcon();
|
||||
} else {
|
||||
this.plugin.hideUploadActiveFileRibbonIcon();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Show upload all files button")
|
||||
.setDesc("Displays a ribbon button to upload all files")
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showUploadAllFilesRibbonButton)
|
||||
.onChange((value) => {
|
||||
this.plugin.settings.showUploadAllFilesRibbonButton = value;
|
||||
this.plugin.saveSettings();
|
||||
if (value) {
|
||||
this.plugin.showUploadAllFilesRibbonIcon();
|
||||
} else {
|
||||
this.plugin.hideUploadAllFilesRibbonIcon();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import GithubClient, {
|
|||
} from "./github/client";
|
||||
import MetadataStore, { FileMetadata, Metadata } from "./metadata-store";
|
||||
import EventsListener from "./events/listener";
|
||||
import EventsConsumer from "./events/consumer";
|
||||
import { GitHubSyncSettings } from "./settings/settings";
|
||||
|
||||
interface SyncAction {
|
||||
|
|
@ -17,7 +16,6 @@ export default class SyncManager {
|
|||
private metadataStore: MetadataStore;
|
||||
private client: GithubClient;
|
||||
private eventsListener: EventsListener;
|
||||
private eventsConsumer: EventsConsumer;
|
||||
private syncIntervalId: number | null = null;
|
||||
|
||||
constructor(
|
||||
|
|
@ -39,7 +37,6 @@ export default class SyncManager {
|
|||
this.settings.localContentDir,
|
||||
this.settings.repoContentDir,
|
||||
);
|
||||
this.eventsConsumer = new EventsConsumer(this);
|
||||
}
|
||||
|
||||
async sync() {
|
||||
|
|
@ -296,6 +293,7 @@ export default class SyncManager {
|
|||
// but not the same happens when the file is modified locally.
|
||||
// So if sha are different and the local file is newer we can't
|
||||
// know for sure which version should be kept.
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteFile.deleted && !localFile.deleted) {
|
||||
|
|
@ -321,6 +319,10 @@ export default class SyncManager {
|
|||
}
|
||||
|
||||
if (remoteFile.lastModified > localFile.lastModified) {
|
||||
console.log("Downloading");
|
||||
console.log(filePath);
|
||||
console.log(`localFile.lastModified: ${localFile.lastModified}`);
|
||||
console.log(`remoteFile.lastModified: ${remoteFile.lastModified}`);
|
||||
actions.push({ type: "download", filePath: filePath });
|
||||
} else if (localFile.lastModified > remoteFile.lastModified) {
|
||||
console.log("Uploading");
|
||||
|
|
@ -402,22 +404,6 @@ export default class SyncManager {
|
|||
await this.client.updateBranchHead(commitSha);
|
||||
}
|
||||
|
||||
async uploadFile(filePath: string) {
|
||||
const { remotePath, sha } = this.metadataStore.data.files[filePath];
|
||||
const normalizedFilePath = normalizePath(filePath);
|
||||
if (!(await this.vault.adapter.exists(normalizedFilePath))) {
|
||||
throw new Error(`Can't find file ${filePath}`);
|
||||
}
|
||||
const fileContent = await this.vault.adapter.readBinary(normalizedFilePath);
|
||||
await this.client.uploadFile(remotePath, fileContent, sha);
|
||||
// Reset dirty state
|
||||
this.metadataStore.data.files[filePath].dirty = false;
|
||||
// Gets the new SHA of the file
|
||||
const newSha = await this.client.getFileSha(remotePath);
|
||||
this.metadataStore.data.files[filePath].sha = newSha;
|
||||
this.metadataStore.save();
|
||||
}
|
||||
|
||||
async downloadFile(file: GetTreeResponseItem, lastModified: number) {
|
||||
const url = file.url;
|
||||
const destinationFile = file.path.replace(
|
||||
|
|
@ -460,52 +446,6 @@ export default class SyncManager {
|
|||
this.metadataStore.save();
|
||||
}
|
||||
|
||||
async deleteRemoteFile(filePath: string) {
|
||||
const { remotePath, sha } = this.metadataStore.data.files[filePath];
|
||||
if (!sha) {
|
||||
// File was never uploaded, no need to delete it
|
||||
// This is unlikely to happen but we handle it anyway
|
||||
return;
|
||||
}
|
||||
await this.client.deleteFile(remotePath, sha);
|
||||
this.metadataStore.save();
|
||||
}
|
||||
|
||||
async deleteFile(filePath: string) {
|
||||
const { remotePath, sha } = this.metadataStore.data.files[filePath];
|
||||
if (!sha) {
|
||||
// File was never uploaded, no need to delete it
|
||||
return;
|
||||
}
|
||||
await this.client.deleteFile(remotePath, sha);
|
||||
this.metadataStore.save();
|
||||
}
|
||||
|
||||
async downloadAllFiles() {
|
||||
const { files } = await this.client.getRepoContent();
|
||||
|
||||
await Promise.all(
|
||||
Object.keys(files)
|
||||
.filter((filePath: string) =>
|
||||
filePath.startsWith(this.settings.repoContentDir),
|
||||
)
|
||||
.map(
|
||||
async (filePath: string) =>
|
||||
await this.downloadFile(files[filePath], Date.now()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async uploadModifiedFiles() {
|
||||
// We upload files sequentially to avoid conflicts.
|
||||
// GitHub rejects commits if they're made in fast succession, thus
|
||||
// forcing us to retry the failed upload.
|
||||
// So parallelization is not an option.
|
||||
for (const event of this.eventsListener.flush()) {
|
||||
await this.eventsConsumer.process(event);
|
||||
}
|
||||
}
|
||||
|
||||
async loadMetadata() {
|
||||
await this.metadataStore.load();
|
||||
if (Object.keys(this.metadataStore.data.files).length === 0) {
|
||||
|
|
@ -577,8 +517,7 @@ export default class SyncManager {
|
|||
this.syncIntervalId = window.setInterval(
|
||||
() => this.sync(),
|
||||
// Sync interval is set in minutes but setInterval expects milliseconds
|
||||
// minutes * 60 * 1000,
|
||||
10000,
|
||||
minutes * 60 * 1000,
|
||||
);
|
||||
return this.syncIntervalId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
import { usePlugin } from "../hooks";
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
|
||||
const UploadDialogQuestion = ({
|
||||
fileCount,
|
||||
onCancel,
|
||||
startUpload: onUpload,
|
||||
}: {
|
||||
fileCount: number;
|
||||
onCancel: () => void;
|
||||
startUpload: () => Promise<void>;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ width: "fit-content" }}>Upload files to GitHub</h1>
|
||||
<p style={{ width: "fit-content" }}>
|
||||
Do you want to upload{" "}
|
||||
<span style={{ fontWeight: "bold" }}>{fileCount}</span> files? This
|
||||
might take a while.
|
||||
</p>
|
||||
<div className="modal-button-container">
|
||||
<button onClick={onCancel}>Cancel</button>
|
||||
<button
|
||||
onClick={async () => await onUpload()}
|
||||
style={{
|
||||
color: "var(--text-on-accent)",
|
||||
backgroundColor: "var(--interactive-accent)",
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UploadDialogUploading = ({
|
||||
completedCount,
|
||||
fileCount,
|
||||
onCancel,
|
||||
}: {
|
||||
completedCount: number;
|
||||
fileCount: number;
|
||||
onCancel: () => void;
|
||||
}) => {
|
||||
const progress = (completedCount * 100) / fileCount;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 style={{ width: "fit-content" }}>
|
||||
{progress === 100 ? "All files uploaded" : "Uploading…"}
|
||||
</h1>
|
||||
<div className="setting-progress-bar">
|
||||
<div
|
||||
className="setting-progress-bar-inner"
|
||||
style={{
|
||||
width: `${progress}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<p>
|
||||
Uploaded {completedCount} file{completedCount === 1 ? "" : "s"} of{" "}
|
||||
{fileCount}
|
||||
</p>
|
||||
<div className="modal-button-container">
|
||||
{/*
|
||||
We use onCancel for both cases as it won't have any effect other
|
||||
than closing the modal if upload is done
|
||||
*/}
|
||||
<button onClick={onCancel}>{progress === 100 ? "Ok" : "Cancel"}</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UploadDialogContent = ({ onCancel }: { onCancel: () => void }) => {
|
||||
const plugin = usePlugin();
|
||||
if (!plugin) {
|
||||
// Unlikely to happen, makes TS happy though
|
||||
throw new Error("Plugin is not initialized");
|
||||
}
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [completedCount, setCompletedCount] = useState(0);
|
||||
|
||||
const files = useMemo(
|
||||
// This is not the most efficient way to get all files in the
|
||||
// folder to sync but it's quick to code.
|
||||
// I'll enhance it in the future.
|
||||
() =>
|
||||
plugin?.app.vault
|
||||
.getFiles()
|
||||
.filter((file) =>
|
||||
file.path.startsWith(plugin.settings.localContentDir),
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const abortController = useMemo(() => new AbortController(), []);
|
||||
|
||||
// Stop the upload when the component is unmounted, otherwise
|
||||
// we keep uploading even when the modal is closed.
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const startUploading = async () => {
|
||||
setUploading(true);
|
||||
// We upload files sequentially to avoid conflicts.
|
||||
// GitHub rejects commits if they're made in fast succession, thus
|
||||
// forcing us to retry the failed upload.
|
||||
// So parallelization is not an option.
|
||||
for (const file of files) {
|
||||
if (abortController.signal.aborted) {
|
||||
return;
|
||||
}
|
||||
await plugin.syncManager.uploadFile(file);
|
||||
if (abortController.signal.aborted) {
|
||||
// Check abort state only after the uploaded file metadata
|
||||
// is updated, otherwise we risk having outdated SHAs
|
||||
return;
|
||||
}
|
||||
setCompletedCount((count) => count + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
}}
|
||||
>
|
||||
{!uploading && (
|
||||
<UploadDialogQuestion
|
||||
fileCount={files.length}
|
||||
onCancel={onCancel}
|
||||
startUpload={startUploading}
|
||||
/>
|
||||
)}
|
||||
{uploading && (
|
||||
<UploadDialogUploading
|
||||
completedCount={completedCount}
|
||||
fileCount={files.length}
|
||||
onCancel={() => {
|
||||
abortController.abort();
|
||||
onCancel();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UploadDialogContent;
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import { Modal } from "obsidian";
|
||||
import { Root, createRoot } from "react-dom/client";
|
||||
import GitHubSyncPlugin from "src/main";
|
||||
import { PluginContext } from "../hooks";
|
||||
import UploadDialogContent from "./component";
|
||||
|
||||
/**
|
||||
* Dialog shown when user tries to upload all local files to GitHub.
|
||||
*/
|
||||
export class UploadDialog extends Modal {
|
||||
root: Root | null = null;
|
||||
|
||||
constructor(private plugin: GitHubSyncPlugin) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
this.root = createRoot(this.modalEl);
|
||||
this.root.render(
|
||||
<PluginContext.Provider value={this.plugin}>
|
||||
<UploadDialogContent onCancel={() => this.close()} />
|
||||
</PluginContext.Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
// It's important to unmount the component so upload stops
|
||||
// in case it's running
|
||||
this.root?.unmount();
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue