summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/api/model/attachment.go6
-rw-r--r--internal/typeutils/internaltofrontend.go6
-rw-r--r--web/source/css/status.css141
-rw-r--r--web/template/status.tmpl163
-rw-r--r--web/template/status_attachments.tmpl94
-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}}&#10;&#13;{{.RemoteURL}}"
- {{else}}
- title="Link to external media.&#10;&#13;{{.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 -}}&#10;&#13;{{- $media.RemoteURL -}}"
+ {{- else }}
+ title="Link to external media.&#10;&#13;{{- $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&nbsp;
+ {{- else -}}
+ Poll&nbsp;
+ {{- end -}}
{{- if .Poll.Expired -}}
- Poll closed&nbsp;{{- .Poll.ExpiresAt | timestampPrecise -}}
+ closed {{ .Poll.ExpiresAt | timestampPrecise -}}
{{- else if .Poll.ExpiresAt -}}
- Poll open until&nbsp;{{- .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>