summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/assets/themes/ecks-pee.css25
-rw-r--r--web/assets/themes/midnight-trip.css10
-rw-r--r--web/assets/themes/moonlight-hunt.css6
-rw-r--r--web/assets/themes/soft.css6
-rw-r--r--web/source/css/_media-wrapper.css3
-rw-r--r--web/source/css/status.css166
-rw-r--r--web/source/css/thread.css13
-rw-r--r--web/source/frontend/index.js22
-rw-r--r--web/source/settings/components/status.tsx113
-rw-r--r--web/template/status.tmpl22
-rw-r--r--web/template/status_info.tmpl161
-rw-r--r--web/template/thread.tmpl4
12 files changed, 408 insertions, 143 deletions
diff --git a/web/assets/themes/ecks-pee.css b/web/assets/themes/ecks-pee.css
index a85e5da0b..bd479c67b 100644
--- a/web/assets/themes/ecks-pee.css
+++ b/web/assets/themes/ecks-pee.css
@@ -238,6 +238,31 @@ blockquote {
border-right: 1px solid #001ea0;
}
+/* Status info dropdown button */
+.status .status-info .status-stats details.stats-more-info > summary {
+ color: var(--button-fg);
+ background: var(--ecks-pee-start-button);
+ border-left: 1px solid var(--ecks-pee-darkest-green);
+ border-right: 1px solid var(--ecks-pee-darkest-green);
+}
+.status .status-info .status-stats details.stats-more-info > summary:hover {
+ outline: 0;
+ background: var(--ecks-pee-light-green);
+}
+
+/* Status info dropdown content */
+.status .status-info .status-stats .stats-more-info-content,
+.status.expanded .status-info .status-stats .stats-more-info-content {
+ color: black;
+ text-shadow: none;
+ background: var(--ecks-pee-beige);
+ border: 0.2rem outset var(--ecks-pee-darker-beige);
+ border-radius: 0;
+}
+.status .status-info .status-stats .stats-item.edit-timeline {
+ border-top: var(--ecks-pee-dotted-trim);
+}
+
/* Button stuff */
button, .button {
border-left: 1px solid var(--ecks-pee-darkest-green);
diff --git a/web/assets/themes/midnight-trip.css b/web/assets/themes/midnight-trip.css
index 059e4ac8e..3f8619098 100644
--- a/web/assets/themes/midnight-trip.css
+++ b/web/assets/themes/midnight-trip.css
@@ -129,6 +129,16 @@ html, body {
background: black;
}
+/* Status info dropdown content */
+.status.expanded .status-info .status-stats .stats-more-info-content,
+.status .status-info .status-stats .stats-more-info-content {
+ background-color: black;
+ border: 0.25rem solid var(--magenta);
+}
+.status .status-info .status-stats .stats-item.edit-timeline {
+ border-top: 0.15rem dotted var(--acid-green);
+}
+
/* Back + next links */
.backnextlinks {
background: var(--gray1);
diff --git a/web/assets/themes/moonlight-hunt.css b/web/assets/themes/moonlight-hunt.css
index 630c7cd21..8dcfb0bb6 100644
--- a/web/assets/themes/moonlight-hunt.css
+++ b/web/assets/themes/moonlight-hunt.css
@@ -143,6 +143,12 @@ blockquote {
background: var(--outer-space);
}
+/* Status info dropdown content */
+.status.expanded .status-info .status-stats .stats-more-info-content,
+.status .status-info .status-stats .stats-more-info-content {
+ background: var(--outer-space);
+}
+
/* Make show more/less buttons more legible */
.status .button {
border: 1px solid var(--feral-orange);
diff --git a/web/assets/themes/soft.css b/web/assets/themes/soft.css
index 691558bee..01a8729f7 100644
--- a/web/assets/themes/soft.css
+++ b/web/assets/themes/soft.css
@@ -142,3 +142,9 @@ code, code[class*="language-"] {
blockquote {
background-color: var(--soft-lilac-translucent);
}
+
+/* Status info dropdown content */
+.status.expanded .status-info .status-stats .stats-more-info-content,
+.status .status-info .status-stats .stats-more-info-content {
+ background: var(--soft-pink);
+}
diff --git a/web/source/css/_media-wrapper.css b/web/source/css/_media-wrapper.css
index 561ae1ed3..b8541df4b 100644
--- a/web/source/css/_media-wrapper.css
+++ b/web/source/css/_media-wrapper.css
@@ -29,7 +29,6 @@
border-radius: $br;
position: relative;
overflow: hidden;
- z-index: 2;
img {
width: 100%;
@@ -59,8 +58,8 @@
position: absolute;
height: 100%;
width: 100%;
- z-index: 3;
overflow: hidden;
+ z-index: 1;
display: grid;
padding: 1rem;
diff --git a/web/source/css/status.css b/web/source/css/status.css
index 91665cd45..81ee7601a 100644
--- a/web/source/css/status.css
+++ b/web/source/css/status.css
@@ -28,8 +28,6 @@
padding-top: 0.75rem;
a {
- position: relative;
- z-index: 1;
color: inherit;
text-decoration: none;
}
@@ -109,11 +107,6 @@
gap: 0.5rem;
}
- .text-spoiler > summary, .text {
- position: relative;
- z-index: 2;
- }
-
.text-spoiler > summary {
list-style: none;
display: flex;
@@ -193,7 +186,6 @@
.poll {
background-color: $gray2;
- z-index: 2;
display: flex;
flex-direction: column;
@@ -260,59 +252,150 @@
display: flex;
gap: 1rem;
- .stats-grouping {
+ .stats-grouping,
+ .stats-more-info-content {
display: flex;
flex-wrap: wrap;
- column-gap: 1rem;
+ }
- .edited-at {
- font-size: smaller;
- }
+ .stats-grouping {
+ column-gap: 1rem;
+ row-gap: 0.25rem;
}
.stats-item {
display: flex;
gap: 0.4rem;
+ width: fit-content;
}
- .stats-item.published-at {
- text-decoration: underline;
+ details.stats-more-info {
+ margin-left: auto;
+
+ & > summary {
+ display: flex;
+
+ /*
+ Make it easy to touch.
+ */
+ width: 3rem;
+ height: 2rem;
+ margin: -0.25rem -0.5rem;
+
+ /*
+ Remove details/summary
+ arrow and use our own.
+ */
+ list-style: none;
+ &::-webkit-details-marker {
+ display: none; /* Safari */
+ }
+
+ /*
+ Don't display the
+ "hide" button initially.
+ */
+ i.hide {
+ display: none;
+ }
+
+ /*
+ Normalize fa
+ icon alignment.
+ */
+ align-items: center;
+ i.fa {
+ text-align: center;
+ }
+
+ cursor: pointer;
+ border-radius: $br-inner;
+ &:focus-visible {
+ outline: $button-focus-outline;
+ }
+
+ &:hover {
+ outline: 0.1rem solid $fg-reduced;
+ }
+ }
+
+ @keyframes fade-in {
+ 0% {opacity: 0}
+ 100% {opacity: 1}
+ }
+
+ &[open] {
+ .stats-more-info-content {
+ animation: fade-in .1s;
+ }
+
+ & > summary i.show {
+ display: none;
+ }
+
+ & > summary i.hide {
+ display: block;
+ }
+ }
}
- .stats-item:not(.published-at):not(.edited-at) {
- z-index: 1;
+ .stats-more-info-content {
+ position: absolute;
+ right: 0;
+ z-index: 2;
+
+ flex-direction: column;
+ max-width: 100%;
+ row-gap: 0.5rem;
+
+ background: $status-info-bg;
+ padding: 0.5rem 0.75rem;
+ border: $boxshadow-border;
+ box-shadow: $boxshadow;
+
+ opacity: 1;
+
+ .stats-grouping {
+ width: 100%;
+ justify-content: space-between;
+ }
+ }
+
+ .stats-item.published-at dd a {
+ time.dt-published {
+ text-decoration: underline;
+ }
+
+ &:focus-visible {
+ outline: 0;
+ time.dt-published {
+ outline: $link-focus-outline;
+ outline-offset: -0.25rem;
+ }
+ }
+ }
+
+ .stats-item:not(.published-at):not(.edit-timeline) {
user-select: none;
}
- .language {
- margin-left: auto;
+ .stats-item.edit-timeline {
+ flex-direction: column;
+ width: 100%;
+ border-top: $boxshadow-border;
+ padding-top: 0.4rem;
+
+ dd {
+ display: flex;
+ align-items: center;
+ gap: 0.4rem;
+ }
}
}
grid-column: span 3;
}
- .status-link {
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- overflow: hidden;
- text-indent: 100%;
- white-space: nowrap;
-
- position: absolute;
- z-index: 0;
-
- &:focus-visible {
- /*
- Inset focus to compensate for themes where
- statuses have a really thick border.
- */
- outline-offset: -0.25rem;
- }
- }
-
&:first-child {
/* top left, top right */
border-top-left-radius: $br;
@@ -327,7 +410,8 @@
&.expanded {
background: $status-focus-bg;
- .status-info {
+ .status-info,
+ .status-info .status-stats .stats-more-info-content {
background: $status-focus-info-bg;
}
}
diff --git a/web/source/css/thread.css b/web/source/css/thread.css
index c67c95d4e..75dda550b 100644
--- a/web/source/css/thread.css
+++ b/web/source/css/thread.css
@@ -79,9 +79,18 @@
&.indent-3,
&.indent-4,
&.indent-5 {
- .status-link {
- margin-left: -0.5rem;
+ /*
+ Show a stripey line to the left of
+ indented statuses for better legibility.
+ */
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
border-left: 0.15rem dashed $border-accent;
+ margin-left: -0.5rem;
}
}
diff --git a/web/source/frontend/index.js b/web/source/frontend/index.js
index 860d6d10a..da158ed77 100644
--- a/web/source/frontend/index.js
+++ b/web/source/frontend/index.js
@@ -338,3 +338,25 @@ Array.from(document.getElementsByTagName('time')).forEach(timeTag => {
timeTag.textContent = dateTimeFormat.format(date);
}
});
+
+// When clicking anywhere that's not an open
+// stats-info-more-content details dropdown,
+// close that open dropdown.
+document.body.addEventListener("click", (e) => {
+ const openStats = document.querySelector("details.stats-more-info[open]");
+ if (!openStats) {
+ // No open stats
+ // details element.
+ return;
+ }
+
+ if (openStats.contains(e.target)) {
+ // Click is within stats
+ // element, leave it alone.
+ return;
+ }
+
+ // Click was outside of
+ // stats elements, close it.
+ openStats.removeAttribute("open");
+});
diff --git a/web/source/settings/components/status.tsx b/web/source/settings/components/status.tsx
index a5b85f214..9d0dfa2b4 100644
--- a/web/source/settings/components/status.tsx
+++ b/web/source/settings/components/status.tsx
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-import React, { useState } from "react";
+import React, { useMemo, useState } from "react";
import { useVerifyCredentialsQuery } from "../lib/query/login";
import { MediaAttachment, Status as StatusType } from "../lib/types/status";
import sanitize from "sanitize-html";
@@ -68,15 +68,6 @@ export function Status({ status }: { status: StatusType }) {
<StatusHeader status={status} />
<StatusBody status={status} />
<StatusFooter status={status} />
- <a
- href={status.url}
- target="_blank"
- className="status-link"
- data-nosnippet
- title="Open this status (opens in new tab)"
- >
- Open this status (opens in new tab)
- </a>
</article>
);
}
@@ -266,25 +257,103 @@ function StatusMediaEntry({ media }: { media: MediaAttachment }) {
);
}
+function useVisibilityIcon(visibility: string): string {
+ return useMemo(() => {
+ switch (true) {
+ case visibility === "direct":
+ return "fa-envelope";
+ case visibility === "followers_only":
+ return "fa-lock";
+ case visibility === "unlisted":
+ return "fa-unlock";
+ case visibility === "public":
+ return "fa-globe";
+ default:
+ return "fa-question";
+ }
+ }, [visibility]);
+}
+
function StatusFooter({ status }: { status: StatusType }) {
+ const visibilityIcon = useVisibilityIcon(status.visibility);
return (
<aside className="status-info">
- <dl className="status-stats">
- <div className="stats-grouping">
+ <div className="status-stats">
+ <dl className="stats-grouping text-cutoff">
<div className="stats-item published-at text-cutoff">
<dt className="sr-only">Published</dt>
- <dd>
- <time dateTime={status.created_at}>
- { new Date(status.created_at).toLocaleString() }
- </time>
+ <dd className="text-cutoff">
+ <a
+ href={status.url}
+ className="u-url text-cutoff"
+ >
+ <time
+ className="dt-published text-cutoff"
+ dateTime={status.created_at}
+ >
+ {new Date(status.created_at).toLocaleString(undefined, {
+ year: 'numeric',
+ month: 'short',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false
+ })}
+ </time>
+ </a>
</dd>
</div>
- </div>
- <div className="stats-item language">
- <dt className="sr-only">Language</dt>
- <dd>{status.language}</dd>
- </div>
- </dl>
+ <div className="stats-grouping">
+ <div className="stats-item visibility-level" title={status.visibility}>
+ <dt className="sr-only">Visibility</dt>
+ <dd>
+ <i className={`fa ${visibilityIcon}`} aria-hidden="true"></i>
+ <span className="sr-only">{status.visibility}</span>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ <details className="stats-more-info">
+ <summary title="More info">
+ <i className="fa fa-fw fa-info" aria-hidden="true"></i>
+ <span className="sr-only">More info</span>
+ <i className="fa fa-fw fa-chevron-right show" aria-hidden="true"></i>
+ <i className="fa fa-fw fa-chevron-down hide" aria-hidden="true"></i>
+ </summary>
+ <dl className="stats-more-info-content">
+ <div className="stats-grouping">
+ <div className="stats-item" title="Language">
+ <dt>
+ <span className="sr-only">Language</span>
+ <i className="fa fa-language" aria-hidden="true"></i>
+ </dt>
+ <dd>{status.language}</dd>
+ </div>
+ <div className="stats-item" title="Replies">
+ <dt>
+ <span className="sr-only">Replies</span>
+ <i className="fa fa-reply-all" aria-hidden="true"></i>
+ </dt>
+ <dd>{status.replies_count}</dd>
+ </div>
+ <div className="stats-item" title="Faves">
+ <dt>
+ <span className="sr-only">Favourites</span>
+ <i className="fa fa-star" aria-hidden="true"></i>
+ </dt>
+ <dd>{status.favourites_count}</dd>
+ </div>
+ <div className="stats-item" title="Boosts">
+ <dt>
+ <span className="sr-only">Reblogs</span>
+ <i className="fa fa-retweet" aria-hidden="true"></i>
+ </dt>
+ <dd>{status.reblogs_count}</dd>
+ </div>
+ </div>
+ </dl>
+ </details>
+ </div>
</aside>
);
}
diff --git a/web/template/status.tmpl b/web/template/status.tmpl
index 4263e6020..9d2f80a5e 100644
--- a/web/template/status.tmpl
+++ b/web/template/status.tmpl
@@ -90,27 +90,7 @@ media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{
</div>
{{- end }}
</div>
-<aside class="status-info" aria-hidden="true">
+<aside class="status-info">
{{- include "status_info.tmpl" . | indent 1 }}
</aside>
-{{- if .Local }}
-<a
- href="{{- .URL -}}"
- class="status-link u-url"
- data-nosnippet
- title="Open thread at this post"
->
- Open thread at this post
-</a>
-{{- else }}
-<a
- href="{{- .URL -}}"
- class="status-link u-url"
- data-nosnippet
- rel="nofollow noreferrer noopener" target="_blank"
- title="Open remote post (opens in a new window)"
->
- Open remote post (opens in a new window)
-</a>
-{{- end }}
{{- end }} \ No newline at end of file
diff --git a/web/template/status_info.tmpl b/web/template/status_info.tmpl
index 5d26811d7..2a3e6cac2 100644
--- a/web/template/status_info.tmpl
+++ b/web/template/status_info.tmpl
@@ -38,50 +38,40 @@
{{- end -}}
{{- with . }}
-<dl class="status-stats">
- <div class="stats-grouping">
- <div class="stats-item visibility-level" title="{{- template "visibility_title" . -}}">
- <dt class="sr-only">Visibility</dt>
- <dd>
- <i class="fa fa-{{- template "visibility_icon" . -}}" aria-hidden="true"></i>
- <span class="sr-only">{{- template "visibility_title" . -}}</span>
- </dd>
- </div>
+<div class="status-stats">
+ <dl class="stats-grouping text-cutoff">
<div class="stats-item published-at text-cutoff">
<dt class="sr-only">Published</dt>
- <dd>
- <time class="dt-published" datetime="{{- .CreatedAt -}}">{{- .CreatedAt | timestampPrecise -}}</time>
- </dd>
- </div>
- {{- if .EditedAt -}}
- <div class="stats-item edited-at text-cutoff">
- <dt class="sr-only">Edited</dt>
- <dd>
- edited <time class="dt-updated" datetime="{{- .EditedAt -}}">{{- .EditedAt | timestampPrecise -}}</time>
+ <dd class="text-cutoff">
+ {{- if .Local }}
+ <a
+ href="{{- .URL -}}"
+ class="u-url text-cutoff"
+ data-nosnippet
+ title="Open thread at this post"
+ >
+ <time class="dt-published text-cutoff" datetime="{{- .CreatedAt -}}">{{- .CreatedAt | timestampPrecise -}}</time>{{- if .EditedAt }}*{{- end }}
+ </a>
+ {{- else }}
+ <a
+ href="{{- .URL -}}"
+ class="u-url text-cutoff"
+ data-nosnippet
+ rel="nofollow noreferrer noopener" target="_blank"
+ title="Open remote post (opens in a new window)"
+ >
+ <time class="dt-published text-cutoff" datetime="{{- .CreatedAt -}}">{{- .CreatedAt | timestampPrecise -}}</time>{{- if .EditedAt }}*{{- end }}
+ </a>
+ {{- end }}
</dd>
</div>
- {{ end }}
<div class="stats-grouping">
- <div class="stats-item" title="Replies">
- <dt>
- <span class="sr-only">Replies</span>
- <i class="fa fa-reply-all" aria-hidden="true"></i>
- </dt>
- <dd>{{- .RepliesCount -}}</dd>
- </div>
- <div class="stats-item" title="Faves">
- <dt>
- <span class="sr-only">Favourites</span>
- <i class="fa fa-star" aria-hidden="true"></i>
- </dt>
- <dd>{{- .FavouritesCount -}}</dd>
- </div>
- <div class="stats-item" title="Boosts">
- <dt>
- <span class="sr-only">Reblogs</span>
- <i class="fa fa-retweet" aria-hidden="true"></i>
- </dt>
- <dd>{{- .ReblogsCount -}}</dd>
+ <div class="stats-item visibility-level" title="{{- template "visibility_title" . -}}">
+ <dt class="sr-only">Visibility</dt>
+ <dd>
+ <i class="fa fa-{{- template "visibility_icon" . -}}" aria-hidden="true"></i>
+ <span class="sr-only">{{- template "visibility_title" . -}}</span>
+ </dd>
</div>
{{- if .Pinned }}
<div class="stats-item" title="Pinned">
@@ -93,17 +83,86 @@
</div>
{{- else }}
{{- end }}
+ {{- if .RepliesCount }}
+ <div class="stats-item" title="Replies">
+ <dt>
+ <span class="sr-only">Replies</span>
+ <i class="fa fa-reply-all" aria-hidden="true"></i>
+ </dt>
+ <dd>{{- .RepliesCount -}}</dd>
+ </div>
+ {{- else }}
+ {{- end }}
</div>
- </div>
- {{- if .LanguageTag.DisplayStr }}
- <div class="stats-item language" title="{{ .LanguageTag.DisplayStr }}">
- <dt class="sr-only">Language</dt>
- <dd>
- <span class="sr-only">{{ .LanguageTag.DisplayStr }}</span>
- <span aria-hidden="true">{{- .LanguageTag.TagStr -}}</span>
- </dd>
- </div>
- {{- else }}
- {{- end }}
-</dl>
+ </dl>
+ <details class="stats-more-info" name="stats-more-info">
+ <summary title="More info">
+ <i class="fa fa-fw fa-info" aria-hidden="true"></i>
+ <span class="sr-only">More info</span>
+ <i class="fa fa-fw fa-chevron-right show" aria-hidden="true"></i>
+ <i class="fa fa-fw fa-chevron-down hide" aria-hidden="true"></i>
+ </summary>
+ <dl class="stats-more-info-content">
+ <div class="stats-grouping">
+ {{- if .LanguageTag.DisplayStr }}
+ <div class="stats-item" title="Language">
+ <dt>
+ <span class="sr-only">Language</span>
+ <i class="fa fa-language" aria-hidden="true"></i>
+ </dt>
+ <dd>
+ <span class="sr-only">{{ .LanguageTag.DisplayStr -}}</span>
+ <span aria-hidden="true" title="{{- .LanguageTag.DisplayStr -}}">{{- .LanguageTag.TagStr -}}</span>
+ </dd>
+ </div>
+ {{- else }}
+ {{- end }}
+ {{- if not .RepliesCount }}
+ <div class="stats-item" title="Replies">
+ <dt>
+ <span class="sr-only">Replies</span>
+ <i class="fa fa-reply-all" aria-hidden="true"></i>
+ </dt>
+ <dd>{{- .RepliesCount -}}</dd>
+ </div>
+ {{- else }}
+ {{- end }}
+ <div class="stats-item" title="Faves">
+ <dt>
+ <span class="sr-only">Favourites</span>
+ <i class="fa fa-star" aria-hidden="true"></i>
+ </dt>
+ <dd>{{- .FavouritesCount -}}</dd>
+ </div>
+ <div class="stats-item" title="Boosts">
+ <dt>
+ <span class="sr-only">Reblogs</span>
+ <i class="fa fa-retweet" aria-hidden="true"></i>
+ </dt>
+ <dd>{{- .ReblogsCount -}}</dd>
+ </div>
+ </div>
+ {{- if and .EditedAt (gt (len .EditTimeline) 1) -}}
+ <div class="stats-item edit-timeline text-cutoff">
+ <dt>Edit timeline:</dt>
+ {{- range $index, $edited := .EditTimeline }}
+ {{- if not (eq $index (add (len $.EditTimeline) -1)) }}
+ <dd class="text-cutoff" title="Edited {{ $edited -}}">
+ <span class="sr-only">Edited</span>
+ <i class="fa fa-asterisk" aria-hidden="true"></i>
+ <time datetime="{{- $edited -}}">{{- $edited | timestampPrecise -}}</time>
+ </dd>
+ {{- else }}
+ <dd class="text-cutoff" title="Published {{ $.CreatedAt -}}">
+ <span class="sr-only">Published</span>
+ <i class="fa fa-pencil" aria-hidden="true"></i>
+ <time datetime="{{- $.CreatedAt -}}">{{- $.CreatedAt | timestampPrecise -}}</time>
+ </dd>
+ {{- end }}
+ {{- end }}
+ </div>
+ {{ end }}
+ </dl>
+ </details>
+</div>
{{- end }} \ No newline at end of file
diff --git a/web/template/thread.tmpl b/web/template/thread.tmpl
index 0cf9ecb05..e7a8fb095 100644
--- a/web/template/thread.tmpl
+++ b/web/template/thread.tmpl
@@ -78,7 +78,6 @@
<a href="#replies">jump to replies</a>
{{- end }}
</div>
-
{{- range $status := .context.Statuses }}
<article
class="status{{- if $status.ThreadContextStatus }} expanded{{- end -}}{{- if $status.Indent }} indent-{{ $status.Indent }}{{- end -}}"
@@ -90,9 +89,6 @@
{{- include "repliesStart" $ | indent 1 }}
{{- end }}
{{- end }}
-
- {{- if .context.ThreadReplies }}
</section>
- {{- end }}
</main>
{{- end }} \ No newline at end of file