From 94997752b60083fc4a6606f2ef7a349675abfafd Mon Sep 17 00:00:00 2001 From: Kolkman Date: Wed, 26 Aug 2026 16:30:25 +0200 Subject: [PATCH] disappearing interface after link safe --- VIBE/CHAT_LOG.md | 12 ++++++++++++ VIBE/PROMPTS.md | 2 ++ XPI/unsigned/LinkLog-0.1.0.xpi | Bin 48918 -> 49318 bytes backend/app/api/links.py | 7 ++++++- backend/tests/test_api.py | 12 ++++++++++++ webextension/_locales/de/messages.json | 3 ++- webextension/_locales/en-US/messages.json | 15 ++++++++++++++- webextension/_locales/es/messages.json | 3 ++- webextension/_locales/fr/messages.json | 3 ++- webextension/_locales/nl/messages.json | 3 ++- webextension/popup.js | 21 ++++++++++++++++++++- 11 files changed, 74 insertions(+), 7 deletions(-) diff --git a/VIBE/CHAT_LOG.md b/VIBE/CHAT_LOG.md index e10ef41..ad5506a 100644 --- a/VIBE/CHAT_LOG.md +++ b/VIBE/CHAT_LOG.md @@ -861,6 +861,18 @@ When the plugin is activated and the link already exists, show “This link alre ### Assistant outcome Added an authenticated duplicate-check request when the popup opens after loading the active page. Existing links now show the requested warning before submission; comment and tag editing plus plugin retriggering remain available. +### User +Display any errors that occur during posting. + +### Assistant outcome +Save responses now include sanitized plugin failure details, and the popup displays publishing errors alongside the save confirmation, including Mastodon HTTP or connection failures. + +### User +When pressing Save link, display “Link saved to ” and hide all other information. Refresh only when the plugin is opened again. + +### Assistant outcome +Added a terminal save state to the popup. After a successful save it displays the localized LinkLog server URL, hides the form, session, warnings, and header information, and performs no refresh until the popup is opened again. + ### User The plugin settings still show `emailLabel`; make that `email` as the title for the email field. diff --git a/VIBE/PROMPTS.md b/VIBE/PROMPTS.md index 83b2c91..41918a2 100644 --- a/VIBE/PROMPTS.md +++ b/VIBE/PROMPTS.md @@ -149,7 +149,9 @@ 140. Change the title of the field "One-time password" to "One-time password (when configured)". 157. The plugin settings still show 'emailLabel'; make that 'email' as title for the email field. 163. When a URL with the same title is already in the database, warn the user, allow comment and hashtag changes, and retrigger plugins such as Mastodon reposting. +166. Display any errors that occur during posting. 164. When the plugin is activated and the link already exists, show "This link already exists. Comment and tags can be updated, and by submitting publishing will be retriggered." +165. When pressing save link the plugin should display "Link saved to " and hide all other information. It should only refresh when the plugin is opened again 141. Remove any leading and trailing spaces when entering fields in the settings page of the plugin. 142. Do a full security audit document in what you have done in detail in Security-audit.md 143. Address issue 1. and improve password storage diff --git a/XPI/unsigned/LinkLog-0.1.0.xpi b/XPI/unsigned/LinkLog-0.1.0.xpi index a124ad7cc8971f343d7eff17d0ad91bf6de66c7d..648866ecd43fcc52439c59cda6e815cee6204a82 100644 GIT binary patch delta 10016 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
X<%KgEtn1OXk=DSaHB-0Z0SejU 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
uFOf8XnLa?&d*m3i0jI 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 E2eF|e(f|Me diff --git a/backend/app/api/links.py b/backend/app/api/links.py index 9d9b791..ce52458 100644 --- a/backend/app/api/links.py +++ b/backend/app/api/links.py @@ -76,7 +76,12 @@ def create_link_endpoint(payload: LinkCreate, response: Response, authorization: mark_mastodon_posted(record['id'], info['user_id'], mastodon_result.get('post_id')) if any(result.get('status') == 'failed' for result in plugin_results): logger.warning('One or more plugins failed for link_id=%s results=%s', record['id'], plugin_results) - return {**record, 'duplicate': duplicate} + plugin_errors = [ + {'plugin': result.get('plugin', 'unknown'), 'reason': result.get('reason', 'Plugin failed')} + for result in plugin_results + if result.get('status') == 'failed' + ] + return {**record, 'duplicate': duplicate, 'plugin_errors': plugin_errors} @router.put('/links/{link_id}') diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index ab80df6..3752474 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -454,6 +454,18 @@ def test_duplicate_link_check_is_authenticated_and_detects_existing_entry(): assert check.json()['exists'] is True +def test_link_save_reports_plugin_posting_errors(): + with patch('backend.app.api.links.plugin_manager.dispatch', return_value=[ + {'status': 'failed', 'plugin': 'mastodon', 'reason': 'HTTP 503: unavailable'}, + ]): + response = client.post('/api/links', headers=login_headers(), json={ + 'title': 'Plugin error report', + 'url': 'https://example.com/plugin-error-report', + }) + assert response.status_code == 201 + assert response.json()['plugin_errors'] == [{'plugin': 'mastodon', 'reason': 'HTTP 503: unavailable'}] + + def test_links_support_tags_and_tag_filtering(): headers = login_headers() response = client.post('/api/links', headers=headers, json={ diff --git a/webextension/_locales/de/messages.json b/webextension/_locales/de/messages.json index 09062ce..00ea489 100644 --- a/webextension/_locales/de/messages.json +++ b/webextension/_locales/de/messages.json @@ -40,9 +40,10 @@ "configureAndLogIn": {"message": "Konfiguriere die Backend-URL und melde dich zuerst an."}, "sessionExpired": {"message": "Sitzung abgelaufen. Authentifiziere dich in den Einstellungen erneut."}, "submissionFailed": {"message": "Senden fehlgeschlagen"}, - "linkSaved": {"message": "Link erfolgreich gespeichert"}, + "linkSaved": {"message": "Link auf $URL$ gespeichert.", "placeholders": {"url": {"content": "$1"}}}, "linkAlreadyExists": {"message": "Dieser Link existiert bereits. Kommentar und Tags wurden aktualisiert und die Veröffentlichung erneut ausgelöst."}, "duplicateLinkWarning": {"message": "Dieser Link existiert bereits. Kommentar und Tags können aktualisiert werden; beim Absenden wird die Veröffentlichung erneut ausgelöst."}, + "publishingErrors": {"message": "Fehler bei der Veröffentlichung: $ERRORS$", "placeholders": {"errors": {"content": "$1"}}}, "submissionFailedConnection": {"message": "Senden fehlgeschlagen. Überprüfe die Verbindung zum Backend."}, "loggedInAt": {"message": "$USERNAME$ ist bei $BACKEND$ angemeldet", "placeholders": {"username": {"content": "$1"}, "backend": {"content": "$2"}}}, "fillAllFields": {"message": "Fülle alle Felder aus"}, diff --git a/webextension/_locales/en-US/messages.json b/webextension/_locales/en-US/messages.json index c23f955..58cfcc4 100644 --- a/webextension/_locales/en-US/messages.json +++ b/webextension/_locales/en-US/messages.json @@ -123,7 +123,12 @@ "message": "Submission failed" }, "linkSaved": { - "message": "Link saved successfully" + "message": "Link saved to $URL$.", + "placeholders": { + "url": { + "content": "$1" + } + } }, "linkAlreadyExists": { "message": "This link already exists. Comment and tags were updated, and publishing was retriggered." @@ -131,6 +136,14 @@ "duplicateLinkWarning": { "message": "This link already exists. Comment and tags can be updated, and by submitting publishing will be retriggered." }, + "publishingErrors": { + "message": "Publishing errors: $ERRORS$", + "placeholders": { + "errors": { + "content": "$1" + } + } + }, "submissionFailedConnection": { "message": "Submission failed. Check your backend connection." }, diff --git a/webextension/_locales/es/messages.json b/webextension/_locales/es/messages.json index 1c81969..39654b4 100644 --- a/webextension/_locales/es/messages.json +++ b/webextension/_locales/es/messages.json @@ -40,9 +40,10 @@ "configureAndLogIn": {"message": "Configura la URL del servidor e inicia sesión primero."}, "sessionExpired": {"message": "La sesión ha caducado. Vuelve a autenticarte en la configuración."}, "submissionFailed": {"message": "Error al enviar"}, - "linkSaved": {"message": "Enlace guardado correctamente"}, + "linkSaved": {"message": "Enlace guardado en $URL$.", "placeholders": {"url": {"content": "$1"}}}, "linkAlreadyExists": {"message": "Este enlace ya existe. Se actualizaron el comentario y las etiquetas, y se volvió a activar la publicación."}, "duplicateLinkWarning": {"message": "Este enlace ya existe. Puedes actualizar el comentario y las etiquetas; al enviarlo se volverá a activar la publicación."}, + "publishingErrors": {"message": "Errores de publicación: $ERRORS$", "placeholders": {"errors": {"content": "$1"}}}, "submissionFailedConnection": {"message": "Error al enviar. Comprueba la conexión con el servidor."}, "loggedInAt": {"message": "$USERNAME$ ha iniciado sesión en $BACKEND$", "placeholders": {"username": {"content": "$1"}, "backend": {"content": "$2"}}}, "fillAllFields": {"message": "Rellena todos los campos"}, diff --git a/webextension/_locales/fr/messages.json b/webextension/_locales/fr/messages.json index d620f72..68b42fe 100644 --- a/webextension/_locales/fr/messages.json +++ b/webextension/_locales/fr/messages.json @@ -40,9 +40,10 @@ "configureAndLogIn": {"message": "Configurez l’URL du serveur et connectez-vous d’abord."}, "sessionExpired": {"message": "Session expirée. Reconnectez-vous dans les paramètres."}, "submissionFailed": {"message": "Échec de l’envoi"}, - "linkSaved": {"message": "Lien enregistré"}, + "linkSaved": {"message": "Lien enregistré sur $URL$.", "placeholders": {"url": {"content": "$1"}}}, "linkAlreadyExists": {"message": "Ce lien existe déjà. Le commentaire et les étiquettes ont été mis à jour et la publication a été relancée."}, "duplicateLinkWarning": {"message": "Ce lien existe déjà. Le commentaire et les étiquettes peuvent être mis à jour ; l’envoi relancera la publication."}, + "publishingErrors": {"message": "Erreurs de publication : $ERRORS$", "placeholders": {"errors": {"content": "$1"}}}, "submissionFailedConnection": {"message": "Échec de l’envoi. Vérifiez la connexion au serveur."}, "loggedInAt": {"message": "$USERNAME$ est connecté à $BACKEND$", "placeholders": {"username": {"content": "$1"}, "backend": {"content": "$2"}}}, "fillAllFields": {"message": "Veuillez remplir tous les champs"}, diff --git a/webextension/_locales/nl/messages.json b/webextension/_locales/nl/messages.json index 3378357..d580c14 100644 --- a/webextension/_locales/nl/messages.json +++ b/webextension/_locales/nl/messages.json @@ -40,9 +40,10 @@ "configureAndLogIn": {"message": "Configureer de backend-URL en log eerst in."}, "sessionExpired": {"message": "Sessie verlopen. Verifieer opnieuw in de instellingen."}, "submissionFailed": {"message": "Verzenden mislukt"}, - "linkSaved": {"message": "Koppeling opgeslagen"}, + "linkSaved": {"message": "Koppeling opgeslagen op $URL$.", "placeholders": {"url": {"content": "$1"}}}, "linkAlreadyExists": {"message": "Deze koppeling bestaat al. De opmerking en tags zijn bijgewerkt en publiceren is opnieuw gestart."}, "duplicateLinkWarning": {"message": "Deze koppeling bestaat al. De opmerking en tags kunnen worden bijgewerkt; na verzenden wordt publiceren opnieuw gestart."}, + "publishingErrors": {"message": "Publicatiefouten: $ERRORS$", "placeholders": {"errors": {"content": "$1"}}}, "submissionFailedConnection": {"message": "Verzenden mislukt. Controleer de verbinding met de backend."}, "loggedInAt": {"message": "$USERNAME$ is ingelogd op $BACKEND$", "placeholders": {"username": {"content": "$1"}, "backend": {"content": "$2"}}}, "fillAllFields": {"message": "Vul alle velden in"}, diff --git a/webextension/popup.js b/webextension/popup.js index c89ac95..3a538a4 100644 --- a/webextension/popup.js +++ b/webextension/popup.js @@ -65,6 +65,19 @@ function showSignedInState(user, backendUrl) { form.classList.remove('hidden'); } +function showSavedState(message) { + document.querySelector('header').classList.add('hidden'); + document.querySelector('.extension-footer').classList.add('hidden'); + authSession.hidden = true; + authSession.classList.add('hidden'); + authWarning.hidden = true; + authWarning.classList.add('hidden'); + form.classList.add('hidden'); + statusEl.textContent = message; + statusEl.classList.remove('hidden', 'error'); + statusEl.classList.add('success'); +} + async function updateFeedLink() { const settings = await browser.storage.local.get(['backendUrl', 'username', 'accessToken']); if (!settings.backendUrl || !settings.username || !settings.accessToken) return; @@ -209,7 +222,13 @@ async function handleSubmit(event) { } const result = await response.json(); - setStatus(result.duplicate ? t('linkAlreadyExists') : t('linkSaved'), Boolean(result.duplicate)); + const saveMessage = result.duplicate ? t('linkAlreadyExists') : t('linkSaved', backendUrl); + if (result.plugin_errors?.length) { + const errors = result.plugin_errors.map((error) => `${error.plugin}: ${error.reason}`).join(' '); + showSavedState(`${saveMessage} ${t('publishingErrors', errors)}`); + return; + } + showSavedState(saveMessage); } catch (error) { setStatus(t('submissionFailedConnection'), true); }