Fix live journal hang on empty new logbooks.
Fast-path today's entry creation, add init timeout, defer auto-position GPS, and migrate logbook keys when the server returns a different id. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -87,8 +87,26 @@ type LiveModal =
|
||||
|
||||
const AUTO_POSITION_INTERVAL_MS = 3 * 60 * 60 * 1000
|
||||
const AUTO_POSITION_CHECK_MS = 60_000
|
||||
const AUTO_POSITION_START_DELAY_MS = 3000
|
||||
const LIVE_LOG_INIT_TIMEOUT_MS = 25_000
|
||||
const UNDO_TIMEOUT_MS = 5000
|
||||
|
||||
function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = window.setTimeout(() => reject(new Error(message)), ms)
|
||||
promise.then(
|
||||
(value) => {
|
||||
window.clearTimeout(timer)
|
||||
resolve(value)
|
||||
},
|
||||
(err) => {
|
||||
window.clearTimeout(timer)
|
||||
reject(err)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function hapticPulse() {
|
||||
navigator.vibrate?.(40)
|
||||
}
|
||||
@@ -186,13 +204,27 @@ export default function LiveLogView({
|
||||
const seq = ++initSeqRef.current
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
setEntryId(null)
|
||||
setEvents([])
|
||||
setYachtSails([])
|
||||
|
||||
if (!logbookId.trim()) {
|
||||
setError(t('logs.live_load_error'))
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const id = await findOrCreateTodayEntry(logbookId)
|
||||
const id = await withTimeout(
|
||||
findOrCreateTodayEntry(logbookId),
|
||||
LIVE_LOG_INIT_TIMEOUT_MS,
|
||||
t('logs.live_load_error')
|
||||
)
|
||||
if (seq !== initSeqRef.current) return
|
||||
setEntryId(id)
|
||||
|
||||
const masterKey = await getLogbookKey(logbookId) || getActiveMasterKey()
|
||||
if (masterKey) {
|
||||
const logbookKey = await getLogbookKey(logbookId)
|
||||
if (logbookKey) {
|
||||
const yacht = await db.yachts.get(logbookId)
|
||||
if (yacht) {
|
||||
try {
|
||||
@@ -200,7 +232,7 @@ export default function LiveLogView({
|
||||
yacht.encryptedData,
|
||||
yacht.iv,
|
||||
yacht.tag,
|
||||
masterKey
|
||||
logbookKey
|
||||
)
|
||||
if (decrypted?.sails && Array.isArray(decrypted.sails)) {
|
||||
setYachtSails(decrypted.sails as string[])
|
||||
@@ -216,18 +248,18 @@ export default function LiveLogView({
|
||||
if (loaded) {
|
||||
applyLoadedEntry(loaded)
|
||||
} else {
|
||||
throw new Error(i18n.t('logs.live_load_error'))
|
||||
throw new Error(t('logs.live_load_error'))
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (seq !== initSeqRef.current) return
|
||||
console.error('Failed to init live log:', err)
|
||||
setError(err instanceof Error ? err.message : i18n.t('logs.live_load_error'))
|
||||
setError(err instanceof Error ? err.message : t('logs.live_load_error'))
|
||||
} finally {
|
||||
if (seq === initSeqRef.current) {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}, [logbookId, applyLoadedEntry])
|
||||
}, [logbookId, applyLoadedEntry, t])
|
||||
|
||||
useEffect(() => {
|
||||
void runInit()
|
||||
@@ -263,7 +295,7 @@ export default function LiveLogView({
|
||||
|
||||
autoPositionBusyRef.current = true
|
||||
try {
|
||||
const coords = await getCurrentPosition()
|
||||
const coords = await getCurrentPosition(8000)
|
||||
await appendQuickEvent(logbookId, entryId, {
|
||||
gpsLat: coords.lat,
|
||||
gpsLng: coords.lng,
|
||||
@@ -277,8 +309,16 @@ export default function LiveLogView({
|
||||
}
|
||||
}
|
||||
|
||||
const interval = window.setInterval(() => void maybeAutoPosition(), AUTO_POSITION_CHECK_MS)
|
||||
return () => window.clearInterval(interval)
|
||||
let intervalRef: number | undefined
|
||||
const startTimer = window.setTimeout(() => {
|
||||
void maybeAutoPosition()
|
||||
intervalRef = window.setInterval(() => void maybeAutoPosition(), AUTO_POSITION_CHECK_MS)
|
||||
}, AUTO_POSITION_START_DELAY_MS)
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(startTimer)
|
||||
if (intervalRef !== undefined) window.clearInterval(intervalRef)
|
||||
}
|
||||
}, [entryId, loading, logbookId, refreshEntry, busy])
|
||||
|
||||
const runQuickAction = async (
|
||||
|
||||
@@ -214,6 +214,10 @@ export async function createLogbook(title: string): Promise<DecryptedLogbook> {
|
||||
|
||||
if (response.ok) {
|
||||
const serverLb = await response.json()
|
||||
if (serverLb.id !== localId) {
|
||||
await saveLogbookKey(serverLb.id, logbookKey)
|
||||
await db.logbookKeys.delete(localId)
|
||||
}
|
||||
await db.logbooks.put({
|
||||
id: serverLb.id,
|
||||
encryptedTitle: serverLb.encryptedTitle,
|
||||
|
||||
@@ -158,12 +158,14 @@ export async function createTodayEntry(logbookId: string): Promise<string> {
|
||||
const localEntries = await db.entries.where({ logbookId }).toArray()
|
||||
const decryptedEntries: Array<LogEntryTankSource & TravelDaySortable> = []
|
||||
|
||||
if (localEntries.length > 0) {
|
||||
for (const entry of localEntries) {
|
||||
const decrypted = await tryDecryptEntryPayload(entry, masterKey)
|
||||
if (decrypted) {
|
||||
decryptedEntries.push(decrypted as LogEntryTankSource & TravelDaySortable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decryptedEntries.sort(compareTravelDaysChronological)
|
||||
const previousEntry = decryptedEntries.at(-1) ?? null
|
||||
@@ -211,10 +213,19 @@ export async function createTodayEntry(logbookId: string): Promise<string> {
|
||||
}
|
||||
|
||||
export async function findOrCreateTodayEntry(logbookId: string): Promise<string> {
|
||||
await ensureLogbookKey(logbookId)
|
||||
const existing = await findTodayEntryId(logbookId)
|
||||
const id = logbookId.trim()
|
||||
if (!id) throw new Error('Logbook id required')
|
||||
|
||||
await ensureLogbookKey(id)
|
||||
|
||||
const entryCount = await db.entries.where({ logbookId: id }).count()
|
||||
if (entryCount === 0) {
|
||||
return createTodayEntry(id)
|
||||
}
|
||||
|
||||
const existing = await findTodayEntryId(id)
|
||||
if (existing) return existing
|
||||
return createTodayEntry(logbookId)
|
||||
return createTodayEntry(id)
|
||||
}
|
||||
|
||||
export interface AppendQuickEventResult {
|
||||
|
||||
Reference in New Issue
Block a user