Skip to content

Commit

Permalink
confirm deleting fwlite entries (#1337)
Browse files Browse the repository at this point in the history
* enable detailed errors in web circuit

* disable prerendering

* allow accessing the project testing page to streamline ui dev

* create a delete dialog in the project root to reuse across components

* confirm deletes

* fix the height of delete dialog so it's not full screen, also change the confirm button to show a trash can and style it appropriately

* allow opening dialog again after dismissing by clicking outside

* simplify delete dialog closing
  • Loading branch information
hahn-kev authored Jan 6, 2025
1 parent 7050665 commit 64ca37e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 7 deletions.
3 changes: 3 additions & 0 deletions backend/FwLite/FwLiteShared/Pages/TestingProject.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@page "/testing/project-view"
@using FwLiteShared.Layout
@layout SvelteLayout;
4 changes: 2 additions & 2 deletions backend/FwLite/FwLiteWeb/Components/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="/"/>
<link rel="icon" type="image/png" href="_content/FwLiteShared/favicon.png"/>
<HeadOutlet @rendermode="InteractiveServer"/>
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)"/>
<script>
window.invokeOnWindow = function (methodName, args) {
if (!(methodName in window)) {
Expand All @@ -20,7 +20,7 @@
</head>

<body>
<Routes @rendermode="InteractiveServer"/>
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)"/>
<script src="_framework/blazor.web.js"></script>
</body>

Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/FwLiteWeb/FwLiteWebServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static WebApplication SetupAppServer(WebApplicationOptions options, Actio
config.LexboxServers = [new(new("https://staging.languagedepot.org"), "Lexbox Staging")]);
builder.Services.Configure<AuthConfig>(c => c.ClientId = "becf2856-0690-434b-b192-a4032b72067f");
builder.Logging.AddDebug();
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
builder.Services.AddRazorComponents().AddInteractiveServerComponents(circuitOptions => circuitOptions.DetailedErrors = true);
if (builder.Configuration.GetValue<string>("FwLiteWeb:LogFileName") is { Length: > 0 } logFileName)
{
builder.Logging.AddFile(logFileName);
Expand Down
7 changes: 7 additions & 0 deletions frontend/viewer/src/ProjectView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import {initProjectCommands, type NewEntryDialogOptions} from './lib/commands';
import throttle from 'just-throttle';
import {SortField} from '$lib/dotnet-types/generated-types/MiniLcm/SortField';
import DeleteDialog from '$lib/entry-editor/DeleteDialog.svelte';
import {initDialogService} from '$lib/entry-editor/dialog-service';
export let loading = false;
export let about: string | undefined = undefined;
Expand Down Expand Up @@ -268,6 +270,10 @@
if (entry) onEntryCreated(entry, options);
return entry;
}
let deleteDialog: DeleteDialog;
$: dialogHolder.dialog = deleteDialog;
const dialogHolder: {dialog?: DeleteDialog} = {};
initDialogService(() => dialogHolder.dialog);
initProjectCommands({
createNewEntry: openNewEntryDialog,
Expand Down Expand Up @@ -427,3 +433,4 @@
<ViewOptionsDrawer bind:open={showOptionsDialog} bind:activeView={$currentView} bind:viewSettings={$viewSettings} bind:features={$features} />
</div>
{/if}
<DeleteDialog bind:this={deleteDialog} />
43 changes: 43 additions & 0 deletions frontend/viewer/src/lib/entry-editor/DeleteDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import {Button, Dialog} from 'svelte-ux';
import {mdiTrashCanOutline} from '@mdi/js';
let subject: string;
let open = false;
let loading = false;
let requester: {
resolve: (result: boolean) => void
} | undefined = undefined;
function confirm() {
resolve(true);
}
function cancel() {
resolve(false);
}
function resolve(shouldDelete: boolean) {
requester?.resolve(shouldDelete);
requester = undefined;
open = false;
}
export function prompt(promptSubject: string): Promise<boolean> {
if (requester) throw new Error('already prompting for a delete');
return new Promise((resolve) => {
requester = { resolve };
subject = promptSubject;
open = true;
});
}
</script>
<Dialog {open} on:close={cancel} {loading} persistent={loading} style="height: auto">
<div slot="title">Delete {subject}</div>
<div class="m-6">
<p>Are you sure you want to delete {subject}?</p>
</div>
<div slot="actions">
<Button on:click={() => cancel()}>Don't delete</Button>
<Button variant="fill-light" color="danger" icon={mdiTrashCanOutline} on:click={_ => confirm()}>Delete {subject}</Button>
</div>
</Dialog>
28 changes: 28 additions & 0 deletions frontend/viewer/src/lib/entry-editor/dialog-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type DeleteDialog from '$lib/entry-editor/DeleteDialog.svelte';
import {getContext, setContext} from 'svelte';

export function initDialogService(rootDialog: () => DeleteDialog | undefined): DialogService {
const dialogService = new DialogService(rootDialog);
setContext('DialogService', dialogService);
return dialogService;
}

export function useDialogService(): DialogService {
const rootDialog = getContext('DialogService');
if (!rootDialog) {
throw new Error('DialogService not found');
}
return rootDialog as DialogService;
}

export class DialogService {

constructor(private deleteDialog: () => DeleteDialog | undefined) {
}

promptDelete(subject: string): Promise<boolean> {
const deleteDialog = this.deleteDialog();
if (!deleteDialog) throw new Error('no deleted dialog found');
return deleteDialog.prompt(subject);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import ComplexFormComponents from '../field-editors/ComplexFormComponents.svelte';
import ComplexForms from '../field-editors/ComplexForms.svelte';
import ComplexFormTypes from '../field-editors/ComplexFormTypes.svelte';
import {useDialogService} from '$lib/entry-editor/dialog-service';
const dialogService = useDialogService();
const dispatch = createEventDispatcher<{
change: { entry: IEntry, sense?: ISense, example?: IExampleSentence};
delete: { entry: IEntry, sense?: ISense, example?: IExampleSentence};
Expand All @@ -36,11 +37,13 @@
sense.exampleSentences = [...sense.exampleSentences, sentence];
entry = entry; // examples counts are not updated without this
}
function deleteEntry() {
async function deleteEntry() {
if (!await dialogService.promptDelete('Entry')) return;
dispatch('delete', {entry});
}
function deleteSense(sense: ISense) {
async function deleteSense(sense: ISense) {
if (!await dialogService.promptDelete('Sense')) return;
entry.senses = entry.senses.filter(s => s !== sense);
dispatch('delete', {entry, sense});
}
Expand All @@ -50,7 +53,8 @@
dispatch('change', {entry, sense});
highlightedEntity = sense;
}
function deleteExample(sense: ISense, example: IExampleSentence) {
async function deleteExample(sense: ISense, example: IExampleSentence) {
if (!await dialogService.promptDelete('Example sentence')) return;
sense.exampleSentences = sense.exampleSentences.filter(e => e !== example);
dispatch('delete', {entry, sense, example});
entry = entry; // examples are not updated without this
Expand Down

0 comments on commit 64ca37e

Please sign in to comment.