Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
16df2e4
move DEFAULT_DELEGATES into `$lib/delegate_presets`
endorpersand Nov 28, 2024
3eefa94
add Dexie dependency
endorpersand Nov 28, 2024
bb0da94
start DB setup
endorpersand Nov 28, 2024
ed6c244
attach delegate database to settings page
endorpersand Nov 28, 2024
5800b4c
impl del del
endorpersand Nov 29, 2024
268c9b5
connect to roll-call
endorpersand Nov 29, 2024
8060ec3
bind to stats page
endorpersand Nov 29, 2024
bea3384
delete now-unused `mapObj` util
endorpersand Nov 29, 2024
404527a
db del utilities
endorpersand Nov 29, 2024
dacbfc5
comment $lib/db/del
endorpersand Nov 29, 2024
cd2a6c8
util
endorpersand Nov 29, 2024
6e8a3ef
cleanup with store utils
endorpersand Nov 29, 2024
94f429f
hell refactoring
endorpersand Nov 29, 2024
09c30ff
ax old stores
endorpersand Nov 29, 2024
38db4d1
more cleanup
endorpersand Nov 29, 2024
1d2c8fe
replace `matchesName` with `nameEq` util
endorpersand Nov 29, 2024
e9897fd
styling changes
endorpersand Nov 29, 2024
2d27e38
return DelLabel's fallback
endorpersand Nov 29, 2024
e6818c1
fix reset buttons not interacting with DB
endorpersand Nov 29, 2024
f0f5acd
input bugs
endorpersand Nov 29, 2024
97fa6e4
move Delegate to types.d.ts
endorpersand Dec 1, 2024
6147e44
load db once per session, rather than once per page
endorpersand Dec 1, 2024
637b239
drop unnecessary uses of svelte/store
endorpersand Dec 1, 2024
509a8af
idk what this does but it surely can only be good
endorpersand Dec 2, 2024
62301b1
simplify $lib/stores
endorpersand Dec 2, 2024
9f42c6a
merge temp Stores context into SessionData context
endorpersand Dec 2, 2024
f366f28
Merge branch 'main' into indexed-db-storage
endorpersand Dec 20, 2024
ab89940
Remove use of writableTableStore
endorpersand Dec 26, 2024
4a94510
fix from merge
endorpersand Dec 27, 2024
b4845f6
Rename db/del to db/delegates
endorpersand Dec 28, 2024
27e2d6c
use mapped classes
endorpersand Dec 28, 2024
13f9968
move utils into Delegate helpers
endorpersand Dec 28, 2024
0e323ae
use get/set here
endorpersand Dec 28, 2024
d0f821a
implement settings in db
endorpersand Dec 30, 2024
8b23c49
Merge branch 'main' into indexed-db-storage
endorpersand Dec 30, 2024
94aa623
rename $lib/db/settings to $lib/db/keyval, add resetSettings helper
endorpersand Dec 30, 2024
e5e2c19
cleaner inputifyMotion?
endorpersand Dec 30, 2024
5387801
update queryStore
endorpersand Dec 30, 2024
1c716b5
impl SessionData db 1
endorpersand Dec 31, 2024
a57b26e
obliterate the final bit of $lib/stores
endorpersand Dec 31, 2024
0e86f4d
encapsulate `getKVStore` in $lib/db
endorpersand Dec 31, 2024
0a5acf6
move things around for SessionData DB
endorpersand Dec 31, 2024
e279175
integrate SessionData into DB fully
endorpersand Dec 31, 2024
9927ed8
remove no longer needed dependency
endorpersand Dec 31, 2024
4c50816
add multi-sessioning
endorpersand Dec 31, 2024
41bfa74
Implement multi-sessioning in stats page
endorpersand Jan 1, 2025
e406fa7
Allow resetAllSettings to reset all sessions // minor styling change
endorpersand Jan 6, 2025
5d501fa
allow current motion's state to persist
endorpersand Jan 6, 2025
24fb767
Merge branch 'main' into indexed-db-storage
endorpersand Jan 7, 2025
fb4da52
fix merge errors
endorpersand Jan 7, 2025
f4f5293
remove useless $state
endorpersand Jan 7, 2025
a162b49
change export stats tab
endorpersand Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"type": "module",
"dependencies": {
"@floating-ui/dom": "^1.6.12",
"dexie": "^4.0.10",
"svelte-dnd-action": "^0.9.53",
"svelte-persisted-store": "^0.12.0",
"zod": "^3.24.1"
},
"overrides": {
Expand Down
21 changes: 8 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions src/lib/components/MotionForm.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<script lang="ts">
import LabeledSlideToggle from "$lib/components/LabeledSlideToggle.svelte";
import DelPopup, { defaultPlaceholder, defaultPopupSettings } from "$lib/components/del-input/DelPopup.svelte";
import { getSessionContext } from "$lib/context/index.svelte";
import { createMotionSchema, inputifyMotion, MOTION_FIELDS, MOTION_LABELS } from "$lib/motions/definitions";
import { formatValidationError } from "$lib/motions/form_validation";
import type { MotionInput, MotionInputWithFields } from "$lib/motions/types";
import { getSessionDataContext } from "$lib/stores/session";
import { addColons, parseTime } from "$lib/util/time";
import type { Motion } from "$lib/types";

import type { z } from "zod";
import { popup } from "@skeletonlabs/skeleton";
import { type Snippet } from 'svelte';

const { settings: { delegateAttributes }, presentDelegates, selectedMotion } = getSessionDataContext();
const motionSchema = createMotionSchema($delegateAttributes, $presentDelegates);
const { selectedMotion, delegates } = getSessionContext();
const motionSchema = $derived(createMotionSchema($delegates));
const defaultInputMotion = () => ({ id: crypto.randomUUID(), kind: "mod" } satisfies MotionInput);
const resetInputErrors = () => { inputError = undefined };

Expand All @@ -34,11 +34,13 @@
// The input after the delegate input.
let afterDel: HTMLElement | undefined = $state();

let noDelegatesPresent = $derived($delegates.every(d => !d.isPresent()));

function submitMotion(e: SubmitEvent) {
e.preventDefault();

if (inputMotion.kind === "rr") {
inputMotion.totalSpeakers = $presentDelegates.length.toString();
inputMotion.totalSpeakers = $delegates.filter(d => d.isPresent()).length.toString();
}

// Filter out any keys that aren't the correct kind:
Expand Down Expand Up @@ -87,7 +89,7 @@
// If extension, disable "topic" and "speakingTime":
$effect(() => {
if (isExtending(inputMotion)) {
let inputified = inputifyMotion($selectedMotion!, $delegateAttributes);
let inputified = inputifyMotion($selectedMotion!);

if ("topic" in inputified) (inputMotion as any).topic = inputified.topic;
if ("speakingTime" in inputified) (inputMotion as any).speakingTime = inputified.speakingTime;
Expand Down Expand Up @@ -130,7 +132,7 @@
bind:value={inputMotion.delegate}
required
use:popup={{...defaultPopupSettings("delegateInputPopup"), event: "focus-click"}}
{...defaultPlaceholder($presentDelegates.length === 0)}
{...defaultPlaceholder(noDelegatesPresent)}
>
</label>
<label class="label">
Expand Down Expand Up @@ -213,8 +215,7 @@
<DelPopup
popupID="delegateInputPopup"
bind:input={inputMotion.delegate}
delegates={$delegateAttributes}
presentDelegates={$presentDelegates}
delegates={$delegates}
on:selection={e => {inputMotion.delegate = e.detail.label; resetInputErrors(); afterDel?.focus()}}
/>
</form>
74 changes: 39 additions & 35 deletions src/lib/components/SpeakerList.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<script lang="ts">
import DelLabel from "$lib/components/del-label/DelLabel.svelte";
import DelPopup, { defaultPlaceholder, defaultPopupSettings } from "$lib/components/del-input/DelPopup.svelte";
import { formatValidationError, nonEmptyString } from "$lib/motions/form_validation";
import type { DelegateAttrs, Speaker } from "$lib/types";
import { type Delegate, findDelegate } from "$lib/db/delegates";
import { formatValidationError } from "$lib/motions/form_validation";
import type { DelegateID, Speaker, SpeakerEntryID } from "$lib/types";
import { getDndItemId, isDndShadow, processDrag } from "$lib/util/dnd";
import { triggerConfirmModal } from "$lib/util";

import { type z } from "zod";
import { z } from "zod";
import Icon from "@iconify/svelte";
import { getModalStore, popup } from "@skeletonlabs/skeleton";
import { tick, untrack } from "svelte";
import { flip } from "svelte/animate";
import { dragHandle, dragHandleZone } from "svelte-dnd-action";
import { getDndItemId, isDndShadow, processDrag } from "$lib/util/dnd";
import { triggerConfirmModal } from "$lib/util";

let dfltControlsInput: string = $state("");
let dfltControlsError: string | undefined = $state(undefined);
Expand All @@ -24,11 +25,9 @@
*/
order?: Speaker[];
/**
* A mapping of key to their delegate attributes.
*
* If no entry exists for a given key, the speaker's key is displayed.
* The list of all delegates recognized by this component.
*/
delegates?: Record<string, DelegateAttrs>;
delegates?: Delegate[];
/**
* If defined, this creates control options to allow for adding
* and removing speakers from the speakers' list.
Expand All @@ -38,32 +37,35 @@
* 2. A custom control is implemented instead
*/
useDefaultControls?: {
presentDelegates: string[],
validator: z.ZodType<string, any, any> | ((dels: Record<string, DelegateAttrs>, presentDels: string[]) => z.ZodType<string, any, any>),
validator: z.ZodType<DelegateID, any, any> | ((dels: Delegate[]) => z.ZodType<DelegateID, any, any>),
popupID?: string
} | undefined;
onBeforeSpeakerUpdate?: ((oldSpeaker: Speaker | undefined, newSpeaker: Speaker | undefined) => unknown) | undefined;
onMarkComplete?: ((key: string, isRepeat: boolean) => unknown) | undefined;
onMarkComplete?: ((key: DelegateID, isRepeat: boolean) => unknown) | undefined;
}

let {
order = $bindable([]),
delegates = {},
delegates = [],
useDefaultControls = undefined,
onBeforeSpeakerUpdate = undefined,
onMarkComplete = undefined
}: Props = $props();

// A clone of order used solely for use:dragHandleZone
let dndItems = $state($state.snapshot(order));
$effect(() => { dndItems = order; });

// Special IDs to track:
let selectedSpeakerId: string | undefined = $state(undefined);
let selectedSpeakerId = $state<SpeakerEntryID>();
// A mapping from IDs to Speakers:
let orderMap = $derived(Object.fromEntries(
order.filter(s => typeof s.id === "string" && s.id)
.map((speaker, i) => [getDndItemId(speaker), { speaker, i }])
));

// List item elements per order item
let liElements = new Map<string, HTMLLIElement>();
let liElements = new Map<SpeakerEntryID, HTMLLIElement>();

// Readonly values
export function isAllDone() {
Expand All @@ -85,14 +87,11 @@

}
//
function getLabel(key: string) {
return delegates?.[key]?.name ?? key;
}
function findSpeaker(speakerId: string | undefined): Speaker | undefined {
function findSpeaker(speakerId: SpeakerEntryID | undefined): Speaker | undefined {
if (typeof speakerId === "undefined") return;
return orderMap[speakerId]?.speaker;
}
function markComplete(speakerId: string | undefined) {
function markComplete(speakerId: SpeakerEntryID | undefined) {
let speaker = findSpeaker(speakerId);
if (typeof speaker !== "undefined") {
onMarkComplete?.(speaker.key, speaker.completed);
Expand Down Expand Up @@ -183,12 +182,16 @@
map.set(key, el);
return { destroy() { map.delete(key); } }
};
let noDelegatesPresent = $derived(delegates.every(d => !d.isPresent()));

// Properties which are only used with default controls:
let validator = $derived(
typeof useDefaultControls?.validator === "function"
? useDefaultControls?.validator(delegates, useDefaultControls.presentDelegates)
: useDefaultControls?.validator ?? nonEmptyString()
);
let validator = $derived.by(() => {
if (typeof useDefaultControls?.validator === "function") {
return useDefaultControls.validator(delegates);
} else {
return useDefaultControls?.validator ?? z.never();
}
});
</script>
<script module lang="ts">
/**
Expand All @@ -197,7 +200,7 @@
* @param completed Whether this speaker has finished talking, by default false
* @return the speaker object
*/
export function createSpeaker(key: string, completed: boolean = false): Speaker {
export function createSpeaker(key: DelegateID, completed: boolean = false): Speaker {
return { key, completed, id: crypto.randomUUID() }
}
</script>
Expand All @@ -208,7 +211,7 @@
</h4>

<ol class="p-2 list overflow-y-auto grid grid-cols-[auto_auto_1fr_auto] auto-rows-min flex-grow" use:dragHandleZone={{
items: order,
items: dndItems,
flipDurationMs: 150,
dropTargetStyle: {},
transformDraggedElement: (el, data, index) => {
Expand All @@ -219,14 +222,16 @@
}
}
}}
onconsider={(e) => order = processDrag(e)}
onfinalize={(e) => order = processDrag(e)}
onconsider={(e) => dndItems = processDrag(e)}
onfinalize={(e) => order = dndItems = processDrag(e)}
aria-labelledby="speaker-list-header"
>
{#each order as speaker, i (speaker.id)}
{@const speakerLabel = getLabel(speaker.key)}
{#each dndItems as speaker, i (speaker.id)}
{@const selected = speaker.id === selectedSpeakerId}
{@const shadow = isDndShadow(speaker)}
{@const delAttrs = findDelegate(delegates, speaker.key)}
{@const speakerLabel = delAttrs?.name ?? "unknown"}

<li
class="!grid grid-cols-subgrid col-span-4 dnd-list-item"
class:!visible={shadow}
Expand All @@ -250,7 +255,7 @@
aria-label="Select {speakerLabel}"
aria-pressed={selected}
>
<DelLabel key={speaker.key} attrs={delegates[speaker.key]} inline />
<DelLabel attrs={delAttrs} fallbackName={speakerLabel} inline />
</button>
<div class="btn-icon">
<button
Expand Down Expand Up @@ -282,13 +287,13 @@
class:input-error={error}
bind:value={dfltControlsInput}
use:popup={{ ...defaultPopupSettings(popupID), placement: "left-end", event: "focus-click" }}
{...defaultPlaceholder(useDefaultControls.presentDelegates.length === 0)}
{...defaultPlaceholder(noDelegatesPresent)}
/>
<div class="ml-2">
<button
type="submit"
class="btn btn-icon variant-filled-primary"
disabled={useDefaultControls.presentDelegates.length === 0}
disabled={noDelegatesPresent}
aria-label="Add to Speakers List"
title="Add to Speakers List"
>
Expand Down Expand Up @@ -326,7 +331,6 @@
{popupID}
bind:input={dfltControlsInput}
{delegates}
presentDelegates={useDefaultControls.presentDelegates}
on:selection={e => addSpeaker(e.detail.label, true)}
/>
{/if}
Expand Down
20 changes: 9 additions & 11 deletions src/lib/components/del-input/DelAutocomplete.svelte
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
<script lang="ts">
import type { DelegateAttrs } from "$lib/types";
import type { Delegate } from "$lib/db/delegates";
import { Autocomplete, type AutocompleteOption } from "@skeletonlabs/skeleton";

interface Props {
input: string | undefined;
delegates: Record<string, DelegateAttrs>;
presentDelegates: string[];
delegates: Delegate[];
maxHeight?: string;
}

let {
input = $bindable(),
delegates,
presentDelegates,
maxHeight = "max-h-96"
}: Props = $props();

let options = $derived(
Array.from(presentDelegates)
.filter(k => typeof delegates[k] !== "undefined")
.map((k) => ({
value: k,
label: delegates[k].name,
keywords: delegates[k].aliases.join(",")
}) satisfies AutocompleteOption<string>)
delegates
.filter(d => d.isPresent())
.map(d => ({
value: d.id,
label: d.name,
keywords: d.aliases.join(",")
}) satisfies AutocompleteOption<number>)
);
</script>

Expand Down
Loading