Skip to content

Commit 6ba1051

Browse files
Stefan ErnstStefan Ernst
authored andcommitted
Cleaning up event listeners and moving them to runed, work item reload functionality, svelte 5 conversions, bugfix for mention picker
1 parent cb16b78 commit 6ba1051

54 files changed

Lines changed: 1610 additions & 1305 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/src/lib/components/DiagramModal.svelte

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
<script>
2-
import { onMount } from 'svelte';
2+
import { useEventListener } from 'runed';
33
import ExcalidrawEditor from './ExcalidrawEditor.svelte';
44
import Button from './Button.svelte';
55
import { api } from '../api.js';
66
import { themeStore } from '../stores/theme.svelte.js';
77
import { t } from '../stores/i18n.svelte.js';
88
9-
export let itemId;
10-
export let diagram = null; // null for new diagram, object for editing
11-
export let onClose = () => {};
12-
export let onSave = () => {};
9+
let { itemId, diagram = null, onClose = () => {}, onSave = () => {} } = $props();
1310
14-
let editorComponent;
15-
let diagramName = diagram ? diagram.name : t('components.diagram.untitled');
11+
let editorComponent = $state(null);
12+
let diagramName = $state(diagram ? diagram.name : t('components.diagram.untitled'));
1613
let initialData = null;
17-
let saving = false;
18-
let hasChanges = false;
14+
let saving = $state(false);
15+
let hasChanges = $state(false);
1916
2017
if (diagram && diagram.diagram_data) {
2118
try {
@@ -73,12 +70,7 @@
7370
}
7471
}
7572
76-
onMount(() => {
77-
window.addEventListener('keydown', handleKeyDown);
78-
return () => {
79-
window.removeEventListener('keydown', handleKeyDown);
80-
};
81-
});
73+
useEventListener(() => window, 'keydown', handleKeyDown);
8274
</script>
8375

8476
<!-- Modal overlay -->

frontend/src/lib/composables/useItemAttachments.svelte.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,21 @@ export function useItemAttachments(getItemId, showError = console.error) {
7777
}
7878

7979
/**
80-
* Handle attachment upload event from AttachmentList component
81-
* @param {CustomEvent} event - Upload event with detail { attachment, message }
80+
* Handle attachment upload event
81+
* Called when an attachment is uploaded
8282
*/
83-
async function handleUpload(_event) {
83+
async function handleUpload() {
8484
// Reload attachments to get updated pagination info
8585
if (isEnabled()) {
8686
await load(1, pageSize); // Go to first page to see new upload
8787
}
8888
}
8989

9090
/**
91-
* Handle attachment delete event from AttachmentList component
92-
* @param {CustomEvent} event - Delete event with attachment detail
91+
* Handle attachment delete
92+
* @param {Object} attachment - The attachment to delete
9393
*/
94-
async function handleDelete(event) {
95-
const attachment = event.detail;
96-
94+
async function handleDelete(attachment) {
9795
try {
9896
await api.attachments.delete(attachment.id);
9997

@@ -108,31 +106,30 @@ export function useItemAttachments(getItemId, showError = console.error) {
108106
}
109107

110108
/**
111-
* Handle page change event
112-
* @param {CustomEvent} event - Event with detail { page, itemsPerPage }
109+
* Handle page change
110+
* @param {Object} detail - { page, itemsPerPage }
113111
*/
114-
async function handlePageChange(event) {
112+
async function handlePageChange({ page, itemsPerPage }) {
115113
if (isEnabled()) {
116-
await load(event.detail.page, event.detail.itemsPerPage);
114+
await load(page, itemsPerPage);
117115
}
118116
}
119117

120118
/**
121-
* Handle page size change event
122-
* @param {CustomEvent} event - Event with detail { page, itemsPerPage }
119+
* Handle page size change
120+
* @param {Object} detail - { page, itemsPerPage }
123121
*/
124-
async function handlePageSizeChange(event) {
122+
async function handlePageSizeChange({ page, itemsPerPage }) {
125123
if (isEnabled()) {
126-
await load(event.detail.page, event.detail.itemsPerPage);
124+
await load(page, itemsPerPage);
127125
}
128126
}
129127

130128
/**
131129
* Upload files directly (from Attach button)
132-
* @param {CustomEvent} event - Event with detail { files }
130+
* @param {Object} detail - { files }
133131
*/
134-
async function uploadFiles(event) {
135-
const { files } = event.detail;
132+
async function uploadFiles({ files }) {
136133
if (!files || files.length === 0) return;
137134

138135
const itemId = getItemId();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { activityStore } from '../stores/activityStore.svelte.js';
2+
3+
const ACTIVE_INTERVAL = 30_000; // 30 seconds when user is active
4+
const IDLE_INTERVAL = 5 * 60_000; // 5 minutes when user is idle
5+
6+
/**
7+
* Composable that manages a polling interval for work item fetching,
8+
* adapting to user activity state.
9+
*
10+
* @param {Function} fetchFn - Async callback to fetch work items
11+
* @returns {{ poll: Function, isPolling: boolean, lastPollTime: number|null }}
12+
*/
13+
export function useWorkItemPoller(fetchFn) {
14+
let isPolling = $state(false);
15+
let lastPollTime = $state(null);
16+
let _timer = null;
17+
18+
async function poll() {
19+
if (isPolling) return;
20+
isPolling = true;
21+
try {
22+
await fetchFn();
23+
lastPollTime = Date.now();
24+
} catch (err) {
25+
console.warn('WorkItemPoller: poll failed', err);
26+
} finally {
27+
isPolling = false;
28+
}
29+
}
30+
31+
function _startTimer(interval) {
32+
_stopTimer();
33+
_timer = setInterval(() => {
34+
poll();
35+
}, interval);
36+
}
37+
38+
function _stopTimer() {
39+
if (_timer) {
40+
clearInterval(_timer);
41+
_timer = null;
42+
}
43+
}
44+
45+
// Reactive effect: switch interval based on activity
46+
$effect(() => {
47+
const idle = activityStore.isIdle;
48+
const interval = idle ? IDLE_INTERVAL : ACTIVE_INTERVAL;
49+
_startTimer(interval);
50+
51+
return () => {
52+
_stopTimer();
53+
};
54+
});
55+
56+
return {
57+
poll,
58+
get isPolling() { return isPolling; },
59+
get lastPollTime() { return lastPollTime; },
60+
};
61+
}

frontend/src/lib/dialogs/CreateModal.svelte

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
2-
import { createEventDispatcher, onMount } from 'svelte';
2+
import { createEventDispatcher } from 'svelte';
3+
import { useEventListener } from 'runed';
34
import { fade } from 'svelte/transition';
45
import { navigate, currentRoute } from '../router.js';
56
import { milestonesStore } from '../stores/milestones.js';
@@ -329,19 +330,10 @@
329330
}
330331
}
331332
332-
onMount(() => {
333-
window.addEventListener('open-create-modal', handleOpenCreateModal);
334-
window.addEventListener('set-create-type', handleSetCreateType);
335-
window.addEventListener('set-create-workspace', handleSetCreateWorkspace);
336-
window.addEventListener('set-create-parent', handleSetCreateParent);
337-
338-
return () => {
339-
window.removeEventListener('open-create-modal', handleOpenCreateModal);
340-
window.removeEventListener('set-create-type', handleSetCreateType);
341-
window.removeEventListener('set-create-workspace', handleSetCreateWorkspace);
342-
window.removeEventListener('set-create-parent', handleSetCreateParent);
343-
};
344-
});
333+
useEventListener(() => window, 'open-create-modal', handleOpenCreateModal);
334+
useEventListener(() => window, 'set-create-type', handleSetCreateType);
335+
useEventListener(() => window, 'set-create-workspace', handleSetCreateWorkspace);
336+
useEventListener(() => window, 'set-create-parent', handleSetCreateParent);
345337
</script>
346338
347339
{#if isOpen}

0 commit comments

Comments
 (0)