diff --git a/src/routes/embed.nim b/src/routes/embed.nim index 5c45f29..5f3e6f3 100644 --- a/src/routes/embed.nim +++ b/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) diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 050eaad..2f6693e 100644 --- a/src/sass/tweet/_base.scss +++ b/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; diff --git a/src/sass/tweet/embed.scss b/src/sass/tweet/embed.scss index 31370e9..9ca1d48 100644 --- a/src/sass/tweet/embed.scss +++ b/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; } } diff --git a/src/views/embed.nim b/src/views/embed.nim index fc5abdf..27fda24 100644 --- a/src/views/embed.nim +++ b/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 diff --git a/src/views/general.nim b/src/views/general.nim index 77e8402..331c1f2 100644 --- a/src/views/general.nim +++ b/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: