fix(webui): Notes aren't duplicated (manifested as unreliable saving)

also rework semantics of `WidgetChange`
feat/tables
Tomáš Mládek 2024-02-06 22:32:10 +01:00
parent f1b608f824
commit 9d6ebfc31c
4 changed files with 64 additions and 79 deletions

View File

@ -233,40 +233,42 @@
async function onChange(ev: CustomEvent<WidgetChange>) {
dbg('onChange', ev.detail);
const change = ev.detail;
switch (change.type) {
case 'create':
await api.putEntry({
entity: address,
attribute: change.attribute,
value: change.value
});
break;
case 'delete':
await api.deleteEntry(change.address);
break;
case 'update':
await api.putEntityAttribute(address, change.attribute, change.value);
break;
case 'entry-add':
await api.putEntry({
entity: change.address,
attribute: ATTR_IN,
value: { t: 'Address', c: address }
});
break;
case 'entry-delete': {
const inEntry = $entity?.attr[`~${ATTR_IN}`]?.find((e) => e.entity === change.address);
if (inEntry) {
await api.deleteEntry(inEntry.address);
} else {
console.warn("Couldn't find IN entry for entity %s?!", change.address);
const changes = Array.isArray(ev.detail) ? ev.detail : [ev.detail];
for (const change of changes) {
switch (change.type) {
case 'create':
await api.putEntry({
entity: address,
attribute: change.attribute,
value: change.value
});
break;
case 'delete':
await api.deleteEntry(change.address);
break;
case 'upsert':
await api.putEntityAttribute(address, change.attribute, change.value);
break;
case 'entry-add':
await api.putEntry({
entity: change.address,
attribute: ATTR_IN,
value: { t: 'Address', c: address }
});
break;
case 'entry-delete': {
const inEntry = $entity?.attr[`~${ATTR_IN}`]?.find((e) => e.entity === change.address);
if (inEntry) {
await api.deleteEntry(inEntry.address);
} else {
console.warn("Couldn't find IN entry for entity %s?!", change.address);
}
break;
}
break;
default:
console.error('Unimplemented AttributeChange', change);
return;
}
default:
console.error('Unimplemented AttributeChange', change);
return;
}
revalidate();
}

View File

@ -2,45 +2,24 @@
import { debounce } from 'lodash';
import { createEventDispatcher } from 'svelte';
import { useEntity } from '$lib/entity';
import type { AttributeCreate, AttributeUpdate } from '$lib/types/base';
import type { UpEntry } from '@upnd/upend';
import type { WidgetChange } from '$lib/types/base';
import LabelBorder from './LabelBorder.svelte';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{ change: WidgetChange }>();
export let address: string;
$: ({ entity } = useEntity(address));
let noteEntry: UpEntry | undefined;
let notes: string | undefined = undefined;
$: {
if ($entity?.attr['NOTE']?.length && $entity?.attr['NOTE'][0]?.value?.c) {
noteEntry = $entity?.attr['NOTE'][0];
notes = String(noteEntry.value.c);
} else {
noteEntry = undefined;
notes = undefined;
}
}
$: notes = $entity?.get('NOTE')?.toString();
let contentEl: HTMLDivElement;
const update = debounce(() => {
if (noteEntry) {
dispatch('change', {
type: 'update',
address: noteEntry.address,
attribute: 'NOTE',
value: { t: 'String', c: contentEl.innerText }
} as AttributeUpdate);
} else {
dispatch('change', {
type: 'create',
address: address,
attribute: 'NOTE',
value: { t: 'String', c: contentEl.innerText }
} as AttributeCreate);
}
dispatch('change', {
type: 'upsert',
attribute: 'NOTE',
value: { t: 'String', c: contentEl.innerText }
});
}, 500);
</script>

View File

@ -4,7 +4,7 @@
import Ellipsis from '../utils/Ellipsis.svelte';
import UpObject from '../display/UpObject.svelte';
import { createEventDispatcher } from 'svelte';
import type { AttributeUpdate, WidgetChange } from '$lib/types/base';
import type { WidgetChange } from '$lib/types/base';
import type { UpEntry, UpListing } from '@upnd/upend';
import IconButton from '../utils/IconButton.svelte';
import Selector, { type SelectorValue, selectorValueAsValue } from '../utils/Selector.svelte';
@ -18,7 +18,7 @@
import UpLink from '../display/UpLink.svelte';
import { ATTR_ADDED, ATTR_LABEL } from '@upnd/upend/constants';
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{ change: WidgetChange }>();
export let columns: string | undefined = undefined;
export let header = true;
@ -63,7 +63,7 @@
type: 'create',
attribute,
value: await selectorValueAsValue(value)
} as WidgetChange);
});
newEntryAttribute = '';
newEntryValue = undefined;
}
@ -72,13 +72,18 @@
dispatch('change', { type: 'delete', address } as WidgetChange);
}
}
async function updateEntry(address: string, attribute: string, value: SelectorValue) {
dispatch('change', {
type: 'update',
address,
attribute,
value: await selectorValueAsValue(value)
} as AttributeUpdate);
async function updateEntry(oldEntry: UpEntry, value: SelectorValue) {
dispatch('change', [
{
type: 'delete',
address: oldEntry.address
},
{
type: 'create',
attribute: oldEntry.attribute,
value: await selectorValueAsValue(value)
}
]);
}
// Labelling
@ -268,10 +273,7 @@
class="cell value mark-value"
data-address={entry.value.t === 'Address' ? entry.value.c : undefined}
>
<Editable
value={entry.value}
on:edit={(ev) => updateEntry(entry.address, entry.attribute, ev.detail)}
>
<Editable value={entry.value} on:edit={(ev) => updateEntry(entry, ev.detail)}>
{#if entry.value.t === 'Address'}
<UpObject
link

View File

@ -1,20 +1,22 @@
import type { IValue } from '@upnd/upend/types';
export type WidgetChange =
export type WidgetOperation =
| AttributeCreate
| AttributeUpdate
| AttributeUpsert
| AttributeDelete
| EntryInAdd
| EntryInDelete;
export type WidgetChange = WidgetOperation | WidgetOperation[];
export interface AttributeCreate {
type: 'create';
attribute: string;
value: IValue;
}
export interface AttributeUpdate {
type: 'update';
export interface AttributeUpsert {
type: 'upsert';
attribute: string;
value: IValue;
}