mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
48b1e879d2
Holding later queued writes behind a parked one only ordered writes that were in the queue. Online, the repos send an edit straight to the server, so an edit made while an older write to the same visit, tour or driving settings was backing off or parked landed first, and Try again (or the next flush) then replayed the older write over it. Those writes carry no version token, so nothing refused the stale replay. The repos of visits, day clears, tours and driving settings now ask the queue before an online write. While an older write to the same entity is pending, in flight, parked or a conflict, the new write is queued too, with the same optimistic Dexie write as offline, and a flush is started so it goes out as soon as the one ahead of it does. When the driving settings left the cache since the older write, they are read in first. Places and packing items keep their direct online path: they send the base token, so a stale replay is refused with a 409 and becomes a conflict. What this does not cover, and the docs now say so: a collaborator's change to a token-less entity is overwritten by the retried write, as by any replayed offline write of those resources, and the planner's two time edits that call the API directly are not held. The wording of the parking commit also named budget items, which never go through the queue.
102 lines
4.5 KiB
TypeScript
102 lines
4.5 KiB
TypeScript
import { assignmentSchema } from '@trek/shared'
|
|
import { saveAssignmentEndDay } from '../api/assignmentEndDay'
|
|
import { assignmentsApi } from '../api/client'
|
|
import { offlineDb } from '../db/offlineDb'
|
|
import { cacheAssignment } from '../db/cacheAssignment'
|
|
import { generateUUID, mutationQueue } from '../sync/mutationQueue'
|
|
import type { Assignment } from '../types'
|
|
|
|
/** Start and End of one visit, as the time route stores them: null is no time. */
|
|
export interface AssignmentTimes {
|
|
place_time: string | null
|
|
end_time: string | null
|
|
}
|
|
|
|
export const assignmentRepo = {
|
|
/**
|
|
* Empties a day (#2470). Offline the cached day loses its places right away and the
|
|
* clear waits in the queue, where the server applies it to whatever the day holds on
|
|
* replay, so a place added meanwhile by someone else goes with the rest.
|
|
*/
|
|
async clearDay(tripId: number | string, dayId: number): Promise<void> {
|
|
if (!(await mutationQueue.mustQueue('dayAssignments', dayId))) {
|
|
await assignmentsApi.clearDay(tripId, dayId)
|
|
await offlineDb.days.where('id').equals(dayId).modify(day => { day.assignments = [] })
|
|
return
|
|
}
|
|
await offlineDb.transaction('rw', offlineDb.days, offlineDb.mutationQueue, async () => {
|
|
await mutationQueue.enqueue({
|
|
id: generateUUID(), tripId: Number(tripId), method: 'DELETE',
|
|
url: `/trips/${tripId}/days/${dayId}/assignments`,
|
|
body: undefined, resource: 'dayAssignments', entityId: dayId,
|
|
})
|
|
await offlineDb.days.where('id').equals(dayId).modify(day => { day.assignments = [] })
|
|
})
|
|
mutationQueue.sendSoon()
|
|
},
|
|
|
|
async setEndDay(tripId: number | string, assignment: Assignment, endDay: boolean): Promise<Assignment> {
|
|
if (!(await mutationQueue.mustQueue('assignments', assignment.id))) {
|
|
const saved = await saveAssignmentEndDay(tripId, assignment.id, { end_day: endDay })
|
|
await cacheAssignment(saved)
|
|
return saved
|
|
}
|
|
const updated = { ...assignment, end_day: endDay }
|
|
await offlineDb.transaction('rw', offlineDb.days, offlineDb.mutationQueue, async () => {
|
|
await mutationQueue.enqueue({
|
|
id: generateUUID(), tripId: Number(tripId), method: 'PUT',
|
|
url: `/trips/${tripId}/assignments/${assignment.id}/end-day`,
|
|
body: { end_day: endDay }, resource: 'assignments', entityId: assignment.id,
|
|
})
|
|
await cacheAssignment(updated)
|
|
})
|
|
mutationQueue.sendSoon()
|
|
return updated
|
|
},
|
|
|
|
/** Takes a stop out of the day's route or puts it back (#2532), offline too. */
|
|
async setRouteExcluded(tripId: number | string, assignment: Assignment, excluded: boolean): Promise<Assignment> {
|
|
if (!(await mutationQueue.mustQueue('assignments', assignment.id))) {
|
|
const saved = assignmentSchema.parse((await assignmentsApi.setRouteExcluded(tripId, assignment.id, excluded)).assignment)
|
|
await cacheAssignment(saved)
|
|
return saved
|
|
}
|
|
const updated = { ...assignment, route_excluded: excluded }
|
|
await offlineDb.transaction('rw', offlineDb.days, offlineDb.mutationQueue, async () => {
|
|
await mutationQueue.enqueue({
|
|
id: generateUUID(), tripId: Number(tripId), method: 'PUT',
|
|
url: `/trips/${tripId}/assignments/${assignment.id}/route`,
|
|
body: { excluded }, resource: 'assignments', entityId: assignment.id,
|
|
})
|
|
await cacheAssignment(updated)
|
|
})
|
|
mutationQueue.sendSoon()
|
|
return updated
|
|
},
|
|
|
|
/**
|
|
* A visit's own Start and End, the pair the time route takes.
|
|
*
|
|
* The route writes both columns every time, so a caller changing one of them hands the
|
|
* other in as it stands; leaving it out would clear it.
|
|
*/
|
|
async setTimes(tripId: number | string, assignment: Assignment, times: AssignmentTimes): Promise<Assignment> {
|
|
if (!(await mutationQueue.mustQueue('assignments', assignment.id))) {
|
|
const saved = assignmentSchema.parse((await assignmentsApi.updateTime(tripId, assignment.id, times)).assignment)
|
|
await cacheAssignment(saved)
|
|
return saved
|
|
}
|
|
const updated = { ...assignment, assignment_time: times.place_time, assignment_end_time: times.end_time }
|
|
await offlineDb.transaction('rw', offlineDb.days, offlineDb.mutationQueue, async () => {
|
|
await mutationQueue.enqueue({
|
|
id: generateUUID(), tripId: Number(tripId), method: 'PUT',
|
|
url: `/trips/${tripId}/assignments/${assignment.id}/time`,
|
|
body: times, resource: 'assignments', entityId: assignment.id,
|
|
})
|
|
await cacheAssignment(updated)
|
|
})
|
|
mutationQueue.sendSoon()
|
|
return updated
|
|
},
|
|
}
|