Plugin follows duplicate behavior behavior of new-entry
This commit is contained in:
@@ -42,7 +42,10 @@
|
||||
"submissionFailed": {"message": "Senden fehlgeschlagen"},
|
||||
"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."},
|
||||
"duplicateLinkSameUrl": {"message": "Dieser Link existiert bereits.\nDu kannst den Eintrag trotzdem speichern; dabei werden Kommentar und Tags des bestehenden Eintrags aktualisiert."},
|
||||
"duplicateLinkDifferentUrl": {"message": "Dieser Link existiert bereits. Der gespeicherte Link hat jedoch eine andere URL (fehlende oder andere Parameter).\nWenn du den Eintrag speicherst, riskierst du einen doppelten Eintrag."},
|
||||
"refetchTitle": {"message": "Titel erneut abrufen"},
|
||||
"titleFetchFailed": {"message": "Titel konnte nicht abgerufen werden"},
|
||||
"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"}}},
|
||||
|
||||
@@ -133,8 +133,17 @@
|
||||
"linkAlreadyExists": {
|
||||
"message": "This link already exists. Comment and tags were updated, and publishing was retriggered."
|
||||
},
|
||||
"duplicateLinkWarning": {
|
||||
"message": "This link already exists. Comment and tags can be updated, and by submitting publishing will be retriggered."
|
||||
"duplicateLinkSameUrl": {
|
||||
"message": "This link already exists.\nYou can still save the entry, which will update the existing entry's comment and or tags."
|
||||
},
|
||||
"duplicateLinkDifferentUrl": {
|
||||
"message": "This link already exists. But, the stored link has a different URL (missing or different parameters).\nWhen you save the entry you risk a duplicate entry."
|
||||
},
|
||||
"refetchTitle": {
|
||||
"message": "Re-fetch title"
|
||||
},
|
||||
"titleFetchFailed": {
|
||||
"message": "Could not fetch title"
|
||||
},
|
||||
"publishingErrors": {
|
||||
"message": "Publishing errors: $ERRORS$",
|
||||
|
||||
@@ -42,7 +42,10 @@
|
||||
"submissionFailed": {"message": "Error al enviar"},
|
||||
"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."},
|
||||
"duplicateLinkSameUrl": {"message": "Este enlace ya existe.\nAún puedes guardar la entrada, lo que actualizará el comentario y las etiquetas de la entrada existente."},
|
||||
"duplicateLinkDifferentUrl": {"message": "Este enlace ya existe. Pero el enlace guardado tiene una URL diferente (parámetros ausentes o distintos).\nSi guardas la entrada, corres el riesgo de crear una entrada duplicada."},
|
||||
"refetchTitle": {"message": "Volver a obtener el título"},
|
||||
"titleFetchFailed": {"message": "No se pudo obtener el título"},
|
||||
"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"}}},
|
||||
|
||||
@@ -42,7 +42,10 @@
|
||||
"submissionFailed": {"message": "Échec de l’envoi"},
|
||||
"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."},
|
||||
"duplicateLinkSameUrl": {"message": "Ce lien existe déjà.\nVous pouvez tout de même enregistrer l’entrée ; le commentaire et les étiquettes de l’entrée existante seront mis à jour."},
|
||||
"duplicateLinkDifferentUrl": {"message": "Ce lien existe déjà. Mais le lien enregistré a une URL différente (paramètres manquants ou différents).\nSi vous enregistrez l’entrée, vous risquez de créer un doublon."},
|
||||
"refetchTitle": {"message": "Récupérer à nouveau le titre"},
|
||||
"titleFetchFailed": {"message": "Impossible de récupérer le titre"},
|
||||
"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"}}},
|
||||
|
||||
@@ -42,7 +42,10 @@
|
||||
"submissionFailed": {"message": "Verzenden mislukt"},
|
||||
"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."},
|
||||
"duplicateLinkSameUrl": {"message": "Deze koppeling bestaat al.\nJe kunt de invoer nog steeds opslaan; de opmerking en tags van de bestaande koppeling worden dan bijgewerkt."},
|
||||
"duplicateLinkDifferentUrl": {"message": "Deze koppeling bestaat al. De opgeslagen koppeling heeft echter een andere URL (ontbrekende of andere parameters).\nAls je de invoer opslaat, loop je het risico op een dubbele koppeling."},
|
||||
"refetchTitle": {"message": "Titel opnieuw ophalen"},
|
||||
"titleFetchFailed": {"message": "Titel kon niet worden opgehaald"},
|
||||
"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"}}},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extensionName__",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"default_locale": "en-US",
|
||||
"permissions": [
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<span data-i18n="titleLabel">Title</span>
|
||||
<input id="title" name="title" type="text" />
|
||||
</label>
|
||||
<button type="button" id="refetch-title" class="secondary" data-i18n="refetchTitle">Re-fetch title</button>
|
||||
|
||||
<label>
|
||||
<span data-i18n="urlLabel">URL</span>
|
||||
|
||||
+60
-4
@@ -13,6 +13,7 @@ const feedLink = document.getElementById('feed-link');
|
||||
const authWarning = document.getElementById('auth-warning');
|
||||
const warningSettingsButton = document.getElementById('warning-settings');
|
||||
const authSession = document.getElementById('auth-session');
|
||||
const refetchTitleButton = document.getElementById('refetch-title');
|
||||
const sessionStore = browser.storage.session;
|
||||
|
||||
const t = window.linklogI18n;
|
||||
@@ -35,8 +36,14 @@ async function hasBackendPermission(backendUrl) {
|
||||
return browser.permissions.contains({origins: [`${origin}/*`]});
|
||||
}
|
||||
|
||||
// Splits on \n into real <br> line breaks without using innerHTML.
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message;
|
||||
const lines = message.split('\n');
|
||||
statusEl.replaceChildren(
|
||||
...lines.flatMap((line, index) => (
|
||||
index === 0 ? [document.createTextNode(line)] : [document.createElement('br'), document.createTextNode(line)]
|
||||
)),
|
||||
);
|
||||
statusEl.classList.remove('hidden');
|
||||
statusEl.classList.toggle('error', isError);
|
||||
statusEl.classList.toggle('success', !isError);
|
||||
@@ -225,20 +232,54 @@ async function checkExistingLink() {
|
||||
const settings = await getSettings();
|
||||
if (!settings.backendUrl || !settings.accessToken || !(await hasBackendPermission(settings.backendUrl)) || !titleInput.value || !urlInput.value) return;
|
||||
try {
|
||||
const url = removeKnownTrackingParams(urlInput.value);
|
||||
const response = await fetch(`${settings.backendUrl}/api/links/check?${new URLSearchParams({
|
||||
title: titleInput.value,
|
||||
url: removeKnownTrackingParams(urlInput.value),
|
||||
url,
|
||||
})}`, {
|
||||
headers: {'Authorization': `Bearer ${settings.accessToken}`},
|
||||
});
|
||||
if (response.ok && (await response.json()).exists) {
|
||||
setStatus(t('duplicateLinkWarning'), true);
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
if (data.exists) {
|
||||
// Re-derive the match locally: browser URL normalization (e.g. trailing slashes) can
|
||||
// make the server's raw string comparison disagree even when the URLs are equivalent.
|
||||
const urlMatches = data.url_matches || (data.stored_url && removeKnownTrackingParams(data.stored_url) === url);
|
||||
const message = urlMatches ? t('duplicateLinkSameUrl') : t('duplicateLinkDifferentUrl');
|
||||
setStatus(message, true);
|
||||
}
|
||||
} catch (error) {
|
||||
// Duplicate checking is advisory; submission remains available.
|
||||
}
|
||||
}
|
||||
|
||||
// Re-fetch the title from the backend scraper, for when the URL was edited after the initial tab-based fill.
|
||||
let lastScrapedUrl = null;
|
||||
async function fetchTitle(url, { force = false } = {}) {
|
||||
const settings = await getSettings();
|
||||
if (!settings.backendUrl || !settings.accessToken || !(await hasBackendPermission(settings.backendUrl))) return;
|
||||
if (!url || (!force && (titleInput.value.trim() || url === lastScrapedUrl))) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${settings.backendUrl}/api/scrape?url=${encodeURIComponent(url)}`, {
|
||||
headers: {'Authorization': `Bearer ${settings.accessToken}`},
|
||||
});
|
||||
if (!response.ok) {
|
||||
if (force) setStatus(t('titleFetchFailed'), true);
|
||||
return;
|
||||
}
|
||||
lastScrapedUrl = url;
|
||||
const data = await response.json();
|
||||
if (data.title) {
|
||||
titleInput.value = data.title;
|
||||
} else if (force) {
|
||||
setStatus(t('titleFetchFailed'), true);
|
||||
}
|
||||
} catch (error) {
|
||||
if (force) setStatus(t('titleFetchFailed'), true);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
setStatus(t('submitting'), false);
|
||||
@@ -305,6 +346,21 @@ async function handleSubmit(event) {
|
||||
openSettingsButton.addEventListener('click', () => browser.runtime.openOptionsPage());
|
||||
warningSettingsButton.addEventListener('click', () => browser.runtime.openOptionsPage());
|
||||
form.addEventListener('submit', handleSubmit);
|
||||
urlInput.addEventListener('blur', async () => {
|
||||
await fetchTitle(removeKnownTrackingParams(urlInput.value.trim()));
|
||||
checkExistingLink();
|
||||
});
|
||||
titleInput.addEventListener('blur', checkExistingLink);
|
||||
refetchTitleButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const url = removeKnownTrackingParams(urlInput.value.trim());
|
||||
if (!url) {
|
||||
setStatus(t('titleFetchFailed'), true);
|
||||
return;
|
||||
}
|
||||
titleInput.value = '';
|
||||
fetchTitle(url, { force: true });
|
||||
});
|
||||
populateCurrentTab().then(checkExistingLink);
|
||||
loadExistingTags();
|
||||
updateFeedLink();
|
||||
|
||||
Reference in New Issue
Block a user