mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-21 16:55:38 +03:00
[chore/frontend] Refactor status templates slightly, put polls behind CWs if present (#2419)
* [chore/frontend] Reorder templates, allow polls to be inside content warnings * show when multiple-choice
This commit is contained in:
parent
18d850ebae
commit
dacfd413dc
6 changed files with 245 additions and 180 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
</details>
|
||||
{{- else }}
|
||||
<div class="text">
|
||||
<div class="content" lang="{{- .LanguageTag.TagStr -}}">
|
||||
{{ emojify .Emojis (noescape .Content) }}
|
||||
</div>
|
||||
{{- if .Poll }}
|
||||
{{ template "status_poll.tmpl" . }}
|
||||
{{- 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}}
|
||||
</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>
|
94
web/template/status_attachments.tmpl
Normal file
94
web/template/status_attachments.tmpl
Normal file
|
@ -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 }}
|
|
@ -25,12 +25,17 @@
|
|||
<figure class="poll">
|
||||
<figcaption class="poll-info">
|
||||
<span class="poll-expiry">
|
||||
{{- if .Poll.Expired -}}
|
||||
Poll closed {{- .Poll.ExpiresAt | timestampPrecise -}}
|
||||
{{- else if .Poll.ExpiresAt -}}
|
||||
Poll open until {{- .Poll.ExpiresAt | timestampPrecise -}}
|
||||
{{- if .Poll.Multiple -}}
|
||||
Multiple-choice poll
|
||||
{{- else -}}
|
||||
Infinite poll (no expiry)
|
||||
Poll
|
||||
{{- end -}}
|
||||
{{- if .Poll.Expired -}}
|
||||
closed {{ .Poll.ExpiresAt | timestampPrecise -}}
|
||||
{{- else if .Poll.ExpiresAt -}}
|
||||
open until {{ .Poll.ExpiresAt | timestampPrecise -}}
|
||||
{{- else -}}
|
||||
open forever
|
||||
{{- end -}}
|
||||
</span>
|
||||
<span class="total-votes">Total votes: {{ .Poll.VotesCount }}</span>
|
Loading…
Reference in a new issue