Bladeren bron

Fix mobile gallery and grid, add size preference

Fixes #1379
Zed 5 maanden geleden
bovenliggende
commit
fea6f59005
7 gewijzigde bestanden met toevoegingen van 51 en 40 verwijderingen
  1. 8 1
      public/js/infiniteScroll.js
  2. 4 0
      src/prefs_impl.nim
  3. 9 20
      src/sass/timeline.scss
  4. 1 1
      src/views/general.nim
  5. 7 0
      src/views/renderutils.nim
  6. 7 5
      src/views/timeline.nim
  7. 15 13
      src/views/tweet.nim

+ 8 - 1
public/js/infiniteScroll.js

@@ -27,6 +27,13 @@ const GAP = 10;
 class Masonry {
   constructor(container) {
     this.container = container;
+    const colSizes = {
+      small:  w => Math.max(130, w * 0.11),
+      medium: w => Math.max(190, Math.min(350, w * 0.22)),
+      large:  w => Math.max(350, Math.min(480, w * 0.22)),
+    };
+    const size = container.dataset.colSize || "medium";
+    this._targetWidth = colSizes[size] || colSizes.medium;
     this.colHeights = [];
     this.colCounts = [];
     this.colCount = 0;
@@ -90,8 +97,8 @@ class Masonry {
   }
 
   _rebuild() {
-    const n = Math.max(1, Math.floor(this.container.clientWidth / 350));
     const w = this.container.clientWidth;
+    const n = Math.max(1, Math.floor(w / this._targetWidth(w)));
     if (n === this.colCount && w === this._lastWidth) return;
 
     const isFirst = this.colCount === 0;

+ 4 - 0
src/prefs_impl.nim

@@ -103,6 +103,10 @@ genPrefs:
     compactGallery(checkbox, false):
       "Compact media gallery (no profile info or text)"
 
+    gallerySize(select, "Medium"):
+      "Gallery column size"
+      options: @["Small", "Medium", "Large"]
+
     mediaView(select, "Timeline"):
       "Default media view"
       options: @["Timeline", "Grid", "Gallery"]

+ 9 - 20
src/sass/timeline.scss

@@ -293,7 +293,15 @@
   .gallery-masonry {
     margin: 10px 0;
     column-gap: 10px;
-    column-width: 350px;
+    column-width: unquote("clamp(190px, 22vw, 350px)");
+
+    &[data-col-size="small"] {
+      column-width: unquote("max(130px, 11vw)");
+    }
+
+    &[data-col-size="large"] {
+      column-width: unquote("clamp(350px, 22vw, 480px)");
+    }
 
     &.masonry-active {
       column-width: unset;
@@ -470,27 +478,8 @@
   }
 }
 
-@media (max-width: 900px) {
-  .timeline.media-grid-view {
-    grid-template-columns: repeat(2, minmax(0, 1fr));
-  }
-}
-
 @media (max-width: 520px) {
-  .timeline.media-grid-view {
-    grid-template-columns: 1fr;
-  }
-
   .timeline.media-gallery-view {
     padding: 8px 0;
-
-    .gallery-masonry {
-      columns: 1;
-      column-gap: 0;
-
-      &.masonry-active {
-        columns: unset;
-      }
-    }
   }
 }

+ 1 - 1
src/views/general.nim

@@ -50,7 +50,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
   let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
 
   buildHtml(head):
-    link(rel="stylesheet", type="text/css", href="/css/style.css?v=31")
+    link(rel="stylesheet", type="text/css", href="/css/style.css?v=32")
     link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=5")
 
     if theme.len > 0:

+ 7 - 0
src/views/renderutils.nim

@@ -4,6 +4,7 @@ import karax/[karaxdsl, vdom, vstyles]
 import ".."/[types, utils]
 
 const smallWebp* = "?name=small&format=webp"
+const mediumWebp* = "?name=medium&format=webp"
 
 proc getSmallPic*(url: string): string =
   result = url
@@ -11,6 +12,12 @@ proc getSmallPic*(url: string): string =
     result &= smallWebp
   result = getPicUrl(result)
 
+proc getMediumPic*(url: string): string =
+  result = url
+  if "?" notin url and not url.endsWith("placeholder.png"):
+    result &= mediumWebp
+  result = getPicUrl(result)
+
 proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
   var c = "icon-" & icon
   if class.len > 0: c = &"{c} {class}"

+ 7 - 5
src/views/timeline.nim

@@ -65,7 +65,7 @@ proc renderNoneFound(): VNode =
     h2(class="timeline-none"):
       text "No items found"
 
-proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode =
+proc renderThread(thread: Tweets; prefs: Prefs; path: string; bigThumb=false): VNode =
   buildHtml(tdiv(class="thread-line")):
     let sortedThread = thread.sortedByIt(it.id)
     for i, tweet in sortedThread:
@@ -79,7 +79,7 @@ proc renderThread(thread: Tweets; prefs: Prefs; path: string): VNode =
       let show = i == thread.high and sortedThread[0].id != tweet.threadId
       let header = if tweet.pinned or tweet.retweet.isSome: "with-header " else: ""
       renderTweet(tweet, prefs, path, class=(header & "thread"),
-                  index=i, last=(i == thread.high))
+                  index=i, last=(i == thread.high), bigThumb=bigThumb)
 
 proc renderUser(user: User; prefs: Prefs): VNode =
   buildHtml(tdiv(class="timeline-item", data-username=user.username)):
@@ -146,10 +146,12 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string;
       let filtered = filterThreads(results.content, prefs)
 
       if results.query.view == "gallery":
-        tdiv(class=if prefs.compactGallery: "gallery-masonry compact" else: "gallery-masonry"):
+        let bigThumb = prefs.gallerySize == "Large"
+        let galClass = if prefs.compactGallery: "gallery-masonry compact" else: "gallery-masonry"
+        tdiv(class=galClass, `data-col-size`=prefs.gallerySize.toLowerAscii):
           for thread in filtered:
-            if thread.len == 1: renderTweet(thread[0], prefs, path)
-            else: renderThread(thread, prefs, path)
+            if thread.len == 1: renderTweet(thread[0], prefs, path, bigThumb=bigThumb)
+            else: renderThread(thread, prefs, path, bigThumb)
       else:
         for thread in filtered:
           if thread.len == 1: renderTweet(thread[0], prefs, path)

+ 15 - 13
src/views/tweet.nim

@@ -42,13 +42,15 @@ proc renderAltText(altText: string): VNode =
   buildHtml(p(class="alt-text")):
     text "ALT  " & altText
 
-proc renderPhotoAttachment(photo: Photo): VNode =
+proc renderPhotoAttachment(photo: Photo; bigThumb=false): VNode =
   buildHtml(tdiv(class="attachment")):
     let
       named = "name=" in photo.url
-      small = if named: photo.url else: photo.url & smallWebp
+      thumb = if named: photo.url
+              elif bigThumb: photo.url & mediumWebp
+              else: photo.url & smallWebp
     a(href=getOrigPicUrl(photo.url), class="still-image", target="_blank"):
-      genImg(small, alt=photo.altText)
+      genImg(thumb, alt=photo.altText)
     if photo.altText.len > 0:
       renderAltText(photo.altText)
 
@@ -76,11 +78,11 @@ proc renderVideoUnavailable(video: Video): VNode =
     else:
       p: text "This media is unavailable"
 
-proc renderVideoAttachment(videoData: Video; prefs: Prefs; path=""): VNode =
+proc renderVideoAttachment(videoData: Video; prefs: Prefs; path=""; bigThumb=false): VNode =
   let
     playbackType = if not prefs.proxyVideos and videoData.hasMp4Url: mp4
                    else: videoData.playbackType
-    thumb = getSmallPic(videoData.thumb)
+    thumb = if bigThumb: getMediumPic(videoData.thumb) else: getSmallPic(videoData.thumb)
 
   buildHtml(tdiv(class="attachment")):
     if not videoData.available:
@@ -106,12 +108,12 @@ proc renderVideoAttachment(videoData: Video; prefs: Prefs; path=""): VNode =
         tdiv(class="overlay-duration"): text getDuration(videoData)
         verbatim "</div>"
 
-proc renderVideo*(video: Video; prefs: Prefs; path: string): VNode =
+proc renderVideo*(video: Video; prefs: Prefs; path: string; bigThumb=false): VNode =
   let hasCardContent = video.description.len > 0 or video.title.len > 0
 
   buildHtml(tdiv(class="attachments card")):
     tdiv(class=("gallery-video" & (if hasCardContent: " card-container" else: ""))):
-      renderVideoAttachment(video, prefs, path)
+      renderVideoAttachment(video, prefs, path, bigThumb)
       if hasCardContent:
         tdiv(class="card-content"):
           h2(class="card-title"): text video.title
@@ -138,14 +140,14 @@ proc renderGif(gif: Gif; prefs: Prefs): VNode =
   buildHtml(tdiv(class="attachments media-gif")):
     renderGifAttachment(gif, prefs)
 
-proc renderMedia(media: seq[Media]; prefs: Prefs; path: string): VNode =
+proc renderMedia(media: seq[Media]; prefs: Prefs; path: string; bigThumb=false): VNode =
   if media.len == 0:
     return nil
 
   if media.len == 1:
     let item = media[0]
     if item.kind == videoMedia:
-      return renderVideo(item.video, prefs, path)
+      return renderVideo(item.video, prefs, path, bigThumb)
     if item.kind == gifMedia:
       return renderGif(item.gif, prefs)
 
@@ -162,9 +164,9 @@ proc renderMedia(media: seq[Media]; prefs: Prefs; path: string): VNode =
         for mediaItem in mediaGroup:
           case mediaItem.kind
           of photoMedia:
-            renderPhotoAttachment(mediaItem.photo)
+            renderPhotoAttachment(mediaItem.photo, bigThumb)
           of videoMedia:
-            renderVideoAttachment(mediaItem.video, prefs, path)
+            renderVideoAttachment(mediaItem.video, prefs, path, bigThumb)
           of gifMedia:
             renderGifAttachment(mediaItem.gif, prefs)
 
@@ -336,7 +338,7 @@ proc renderLocation*(tweet: Tweet): string =
   return $node
 
 proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
-                  last=false; mainTweet=false; afterTweet=false): VNode =
+                  last=false; mainTweet=false; afterTweet=false; bigThumb=false): VNode =
   var divClass = class
   if index == -1 or last:
     divClass = "thread-last " & class
@@ -389,7 +391,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
         renderCard(tweet.card.get(), prefs, path)
 
       if tweet.media.len > 0:
-        renderMedia(tweet.media, prefs, path)
+        renderMedia(tweet.media, prefs, path, bigThumb)
 
       if tweet.poll.isSome:
         renderPoll(tweet.poll.get())