#23 Prevent bookmark duplicates
* Show hint if URL is already bookmarked * Remove hint if URL belongs to edited bookmark * Fix query param encoding * Update bookmark instead of duplicating it Co-authored-by: Sascha Ißbrücker <sissbruecker@lyska.io>
This commit is contained in:
parent
10fd3d89be
commit
348a536aa3
@ -7,6 +7,14 @@ from bookmarks.services.website_loader import load_website_metadata
|
||||
|
||||
|
||||
def create_bookmark(form: BookmarkForm, current_user: User):
|
||||
# If URL is already bookmarked, then update it
|
||||
existing_bookmark = Bookmark.objects.filter(owner=current_user, url=form.data['url']).first()
|
||||
|
||||
if existing_bookmark is not None:
|
||||
update_form = BookmarkForm(data=form.data, instance=existing_bookmark)
|
||||
update_bookmark(update_form, current_user)
|
||||
return
|
||||
|
||||
bookmark = form.save(commit=False)
|
||||
# Update website info
|
||||
_update_website_metadata(bookmark)
|
||||
|
@ -55,4 +55,15 @@ ul.bookmark-list {
|
||||
.form-icon.loading {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.form-input-hint.bookmark-exists {
|
||||
visibility: hidden;
|
||||
color: $warning-color;
|
||||
|
||||
a {
|
||||
color: $warning-color;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<h2>Edit bookmark</h2>
|
||||
</div>
|
||||
<form action="{% url 'bookmarks:edit' bookmark_id %}" method="post" class="col-6 col-md-12" novalidate>
|
||||
{% bookmark_form form all_tags %}
|
||||
{% bookmark_form form all_tags bookmark_id %}
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
@ -12,6 +12,9 @@
|
||||
{{ form.url.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-input-hint bookmark-exists">
|
||||
This URL is already bookmarked. You can <a href="#">edit</a> it or you can overwrite the existing bookmark by saving this form.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label>
|
||||
@ -77,28 +80,42 @@
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Pre-fill title and description placeholders with metadata from website as soon as URL changes
|
||||
* - Pre-fill title and description placeholders with metadata from website as soon as URL changes
|
||||
* - Show hint if URL is already bookmarked
|
||||
*/
|
||||
(function init() {
|
||||
const urlInput = document.getElementById('{{ form.url.id_for_label }}');
|
||||
const titleInput = document.getElementById('{{ form.title.id_for_label }}');
|
||||
const descriptionInput = document.getElementById('{{ form.description.id_for_label }}');
|
||||
const editedBookmarkId = {{ bookmark_id }}
|
||||
|
||||
urlInput.addEventListener('input', updateMetadata);
|
||||
urlInput.addEventListener('input', checkUrl);
|
||||
|
||||
function updateMetadata() {
|
||||
function checkUrl() {
|
||||
toggleIcon(titleInput, true);
|
||||
toggleIcon(descriptionInput, true);
|
||||
|
||||
const websiteUrl = urlInput.value;
|
||||
const requestUrl = `{% url 'bookmarks:api.website_metadata' %}?url=${websiteUrl}`;
|
||||
const websiteUrl = encodeURIComponent(urlInput.value);
|
||||
const requestUrl = `{% url 'bookmarks:api.check_url' %}?url=${websiteUrl}`;
|
||||
fetch(requestUrl)
|
||||
.then(response => response.json())
|
||||
.then(metadata => {
|
||||
.then(data => {
|
||||
const metadata = data.metadata
|
||||
titleInput.setAttribute('placeholder', metadata.title || '');
|
||||
descriptionInput.setAttribute('placeholder', metadata.description || '');
|
||||
toggleIcon(titleInput, false);
|
||||
toggleIcon(descriptionInput, false);
|
||||
|
||||
// Display hint if URL is already bookmarked
|
||||
const bookmarkExistsHint = document.querySelector('.form-input-hint.bookmark-exists')
|
||||
const editExistingBookmarkLink = bookmarkExistsHint.querySelector('a')
|
||||
|
||||
if(data.bookmark && data.bookmark.id !== editedBookmarkId) {
|
||||
bookmarkExistsHint.style['visibility'] = 'visible'
|
||||
editExistingBookmarkLink.href = data.bookmark.edit_url
|
||||
} else {
|
||||
bookmarkExistsHint.style['visibility'] = 'hidden'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -107,7 +124,7 @@
|
||||
icon.style['visibility'] = show ? 'visible' : 'hidden';
|
||||
}
|
||||
|
||||
if (urlInput.value) updateMetadata();
|
||||
if (urlInput.value) checkUrl();
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<h2>New bookmark</h2>
|
||||
</div>
|
||||
<form action="{% url 'bookmarks:new' %}" method="post" class="col-6 col-md-12" novalidate>
|
||||
{% bookmark_form form all_tags auto_close %}
|
||||
{% bookmark_form form all_tags auto_close=auto_close %}
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
@ -9,7 +9,7 @@ register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('bookmarks/form.html', name='bookmark_form')
|
||||
def bookmark_form(form: BookmarkForm, all_tags: List[Tag], auto_close: bool = False):
|
||||
def bookmark_form(form: BookmarkForm, all_tags: List[Tag], bookmark_id: int = 0, auto_close: bool = False):
|
||||
|
||||
all_tag_names = [tag.name for tag in all_tags]
|
||||
all_tags_string = build_tag_string(all_tag_names, ' ')
|
||||
@ -17,7 +17,8 @@ def bookmark_form(form: BookmarkForm, all_tags: List[Tag], auto_close: bool = Fa
|
||||
return {
|
||||
'form': form,
|
||||
'auto_close': auto_close,
|
||||
'all_tags': all_tags_string
|
||||
'all_tags': all_tags_string,
|
||||
'bookmark_id': bookmark_id
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,5 +20,5 @@ urlpatterns = [
|
||||
path('settings/import', views.settings.bookmark_import, name='settings.import'),
|
||||
path('settings/export', views.settings.bookmark_export, name='settings.export'),
|
||||
# API
|
||||
path('api/website_metadata', views.api.website_metadata, name='api.website_metadata'),
|
||||
path('api/check_url', views.api.check_url, name='api.check_url'),
|
||||
]
|
||||
|
@ -1,11 +1,27 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.forms import model_to_dict
|
||||
from django.http import JsonResponse
|
||||
from django.urls import reverse
|
||||
|
||||
from bookmarks.services.website_loader import load_website_metadata
|
||||
from bookmarks.models import Bookmark
|
||||
|
||||
|
||||
@login_required
|
||||
def website_metadata(request):
|
||||
def check_url(request):
|
||||
url = request.GET.get('url')
|
||||
bookmark = Bookmark.objects.filter(owner=request.user, url=url).first()
|
||||
existing_bookmark_data = None
|
||||
|
||||
if bookmark is not None:
|
||||
existing_bookmark_data = {
|
||||
'id': bookmark.id,
|
||||
'edit_url': reverse('bookmarks:edit', args=[bookmark.id])
|
||||
}
|
||||
|
||||
metadata = load_website_metadata(url)
|
||||
return JsonResponse(metadata.to_dict())
|
||||
|
||||
return JsonResponse({
|
||||
'bookmark': existing_bookmark_data,
|
||||
'metadata': metadata.to_dict()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user