| @@ -22,6 +22,8 @@ export const store = reactive({ |
| 22 | const base = (pid: string) => `/api/projects/${pid}`; | 22 | const base = (pid: string) => `/api/projects/${pid}`; |
| 23 | | 23 | |
| 24 | let pending: ProjectPatch = {}; | 24 | let pending: ProjectPatch = {}; |
| | 25 | +/** envoyé, pas encore enregistré : une relecture partie avant ne doit pas le défaire */ |
| | 26 | +let sending: ProjectPatch = {}; |
| 25 | let flushTimer: ReturnType<typeof setTimeout> | undefined; | 27 | let flushTimer: ReturnType<typeof setTimeout> | undefined; |
| 26 | let pollTimer: ReturnType<typeof setTimeout> | undefined; | 28 | let pollTimer: ReturnType<typeof setTimeout> | undefined; |
| 27 | | 29 | |
| @@ -42,7 +44,7 @@ function applyPatch(p: Project, changes: ProjectPatch): void { |
| 42 | | 44 | |
| 43 | /** Remplace le projet affiché par ce que renvoie le serveur, sans perdre ce qui n'est pas encore envoyé. */ | 45 | /** Remplace le projet affiché par ce que renvoie le serveur, sans perdre ce qui n'est pas encore envoyé. */ |
| 44 | export function receive(view: Project): void { | 46 | export function receive(view: Project): void { |
| 45 | - if (store.project && store.project.id === view.id) applyPatch(view, pending); | 47 | + if (store.project && store.project.id === view.id) applyPatch(view, { ...sending, ...pending }); |
| 46 | store.project = view; | 48 | store.project = view; |
| 47 | const i = store.list.findIndex((p) => p.id === view.id); | 49 | const i = store.list.findIndex((p) => p.id === view.id); |
| 48 | if (i >= 0) store.list[i] = { ...store.list[i], title: view.title, status: view.status, brief: view.brief }; | 50 | if (i >= 0) store.list[i] = { ...store.list[i], title: view.title, status: view.status, brief: view.brief }; |
| @@ -56,6 +58,7 @@ export async function open(pid: string): Promise<void> { |
| 56 | try { | 58 | try { |
| 57 | const view = await api<Project>(base(pid)); | 59 | const view = await api<Project>(base(pid)); |
| 58 | pending = {}; | 60 | pending = {}; |
| | 61 | + sending = {}; |
| 59 | store.project = view; | 62 | store.project = view; |
| 60 | schedulePoll(); | 63 | schedulePoll(); |
| 61 | // dernier projet ouvert : c'est lui que l'accueil propose de reprendre | 64 | // dernier projet ouvert : c'est lui que l'accueil propose de reprendre |
| @@ -86,10 +89,15 @@ export async function flush(): Promise<void> { |
| 86 | if (!p || !Object.keys(pending).length) return; | 89 | if (!p || !Object.keys(pending).length) return; |
| 87 | const changes = pending; | 90 | const changes = pending; |
| 88 | pending = {}; | 91 | pending = {}; |
| | 92 | + sending = { ...sending, ...changes }; |
| 89 | try { | 93 | try { |
| 90 | receive(await patchJson<Project>(base(p.id), changes)); | 94 | receive(await patchJson<Project>(base(p.id), changes)); |
| 91 | } catch (e) { | 95 | } catch (e) { |
| 92 | toast(t("project.saveFailed", { error: (e as Error).message }), true); | 96 | toast(t("project.saveFailed", { error: (e as Error).message }), true); |
| | 97 | + } finally { |
| | 98 | + for (const key of Object.keys(changes) as (keyof ProjectPatch)[]) { |
| | 99 | + if (sending[key] === changes[key]) delete sending[key]; |
| | 100 | + } |
| 93 | } | 101 | } |
| 94 | } | 102 | } |
| 95 | | 103 | |
| @@ -118,6 +126,7 @@ export async function createProject(body: unknown): Promise<Project> { |
| 118 | const view = await post<Project>("/api/projects", body); | 126 | const view = await post<Project>("/api/projects", body); |
| 119 | store.project = view; | 127 | store.project = view; |
| 120 | pending = {}; | 128 | pending = {}; |
| | 129 | + sending = {}; |
| 121 | await loadList(); | 130 | await loadList(); |
| 122 | return view; | 131 | return view; |
| 123 | } | 132 | } |