diff --git a/app.go b/app.go index 8b990fc..46679ed 100644 --- a/app.go +++ b/app.go @@ -6508,7 +6508,16 @@ func (a *App) DeleteQSOs(ids []int64) (int64, error) { } a.deleteRemoteCopies(ids) a.syncPublishDeletes(ids) - return a.qso.DeleteMany(a.ctx, ids) + n, err := a.qso.DeleteMany(a.ctx, ids) + // Logged because a delete that does nothing is otherwise indistinguishable + // from a delete that worked: the rows leave the grid either way once it + // reloads, and the only place the truth survives is here. + if err != nil { + applog.Printf("delete: %d QSO(s) requested, FAILED: %v", len(ids), err) + } else { + applog.Printf("delete: %d QSO(s) requested, %d row(s) removed", len(ids), n) + } + return n, err } // DuplicateGroup is a set of QSOs the log considers the same contact. diff --git a/changelog.json b/changelog.json index 63344f0..44ed9e7 100644 --- a/changelog.json +++ b/changelog.json @@ -19,7 +19,9 @@ "The QSO right-click menu can now export the selection to ADIF with the OpsLog fields, so the award references (RDA@KR-04 and the like) travel with the contacts. The existing entry still writes standard ADIF for other loggers.", "The filtered-view export gets the same treatment: a second right-click entry writes the whole filtered view to ADIF with the OpsLog fields.", "Edit QSO: HAMLOG.online joins the QSL Info tab — its own channel in the picker, with sent/received and both dates, and a row in the status table. Its four columns also move from the QSL group to Uploads, next to Club Log and QRZ.com.", - "The filter can now match on when a QSO was added to the log ('Added to the log on'), which is the only way to isolate what an import brought in — an import of old contacts carries old QSO dates and otherwise hides inside the log." + "The filter can now match on when a QSO was added to the log ('Added to the log on'), which is the only way to isolate what an import brought in — an import of old contacts carries old QSO dates and otherwise hides inside the log.", + "QSL Manager: HAMLOG.online gains 'Import confirmations (ADIF)…'. Their site exports a log, this reads it back and stamps the confirmations on QSOs already present — it never inserts. Importing that same file through the ordinary ADIF import does insert, because it matches on the UTC minute and their export rarely agrees to the minute.", + "Deleting QSOs now says how many rows were actually removed, and writes it to the log. A delete that removes nothing used to look exactly like one that worked." ], "fr": [ "Yaesu : une ligne ANT dans le panneau du poste sélectionne la prise d'antenne, et le choix est mémorisé pour la bande sur laquelle il a été fait — on change de bande, l'antenne suit. Aucune page à configurer : choisir l'antenne une fois sur une bande le dit très bien. Les postes à une seule prise n'affichent jamais la ligne.", @@ -38,7 +40,9 @@ "Le menu contextuel des QSO peut maintenant exporter la sélection en ADIF avec les champs OpsLog, pour que les références de diplômes (RDA@KR-04 et compagnie) suivent les contacts. L'entrée existante écrit toujours de l'ADIF standard pour les autres carnets.", "L'export de la vue filtrée reçoit le même traitement : une seconde entrée du menu contextuel écrit toute la vue filtrée en ADIF avec les champs OpsLog.", "Édition de QSO : HAMLOG.online rejoint l'onglet QSL Info — son propre canal dans la liste, avec envoyé/reçu et les deux dates, et une ligne dans le tableau de statut. Ses quatre colonnes passent aussi du groupe QSL au groupe Uploads, à côté de Club Log et QRZ.com.", - "Le filtre peut maintenant porter sur la date d'ajout au journal (« Ajouté au journal le »), seul moyen d'isoler ce qu'un import a apporté : un import de vieux contacts porte de vieilles dates de QSO et se fond sinon dans le journal." + "Le filtre peut maintenant porter sur la date d'ajout au journal (« Ajouté au journal le »), seul moyen d'isoler ce qu'un import a apporté : un import de vieux contacts porte de vieilles dates de QSO et se fond sinon dans le journal.", + "Gestionnaire QSL : HAMLOG.online reçoit « Importer les confirmations (ADIF)… ». Leur site exporte un log, cette fonction le relit et appose les confirmations sur les QSO déjà présents — elle n'ajoute jamais rien. Le même fichier passé par l'import ADIF ordinaire, lui, ajoute : il compare à la minute UTC près et leur export s'accorde rarement à la minute.", + "La suppression de QSO indique maintenant combien de lignes ont réellement été supprimées, et l'écrit dans le journal. Une suppression sans effet ressemblait exactement à une suppression réussie." ] }, { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 05468a5..161dd23 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4297,8 +4297,14 @@ export default function App() { if (deletingIds.length === 0) return; const ids = deletingIds; try { - if (ids.length === 1) await DeleteQSO(ids[0]); - else await DeleteQSOs(ids as any); + // Report what was actually removed rather than assuming it matched what + // was asked: a delete that silently removes nothing looks exactly like one + // that worked, since the rows leave the grid when it reloads either way. + if (ids.length === 1) { await DeleteQSO(ids[0]); showToast(t('toast.deletedOne')); } + else { + const n = await DeleteQSOs(ids as any); + showToast(t('toast.deletedN', { n: Number(n ?? 0), asked: ids.length })); + } setDeletingIds([]); setSelectedId(null); setSelectedIds([]); diff --git a/frontend/src/components/QSLManagerModal.tsx b/frontend/src/components/QSLManagerModal.tsx index 9e867ea..d93dd50 100644 --- a/frontend/src/components/QSLManagerModal.tsx +++ b/frontend/src/components/QSLManagerModal.tsx @@ -8,7 +8,7 @@ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from '@/components/ui/select'; import { cn } from '@/lib/utils'; -import { FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App'; +import { FindQSOsForUpload, UploadQSOsManual, DownloadConfirmations, CancelConfirmations, ImportHamlogConfirmations, OpenADIFFile, SyncPOTAHunterLog, ListQSO, BulkUpdateQSL, UploadCallsign, GetSlotStats } from '../../wailsjs/go/main/App'; import { Input } from '@/components/ui/input'; import { RecentQSOsGrid } from '@/components/RecentQSOsGrid'; import { EventsOn } from '../../wailsjs/runtime/runtime'; @@ -350,6 +350,19 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: { catch (e: any) { setLogLines((p) => [...p, 'Error: ' + String(e?.message ?? e)]); setBusy(false); } } + // HAMLOG.online has no download verb — their agent protocol only uploads — + // so confirmations come back as an ADIF exported from their site. It is read + // here rather than through the ordinary ADIF import because that one matches + // on the UTC minute and INSERTS whatever fails to match: a few hundred copies + // of contacts already in the log. This path only ever stamps QSOs it finds. + async function importHamlogCfm() { + const path = await OpenADIFFile(); + if (!path) return; + setLogLines([]); setBusy(true); setLogAction('download'); setShowLog(true); + try { await ImportHamlogConfirmations(path); } + catch (e: any) { setLogLines((p) => [...p, 'Error: ' + String(e?.message ?? e)]); setBusy(false); } + } + function viewResults() { setShowLog(false); if (logAction === 'upload') selectRequired(); @@ -666,11 +679,19 @@ export function QSLManagerPanel({ onEditQSO, actions, paperRequest }: { {service !== 'pota' && service !== 'paper' && (
+ {service === 'hamlog' ? ( + + ) : ( + )} {/* Date window */} + {service !== 'hamlog' && (<>