Add bulk and single bookmark metadata refresh (#999)

* Add url create/edit query paramter to clear cache

* Add refresh bookmark metadata button in create/edit bookmark page

* Fix refresh bookmark metadata when editing existing bookmark

* Add bulk refresh metadata functionality

* Fix test cases for bulk view dropdown selection list

* Allow bulk metadata refresh when background tasks are disabled

* Move load preview image call on refresh metadata

* Update bookmark modified time on metadata refresh

* Rename function to align with convention

* Add tests for refresh task

* Add tests for bookmarks service refresh metadata

* Add tests for bookmarks api disable cache on check

* Remove bulk refresh metadata when background tasks disabled

* Refactor refresh metadata task

* Remove unnecessary call

* Fix testing mock name

* Abstract clearing metadata cache

* Add test to check if load page is called twice when cache disabled

* Remove refresh button for new bookmarks

* Remove strict disable cache is true check

* Refactor refresh metadata form logic into its own function

* move button and highlight changes

* polish and update tests

---------

Co-authored-by: Sascha Ißbrücker <sascha.issbruecker@gmail.com>
This commit is contained in:
Josh S
2025-03-22 06:34:10 -04:00
committed by GitHub
parent f1acb4f7c9
commit a23c357f2f
14 changed files with 300 additions and 10 deletions

View File

@@ -22,6 +22,7 @@
<option value="bulk_share">Share</option>
<option value="bulk_unshare">Unshare</option>
{% endif %}
<option value="bulk_refresh">Refresh from website</option>
</select>
<div class="tag-autocomplete d-none" ld-tag-autocomplete>
<input name="bulk_tag_string" class="form-input input-sm" placeholder="Tag names..." variant="small">

View File

@@ -33,9 +33,12 @@
<div class="form-group">
<div class="d-flex justify-between align-baseline">
<label for="{{ form.title.id_for_label }}" class="form-label">Title</label>
<button ld-clear-button data-for="{{ form.title.id_for_label }}" class="btn btn-link clear-button"
type="button">Clear
</button>
<div class="flex">
<button id="refresh-button" class="btn btn-link suffix-button" type="button">Refresh from website</button>
<button ld-clear-button data-for="{{ form.title.id_for_label }}" class="ml-2 btn btn-link suffix-button clear-button"
type="button">Clear
</button>
</div>
</div>
{{ form.title|add_class:"form-input"|attr:"autocomplete:off" }}
{{ form.title.errors }}
@@ -43,7 +46,8 @@
<div class="form-group">
<div class="d-flex justify-between align-baseline">
<label for="{{ form.description.id_for_label }}" class="form-label">Description</label>
<button ld-clear-button data-for="{{ form.description.id_for_label }}" class="btn btn-link clear-button"
<button ld-clear-button data-for="{{ form.description.id_for_label }}"
class="btn btn-link suffix-button clear-button"
type="button">Clear
</button>
</div>
@@ -111,6 +115,7 @@
const notesInput = document.getElementById('{{ form.notes.id_for_label }}');
const unreadCheckbox = document.getElementById('{{ form.unread.id_for_label }}');
const sharedCheckbox = document.getElementById('{{ form.shared.id_for_label }}');
const refreshButton = document.getElementById('refresh-button');
const bookmarkExistsHint = document.querySelector('.form-input-hint.bookmark-exists');
const editedBookmarkId = {{ form.instance.id|default:0 }};
let isTitleModified = !!titleInput.value;
@@ -154,6 +159,7 @@
// Display hint if URL is already bookmarked
const existingBookmark = data.bookmark;
bookmarkExistsHint.style['display'] = existingBookmark ? 'block' : 'none';
refreshButton.style['display'] = existingBookmark ? 'inline-block' : 'none';
// Prefill form with existing bookmark data
if (existingBookmark) {
@@ -193,6 +199,36 @@
});
}
function refreshMetadata() {
if (!urlInput.value) {
return;
}
toggleLoadingIcon(urlInput, true);
const websiteUrl = encodeURIComponent(urlInput.value);
const requestUrl = `{% url 'linkding:api-root' %}bookmarks/check?url=${websiteUrl}&ignore_cache=true`;
fetch(requestUrl)
.then(response => response.json())
.then(data => {
const metadata = data.metadata;
const existingBookmark = data.bookmark;
toggleLoadingIcon(urlInput, false);
if (metadata.title && metadata.title !== existingBookmark?.title) {
titleInput.value = metadata.title;
titleInput.classList.add("modified");
}
if (metadata.description && metadata.description !== existingBookmark?.description) {
descriptionInput.value = metadata.description;
descriptionInput.classList.add("modified");
}
});
}
refreshButton.addEventListener('click', refreshMetadata);
// Fetch website metadata when page loads and when URL changes, unless we are editing an existing bookmark
if (!editedBookmarkId) {
checkUrl();
@@ -203,6 +239,8 @@
descriptionInput.addEventListener('input', () => {
isDescriptionModified = true;
});
} else {
refreshButton.style['display'] = 'inline-block';
}
})();
</script>