Plugin follows duplicate behavior behavior of new-entry
This commit is contained in:
+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