Add bundles for organizing bookmarks (#1097)

* add bundle model and query logic

* cleanup tests

* add basic form

* add success message

* Add form tests

* Add bundle list view

* fix edit view

* Add remove button

* Add basic preview logic

* Make pagination use absolute URLs

* Hide bookmark edits when rendering preview

* Render bookmark list in preview

* Reorder bundles

* Show bundles in bookmark view

* Make bookmark search respect selected bundle

* UI tweaks

* Fix bookmark scope

* Improve bundle preview

* Skip preview if form is submitted

* Show correct preview after invalid form submission

* Add option to hide bundles

* Merge new migrations

* Add tests for bundle menu

* Improve check for preview being removed
This commit is contained in:
Sascha Ißbrücker
2025-06-19 16:47:29 +02:00
committed by GitHub
parent 8be72a5d1f
commit 1672dc0152
59 changed files with 2290 additions and 267 deletions

View File

@@ -13,6 +13,7 @@ from bookmarks import utils
from bookmarks.models import (
Bookmark,
BookmarkAsset,
BookmarkBundle,
BookmarkSearch,
User,
UserProfile,
@@ -178,15 +179,13 @@ class BookmarkItem:
class BookmarkListContext:
request_context = RequestContext
def __init__(self, request: HttpRequest) -> None:
def __init__(self, request: HttpRequest, search: BookmarkSearch) -> None:
request_context = self.request_context(request)
user = request.user
user_profile = request.user_profile
self.request = request
self.search = BookmarkSearch.from_request(
self.request.GET, user_profile.search_preferences
)
self.search = search
query_set = request_context.get_bookmark_query_set(self.search)
page_number = request.GET.get("page")
@@ -219,6 +218,7 @@ class BookmarkListContext:
self.show_preview_images = user_profile.enable_preview_images
self.show_notes = user_profile.permanent_notes
self.collapse_side_panel = user_profile.collapse_side_panel
self.is_preview = False
@staticmethod
def generate_return_url(search: BookmarkSearch, base_url: str, page: int = None):
@@ -315,14 +315,12 @@ class TagGroup:
class TagCloudContext:
request_context = RequestContext
def __init__(self, request: HttpRequest) -> None:
def __init__(self, request: HttpRequest, search: BookmarkSearch) -> None:
request_context = self.request_context(request)
user_profile = request.user_profile
self.request = request
self.search = BookmarkSearch.from_request(
self.request.GET, user_profile.search_preferences
)
self.search = search
query_set = request_context.get_tag_query_set(self.search)
tags = list(query_set)
@@ -461,3 +459,23 @@ def get_details_context(
return None
return context_type(request, bookmark)
class BundlesContext:
def __init__(self, request: HttpRequest) -> None:
self.request = request
self.user = request.user
self.user_profile = request.user_profile
self.bundles = (
BookmarkBundle.objects.filter(owner=self.user).order_by("order").all()
)
self.is_empty = len(self.bundles) == 0
selected_bundle_id = (
int(request.GET.get("bundle")) if request.GET.get("bundle") else None
)
self.selected_bundle = next(
(bundle for bundle in self.bundles if bundle.id == selected_bundle_id),
None,
)