Repository: sissbruecker/linkding
Pull request #842 (2024-09-20 06:50:29)

Show placeholder if there is no preview image


Patch:
diff --git a/bookmarks/static/preview-placeholder.svg b/bookmarks/static/preview-placeholder.svg
new file mode 100644
index 0000000..1bf4597
--- /dev/null
+++ b/bookmarks/static/preview-placeholder.svg
@@ -0,0 +1,1 @@
+<svg  xmlns="http://www.w3.org/2000/svg"  width="24"  height="24"  viewBox="0 0 24 24"  fill="none"  stroke="currentColor"  stroke-width="2"  stroke-linecap="round"  stroke-linejoin="round"  class="icon icon-tabler icons-tabler-outline icon-tabler-photo"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 8h.01" /><path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z" /><path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5" /><path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3" /></svg>
\ No newline at end of file
diff --git a/bookmarks/styles/bookmark-page.css b/bookmarks/styles/bookmark-page.css
index 8adc88d..093624a 100644
--- a/bookmarks/styles/bookmark-page.css
+++ b/bookmarks/styles/bookmark-page.css
@@ -154,14 +154,29 @@ li[ld-bookmark-item] {
         min-width: 0;
     }
 
-    & img.preview-image {
+    & .preview-image {
         flex: 0 0 auto;
         width: 100px;
         height: 60px;
         margin-top: var(--unit-h);
-        object-fit: cover;
         border-radius: var(--border-radius);
         border: solid 1px var(--border-color);
+        object-fit: cover;
+
+        &.placeholder {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background: var(--body-color-contrast);
+
+            & .img {
+                width: var(--unit-12);
+                height: var(--unit-12);
+                background-color: var(--tertiary-text-color);
+                -webkit-mask: url(preview-placeholder.svg) no-repeat center;
+                mask: url(preview-placeholder.svg) no-repeat center;
+            }
+        }
     }
 
     & .form-checkbox.bulk-edit-checkbox {
diff --git a/bookmarks/templates/bookmarks/bookmark_list.html b/bookmarks/templates/bookmarks/bookmark_list.html
index d68bdc1..e115975 100644
--- a/bookmarks/templates/bookmarks/bookmark_list.html
+++ b/bookmarks/templates/bookmarks/bookmark_list.html
@@ -142,8 +142,14 @@
             {% endif %}
           </div>
         </div>
-        {% if bookmark_list.show_preview_images and bookmark_item.preview_image_file %}
-          <img class="preview-image" src="{% static bookmark_item.preview_image_file %}" loading="lazy"/>
+        {% if bookmark_list.show_preview_images %}
+          {% if bookmark_item.preview_image_file %}
+            <img class="preview-image" src="{% static bookmark_item.preview_image_file %}" loading="lazy"/>
+          {% else %}
+            <div class="preview-image placeholder">
+              <div class="img"/>
+            </div>
+          {% endif %}
         {% endif %}
       </li>
     {% endfor %}


Test patch:
diff --git a/bookmarks/tests/test_bookmarks_list_template.py b/bookmarks/tests/test_bookmarks_list_template.py
index 8115bcc..5b24914 100644
--- a/bookmarks/tests/test_bookmarks_list_template.py
+++ b/bookmarks/tests/test_bookmarks_list_template.py
@@ -171,6 +171,11 @@ class BookmarkListTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
         self.assertIsNotNone(preview_image)
         self.assertEqual(preview_image["src"], url)
 
+    def assertPreviewImagePlaceholder(self, html: str):
+        soup = self.make_soup(html)
+        placeholder = soup.select_one(".preview-image.placeholder")
+        self.assertIsNotNone(placeholder)
+
     def assertBookmarkURLCount(
         self, html: str, bookmark: Bookmark, link_target: str = "_blank", count=0
     ):
@@ -691,15 +696,15 @@ class BookmarkListTemplateTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
 
         self.assertPreviewImageHidden(html, bookmark)
 
-    def test_preview_image_should_be_hidden_when_there_is_no_preview_image(self):
+    def test_preview_image_shows_placeholder_when_there_is_no_preview_image(self):
         profile = self.get_or_create_test_user().profile
         profile.enable_preview_images = True
         profile.save()
 
-        bookmark = self.setup_bookmark()
+        self.setup_bookmark()
         html = self.render_template()
 
-        self.assertPreviewImageHidden(html, bookmark)
+        self.assertPreviewImagePlaceholder(html)
 
     def test_favicon_should_be_visible_when_favicons_enabled(self):
         profile = self.get_or_create_test_user().profile