fix: Prevent signature alert loop when adding log events.
Stabilize dialog callbacks and dedupe signature-invalidation alerts so the UI no longer freezes after adding an event to a signed travel day. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -76,6 +76,8 @@ export default function LogEntryEditor({
|
|||||||
}: LogEntryEditorProps) {
|
}: LogEntryEditorProps) {
|
||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
const { showAlert, showConfirm } = useDialog()
|
const { showAlert, showConfirm } = useDialog()
|
||||||
|
const showAlertRef = useRef(showAlert)
|
||||||
|
showAlertRef.current = showAlert
|
||||||
|
|
||||||
// General details state
|
// General details state
|
||||||
const [date, setDate] = useState('')
|
const [date, setDate] = useState('')
|
||||||
@@ -145,6 +147,7 @@ export default function LogEntryEditor({
|
|||||||
const fileInputRef = useRef<HTMLInputElement | null>(null)
|
const fileInputRef = useRef<HTMLInputElement | null>(null)
|
||||||
const lockedContentHashRef = useRef<string | null>(null)
|
const lockedContentHashRef = useRef<string | null>(null)
|
||||||
const contentReadyRef = useRef(false)
|
const contentReadyRef = useRef(false)
|
||||||
|
const lastSignatureAlertHashRef = useRef<string | null>(null)
|
||||||
|
|
||||||
const applyTrackStats = (waypoints: SavedTrack['waypoints']) => {
|
const applyTrackStats = (waypoints: SavedTrack['waypoints']) => {
|
||||||
const stats = computeTrackStats(waypoints)
|
const stats = computeTrackStats(waypoints)
|
||||||
@@ -252,12 +255,15 @@ export default function LogEntryEditor({
|
|||||||
lockedContentHashRef.current = null
|
lockedContentHashRef.current = null
|
||||||
setSignSkipper('')
|
setSignSkipper('')
|
||||||
setSignCrew('')
|
setSignCrew('')
|
||||||
void showAlert(
|
if (lastSignatureAlertHashRef.current !== entryHash) {
|
||||||
t('logs.sign_cleared_re_sign'),
|
lastSignatureAlertHashRef.current = entryHash
|
||||||
t('logs.sign_cleared_re_sign_title')
|
void showAlertRef.current(
|
||||||
)
|
t('logs.sign_cleared_re_sign'),
|
||||||
|
t('logs.sign_cleared_re_sign_title')
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [entryHash, signSkipper, signCrew, readOnly, showAlert, t])
|
}, [entryHash, signSkipper, signCrew, readOnly, t])
|
||||||
|
|
||||||
const confirmSignWarning = useCallback(async (): Promise<boolean> => {
|
const confirmSignWarning = useCallback(async (): Promise<boolean> => {
|
||||||
return showConfirm(
|
return showConfirm(
|
||||||
@@ -355,6 +361,7 @@ export default function LogEntryEditor({
|
|||||||
setError(null)
|
setError(null)
|
||||||
lockedContentHashRef.current = null
|
lockedContentHashRef.current = null
|
||||||
contentReadyRef.current = false
|
contentReadyRef.current = false
|
||||||
|
lastSignatureAlertHashRef.current = null
|
||||||
try {
|
try {
|
||||||
if (readOnly && preloadedEntry) {
|
if (readOnly && preloadedEntry) {
|
||||||
setDate(preloadedEntry.date || '')
|
setDate(preloadedEntry.date || '')
|
||||||
@@ -1330,7 +1337,7 @@ export default function LogEntryEditor({
|
|||||||
style={{ width: 'auto', padding: '10px 20px', marginLeft: 'auto', display: 'flex' }}
|
style={{ width: 'auto', padding: '10px 20px', marginLeft: 'auto', display: 'flex' }}
|
||||||
>
|
>
|
||||||
<Plus size={16} />
|
<Plus size={16} />
|
||||||
Add Event Entry
|
{t('logs.add_event_btn')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useContext, useState, useRef } from 'react'
|
import React, { createContext, useContext, useState, useRef, useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
interface DialogContextType {
|
interface DialogContextType {
|
||||||
showAlert: (message: string, title?: string, confirmText?: string) => Promise<void>
|
showAlert: (message: string, title?: string, confirmText?: string) => Promise<void>
|
||||||
@@ -25,7 +25,7 @@ export function DialogProvider({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
const resolveRef = useRef<((val: any) => void) | null>(null)
|
const resolveRef = useRef<((val: any) => void) | null>(null)
|
||||||
|
|
||||||
const showAlert = (msg: string, headerTitle?: string, btnText?: string): Promise<void> => {
|
const showAlert = useCallback((msg: string, headerTitle?: string, btnText?: string): Promise<void> => {
|
||||||
setMessage(msg)
|
setMessage(msg)
|
||||||
setTitle(headerTitle || '')
|
setTitle(headerTitle || '')
|
||||||
setType('alert')
|
setType('alert')
|
||||||
@@ -35,9 +35,14 @@ export function DialogProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
resolveRef.current = resolve
|
resolveRef.current = resolve
|
||||||
})
|
})
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const showConfirm = (msg: string, headerTitle?: string, btnConfirm?: string, btnCancel?: string): Promise<boolean> => {
|
const showConfirm = useCallback((
|
||||||
|
msg: string,
|
||||||
|
headerTitle?: string,
|
||||||
|
btnConfirm?: string,
|
||||||
|
btnCancel?: string
|
||||||
|
): Promise<boolean> => {
|
||||||
setMessage(msg)
|
setMessage(msg)
|
||||||
setTitle(headerTitle || '')
|
setTitle(headerTitle || '')
|
||||||
setType('confirm')
|
setType('confirm')
|
||||||
@@ -48,26 +53,31 @@ export function DialogProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return new Promise<boolean>((resolve) => {
|
return new Promise<boolean>((resolve) => {
|
||||||
resolveRef.current = resolve
|
resolveRef.current = resolve
|
||||||
})
|
})
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = useCallback(() => {
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
if (resolveRef.current) {
|
if (resolveRef.current) {
|
||||||
resolveRef.current(type === 'confirm' ? true : undefined)
|
resolveRef.current(type === 'confirm' ? true : undefined)
|
||||||
resolveRef.current = null
|
resolveRef.current = null
|
||||||
}
|
}
|
||||||
}
|
}, [type])
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = useCallback(() => {
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
if (resolveRef.current) {
|
if (resolveRef.current) {
|
||||||
resolveRef.current(false)
|
resolveRef.current(false)
|
||||||
resolveRef.current = null
|
resolveRef.current = null
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
|
const contextValue = useMemo(
|
||||||
|
() => ({ showAlert, showConfirm }),
|
||||||
|
[showAlert, showConfirm]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogContext.Provider value={{ showAlert, showConfirm }}>
|
<DialogContext.Provider value={contextValue}>
|
||||||
{children}
|
{children}
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="custom-dialog-overlay" onClick={type === 'alert' ? handleConfirm : undefined}>
|
<div className="custom-dialog-overlay" onClick={type === 'alert' ? handleConfirm : undefined}>
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
"new_entry": "Neuer Reisetag",
|
"new_entry": "Neuer Reisetag",
|
||||||
"travel_details": "Reisedetails",
|
"travel_details": "Reisedetails",
|
||||||
"add_event": "Neuen Logbucheintrag hinzufügen",
|
"add_event": "Neuen Logbucheintrag hinzufügen",
|
||||||
|
"add_event_btn": "Ereignis hinzufügen",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"day_of_travel": "Tag der Reise / Reisetag",
|
"day_of_travel": "Tag der Reise / Reisetag",
|
||||||
"departure": "Start-Hafen (Reise von)",
|
"departure": "Start-Hafen (Reise von)",
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
"new_entry": "New Travel Day",
|
"new_entry": "New Travel Day",
|
||||||
"travel_details": "Travel Details",
|
"travel_details": "Travel Details",
|
||||||
"add_event": "Add Event Log Record",
|
"add_event": "Add Event Log Record",
|
||||||
|
"add_event_btn": "Add Event Entry",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"day_of_travel": "Day of Travel",
|
"day_of_travel": "Day of Travel",
|
||||||
"departure": "Departure Port (von)",
|
"departure": "Departure Port (von)",
|
||||||
|
|||||||
Reference in New Issue
Block a user