Selaa lähdekoodia

Improve video and error embeds

Zed 4 viikkoa sitten
vanhempi
commit
67f620c83c
5 muutettua tiedostoa jossa 161 lisäystä ja 115 poistoa
  1. 10 5
      src/routes/embed.nim
  2. 0 103
      src/sass/tweet/_base.scss
  3. 131 2
      src/sass/tweet/embed.scss
  4. 19 4
      src/views/embed.nim
  5. 1 1
      src/views/general.nim

+ 10 - 5
src/routes/embed.nim

@@ -32,25 +32,30 @@ proc createEmbedRouter*(cfg: Config) =
   router embed:
     get "/i/videos/tweet/@id":
       let
-        tweet = await getTweetByRestId(@"id")
+        id = @"id"
+        tweet = await getTweetByRestId(id)
         prefs = requestPrefs()
 
       if tweet == nil:
-        resp renderErrorEmbed("Tweet not found", prefs, cfg, request)
+        resp renderErrorEmbed("Tweet not found", prefs, cfg, request, tweetId=id)
 
       if not tweet.hasVideos:
-        resp renderErrorEmbed("No video in tweet", prefs, cfg, request)
+        resp renderErrorEmbed("No video in tweet", prefs, cfg, request,
+                              tweetId=id, username=tweet.user.username)
 
       resp renderVideoEmbed(tweet, cfg, request)
 
     get "/@user/status/@id/embed":
       let
-        tweet = await getTweetByRestId(@"id")
+        id = @"id"
+        user = @"user"
+        tweet = await getTweetByRestId(id)
         prefs = requestPrefs()
         path = getPath()
 
       if tweet == nil:
-        resp renderErrorEmbed("Tweet not found", prefs, cfg, request)
+        resp renderErrorEmbed("Tweet not found", prefs, cfg, request,
+                              tweetId=id, username=user)
 
       resp renderTweetEmbed(tweet, path, prefs, cfg, request)
 

+ 0 - 103
src/sass/tweet/_base.scss

@@ -117,109 +117,6 @@
   }
 }
 
-body:has(> .embed-wrapper),
-body:has(> .tweet-embed) {
-  margin: 0;
-  padding: 0;
-  width: 100%;
-  background: transparent;
-  overflow: hidden;
-}
-
-html:has(body > .embed-wrapper),
-html:has(body > .tweet-embed) {
-  background: transparent;
-  overflow: hidden;
-}
-
-.embed-wrapper {
-  width: 100%;
-  box-sizing: border-box;
-  border: 1px solid var(--border_grey);
-  border-radius: 12px;
-  overflow: hidden;
-
-  .embed-footer {
-    display: block;
-    padding: 12px 16px;
-    border-top: 1px solid var(--border_grey);
-    background: var(--bg_panel);
-    color: var(--accent);
-    font-size: 14px;
-    font-weight: 500;
-    text-align: center;
-    text-decoration: none;
-    transition: background-color 0.15s;
-
-    &:hover {
-      background: var(--bg_hover);
-    }
-  }
-}
-
-.tweet-embed {
-  position: relative;
-  width: 100%;
-  box-sizing: border-box;
-  background-color: var(--bg_panel);
-  transition: background-color 0.15s ease;
-
-  &:hover {
-    background-color: var(--bg_hover);
-  }
-
-  .timeline-item {
-    display: block;
-    pointer-events: none;
-    background-color: transparent;
-  }
-
-  .tweet-link:hover {
-    background-color: transparent;
-  }
-
-  .tweet-content {
-    font-size: 18px;
-  }
-
-  .avatar:not(.mini) {
-    position: absolute;
-  }
-
-  .quote-media-container {
-    max-height: 600px;
-  }
-
-  .quote-media-container .gallery-row .attachment,
-  .quote-media-container .gallery-row .attachment > video,
-  .quote-media-container .gallery-row .attachment > img,
-  .quote-media-container .still-image,
-  .quote-media-container .still-image img,
-  .still-image,
-  .still-image img {
-    max-height: 600px;
-  }
-
-  &.error-embed {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    min-height: 160px;
-    padding: 20px;
-    border: 1px solid var(--border_grey);
-    border-radius: 12px;
-    cursor: default;
-
-    &:hover {
-      background-color: var(--bg_panel);
-    }
-
-    .error-panel {
-      margin: 0;
-    }
-  }
-}
-
 .attribution {
   display: flex;
   pointer-events: all;

+ 131 - 2
src/sass/tweet/embed.scss

@@ -1,21 +1,150 @@
 @import "_variables";
 @import "_mixins";
 
+// Embed page: transparent background, no scrollbars
+html:has(body > .embed-wrapper),
+html:has(body > .embed-video) {
+  background: transparent;
+  overflow: hidden;
+
+  body {
+    background: transparent;
+    overflow: hidden;
+  }
+}
+
+// Tweet embed wrapper
+.embed-wrapper {
+  box-sizing: border-box;
+  border: 1px solid var(--border_grey);
+  border-radius: 12px;
+  overflow: hidden;
+
+  .embed-footer {
+    display: block;
+    padding: 12px 16px;
+    border-top: 1px solid var(--border_grey);
+    background: var(--bg_panel);
+    color: var(--accent);
+    font-size: 14px;
+    font-weight: 500;
+    text-align: center;
+    text-decoration: none;
+    transition: background-color 0.15s;
+
+    &:hover {
+      background: var(--bg_hover);
+    }
+  }
+}
+
+// Tweet embed content
+.tweet-embed {
+  position: relative;
+  background-color: var(--bg_panel);
+  transition: background-color 0.15s;
+
+  &:hover {
+    background-color: var(--bg_hover);
+  }
+
+  .timeline-item {
+    pointer-events: none;
+    background-color: transparent;
+  }
+
+  .tweet-link:hover {
+    background-color: transparent;
+  }
+
+  .tweet-content {
+    font-size: 18px;
+  }
+
+  .avatar:not(.mini) {
+    position: absolute;
+  }
+
+  // Cap media height in embeds
+  .still-image img,
+  .quote-media-container img,
+  .quote-media-container video {
+    max-height: 600px;
+  }
+
+  &.error-embed {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 120px;
+    padding: 20px;
+
+    .error-panel {
+      margin: 0;
+    }
+  }
+}
+
+// Video-only embed
 .embed-video {
-  width: 100%;
+  position: relative;
   background-color: black;
+  border: 1px solid var(--border_grey);
+
+  .attachments {
+    margin: 0;
+    border-radius: 0;
+    max-height: 560px;
+    background-color: unset;
+  }
+
+  .card {
+    margin: 0;
+  }
 
   .gallery-video {
     width: 100%;
   }
 
   .gallery-video > .attachment {
-    max-height: unset;
+    max-height: 560px;
     width: 100%;
   }
 
   video {
     width: 100%;
     height: auto;
+    max-height: 560px;
+    object-fit: contain;
+  }
+
+  .video-download {
+    display: none;
+  }
+
+  .video-overlay-link {
+    position: absolute;
+    top: 12px;
+    right: 12px;
+    padding: 6px 12px;
+    background: rgba(30, 30, 30, 0.75);
+    backdrop-filter: blur(4px);
+    color: #fff;
+    font-size: 13px;
+    font-weight: 700;
+    text-decoration: none;
+    border-radius: 9999px;
+    border: 1px solid transparent;
+    transition: background 0.15s, opacity 0.15s;
+    z-index: 10;
+
+    &:hover {
+      background: rgba(60, 60, 60, 0.9);
+    }
+  }
+
+  &:has(video:playing) .video-overlay-link {
+    opacity: 0;
+    pointer-events: none;
   }
 }

+ 19 - 4
src/views/embed.nim

@@ -15,13 +15,17 @@ proc renderVideoEmbed*(tweet: Tweet; cfg: Config; req: Request): string =
     thumb = video.thumb
     vidUrl = getVideoEmbed(cfg, tweet.id)
     prefs = Prefs(hlsPlayback: true, mp4Playback: true)
+    tweetUrl = getLink(tweet)
 
   let node = buildHtml(html(lang="en")):
     renderHead(prefs, cfg, req, video=vidUrl, images=(@[thumb]))
+    base(target="_blank")
 
     body:
       tdiv(class="embed-video"):
         renderVideo(video, prefs, "")
+        a(class="video-overlay-link", href=tweetUrl):
+          text "Watch on " & cfg.hostname
 
       script:
         verbatim embedResizeJs
@@ -46,14 +50,25 @@ proc renderTweetEmbed*(tweet: Tweet; path: string; prefs: Prefs; cfg: Config; re
 
   result = doctype & $node
 
-proc renderErrorEmbed*(error: string; prefs: Prefs; cfg: Config; req: Request): string =
+proc renderErrorEmbed*(error: string; prefs: Prefs; cfg: Config; req: Request;
+                       tweetId = ""; username = ""): string =
+  let link = if tweetId.len > 0:
+               if username.len > 0: "/" & username & "/status/" & tweetId
+               else: "/i/status/" & tweetId
+             else: "/"
+
   let node = buildHtml(html(lang="en")):
     renderHead(prefs, cfg, req)
+    base(target="_blank")
 
     body:
-      tdiv(class="tweet-embed error-embed"):
-        tdiv(class="error-panel"):
-          span: text error
+      tdiv(class="embed-wrapper"):
+        tdiv(class="tweet-embed error-embed"):
+          a(class="tweet-link", href=link)
+          tdiv(class="error-panel"):
+            span: text error
+        a(class="embed-footer", href=link):
+          text "Read more on " & cfg.hostname
 
       script:
         verbatim embedResizeJs

+ 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=85")
+    link(rel="stylesheet", type="text/css", href="/css/style.css?v=101")
     link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=7")
 
     if theme.len > 0: