@kernhq/module-quire 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
toast,
|
|
18
18
|
} from '@kernhq/ui'
|
|
19
19
|
import { createQuery, useQueryClient } from '@tanstack/svelte-query'
|
|
20
|
+
import { untrack } from 'svelte'
|
|
20
21
|
import { getQuireApi } from '../api-instance.js'
|
|
21
22
|
import CommentsPanel from '../components/CommentsPanel.svelte'
|
|
22
23
|
import ConfirmDialog from '../components/ConfirmDialog.svelte'
|
|
@@ -148,19 +149,69 @@ $effect(() => {
|
|
|
148
149
|
dirty = false
|
|
149
150
|
})
|
|
150
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The title saves as you type, not only when you leave the field.
|
|
154
|
+
*
|
|
155
|
+
* It used to save on `blur` alone, and nothing else — so typing a name and then going somewhere
|
|
156
|
+
* without blurring first (a keyboard shortcut, ⌘K, closing the tab, any programmatic navigation)
|
|
157
|
+
* threw the name away and left the page called "Untitled" for ever. Measured against the live
|
|
158
|
+
* stack: fifteen seconds after typing, `mod_quire.pages.title` was still `''`; it only ever became
|
|
159
|
+
* the typed value on blur.
|
|
160
|
+
*
|
|
161
|
+
* A page's *body* has never had this problem, because it is a Y.Doc the collab service persists on
|
|
162
|
+
* its own schedule. The title is not — it is a plain column behind `pages.update` — so the schedule
|
|
163
|
+
* has to be written here. `docs/adr/0006` says the title should live in the Y.Doc beside the body
|
|
164
|
+
* for exactly this reason, and because two people renaming at once currently clobber each other;
|
|
165
|
+
* that is a larger change and this is not a substitute for it.
|
|
166
|
+
*/
|
|
167
|
+
const TITLE_SAVE_AFTER_MS = 700
|
|
168
|
+
let titleTimer: ReturnType<typeof setTimeout> | null = null
|
|
169
|
+
/* Set in the same tick as the call, because `isPending` arrives a render late and two saves would
|
|
170
|
+
race to write the same column in an order neither of them chose. */
|
|
171
|
+
let savingTitle = false
|
|
172
|
+
|
|
173
|
+
function queueTitleSave() {
|
|
174
|
+
if (titleTimer) clearTimeout(titleTimer)
|
|
175
|
+
titleTimer = setTimeout(() => {
|
|
176
|
+
titleTimer = null
|
|
177
|
+
void saveTitle()
|
|
178
|
+
}, TITLE_SAVE_AFTER_MS)
|
|
179
|
+
}
|
|
180
|
+
|
|
151
181
|
async function saveTitle() {
|
|
152
|
-
if (
|
|
182
|
+
if (titleTimer) {
|
|
183
|
+
clearTimeout(titleTimer)
|
|
184
|
+
titleTimer = null
|
|
185
|
+
}
|
|
186
|
+
if (!doc || !dirty || savingTitle) return
|
|
153
187
|
const next = title.trim()
|
|
154
188
|
if (next === doc.title) {
|
|
155
189
|
dirty = false
|
|
156
190
|
return
|
|
157
191
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
192
|
+
savingTitle = true
|
|
193
|
+
try {
|
|
194
|
+
await api.pages.update({ workspaceId, pageId, title: next })
|
|
195
|
+
dirty = false
|
|
196
|
+
await client.invalidateQueries({ queryKey: quireKeys.page(workspaceId, pageId) })
|
|
197
|
+
await client.invalidateQueries({ queryKey: quireKeys.tree(workspaceId, doc.spaceId) })
|
|
198
|
+
} finally {
|
|
199
|
+
savingTitle = false
|
|
200
|
+
}
|
|
162
201
|
}
|
|
163
202
|
|
|
203
|
+
/* Leaving the page mid-word must not lose the word. */
|
|
204
|
+
$effect(() => {
|
|
205
|
+
void pageId
|
|
206
|
+
return () => {
|
|
207
|
+
if (titleTimer) {
|
|
208
|
+
clearTimeout(titleTimer)
|
|
209
|
+
titleTimer = null
|
|
210
|
+
}
|
|
211
|
+
if (untrack(() => dirty)) void saveTitle()
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
164
215
|
async function archive(archived: boolean) {
|
|
165
216
|
if (!doc) return
|
|
166
217
|
await api.pages.archive({ workspaceId, pageId, archived })
|
|
@@ -402,6 +453,7 @@ async function undoTrash(workspace: string, id: string, spaceId: string, title:
|
|
|
402
453
|
oninput={(e) => {
|
|
403
454
|
title = (e.currentTarget as HTMLInputElement).value
|
|
404
455
|
dirty = true
|
|
456
|
+
queueTitleSave()
|
|
405
457
|
}}
|
|
406
458
|
onblur={saveTitle}
|
|
407
459
|
onkeydown={(e) => {
|