fix issues with dialogs, and issue with vault rename
This commit is contained in:
parent
e1d484fd28
commit
2418f125f0
6 changed files with 201 additions and 32 deletions
|
|
@ -78,16 +78,17 @@ export const vaultService = {
|
||||||
if (window.__vaultConfig) {
|
if (window.__vaultConfig) {
|
||||||
window.__vaultConfig.id = newName;
|
window.__vaultConfig.id = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
history.replaceState(null, "", "/?vault=" + encodeURIComponent(newName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.listVaults();
|
return this.listVaults();
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteVault(id) {
|
async deleteVault(id) {
|
||||||
await fetchJson(
|
await fetchJson(API_BASE + "/remove?vault=" + encodeURIComponent(id), {
|
||||||
API_BASE + "/remove?vault=" + encodeURIComponent(id),
|
method: "DELETE",
|
||||||
{ method: "DELETE" },
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const wasCurrentVault = id === this.getCurrentVaultId();
|
const wasCurrentVault = id === this.getCurrentVaultId();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
import {
|
||||||
|
showMessageDialog,
|
||||||
|
showConfirmDialog,
|
||||||
|
showPromptDialog,
|
||||||
|
} from "../../../ui/bootstrap.js";
|
||||||
|
|
||||||
export const dialogShim = {
|
export const dialogShim = {
|
||||||
async showOpenDialog(browserWindow, options) {
|
async showOpenDialog(browserWindow, options) {
|
||||||
// TODO: implement custom modal with server-side file listing
|
// TODO: implement custom modal with server-side file listing
|
||||||
|
|
@ -5,21 +11,28 @@ export const dialogShim = {
|
||||||
return { canceled: true, filePaths: [] };
|
return { canceled: true, filePaths: [] };
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: replace prompt() with a styled modal (matching vault manager style)
|
|
||||||
async showSaveDialog(browserWindow, options) {
|
async showSaveDialog(browserWindow, options) {
|
||||||
if (typeof browserWindow === "object" && !options) {
|
if (typeof browserWindow === "object" && !options) {
|
||||||
options = browserWindow;
|
options = browserWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultName =
|
const defaultName =
|
||||||
options?.defaultPath?.split(/[/\\]/).pop() || "download";
|
options?.defaultPath?.split(/[\/\\]/).pop() || "download";
|
||||||
const name = prompt("Save as:", defaultName);
|
const name = await showPromptDialog(
|
||||||
|
"Save File",
|
||||||
|
"Save as:",
|
||||||
|
"filename",
|
||||||
|
defaultName,
|
||||||
|
"Save",
|
||||||
|
);
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return { canceled: true, filePath: undefined };
|
return { canceled: true, filePath: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { canceled: false, filePath: "/downloads/" + name };
|
return { canceled: false, filePath: "/downloads/" + name };
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: replace alert() with a styled modal (matching vault manager style)
|
|
||||||
async showMessageBox(browserWindow, options) {
|
async showMessageBox(browserWindow, options) {
|
||||||
if (typeof browserWindow === "object" && !options) {
|
if (typeof browserWindow === "object" && !options) {
|
||||||
options = browserWindow;
|
options = browserWindow;
|
||||||
|
|
@ -30,21 +43,20 @@ export const dialogShim = {
|
||||||
const message = options.message || "";
|
const message = options.message || "";
|
||||||
const detail = options.detail || "";
|
const detail = options.detail || "";
|
||||||
const buttons = options.buttons || ["OK"];
|
const buttons = options.buttons || ["OK"];
|
||||||
|
const fullMessage = message + (detail ? "\n\n" + detail : "");
|
||||||
|
|
||||||
if (buttons.length <= 1) {
|
if (buttons.length <= 1) {
|
||||||
alert(message + (detail ? "\n\n" + detail : ""));
|
await showMessageDialog(options.title || "Message", fullMessage);
|
||||||
return { response: 0, checkboxChecked: false };
|
return { response: 0, checkboxChecked: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = confirm(
|
const result = await showConfirmDialog(
|
||||||
message +
|
options.title || "Confirm",
|
||||||
(detail ? "\n\n" + detail : "") +
|
message,
|
||||||
'\n\n[OK] = "' +
|
detail,
|
||||||
buttons[0] +
|
buttons[0],
|
||||||
'", [Cancel] = "' +
|
|
||||||
buttons[1] +
|
|
||||||
'"',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: result ? 0 : 1,
|
response: result ? 0 : 1,
|
||||||
checkboxChecked: false,
|
checkboxChecked: false,
|
||||||
|
|
@ -53,6 +65,6 @@ export const dialogShim = {
|
||||||
|
|
||||||
showErrorBox(title, content) {
|
showErrorBox(title, content) {
|
||||||
console.error("[shim:dialog] Error:", title, content);
|
console.error("[shim:dialog] Error:", title, content);
|
||||||
alert(title + "\n\n" + content);
|
showMessageDialog(title, content);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
63
ui/bootstrap.js
vendored
63
ui/bootstrap.js
vendored
|
|
@ -9,3 +9,66 @@ export function showVaultManager() {
|
||||||
props: { vaultService },
|
props: { vaultService },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showMessageDialog(title, message) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const dialog = new window.IgnisUI.MessageDialog({
|
||||||
|
target: document.body,
|
||||||
|
props: { title, message },
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.$on("confirm", () => {
|
||||||
|
dialog.$destroy();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showConfirmDialog(
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
description,
|
||||||
|
confirmText = "OK",
|
||||||
|
) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const dialog = new window.IgnisUI.ConfirmDialog({
|
||||||
|
target: document.body,
|
||||||
|
props: { title, message, description, confirmText },
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.$on("confirm", () => {
|
||||||
|
dialog.$destroy();
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.$on("cancel", () => {
|
||||||
|
dialog.$destroy();
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPromptDialog(
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
placeholder = "",
|
||||||
|
value = "",
|
||||||
|
confirmText = "OK",
|
||||||
|
) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const dialog = new window.IgnisUI.PromptDialog({
|
||||||
|
target: document.body,
|
||||||
|
props: { title, label, placeholder, value, confirmText },
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.$on("confirm", (event) => {
|
||||||
|
dialog.$destroy();
|
||||||
|
resolve(event.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.$on("cancel", () => {
|
||||||
|
dialog.$destroy();
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
69
ui/components/layout/MessageDialog.svelte
Normal file
69
ui/components/layout/MessageDialog.svelte
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import Modal from "./Modal.svelte";
|
||||||
|
import Button from "../input/Button.svelte";
|
||||||
|
import { CircleAlert } from "lucide-svelte";
|
||||||
|
|
||||||
|
export let title = "Message";
|
||||||
|
export let message = "";
|
||||||
|
export let width = "500px";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let modalRef;
|
||||||
|
|
||||||
|
function onConfirm() {
|
||||||
|
dispatch("confirm");
|
||||||
|
modalRef.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEscape() {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dismiss() {
|
||||||
|
modalRef.dismiss();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
{title}
|
||||||
|
{width}
|
||||||
|
bind:this={modalRef}
|
||||||
|
on:escape={onEscape}
|
||||||
|
closeOnOverlayClick={false}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="icon">
|
||||||
|
<CircleAlert size="1.25rem" />
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
<p class="message-text">{message}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:fragment slot="footer">
|
||||||
|
<div class="message-footer">
|
||||||
|
<Button variant="primary" on:click={onConfirm}>OK</Button>
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.message-body {
|
||||||
|
padding: 1.25rem 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--background-modifier-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: var(--text-normal);
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
export { default as VaultManager } from "./views/VaultManager.svelte";
|
export { default as VaultManager } from "./views/VaultManager.svelte";
|
||||||
|
export { default as MessageDialog } from "./components/layout/MessageDialog.svelte";
|
||||||
|
export { default as ConfirmDialog } from "./components/layout/ConfirmDialog.svelte";
|
||||||
|
export { default as PromptDialog } from "./components/layout/PromptDialog.svelte";
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
import Modal from "../components/layout/Modal.svelte";
|
import Modal from "../components/layout/Modal.svelte";
|
||||||
import PromptDialog from "../components/layout/PromptDialog.svelte";
|
import PromptDialog from "../components/layout/PromptDialog.svelte";
|
||||||
import ConfirmDialog from "../components/layout/ConfirmDialog.svelte";
|
import ConfirmDialog from "../components/layout/ConfirmDialog.svelte";
|
||||||
|
import MessageDialog from "../components/layout/MessageDialog.svelte";
|
||||||
import SearchInput from "../components/input/SearchInput.svelte";
|
import SearchInput from "../components/input/SearchInput.svelte";
|
||||||
import Button from "../components/input/Button.svelte";
|
import Button from "../components/input/Button.svelte";
|
||||||
import ListItem from "../components/display/ListItem.svelte";
|
import ListItem from "../components/display/ListItem.svelte";
|
||||||
|
|
@ -27,6 +28,8 @@
|
||||||
let activeDialog = null;
|
let activeDialog = null;
|
||||||
let targetVault = null;
|
let targetVault = null;
|
||||||
let dialogValue = "";
|
let dialogValue = "";
|
||||||
|
let errorMessage = "";
|
||||||
|
let pendingReload = false;
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ id: "rename", label: "Rename" },
|
{ id: "rename", label: "Rename" },
|
||||||
|
|
@ -108,12 +111,11 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
vaults = await vaultService.createVault(name);
|
vaults = await vaultService.createVault(name);
|
||||||
|
closeDialog();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Failed to create vault: " + err.message);
|
errorMessage = "Failed to create vault: " + err.message;
|
||||||
return;
|
activeDialog = "error";
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onRenameConfirm(e) {
|
async function onRenameConfirm(e) {
|
||||||
|
|
@ -128,15 +130,15 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
vaults = await vaultService.renameVault(targetVault.id, trimmed);
|
vaults = await vaultService.renameVault(targetVault.id, trimmed);
|
||||||
|
closeDialog();
|
||||||
|
|
||||||
|
if (wasCurrentVault) {
|
||||||
|
currentVaultId = vaultService.getCurrentVaultId();
|
||||||
|
pendingReload = true;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Failed to rename vault: " + err.message);
|
errorMessage = "Failed to rename vault: " + err.message;
|
||||||
return;
|
activeDialog = "error";
|
||||||
}
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
|
|
||||||
if (wasCurrentVault) {
|
|
||||||
currentVaultId = vaultService.getCurrentVaultId();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +156,14 @@
|
||||||
vaultService.openVault("");
|
vaultService.openVault("");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert("Failed to delete vault: " + err.message);
|
errorMessage = "Failed to delete vault: " + err.message;
|
||||||
|
activeDialog = "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onModalClose() {
|
||||||
|
if (pendingReload) {
|
||||||
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,6 +185,7 @@
|
||||||
width="600px"
|
width="600px"
|
||||||
bind:this={modalRef}
|
bind:this={modalRef}
|
||||||
on:escape={onEscape}
|
on:escape={onEscape}
|
||||||
|
on:close={onModalClose}
|
||||||
closeOnOverlayClick={false}
|
closeOnOverlayClick={false}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="icon">
|
<svelte:fragment slot="icon">
|
||||||
|
|
@ -270,7 +280,7 @@
|
||||||
<PromptDialog
|
<PromptDialog
|
||||||
title="Rename Item"
|
title="Rename Item"
|
||||||
label="New Name:"
|
label="New Name:"
|
||||||
confirmText="Save Name"
|
confirmText="Save"
|
||||||
bind:value={dialogValue}
|
bind:value={dialogValue}
|
||||||
on:confirm={onRenameConfirm}
|
on:confirm={onRenameConfirm}
|
||||||
on:cancel={closeDialog}
|
on:cancel={closeDialog}
|
||||||
|
|
@ -303,6 +313,17 @@
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if activeDialog === "error"}
|
||||||
|
<MessageDialog
|
||||||
|
title="Error"
|
||||||
|
message={errorMessage}
|
||||||
|
on:confirm={() => {
|
||||||
|
activeDialog = null;
|
||||||
|
errorMessage = "";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.section-header {
|
.section-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue