fix issues
This commit is contained in:
+15
-11
@@ -1208,7 +1208,13 @@ export default function App() {
|
||||
}
|
||||
return out.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
function wkSend(rawText: string) { setWkSent(''); WinkeyerSend(resolveCW(rawText)).catch((e) => setError(String(e?.message ?? e))); }
|
||||
function wkSend(rawText: string) {
|
||||
setWkSent('');
|
||||
WinkeyerSend(resolveCW(rawText)).catch((e) => setError(String(e?.message ?? e)));
|
||||
// <LOGQSO> in a macro (e.g. "73 TU <LOGQSO>") logs the contact after sending.
|
||||
// resolveCW already strips the token from the keyed text (unknown var → "").
|
||||
if (/<LOGQSO>/i.test(rawText)) void save();
|
||||
}
|
||||
function wkSendMacro(i: number) { const m = wkMacros[i]; if (m) wkSend(m.text); }
|
||||
wkSendMacroRef.current = wkSendMacro;
|
||||
// send-on-type: key the typed chars verbatim (no variable substitution).
|
||||
@@ -1298,6 +1304,7 @@ export default function App() {
|
||||
const loggedDxcc = typeof payload.dxcc === 'number' ? payload.dxcc : 0;
|
||||
await AddQSO(payload);
|
||||
resetEntry();
|
||||
callsignRef.current?.focus(); // return focus to the call field, wherever it was (e.g. Name)
|
||||
await refresh();
|
||||
// Refresh the Worked-before matrix so the just-logged band/mode flips to
|
||||
// "worked" — resetEntry cleared it, so re-fetch for the logged call (a
|
||||
@@ -1832,12 +1839,12 @@ export default function App() {
|
||||
);
|
||||
const rstTxBlock = (
|
||||
<div className="flex flex-col w-20"><Label className="mb-1 h-3.5">RST tx</Label>
|
||||
<Combobox value={rstSent} options={rstOptions(mode, rstLists)} onChange={(v) => { setRstSent(v); rstUserEditedRef.current = true; }} />
|
||||
<Combobox value={rstSent} options={rstOptions(mode, rstLists)} allowFreeText commitOnType onChange={(v) => { setRstSent(v); rstUserEditedRef.current = true; }} />
|
||||
</div>
|
||||
);
|
||||
const rstRxBlock = (
|
||||
<div className="flex flex-col w-20"><Label className="mb-1 h-3.5">RST rx</Label>
|
||||
<Combobox value={rstRcvd} options={rstOptions(mode, rstLists)} onChange={(v) => { setRstRcvd(v); rstUserEditedRef.current = true; }} />
|
||||
<Combobox value={rstRcvd} options={rstOptions(mode, rstLists)} allowFreeText commitOnType onChange={(v) => { setRstRcvd(v); rstUserEditedRef.current = true; }} />
|
||||
</div>
|
||||
);
|
||||
// Deferred-entry date: only shown when the start time is locked (back-entering
|
||||
@@ -2372,17 +2379,14 @@ export default function App() {
|
||||
|
||||
|
||||
{/* "You have been spotted" banner — shows when our own callsign appears
|
||||
in a cluster spot (Log4OM-style). Floated as a bottom-center overlay
|
||||
so it never shifts the layout (push-down / spring-back) and never
|
||||
covers the entry fields; auto-hides 3s after the last self-spot. */}
|
||||
in a cluster spot. Floated top-centre (with the other notifications),
|
||||
never shifts the layout; auto-hides 3s after the last self-spot. */}
|
||||
{!compact && selfSpot && (
|
||||
<div className="fixed bottom-16 left-1/2 -translate-x-1/2 z-[100] flex items-center gap-2 rounded-lg border border-amber-300 bg-amber-100 text-amber-900 px-3.5 py-2 text-xs shadow-lg animate-in fade-in slide-in-from-bottom-2">
|
||||
<div className="fixed top-12 left-1/2 -translate-x-1/2 z-[100] flex items-center gap-2 rounded-lg border border-amber-300 bg-amber-100 text-amber-900 px-3.5 py-2 text-xs shadow-lg animate-in fade-in slide-in-from-top-2">
|
||||
<RadioTower className="size-3.5 shrink-0" />
|
||||
<span>
|
||||
You've been spotted by <strong className="font-mono">{selfSpot.spotter || '?'}</strong>
|
||||
{' '}on <strong className="font-mono">{selfSpot.freqKHz?.toFixed(1)} kHz</strong>
|
||||
{selfSpot.band ? ` (${selfSpot.band})` : ''}
|
||||
{selfSpot.comment ? <span className="text-amber-800"> — {selfSpot.comment}</span> : null}
|
||||
Spotted by <strong className="font-mono">{selfSpot.spotter || '?'}</strong>
|
||||
{selfSpot.comment ? <span className="text-amber-800"> with {selfSpot.comment}</span> : null}
|
||||
</span>
|
||||
<div className="flex-1" />
|
||||
<button className="text-amber-700 hover:text-amber-900" title="Dismiss" onClick={() => setSelfSpot(null)}>
|
||||
|
||||
@@ -1836,7 +1836,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
<div className="border-t border-border/60 pt-3">
|
||||
<Label className="text-sm font-medium">CW message macros (F1…)</Label>
|
||||
<p className="text-[11px] text-muted-foreground mb-2">
|
||||
Use variables: <span className="font-mono"><MY_CALL> <CALL> <STX> <STRX> <MY_NAME> <HIS_NAME> <MY_QTH> <GRID> <CONT_TX> <n></span> (cut numbers: 9→N, 0→T). <span className="font-mono">*</span>=my call, <span className="font-mono">!</span>=his call.
|
||||
Use variables: <span className="font-mono"><MY_CALL> <CALL> <STX> <STRX> <MY_NAME> <HIS_NAME> <MY_QTH> <GRID> <CONT_TX> <n></span> (cut numbers: 9→N, 0→T). <span className="font-mono">*</span>=my call, <span className="font-mono">!</span>=his call. <span className="font-mono"><LOGQSO></span> = log the QSO when the macro is sent (e.g. <span className="font-mono">73 TU <LOGQSO></span>).
|
||||
</p>
|
||||
<div className="space-y-1.5 max-h-[34vh] overflow-y-auto pr-1">
|
||||
{Array.from({ length: 12 }).map((_, i) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { cn } from '@/lib/utils';
|
||||
// only an exact (case-insensitive) match — otherwise it reverts, so the field
|
||||
// can't hold a typo'd value that isn't in the list.
|
||||
export function Combobox({
|
||||
value, onChange, options, placeholder, className, allowFreeText = false,
|
||||
value, onChange, options, placeholder, className, allowFreeText = false, commitOnType = false,
|
||||
}: {
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
@@ -14,6 +14,10 @@ export function Combobox({
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
allowFreeText?: boolean;
|
||||
// Commit each keystroke to the parent immediately (not just on blur). Use for
|
||||
// fields read live by other actions — e.g. RST, so a CW macro sent without
|
||||
// leaving the field uses the value just typed.
|
||||
commitOnType?: boolean;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
@@ -41,9 +45,13 @@ export function Combobox({
|
||||
// Defer so a click on an option registers first.
|
||||
setTimeout(() => {
|
||||
setOpen(false);
|
||||
const exact = options.find((o) => o.toLowerCase() === query.trim().toLowerCase());
|
||||
if (exact) { onChange(exact); setQuery(exact); }
|
||||
else if (allowFreeText) { onChange(query.trim()); }
|
||||
const trimmed = query.trim();
|
||||
const exact = options.find((o) => o.toLowerCase() === trimmed.toLowerCase());
|
||||
// Only fire onChange when the value actually changed — committing an
|
||||
// unchanged value on a plain tab-through would wrongly flag the field as
|
||||
// "user-edited" (e.g. RST, which then blocks the CW/SSB 599↔59 default).
|
||||
if (exact) { if (exact !== value) onChange(exact); setQuery(exact); }
|
||||
else if (allowFreeText) { if (trimmed !== value) onChange(trimmed); }
|
||||
else { setQuery(value); } // revert typo
|
||||
}, 120);
|
||||
}
|
||||
@@ -56,7 +64,7 @@ export function Combobox({
|
||||
// Focus selects the text so a keystroke replaces it — but does NOT
|
||||
// open the list (so tabbing in doesn't pop the dropdown).
|
||||
onFocus={(e) => { setQuery(value); e.currentTarget.select(); }}
|
||||
onChange={(e) => { setQuery(e.target.value); setOpen(true); }}
|
||||
onChange={(e) => { setQuery(e.target.value); setOpen(true); if (commitOnType) onChange(e.target.value); }}
|
||||
onBlur={onBlur}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.key === 'ArrowDown' || e.key === 'Alt') && !open) { setOpen(true); }
|
||||
|
||||
Reference in New Issue
Block a user