diff options
-rw-r--r-- | internal/api/model/attachment.go | 6 | ||||
-rw-r--r-- | internal/typeutils/internaltofrontend.go | 6 | ||||
-rw-r--r-- | web/source/css/status.css | 141 | ||||
-rw-r--r-- | web/template/status.tmpl | 163 | ||||
-rw-r--r-- | web/template/status_attachments.tmpl | 94 | ||||
-rw-r--r-- | web/template/status_poll.tmpl (renamed from web/template/poll.tmpl) | 11 |
6 files changed, 243 insertions, 178 deletions
diff --git a/internal/api/model/attachment.go b/internal/api/model/attachment.go index 1911fc9c3..7e81759f2 100644 --- a/internal/api/model/attachment.go +++ b/internal/api/model/attachment.go @@ -90,6 +90,12 @@ type Attachment struct { // A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet. // See https://github.com/woltapp/blurhash Blurhash *string `json:"blurhash"` + + // Additional fields not exposed via JSON + // (used only internally for templating etc). + + // Parent status of this media is sensitive. + Sensitive bool `json:"-"` } // MediaMeta models media metadata. diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index fe5c8ec8f..b3d263963 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -721,6 +721,12 @@ func (c *Converter) StatusToWebStatus( webStatus.WebPollOptions = webPollOptions } + // Set additional templating + // variables on media attachments. + for _, a := range webStatus.MediaAttachments { + a.Sensitive = webStatus.Sensitive + } + return webStatus, nil } diff --git a/web/source/css/status.css b/web/source/css/status.css index 49ae63641..d2aa658f1 100644 --- a/web/source/css/status.css +++ b/web/source/css/status.css @@ -113,10 +113,17 @@ main { gap: 0.5rem; } - details > summary { + .text-spoiler > summary, .text { + position: relative; + z-index: 2; + } + + .text-spoiler > summary { display: inline-block; list-style: none; + padding-bottom: 0.5rem; + &::-webkit-details-marker { display: none; /* Safari */ } @@ -124,6 +131,8 @@ main { .button { white-space: nowrap; cursor: pointer; + padding: 0.2rem 0.3rem; + font-size: 1rem; } } @@ -132,20 +141,12 @@ main { grid-row: span 1; grid-column: 1 / span 3; - position: relative; - z-index: 2; + display: flex; + flex-direction: column; + gap: 0.5rem; width: 100%; - details > summary { - padding-bottom: 0.5rem; - - .button { - padding: 0.2rem 0.3rem; - font-size: 1rem; - } - } - a { color: $link-fg; text-decoration: underline; @@ -239,6 +240,64 @@ main { border: $boxshadow-border; border-radius: $br-inner; } + + .poll { + background-color: $gray2; + z-index: 2; + + display: flex; + flex-direction: column; + border-radius: $br; + padding: 0.5rem; + margin: 0; + gap: 1rem; + + .poll-options { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 1rem; + + .poll-option { + display: flex; + flex-direction: column; + gap: 0.1rem; + + label { + cursor: default; + } + + meter { + width: 100%; + } + + .poll-vote-summary { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + white-space: nowrap; + } + } + } + + .poll-info { + background-color: $gray4; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + border-radius: $br-inner; + padding: 0.25rem; + gap: 0.25rem; + + span { + justify-self: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } } .media { @@ -391,64 +450,6 @@ main { } } - .poll { - background-color: $gray2; - z-index: 2; - - display: flex; - flex-direction: column; - border-radius: $br; - padding: 0.5rem; - margin: 0; - gap: 1rem; - - .poll-options { - margin: 0; - padding: 0; - display: flex; - flex-direction: column; - gap: 1rem; - - .poll-option { - display: flex; - flex-direction: column; - gap: 0.1rem; - - label { - cursor: default; - } - - meter { - width: 100%; - } - - .poll-vote-summary { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - white-space: nowrap; - } - } - } - - .poll-info { - background-color: $gray4; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - border-radius: $br-inner; - padding: 0.25rem; - gap: 0.25rem; - - span { - justify-self: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - } - .info { display: flex; background: $toot-info-bg; diff --git a/web/template/status.tmpl b/web/template/status.tmpl index 59725a470..d4df926f0 100644 --- a/web/template/status.tmpl +++ b/web/template/status.tmpl @@ -17,127 +17,80 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. */ -}} +<a data-nosnippet href="{{- .URL -}}" class="toot-link">Open thread</a> <section class="author"> - <a href="{{.Account.URL}}"> - <img class="avatar" src="{{.Account.Avatar}}" alt=""> - <span class="displayname"> - {{if .Account.DisplayName}} - {{emojify .Account.Emojis (escape .Account.DisplayName)}} - {{else}} - {{.Account.Username}} - {{end}} - <span class="sr-only">.</span> + <a href="{{- .Account.URL -}}"> + <img class="avatar" src="{{- .Account.Avatar -}}" alt=""> + <span aria-hidden="true" class="displayname"> + {{- if .Account.DisplayName -}} + {{- emojify .Account.Emojis (escape .Account.DisplayName) -}} + {{- else -}} + {{- .Account.Username -}} + {{- end -}} + </span> + <span aria-hidden="true" class="username">@{{- .Account.Username -}}</span> + <span class="sr-only"> + {{- if .Account.DisplayName -}} + {{- emojify .Account.Emojis (escape .Account.DisplayName) -}}. Username: @{{ .Account.Acct -}}. + {{- else -}} + @{{- .Account.Acct -}}. + {{- end -}} </span> - <span class="username">@{{.Account.Username}}<span class="sr-only">, </span>{{acctInstance .Account.Acct}}</span> </a> </section> <section class="body"> - <div class="text"> - {{if .SpoilerText}} - <details class="text-spoiler"> - <summary> - <span class="spoiler-text" lang="{{ .LanguageTag.TagStr }}">{{emojify .Emojis (escape .SpoilerText)}}</span> - <span class="button" role="button" tabindex="0">Toggle visibility</span> - </summary> - <div class="content" lang="{{ .LanguageTag.TagStr }}"> - {{emojify .Emojis (noescape .Content)}} + {{- if .SpoilerText }} + <details class="text-spoiler"> + <summary> + <span class="spoiler-text" lang="{{- .LanguageTag.TagStr -}}">{{- emojify .Emojis (escape .SpoilerText) -}}</span> + <span class="button" role="button" tabindex="0">Toggle visibility</span> + </summary> + <div class="text"> + <div class="content" lang="{{- .LanguageTag.TagStr -}}"> + {{ emojify .Emojis (noescape .Content) }} </div> - </details> - {{else}} - <div class="content" lang="{{ .LanguageTag.TagStr }}"> - {{emojify .Emojis (noescape .Content)}} + {{- if .Poll }} + {{ template "status_poll.tmpl" . }} + {{- end }} </div> - {{end}} - </div> - {{with .MediaAttachments}} - <div - class="media photoswipe-gallery {{(len .) | oddOrEven }}{{if eq (len .) 1}} single{{end}}{{if eq (len .) 2}} double{{end}}"> - {{range $index, $media := .}} - {{with $media}} - <div class="media-wrapper"> - <details class="{{.Type}}-spoiler media-spoiler" {{if not $.Sensitive}}open{{end}}> - <summary> - <div class="show sensitive button" aria-hidden="true"> - Show sensitive media - </div> - <span class="eye button" role="button" tabindex="0" aria-label="Toggle media"> - <i class="hide fa fa-fw fa-eye-slash" aria-hidden="true"></i> - <i class="show fa fa-fw fa-eye" aria-hidden="true"></i> - </span> - - {{if eq .Type "video"}} - <video {{if .Description}} title="{{.Description}}" {{end}}> - <source type="video/mp4" src="{{.URL}}" /> - </video> - {{else if eq .Type "image"}} - <img {{if .Description}} title="{{.Description}}" {{end}} src="{{.PreviewURL}}" /> - {{end}} - </summary> - {{if eq .Type "video"}} - <video class="plyr-video photoswipe-slide" controls {{if .Description}}alt="{{.Description}}" - title="{{.Description}}" {{end}} data-pswp-index="{{$index}}" data-pswp-width="{{.Meta.Original.Width}}px" - data-pswp-height="{{.Meta.Original.Height}}px"> - <source type="video/mp4" src="{{.URL}}" /> - </video> - {{else if eq .Type "image"}} - <a class="photoswipe-slide" href="{{.URL}}" target="_blank" {{if .Description}}title="{{.Description}}" {{end}} - data-pswp-width="{{.Meta.Original.Width}}px" data-pswp-height="{{.Meta.Original.Height}}px" - data-cropped="true"> - <img src="{{.PreviewURL}}" {{if .Description}}alt="{{.Description}}" {{end}} /> - </a> - {{else}} - <a - class="unknown-attachment" - href="{{.RemoteURL}}" - target="_blank" - {{if .Description}} - title="Link to external media: {{.Description}} {{.RemoteURL}}" - {{else}} - title="Link to external media. {{.RemoteURL}}" - {{end}} - > - <div class="placeholder" aria-hidden="true"> - <i class="placeholder-external-link fa fa-external-link"></i> - <i class="placeholder-icon fa fa-file-text"></i> - <div class="placeholder-link-to">External media</div> - </div> - </a> - {{end}} - </details> - </div> - {{end}} - {{end}} + </details> + {{- else }} + <div class="text"> + <div class="content" lang="{{- .LanguageTag.TagStr -}}"> + {{ emojify .Emojis (noescape .Content) }} + </div> + {{- if .Poll }} + {{ template "status_poll.tmpl" . }} + {{- end }} </div> - {{end}} - {{- if .Poll -}}{{ template "poll.tmpl" . }}{{ end -}} + {{- end }} + {{- if .MediaAttachments }} + {{ template "status_attachments.tmpl" . }} + {{- end }} </section> <aside class="info"> - <time datetime="{{.CreatedAt}}">{{.CreatedAt | timestampPrecise}}</time> + <time datetime="{{- .CreatedAt -}}">{{- .CreatedAt | timestampPrecise -}}</time> <div class="stats" role="group"> <div class="stats-item"> - <span aria-hidden="true"><i class="fa fa-reply-all"></i> {{.RepliesCount}}</span> - <span class="sr-only">{{.RepliesCount}} {{if .RepliesCount | eq 1}}reply{{else}}replies{{end}}</span> + <span aria-hidden="true"><i class="fa fa-reply-all"></i> {{ .RepliesCount -}}</span> + <span class="sr-only">{{- .RepliesCount }} {{ if .RepliesCount | eq 1 }}reply{{ else }}replies{{ end -}}</span> </div> <div class="stats-item"> - <span aria-hidden="true"><i class="fa fa-star"></i> {{.FavouritesCount}}</span> - <span class="sr-only">{{.FavouritesCount}} favourite{{if .FavouritesCount | eq 1 | not}}s{{end}}</span> + <span aria-hidden="true"><i class="fa fa-star"></i> {{ .FavouritesCount -}}</span> + <span class="sr-only">{{- .FavouritesCount }} {{ if .FavouritesCount | eq 1 }}favourite{{ else }}favourites{{ end -}}</span> </div> <div class="stats-item"> - <span aria-hidden="true"><i class="fa fa-retweet"></i> {{.ReblogsCount}}</span> - <span class="sr-only">{{.ReblogsCount}} boost{{if .ReblogsCount | eq 1 | not}}s{{end}}</span> + <span aria-hidden="true"><i class="fa fa-retweet"></i> {{ .ReblogsCount -}}</span> + <span class="sr-only">{{- .ReblogsCount }} {{ if .ReblogsCount | eq 1 }}boost{{ else }}boosts{{ end -}}</span> </div> - {{if .Pinned}} - <div class="stats-item"> - <i class="fa fa-thumb-tack" aria-hidden="true"></i> - <span class="sr-only">pinned</span> - </div> - {{end}} - {{ if .LanguageTag.DisplayStr }} - <div class="stats-item language" title="Language: {{ .LanguageTag.DisplayStr }}"> - {{ .LanguageTag.TagStr }} - </div> - {{ end }} + {{- if .Pinned }} + <div class="stats-item"> + <i class="fa fa-thumb-tack" aria-hidden="true"></i> + <span class="sr-only">pinned</span> + </div> + {{- end }} + {{- if .LanguageTag.DisplayStr }} + <div class="stats-item language" title="Language: {{ .LanguageTag.DisplayStr }}">{{ .LanguageTag.TagStr }}</div> + {{- end }} </div> </aside> -<a data-nosnippet href="{{.URL}}" class="toot-link">Open - thread</a>
\ No newline at end of file diff --git a/web/template/status_attachments.tmpl b/web/template/status_attachments.tmpl new file mode 100644 index 000000000..bd26c82a6 --- /dev/null +++ b/web/template/status_attachments.tmpl @@ -0,0 +1,94 @@ +{{- /* +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ -}} + +{{- /* + Template for rendering a gallery of status media attachments. + To use this template, pass a web view status into it. +*/ -}} + +{{ with .MediaAttachments }} + <div class="media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{ else if eq (len .) 2 }}double{{- end -}}"> + {{- range $index, $media := . }} + <div class="media-wrapper"> + <details class="{{- $media.Type -}}-spoiler media-spoiler" {{- if not $media.Sensitive }} open{{ end -}}> + <summary> + <div class="show sensitive button" aria-hidden="true">Show sensitive media</div> + <span class="eye button" role="button" tabindex="0" aria-label="Toggle media"> + <i class="hide fa fa-fw fa-eye-slash" aria-hidden="true"></i> + <i class="show fa fa-fw fa-eye" aria-hidden="true"></i> + </span> + {{- if eq .Type "video" }} + <video {{- if .Description }} title="{{- $media.Description -}}" {{- end -}}> + <source type="video/mp4" src="{{- $media.URL -}}"/> + </video> + {{- else if eq .Type "image" }} + <img src="{{- $media.PreviewURL -}}" {{- if .Description }} title="{{- $media.Description -}}" {{- end }}/> + {{- end }} + </summary> + {{- if eq .Type "video" }} + <video + class="plyr-video photoswipe-slide" + controls + data-pswp-index="{{- $index -}}" + data-pswp-width="{{- $media.Meta.Original.Width -}}px" + data-pswp-height="{{- $media.Meta.Original.Height -}}px" + {{- if .Description }} + alt="{{- $media.Description -}}" + title="{{- $media.Description -}}" + {{- end }} + > + <source type="video/mp4" src="{{- $media.URL -}}"/> + </video> + {{- else if eq .Type "image" }} + <a + class="photoswipe-slide" + href="{{- $media.URL -}}" + target="_blank" + data-pswp-width="{{- $media.Meta.Original.Width -}}px" + data-pswp-height="{{- $media.Meta.Original.Height -}}px" + data-cropped="true" + {{- if .Description }} + title="{{- $media.Description -}}" + {{- end }} + > + <img src="{{$media.PreviewURL}}" {{if .Description}}alt="{{$media.Description}}" {{end}} /> + </a> + {{- else }} + <a + class="unknown-attachment" + href="{{- $media.RemoteURL -}}" + target="_blank" + {{- if .Description }} + title="Link to external media: {{ $media.Description -}} {{- $media.RemoteURL -}}" + {{- else }} + title="Link to external media. {{- $media.RemoteURL -}}" + {{- end }} + > + <div class="placeholder" aria-hidden="true"> + <i class="placeholder-external-link fa fa-external-link"></i> + <i class="placeholder-icon fa fa-file-text"></i> + <div class="placeholder-link-to">External media</div> + </div> + </a> + {{- end }} + </details> + </div> + {{- end }} + </div> +{{- end }} diff --git a/web/template/poll.tmpl b/web/template/status_poll.tmpl index 7aa323e4f..f6acecfa0 100644 --- a/web/template/poll.tmpl +++ b/web/template/status_poll.tmpl @@ -25,12 +25,17 @@ <figure class="poll"> <figcaption class="poll-info"> <span class="poll-expiry"> + {{- if .Poll.Multiple -}} + Multiple-choice poll + {{- else -}} + Poll + {{- end -}} {{- if .Poll.Expired -}} - Poll closed {{- .Poll.ExpiresAt | timestampPrecise -}} + closed {{ .Poll.ExpiresAt | timestampPrecise -}} {{- else if .Poll.ExpiresAt -}} - Poll open until {{- .Poll.ExpiresAt | timestampPrecise -}} + open until {{ .Poll.ExpiresAt | timestampPrecise -}} {{- else -}} - Infinite poll (no expiry) + open forever {{- end -}} </span> <span class="total-votes">Total votes: {{ .Poll.VotesCount }}</span> |