Disallow changing settings while applying previous changes

This commit is contained in:
Andras Schmelczer 2025-11-30 14:41:13 +00:00
commit 3517af1461
2 changed files with 245 additions and 77 deletions

View file

@ -13,6 +13,9 @@
} }
} }
.vault-link-settings-container {
position: relative;
.vault-link-settings { .vault-link-settings {
h2 { h2 {
display: flex; display: flex;
@ -54,4 +57,78 @@
resize: none; resize: none;
height: 75px; height: 75px;
} }
.applying-changes-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%);
z-index: 10;
backdrop-filter: blur(10px);
.spinner-container {
background-color: rgba(var(--background-primary), 0.5);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
padding: var(--size-4-8);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--size-4-3);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
min-width: 200px;
}
.spinner {
width: 48px;
height: 48px;
border: 4px solid var(--background-modifier-border);
border-top-color: var(--interactive-accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.spinner-text {
color: var(--text-normal);
font-size: var(--font-ui-medium);
font-weight: 500;
}
.spinner-warning {
color: var(--text-muted);
font-size: var(--font-ui-small);
text-align: center;
margin-top: var(--size-2-2);
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
&.applying-changes {
.setting-item-control {
pointer-events: none;
opacity: 0.5;
}
button:not(.applying-changes-overlay button) {
pointer-events: none;
opacity: 0.5;
}
input,
textarea,
select {
pointer-events: none;
opacity: 0.5;
}
}
}
} }

View file

@ -13,6 +13,9 @@ export class SyncSettingsTab extends PluginSettingTab {
private editedToken: string; private editedToken: string;
private editedVaultName: string; private editedVaultName: string;
private _isApplyingChanges = false;
private syncEnabledOverride: boolean | undefined = undefined;
private readonly plugin: VaultLinkPlugin; private readonly plugin: VaultLinkPlugin;
private readonly syncClient: SyncClient; private readonly syncClient: SyncClient;
private readonly statusDescription: StatusDescription; private readonly statusDescription: StatusDescription;
@ -64,11 +67,28 @@ export class SyncSettingsTab extends PluginSettingTab {
); );
} }
private get isApplyingChanges(): boolean {
return this._isApplyingChanges;
}
private set isApplyingChanges(value: boolean) {
this._isApplyingChanges = value;
this.display()
}
public display(): void { public display(): void {
const { containerEl } = this; const { containerEl } = this;
containerEl.empty(); containerEl.empty();
containerEl.addClass("vault-link-settings"); containerEl.addClass("vault-link-settings");
containerEl.parentElement?.addClass("vault-link-settings-container");
if (this.isApplyingChanges) {
containerEl.addClass("applying-changes");
} else {
containerEl.removeClass("applying-changes");
}
this.renderApplyingChanges(containerEl);
this.renderSettingsHeader(containerEl); this.renderSettingsHeader(containerEl);
this.renderConnectionSettings(containerEl); this.renderConnectionSettings(containerEl);
this.renderSyncSettings(containerEl); this.renderSyncSettings(containerEl);
@ -80,6 +100,32 @@ export class SyncSettingsTab extends PluginSettingTab {
this.setStatusDescriptionSubscription(); this.setStatusDescriptionSubscription();
} }
private renderApplyingChanges(containerEl: HTMLElement): void {
if (this.isApplyingChanges) {
const overlay = containerEl.createDiv({
cls: "applying-changes-overlay"
});
const spinnerContainer = overlay.createDiv({
cls: "spinner-container"
});
spinnerContainer.createDiv({
cls: "spinner"
});
spinnerContainer.createDiv({
text: "Applying changes...",
cls: "spinner-text"
});
spinnerContainer.createDiv({
text: "You can exit, but changes won't be saved",
cls: "spinner-warning"
});
}
}
private renderSettingsHeader(containerEl: HTMLElement): void { private renderSettingsHeader(containerEl: HTMLElement): void {
containerEl.createEl("h2", { text: "VaultLink" }).createSpan({ containerEl.createEl("h2", { text: "VaultLink" }).createSpan({
text: this.plugin.manifest.version, text: this.plugin.manifest.version,
@ -197,13 +243,29 @@ export class SyncSettingsTab extends PluginSettingTab {
new Setting(containerEl).addButton((button) => new Setting(containerEl).addButton((button) =>
button button
.setButtonText("Apply & test connection") .setButtonText("Apply & test connection")
.onClick(async () => { .setDisabled(this.isApplyingChanges)
.setTooltip(
this.isApplyingChanges
? "Waiting for applying changes to finish..."
: "Apply the changes made to the connection settings and test the connection to the server."
)
.onClick(() => {
// don't show loader within the button
void (async () => {
if (this.areThereUnsavedChanges()) { if (this.areThereUnsavedChanges()) {
new Notice("Applying changes to the server...");
this.isApplyingChanges = true;
try {
await this.syncClient.setSettings({ await this.syncClient.setSettings({
vaultName: this.editedVaultName, vaultName: this.editedVaultName,
remoteUri: this.editedServerUri, remoteUri: this.editedServerUri,
token: this.editedToken token: this.editedToken
}); });
} finally {
this.isApplyingChanges = false;
}
new Notice("Checking connection to the server..."); new Notice("Checking connection to the server...");
new Notice( new Notice(
( (
@ -214,6 +276,7 @@ export class SyncSettingsTab extends PluginSettingTab {
} else { } else {
new Notice("No changes to apply"); new Notice("No changes to apply");
} }
})();
}) })
); );
} }
@ -239,9 +302,24 @@ export class SyncSettingsTab extends PluginSettingTab {
) )
.addToggle((toggle) => .addToggle((toggle) =>
toggle toggle
.setValue(this.syncClient.getSettings().isSyncEnabled) .setValue(this.syncEnabledOverride ?? this.syncClient.getSettings().isSyncEnabled)
.onChange(async (value) => .setDisabled(this.isApplyingChanges)
this.syncClient.setSetting("isSyncEnabled", value) .setTooltip(
this.isApplyingChanges
? "Waiting for applying changes to finish..."
: "Enable or disable syncing."
)
.onChange((value) => void (async () => {
this.syncEnabledOverride = value;
this.isApplyingChanges = true;
try {
await this.syncClient.setSetting("isSyncEnabled", value);
} finally {
this.syncEnabledOverride = undefined;
this.isApplyingChanges = false;
}
}
)()
) )
); );
@ -321,12 +399,26 @@ export class SyncSettingsTab extends PluginSettingTab {
"Delete the local metadata database while leaving the local and remote files intact." "Delete the local metadata database while leaving the local and remote files intact."
) )
.addButton((button) => .addButton((button) =>
button.setButtonText("Reset sync state").onClick(async () => { button
await this.syncClient.applyChangedConnectionSettings(); .setDisabled(this.isApplyingChanges)
.setTooltip(
this.isApplyingChanges
? "Waiting for applying changes to finish..."
: "Reset sync state"
)
.setButtonText("Reset sync state")
.onClick(() => void (async () => {
this.isApplyingChanges = true;
try {
await this.syncClient.reset();
} finally {
this.isApplyingChanges = false;
}
new Notice( new Notice(
"Sync state has been reset, you will need to resync" "Sync state has been reset, you will need to resync"
); );
}) })())
); );
} }
@ -453,8 +545,7 @@ export class SyncSettingsTab extends PluginSettingTab {
const updateTitle = ( const updateTitle = (
currentValue: SyncSettings[keyof SyncSettings] currentValue: SyncSettings[keyof SyncSettings]
): void => { ): void => {
title.innerText = `${name}${ title.innerText = `${name}${currentValue !== this.syncClient.getSettings()[settingName]
currentValue !== this.syncClient.getSettings()[settingName]
? " (unsaved)" ? " (unsaved)"
: "" : ""
}`; }`;