Repository: sissbruecker/linkding

Issue:
Default new links to unread?
Hey, I use linkding a lot to collect articles that I want to read later. Kind of like Pocket or Wallabag or something like that. Would it be possible to have a setting to make "Mark unread" default to true when adding new links? Or does that exist and did I just miss it?


Discussion:
At the moment only with the browser extension, which has a setting for this. I'd be fine with a contribution that adds an option for this to linkding itself.
![1pf-1me](https://github.com/sissbruecker/linkding/assets/1291364/b2f7807a-77c2-493c-b051-ec935f1c2bb6)

Grab it here — https://chromewebstore.google.com/detail/linkding-extension/beakmhbijpdhipnjhnclmhgjlddhidpe



Patch:
diff --git a/bookmarks/migrations/0033_userprofile_default_mark_unread.py b/bookmarks/migrations/0033_userprofile_default_mark_unread.py
new file mode 100644
index 00000000..e5bcfe5a
--- /dev/null
+++ b/bookmarks/migrations/0033_userprofile_default_mark_unread.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.0.3 on 2024-04-17 19:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("bookmarks", "0032_html_snapshots_hint_toast"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="userprofile",
+            name="default_mark_unread",
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/bookmarks/models.py b/bookmarks/models.py
index c27ae58b..9087829d 100644
--- a/bookmarks/models.py
+++ b/bookmarks/models.py
@@ -399,6 +399,7 @@ class UserProfile(models.Model):
     custom_css = models.TextField(blank=True, null=False)
     search_preferences = models.JSONField(default=dict, null=False)
     enable_automatic_html_snapshots = models.BooleanField(default=True, null=False)
+    default_mark_unread = models.BooleanField(default=False, null=False)
 
 
 class UserProfileForm(forms.ModelForm):
@@ -422,6 +423,7 @@ class Meta:
             "display_archive_bookmark_action",
             "display_remove_bookmark_action",
             "permanent_notes",
+            "default_mark_unread",
             "custom_css",
         ]
 
diff --git a/bookmarks/templates/settings/general.html b/bookmarks/templates/settings/general.html
index acec66d9..436f4045 100644
--- a/bookmarks/templates/settings/general.html
+++ b/bookmarks/templates/settings/general.html
@@ -175,6 +175,17 @@ <h2>Profile</h2>
             <button class="btn mt-2" name="create_missing_html_snapshots">Create missing HTML snapshots</button>
           </div>
         {% endif %}
+        <div class="form-group">
+          <label for="{{ form.default_mark_unread.id_for_label }}" class="form-checkbox">
+            {{ form.default_mark_unread }}
+            <i class="form-icon"></i> Create bookmarks as unread by default
+          </label>
+          <div class="form-input-hint">
+            Sets the default state for the "Mark as unread" option when creating a new bookmark.
+            Setting this option will make all new bookmarks default to unread.
+            This can be overridden when creating each new bookmark.
+          </div>
+        </div>
         <div class="form-group">
           <details {% if form.custom_css.value %}open{% endif %}>
             <summary>Custom CSS</summary>
diff --git a/bookmarks/views/bookmarks.py b/bookmarks/views/bookmarks.py
index 4cc4ee5c..513f7bc1 100644
--- a/bookmarks/views/bookmarks.py
+++ b/bookmarks/views/bookmarks.py
@@ -188,6 +188,7 @@ def new(request):
     initial_title = request.GET.get("title")
     initial_description = request.GET.get("description")
     initial_auto_close = "auto_close" in request.GET
+    initial_mark_unread = request.user.profile.default_mark_unread
 
     if request.method == "POST":
         form = BookmarkForm(request.POST)
@@ -210,6 +211,8 @@ def new(request):
             form.initial["description"] = initial_description
         if initial_auto_close:
             form.initial["auto_close"] = "true"
+        if initial_mark_unread:
+            form.initial["unread"] = "true"
 
     context = {
         "form": form,


Test patch:
diff --git a/bookmarks/tests/test_bookmark_new_view.py b/bookmarks/tests/test_bookmark_new_view.py
index a8ebd443..cef1812f 100644
--- a/bookmarks/tests/test_bookmark_new_view.py
+++ b/bookmarks/tests/test_bookmark_new_view.py
@@ -210,3 +210,25 @@ def test_should_hide_notes_if_there_are_no_notes(self):
         response = self.client.get(reverse("bookmarks:edit", args=[bookmark.id]))
 
         self.assertContains(response, '<details class="notes">', count=1)
+
+    def test_should_not_check_unread_by_default(self):
+        response = self.client.get(reverse("bookmarks:new"))
+        html = response.content.decode()
+
+        self.assertInHTML(
+            '<input type="checkbox" name="unread" id="id_unread">',
+            html,
+        )
+
+    def test_should_check_unread_when_configured_in_profile(self):
+        self.user.profile.default_mark_unread = True
+        self.user.profile.save()
+
+        response = self.client.get(reverse("bookmarks:new"))
+        html = response.content.decode()
+
+        self.assertInHTML(
+            '<input type="checkbox" name="unread" value="true" '
+            'id="id_unread" checked="">',
+            html,
+        )
diff --git a/bookmarks/tests/test_settings_general_view.py b/bookmarks/tests/test_settings_general_view.py
index 3f6c4c7c..2598ae77 100644
--- a/bookmarks/tests/test_settings_general_view.py
+++ b/bookmarks/tests/test_settings_general_view.py
@@ -96,6 +96,7 @@ def test_update_profile(self):
             "display_archive_bookmark_action": False,
             "display_remove_bookmark_action": False,
             "permanent_notes": True,
+            "default_mark_unread": True,
             "custom_css": "body { background-color: #000; }",
         }
         response = self.client.post(reverse("bookmarks:settings.general"), form_data)
@@ -155,6 +156,9 @@ def test_update_profile(self):
         self.assertEqual(
             self.user.profile.permanent_notes, form_data["permanent_notes"]
         )
+        self.assertEqual(
+            self.user.profile.default_mark_unread, form_data["default_mark_unread"]
+        )
         self.assertEqual(self.user.profile.custom_css, form_data["custom_css"])
         self.assertSuccessMessage(html, "Profile updated")