Disallow changing settings while applying previous changes
This commit is contained in:
parent
acffd0006d
commit
8f2190f6a0
2 changed files with 249 additions and 81 deletions
|
|
@ -13,45 +13,122 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vault-link-settings {
|
.vault-link-settings-container {
|
||||||
h2 {
|
position: relative;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: var(--h2-size);
|
|
||||||
|
|
||||||
.version {
|
.vault-link-settings {
|
||||||
@include number-card;
|
h2 {
|
||||||
margin: var(--size-2-2) 0 0 var(--size-4-2);
|
display: flex;
|
||||||
background-color: var(--color-base-30);
|
align-items: center;
|
||||||
color: var(--color-base-70);
|
font-size: var(--h2-size);
|
||||||
font-size: var(--font-ui-smaller);
|
|
||||||
|
.version {
|
||||||
|
@include number-card;
|
||||||
|
margin: var(--size-2-2) 0 0 var(--size-4-2);
|
||||||
|
background-color: var(--color-base-30);
|
||||||
|
color: var(--color-base-70);
|
||||||
|
font-size: var(--font-ui-smaller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-4-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: var(--font-ui-large);
|
||||||
|
margin-top: var(--heading-spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="range"],
|
||||||
|
.checkbox-container,
|
||||||
|
.slider::-webkit-slider-thumb {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
textarea {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: none;
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.button-container {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--size-4-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: var(--font-ui-large);
|
|
||||||
margin-top: var(--heading-spacing);
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
input[type="range"],
|
|
||||||
.checkbox-container,
|
|
||||||
.slider::-webkit-slider-thumb {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
textarea {
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: none;
|
|
||||||
height: 75px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -111,10 +157,10 @@ export class SyncSettingsTab extends PluginSettingTab {
|
||||||
text: "Show history"
|
text: "Show history"
|
||||||
},
|
},
|
||||||
(button) =>
|
(button) =>
|
||||||
(button.onclick = async (): Promise<void> => {
|
(button.onclick = async (): Promise<void> => {
|
||||||
this.plugin.closeSettings();
|
this.plugin.closeSettings();
|
||||||
await this.plugin.activateView(HistoryView.TYPE);
|
await this.plugin.activateView(HistoryView.TYPE);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
buttonContainer.createEl(
|
buttonContainer.createEl(
|
||||||
|
|
@ -123,10 +169,10 @@ export class SyncSettingsTab extends PluginSettingTab {
|
||||||
text: "Show logs"
|
text: "Show logs"
|
||||||
},
|
},
|
||||||
(button) =>
|
(button) =>
|
||||||
(button.onclick = async (): Promise<void> => {
|
(button.onclick = async (): Promise<void> => {
|
||||||
this.plugin.closeSettings();
|
this.plugin.closeSettings();
|
||||||
await this.plugin.activateView(LogsView.TYPE);
|
await this.plugin.activateView(LogsView.TYPE);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -197,23 +243,40 @@ 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)
|
||||||
if (this.areThereUnsavedChanges()) {
|
.setTooltip(
|
||||||
await this.syncClient.setSettings({
|
this.isApplyingChanges
|
||||||
vaultName: this.editedVaultName,
|
? "Waiting for applying changes to finish..."
|
||||||
remoteUri: this.editedServerUri,
|
: "Apply the changes made to the connection settings and test the connection to the server."
|
||||||
token: this.editedToken
|
)
|
||||||
});
|
.onClick(() => {
|
||||||
new Notice("Checking connection to the server...");
|
// don't show loader within the button
|
||||||
new Notice(
|
void (async () => {
|
||||||
(
|
if (this.areThereUnsavedChanges()) {
|
||||||
await this.syncClient.checkConnection()
|
new Notice("Applying changes to the server...");
|
||||||
).serverMessage
|
|
||||||
);
|
this.isApplyingChanges = true;
|
||||||
await this.statusDescription.updateConnectionState();
|
try {
|
||||||
} else {
|
await this.syncClient.setSettings({
|
||||||
new Notice("No changes to apply");
|
vaultName: this.editedVaultName,
|
||||||
}
|
remoteUri: this.editedServerUri,
|
||||||
|
token: this.editedToken
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.isApplyingChanges = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Notice("Checking connection to the server...");
|
||||||
|
new Notice(
|
||||||
|
(
|
||||||
|
await this.syncClient.checkConnection()
|
||||||
|
).serverMessage
|
||||||
|
);
|
||||||
|
await this.statusDescription.updateConnectionState();
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
new Notice(
|
.setTooltip(
|
||||||
"Sync state has been reset, you will need to resync"
|
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(
|
||||||
|
"Sync state has been reset, you will need to resync"
|
||||||
|
);
|
||||||
|
})())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,9 +533,9 @@ export class SyncSettingsTab extends PluginSettingTab {
|
||||||
name: string,
|
name: string,
|
||||||
settingName: keyof SyncSettings
|
settingName: keyof SyncSettings
|
||||||
): [
|
): [
|
||||||
DocumentFragment,
|
DocumentFragment,
|
||||||
(newValue: SyncSettings[keyof SyncSettings]) => unknown
|
(newValue: SyncSettings[keyof SyncSettings]) => unknown
|
||||||
] {
|
] {
|
||||||
const titleContainer = document.createDocumentFragment();
|
const titleContainer = document.createDocumentFragment();
|
||||||
const title = titleContainer.createEl("div", {
|
const title = titleContainer.createEl("div", {
|
||||||
text: name,
|
text: name,
|
||||||
|
|
@ -453,11 +545,10 @@ 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)"
|
: ""
|
||||||
: ""
|
}`;
|
||||||
}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return [titleContainer, updateTitle];
|
return [titleContainer, updateTitle];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue