summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/assets/themes/brutalist-dark.css4
-rw-r--r--web/assets/themes/brutalist.css4
-rw-r--r--web/source/css/_media-wrapper.css207
-rw-r--r--web/source/css/_prism.css (renamed from web/source/css/prism.css)0
-rw-r--r--web/source/css/_profile-header.css343
-rw-r--r--web/source/css/_status-media.css44
-rw-r--r--web/source/css/base.css2
-rw-r--r--web/source/css/profile-gallery.css108
-rw-r--r--web/source/css/profile.css318
-rw-r--r--web/source/css/status.css210
-rw-r--r--web/source/css/tag.css8
-rw-r--r--web/source/frontend/index.js65
-rw-r--r--web/source/package.json2
-rw-r--r--web/source/settings/lib/types/account.ts2
-rw-r--r--web/source/settings/views/user/profile.tsx47
-rw-r--r--web/source/yarn.lock8
-rw-r--r--web/template/profile-gallery.tmpl87
-rw-r--r--web/template/profile.tmpl197
-rw-r--r--web/template/profile_about_user.tmpl56
-rw-r--r--web/template/profile_header.tmpl185
-rw-r--r--web/template/status.tmpl20
-rw-r--r--web/template/status_attachment.tmpl179
-rw-r--r--web/template/status_attachments.tmpl195
-rw-r--r--web/template/status_info.tmpl29
24 files changed, 1329 insertions, 991 deletions
diff --git a/web/assets/themes/brutalist-dark.css b/web/assets/themes/brutalist-dark.css
index 4cf8cd655..9be12ba92 100644
--- a/web/assets/themes/brutalist-dark.css
+++ b/web/assets/themes/brutalist-dark.css
@@ -135,12 +135,12 @@ html, body {
}
/* Make show more/less buttons more legible */
-.status button, .status .button {
+.button {
background-color: var(--almost-white);
color: var(--almost-black);
border: var(--dashed-border);
}
-.status button:hover, .status .button:hover {
+.button:hover {
background-color: var(--almost-black);
color: var(--almost-white);
border: var(--dashed-border);
diff --git a/web/assets/themes/brutalist.css b/web/assets/themes/brutalist.css
index e29509c21..e183ee50c 100644
--- a/web/assets/themes/brutalist.css
+++ b/web/assets/themes/brutalist.css
@@ -130,12 +130,12 @@ html, body {
}
/* Make show more/less buttons more legible */
-.status button, .status .button {
+.button {
background-color: var(--almost-black);
color: var(--almost-white);
border: var(--dashed-border);
}
-.status button:hover, .status .button:hover {
+.button:hover {
background-color: var(--almost-white);
color: var(--almost-black);
border: var(--dashed-border);
diff --git a/web/source/css/_media-wrapper.css b/web/source/css/_media-wrapper.css
new file mode 100644
index 000000000..1c2ae1503
--- /dev/null
+++ b/web/source/css/_media-wrapper.css
@@ -0,0 +1,207 @@
+/*
+ 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/>.
+*/
+
+@import "photoswipe/dist/photoswipe.css";
+@import "photoswipe-dynamic-caption-plugin/photoswipe-dynamic-caption-plugin.css";
+@import "plyr/dist/plyr.css";
+
+.media-wrapper {
+ height: 100%;
+ width: 100%;
+ box-sizing: border-box;
+ border: 0.15rem solid $gray1;
+ border-radius: $br;
+ position: relative;
+ overflow: hidden;
+ z-index: 2;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ details {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+
+ &[open] summary {
+ height: auto;
+ width: auto;
+ margin: 1rem;
+ padding: 0;
+
+ .show, video, img {
+ display: none;
+ }
+
+ .eye.button .hide {
+ display: inline-block;
+ grid-column: 1 / span 3;
+ grid-row: 1 / span 2;
+ }
+ }
+
+ summary {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ z-index: 3;
+ overflow: hidden;
+
+ display: grid;
+ padding: 1rem;
+ grid-template-columns: 1fr auto 1fr;
+ grid-template-rows: 1fr 1fr;
+ grid-template-areas:
+ "eye sensitive ."
+ ". sensitive .";
+
+ &::-webkit-details-marker {
+ display: none; /* Safari */
+ }
+
+ .eye.button {
+ grid-area: eye;
+ align-self: start;
+ justify-self: start;
+ margin: 0;
+ padding: 0.4rem;
+
+ .fa-fw {
+ line-height: $fa-fw;
+ }
+
+ .hide {
+ display: none;
+ }
+ }
+
+ .show.sensitive {
+ grid-area: sensitive;
+ align-self: center;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+
+ .button {
+ cursor: pointer;
+ align-self: center;
+ }
+ }
+
+ video, img {
+ z-index: -1;
+ position: absolute;
+ height: calc(100% + 1.2rem);
+ width: calc(100% + 1.2rem);
+ top: -0.6rem;
+ left: -0.6rem;
+ filter: blur(1.2rem);
+ }
+ }
+
+ video.plyr-video, .plyr {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ background: $gray1;
+ }
+
+ .unknown-attachment {
+ .placeholder {
+ width: 100%;
+ height: 100%;
+ padding: 0.8rem;
+ border: 0.2rem dashed $white2;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.25rem;
+
+ color: $white2;
+
+ .placeholder-external-link {
+ align-self: end;
+ font-size: 2.5rem;
+ }
+
+ .placeholder-icon {
+ width: 100%;
+ font-size: 3.5rem;
+ text-align: center;
+ margin-top: auto;
+ }
+
+ .placeholder-link-to {
+ width: 100%;
+ text-align: center;
+ margin-bottom: auto;
+ }
+ }
+ }
+ }
+}
+
+.pswp__button--open-post-link {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ span > i {
+ background: $status-bg;
+ color: $fg;
+ border-radius: 25%;
+ }
+}
+
+.plyr--video {
+ flex-direction: column-reverse;
+
+ .plyr__video-wrapper {
+ position: relative;
+ }
+
+ .plyr__controls {
+ align-self: stretch;
+ position: initial;
+ padding: 0.1rem;
+ padding-top: 0.2rem;
+ }
+
+ .plyr__control {
+ box-shadow: none;
+ }
+
+ .plyr__control--overlaid {
+ top: calc(50% - 18px);
+ }
+}
+
+.pswp__content {
+ padding: 2rem;
+
+ .plyr {
+ max-height: 100%;
+ }
+}
diff --git a/web/source/css/prism.css b/web/source/css/_prism.css
index c1d369a9c..c1d369a9c 100644
--- a/web/source/css/prism.css
+++ b/web/source/css/_prism.css
diff --git a/web/source/css/_profile-header.css b/web/source/css/_profile-header.css
new file mode 100644
index 000000000..b4ebadf8d
--- /dev/null
+++ b/web/source/css/_profile-header.css
@@ -0,0 +1,343 @@
+/*
+ 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/>.
+*/
+
+.profile .profile-header {
+ background: $profile-bg;
+ border-radius: $br;
+ overflow: hidden;
+ margin-bottom: 1rem;
+
+ .moved-to {
+ padding: 1rem;
+ text-align: center;
+ }
+
+ .header-image-wrapper {
+ position: relative;
+ padding-top: 33.33%; /* aspect-ratio 1/3 */
+
+ img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+ }
+
+ /*
+ Basic info container has the user's avatar, display- and username, and role
+ It's partially overlapped over the header image, by a negative margin-top.
+ */
+ $avatar-size: 8.5rem;
+ $name-size: 3rem;
+ $username-size: 2rem;
+ $overlap: calc($avatar-size - $name-size - $username-size);
+
+ .basic-info {
+ position: relative;
+ display: grid;
+ box-sizing: border-box;
+ grid-template-columns: $avatar-size auto 1fr;
+ grid-template-rows: $overlap $name-size auto;
+ grid-template-areas:
+ "avatar . ."
+ "avatar namerole namerole"
+ "avatar namerole namerole";
+
+ margin: 1rem;
+ margin-top: calc(-1 * $overlap);
+ gap: 0 1rem;
+
+ .avatar-image-wrapper {
+ grid-area: avatar;
+
+ border: 0.2rem solid $avatar-border;
+ border-radius: $br;
+
+ /*
+ Wrapper always same
+ size + proportions no
+ matter image inside.
+ */
+ height: $avatar-size;
+ width: $avatar-size;
+
+ .avatar {
+ /*
+ Fit 100% of the wrapper.
+ */
+ height: 100%;
+ width: 100%;
+
+ /*
+ Normalize non-square images.
+ */
+ object-fit: cover;
+
+ /*
+ Prevent image extending
+ beyond rounded borders.
+ */
+ border-radius: $br-inner;
+ }
+ }
+
+ .namerole {
+ grid-area: namerole;
+
+ display: grid;
+ gap: 0 1rem;
+ box-sizing: border-box;
+ grid-template-columns: 1fr auto;
+ grid-template-rows: $name-size auto;
+ grid-template-areas:
+ "displayname displayname"
+ "username role";
+
+ .displayname {
+ grid-area: displayname;
+ line-height: $name-size;
+ font-size: 1.5rem;
+ font-weight: bold;
+ }
+
+ .bot-username-wrapper {
+ display: flex;
+ gap: 0.5rem;
+ grid-area: username;
+ align-items: center;
+
+ .bot-legend-wrapper {
+ display: flex;
+ gap: 0.25rem;
+ align-items: center;
+
+ background: $bg;
+ color: $fg;
+
+ border-radius: $br;
+ padding: 0.1rem 0.4rem 0.2rem 0.4rem;
+
+ font-variant: small-caps;
+ font-weight: bold;
+
+ cursor: default;
+
+ .bot-icon {
+ /*
+ FA icon is weirdly
+ aligned so tweak it
+ */
+ margin-top: 0.25rem;
+ }
+ }
+
+ .username {
+ min-width: 0;
+ line-height: $username-size;
+
+ font-size: 1rem;
+ font-weight: bold;
+ color: $fg-accent;
+ user-select: all;
+ }
+ }
+
+ .role {
+ background: $bg;
+ color: $fg;
+ border: 0.13rem solid $bg;
+
+ grid-area: role;
+ align-self: center;
+ justify-self: start;
+ border-radius: $br;
+ padding: 0.3rem;
+
+ line-height: 1.1rem;
+ font-size: 0.9rem;
+ font-variant: small-caps;
+ font-weight: bold;
+
+ &.admin {
+ color: $role-admin;
+ border-color: $role-admin;
+ }
+
+ &.moderator {
+ color: $role-mod;
+ border-color: $role-mod;
+ }
+ }
+ }
+ }
+}
+
+.profile .about-user {
+ flex: 35 14rem;
+ border-radius: $br;
+ overflow: hidden;
+
+ .col-header {
+ margin-bottom: -0.25rem;
+ }
+
+ dt {
+ font-weight: bold;
+ }
+
+ .fields {
+ background: $profile-bg;
+ display: flex;
+ flex-direction: column;
+ padding: 0 0.5rem;
+ padding-top: 0.25rem;
+
+ .field {
+ padding: 0.25rem;
+ display: flex;
+ flex-direction: column;
+ border-bottom: 0.1rem solid $gray2;
+
+ > dt, > dd {
+ word-break: break-word;
+ }
+
+ &:first-child {
+ border-top: 0.1rem solid $gray2;
+ }
+ }
+ }
+
+ .bio {
+ background: $profile-bg;
+ padding: 1rem 0.75rem;
+ padding-bottom: 1.25rem;
+ }
+
+ .accountstats {
+ background: $bg-accent;
+ padding: 0.75rem;
+
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+
+ .stats-item {
+ display: flex;
+ dt {
+ width: 7rem;
+ }
+ }
+ }
+}
+
+/*
+ RSS icon isn't really part of the profile header exactly,
+ but also it sort of is, and we want it styled the same for
+ both microblog and gallery view anyway, so include it here.
+*/
+.rss-icon {
+ display: block;
+ margin: -0.25rem 0;
+
+ .fa {
+ font-size: 2rem;
+ object-fit: contain;
+ vertical-align: middle;
+ color: $orange2;
+ /*
+ Can't size a single-color background, so we use
+ a linear-gradient that's effectively white.
+ */
+ background: linear-gradient(to right, $white1 100%, transparent 0) no-repeat center center;
+ background-size: 1.2rem 1.4rem;
+ /* light mode */
+ @media (prefers-color-scheme: light) {
+ background: linear-gradient(to right, $white 100%, transparent 0) no-repeat center center;
+ background-size: 1.2rem 1.4rem;
+ }
+ }
+}
+
+/*
+ Tablet-ish-kinda size.
+*/
+@media screen and (max-width: 750px) {
+ .profile .profile-header {
+ .basic-info {
+ grid-template-columns: auto 1fr;
+ grid-template-rows: $avatar-size $name-size auto;
+ grid-template-areas:
+ "avatar avatar"
+ "namerole namerole"
+ "namerole namerole";
+
+ /*
+ Make display name a bit smaller
+ so there's more chance of being
+ able to read everything.
+ */
+ .namerole {
+ .displayname {
+ font-size: 1.2rem;
+ line-height: 2rem;
+ margin-top: 0.5rem;
+ }
+ }
+ }
+ }
+}
+
+/*
+ Phone-ish-kinda size.
+*/
+@media screen and (max-width: 500px) {
+ .profile
+ .profile-header
+ .basic-info
+ .namerole {
+ /*
+ Line up in smallest possible
+ horizontal space to avoid overflow.
+ */
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ /*
+ Don't hug the right anymore
+ (good life advice in general).
+ */
+ .role {
+ align-self: flex-start;
+ }
+
+ /*
+ Allow this to wrap in case
+ of a really skinny screen.
+ */
+ .bot-username-wrapper {
+ flex-wrap: wrap;
+ }
+ }
+}
diff --git a/web/source/css/_status-media.css b/web/source/css/_status-media.css
new file mode 100644
index 000000000..e8386c87a
--- /dev/null
+++ b/web/source/css/_status-media.css
@@ -0,0 +1,44 @@
+/*
+ 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/>.
+*/
+
+@import "./_media-wrapper.css";
+
+.media {
+ grid-column: span 3;
+ display: grid;
+ grid-template-columns: 50% 50%;
+ grid-auto-rows: 10rem;
+ overflow: hidden;
+
+ &.single .media-wrapper {
+ grid-column: span 2;
+ }
+
+ &.odd .media-wrapper:first-child,
+ &.double .media-wrapper {
+ grid-row: span 2;
+ }
+
+ @media screen and (max-width: 42rem) {
+ .media-wrapper {
+ grid-column: span 2;
+ grid-row: span 2;
+ }
+ }
+}
diff --git a/web/source/css/base.css b/web/source/css/base.css
index 5f2f75802..765453ac2 100644
--- a/web/source/css/base.css
+++ b/web/source/css/base.css
@@ -22,7 +22,7 @@
****************************************/
@import "modern-normalize/modern-normalize.css";
-@import "./prism.css";
+@import "./_prism.css";
/* noto-sans-regular - latin */
@font-face {
diff --git a/web/source/css/profile-gallery.css b/web/source/css/profile-gallery.css
new file mode 100644
index 000000000..cb70eff22
--- /dev/null
+++ b/web/source/css/profile-gallery.css
@@ -0,0 +1,108 @@
+/*
+ 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/>.
+*/
+
+@import "./_profile-header.css";
+@import "./_media-wrapper.css";
+
+.page {
+ /*
+ Profile gallery can be wider than default.
+ */
+ grid-template-columns: 1fr min(95%, 65rem) 1fr;
+}
+
+.profile {
+ .about-user {
+ margin-bottom: 1rem;
+
+ .accountstats {
+ flex-direction: row;
+ justify-content: space-between;
+
+ .stats-item {
+ gap: 0.5rem;
+ width: 25%;
+ justify-content: space-around;
+
+ dt {
+ width: fit-content;
+ margin-left: auto;
+ }
+
+ dd {
+ margin-right: auto;
+ }
+ }
+
+ @media screen and (max-width: 750px) {
+ flex-direction: column;
+ .stats-item {
+ width: fit-content;
+ dt {
+ width: 7rem;
+ }
+ }
+ }
+ }
+ }
+
+ .media-galleries-wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ min-width: 0%;
+
+ .media-gallery {
+ margin-top: 0.15rem;
+ margin-bottom: 0.15rem;
+
+ display: grid;
+ gap: 0.15rem;
+
+ /* Desktop-ish width, show 3 cols of media */
+ grid-template-columns: repeat(3, 1fr);
+
+ @media screen and (max-width: 55rem) {
+ /* Tablet-ish width, switch to 2 cols */
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ @media screen and (max-width: 36rem) {
+ /* Mobile-ish width, switch to 1 col */
+ grid-template-columns: repeat(1, 1fr);
+ }
+
+ .media-wrapper {
+ aspect-ratio: 4/3;
+ border: 0;
+ border-radius: 0;
+ background: $bg;
+ }
+ }
+
+ .backnextlinks {
+ display: flex;
+ justify-content: space-between;
+
+ .next {
+ margin-left: auto;
+ }
+ }
+ }
+}
diff --git a/web/source/css/profile.css b/web/source/css/profile.css
index 1296b8927..5b9f2188e 100644
--- a/web/source/css/profile.css
+++ b/web/source/css/profile.css
@@ -17,13 +17,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+@import "./_profile-header.css";
+
.page {
/*
Profile page can be a little wider than default
page, since we're using a side-by-side column view.
*/
- grid-template-columns: 1fr minmax(auto, 60rem) 1fr;
- grid-template-columns: 1fr min(92%, 65rem) 1fr;
+ grid-template-columns: 1fr min(95%, 65rem) 1fr;
}
.profile .column-split {
@@ -32,244 +33,6 @@
gap: 1rem;
}
-.profile .profile-header {
- background: $profile-bg;
- border-radius: $br;
- overflow: hidden;
- margin-bottom: 1rem;
-
- .moved-to {
- padding: 1rem;
- text-align: center;
- }
-
- .header-image-wrapper {
- position: relative;
- padding-top: 33.33%; /* aspect-ratio 1/3 */
-
- img {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
-
- /*
- Basic info container has the user's avatar, display- and username, and role
- It's partially overlapped over the header image, by a negative margin-top.
- */
- $avatar-size: 8.5rem;
- $name-size: 3rem;
- $username-size: 2rem;
- $overlap: calc($avatar-size - $name-size - $username-size);
-
- .basic-info {
- position: relative;
- display: grid;
- box-sizing: border-box;
- grid-template-columns: $avatar-size auto 1fr;
- grid-template-rows: $overlap $name-size auto;
- grid-template-areas:
- "avatar . ."
- "avatar namerole namerole"
- "avatar namerole namerole";
-
- margin: 1rem;
- margin-top: calc(-1 * $overlap);
- gap: 0 1rem;
-
- .avatar-image-wrapper {
- grid-area: avatar;
-
- border: 0.2rem solid $avatar-border;
- border-radius: $br;
-
- /*
- Wrapper always same
- size + proportions no
- matter image inside.
- */
- height: $avatar-size;
- width: $avatar-size;
-
- .avatar {
- /*
- Fit 100% of the wrapper.
- */
- height: 100%;
- width: 100%;
-
- /*
- Normalize non-square images.
- */
- object-fit: cover;
-
- /*
- Prevent image extending
- beyond rounded borders.
- */
- border-radius: $br-inner;
- }
- }
-
- .namerole {
- grid-area: namerole;
-
- display: grid;
- gap: 0 1rem;
- box-sizing: border-box;
- grid-template-columns: 1fr auto;
- grid-template-rows: $name-size auto;
- grid-template-areas:
- "displayname displayname"
- "username role";
-
- .displayname {
- grid-area: displayname;
- line-height: $name-size;
- font-size: 1.5rem;
- font-weight: bold;
- }
-
- .bot-username-wrapper {
- display: flex;
- gap: 0.5rem;
- grid-area: username;
- align-items: center;
-
- .bot-legend-wrapper {
- display: flex;
- gap: 0.25rem;
- align-items: center;
-
- background: $bg;
- color: $fg;
-
- border-radius: $br;
- padding: 0.1rem 0.4rem 0.2rem 0.4rem;
-
- font-variant: small-caps;
- font-weight: bold;
-
- cursor: default;
-
- .bot-icon {
- /*
- FA icon is weirdly
- aligned so tweak it
- */
- margin-top: 0.25rem;
- }
- }
-
- .username {
- min-width: 0;
- line-height: $username-size;
-
- font-size: 1rem;
- font-weight: bold;
- color: $fg-accent;
- user-select: all;
- }
- }
-
- .role {
- background: $bg;
- color: $fg;
- border: 0.13rem solid $bg;
-
- grid-area: role;
- align-self: center;
- justify-self: start;
- border-radius: $br;
- padding: 0.3rem;
-
- line-height: 1.1rem;
- font-size: 0.9rem;
- font-variant: small-caps;
- font-weight: bold;
-
- &.admin {
- color: $role-admin;
- border-color: $role-admin;
- }
-
- &.moderator {
- color: $role-mod;
- border-color: $role-mod;
- }
- }
- }
- }
-}
-
-/*
- Tablet-ish-kinda size.
-*/
-@media screen and (max-width: 750px) {
- .profile .profile-header {
- .basic-info {
- grid-template-columns: auto 1fr;
- grid-template-rows: $avatar-size $name-size auto;
- grid-template-areas:
- "avatar avatar"
- "namerole namerole"
- "namerole namerole";
-
- /*
- Make display name a bit smaller
- so there's more chance of being
- able to read everything.
- */
- .namerole {
- .displayname {
- font-size: 1.2rem;
- line-height: 2rem;
- margin-top: 0.5rem;
- }
- }
- }
- }
-}
-
-/*
- Phone-ish-kinda size.
-*/
-@media screen and (max-width: 500px) {
- .profile
- .profile-header
- .basic-info
- .namerole {
- /*
- Line up in smallest possible
- horizontal space to avoid overflow.
- */
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
-
- /*
- Don't hug the right anymore
- (good life advice in general).
- */
- .role {
- align-self: flex-start;
- }
-
- /*
- Allow this to wrap in case
- of a really skinny screen.
- */
- .bot-username-wrapper {
- flex-wrap: wrap;
- }
- }
-}
-
.profile .statuses-wrapper {
flex: 65 25rem;
display: flex;
@@ -283,29 +46,6 @@
flex-direction: column;
gap: 0.4rem;
- .rss-icon {
- display: block;
- margin: -0.25rem 0;
-
- .fa {
- font-size: 2rem;
- object-fit: contain;
- vertical-align: middle;
- color: $orange2;
- /*
- Can't size a single-color background, so we use
- a linear-gradient that's effectively white.
- */
- background: linear-gradient(to right, $white1 100%, transparent 0) no-repeat center center;
- background-size: 1.2rem 1.4rem;
- /* light mode */
- @media (prefers-color-scheme: light) {
- background: linear-gradient(to right, $white 100%, transparent 0) no-repeat center center;
- background-size: 1.2rem 1.4rem;
- }
- }
- }
-
.backnextlinks {
display: flex;
justify-content: space-between;
@@ -315,55 +55,3 @@
}
}
}
-
-.profile .about-user {
- flex: 35 14rem;
- border-radius: $br;
- overflow: hidden;
-
- .col-header {
- margin-bottom: -0.25rem;
- }
-
- dt {
- font-weight: bold;
- }
-
- .fields {
- background: $profile-bg;
- display: flex;
- flex-direction: column;
- padding: 0 0.5rem;
- padding-top: 0.25rem;
-
- .field {
- padding: 0.25rem;
- display: flex;
- flex-direction: column;
- border-bottom: 0.1rem solid $gray2;
-
- > dt, > dd {
- word-break: break-word;
- }
-
- &:first-child {
- border-top: 0.1rem solid $gray2;
- }
- }
- }
-
- .bio {
- background: $profile-bg;
- padding: 1rem 0.75rem;
- padding-bottom: 1.25rem;
- }
-
- .accountstats {
- background: $bg-accent;
- padding: 0.75rem;
-
- display: grid;
- grid-template-columns: auto 1fr;
- gap: 0.25rem 1rem;
- }
-} \ No newline at end of file
diff --git a/web/source/css/status.css b/web/source/css/status.css
index 4b2d7e2a7..ec6cac3e5 100644
--- a/web/source/css/status.css
+++ b/web/source/css/status.css
@@ -17,14 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-@import "photoswipe/dist/photoswipe.css";
-@import "photoswipe-dynamic-caption-plugin/photoswipe-dynamic-caption-plugin.css";
-@import "plyr/dist/plyr.css";
-
-main {
- background: transparent;
- grid-auto-rows: auto;
-}
+@import "./_status-media.css";
.status {
background: $status-bg;
@@ -257,172 +250,6 @@ main {
}
}
- .media {
- grid-column: span 3;
- display: grid;
- grid-template-columns: 50% 50%;
- grid-auto-rows: 10rem;
- overflow: hidden;
-
- .media-wrapper {
- height: 100%;
- width: 100%;
- box-sizing: border-box;
- border: 0.15rem solid $gray1;
- border-radius: $br;
- position: relative;
- overflow: hidden;
- z-index: 2;
-
- details {
- position: absolute;
- height: 100%;
- width: 100%;
-
- &[open] summary {
- height: auto;
- width: auto;
- margin: 1rem;
- padding: 0;
-
- .show, video, img {
- display: none;
- }
-
- .eye.button .hide {
- display: inline-block;
- grid-column: 1 / span 3;
- grid-row: 1 / span 2;
- }
- }
-
- summary {
- position: absolute;
- height: 100%;
- width: 100%;
- z-index: 3;
- overflow: hidden;
-
- display: grid;
- padding: 1rem;
- grid-template-columns: 1fr auto 1fr;
- grid-template-rows: 1fr 1fr;
- grid-template-areas:
- "eye sensitive ."
- ". sensitive .";
-
- &::-webkit-details-marker {
- display: none; /* Safari */
- }
-
- .eye.button {
- grid-area: eye;
- align-self: start;
- justify-self: start;
- margin: 0;
- padding: 0.4rem;
-
- .fa-fw {
- line-height: $fa-fw;
- }
-
- .hide {
- display: none;
- }
- }
-
- .show.sensitive {
- grid-area: sensitive;
- align-self: center;
-
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
-
- .button {
- cursor: pointer;
- align-self: center;
- }
- }
-
- video, img {
- z-index: -1;
- position: absolute;
- height: calc(100% + 1.2rem);
- width: calc(100% + 1.2rem);
- top: -0.6rem;
- left: -0.6rem;
- filter: blur(1.2rem);
- }
- }
-
- video.plyr-video, .plyr {
- position: absolute;
- height: 100%;
- width: 100%;
- object-fit: contain;
- background: $gray1;
- }
-
- .unknown-attachment {
- .placeholder {
- width: 100%;
- height: 100%;
- padding: 0.8rem;
- border: 0.2rem dashed $white2;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 0.25rem;
-
- color: $white2;
-
- .placeholder-external-link {
- align-self: end;
- font-size: 2.5rem;
- }
-
- .placeholder-icon {
- width: 100%;
- font-size: 3.5rem;
- text-align: center;
- margin-top: auto;
- }
-
- .placeholder-link-to {
- width: 100%;
- text-align: center;
- margin-bottom: auto;
- }
- }
- }
- }
- }
-
- &.single .media-wrapper {
- grid-column: span 2;
- }
-
- &.odd .media-wrapper:first-child,
- &.double .media-wrapper {
- grid-row: span 2;
- }
-
- @media screen and (max-width: 42rem) {
- .media-wrapper {
- grid-column: span 2;
- grid-row: span 2;
- }
- }
-
- img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- }
-
.status-info {
background: $status-info-bg;
color: $fg-reduced;
@@ -448,10 +275,6 @@ main {
gap: 0.4rem;
}
- .stats-item.published-at {
- text-decoration: underline;
- }
-
.stats-item:not(.published-at):not(.edited-at) {
z-index: 1;
user-select: none;
@@ -497,34 +320,3 @@ main {
}
}
}
-
-.plyr--video {
- flex-direction: column-reverse;
-
- .plyr__video-wrapper {
- position: relative;
- }
-
- .plyr__controls {
- align-self: stretch;
- position: initial;
- padding: 0.1rem;
- padding-top: 0.2rem;
- }
-
- .plyr__control {
- box-shadow: none;
- }
-
- .plyr__control--overlaid {
- top: calc(50% - 18px);
- }
-}
-
-.pswp__content {
- padding: 2rem;
-
- .plyr {
- max-height: 100%;
- }
-} \ No newline at end of file
diff --git a/web/source/css/tag.css b/web/source/css/tag.css
index 2f1c9db98..99dbd6f61 100644
--- a/web/source/css/tag.css
+++ b/web/source/css/tag.css
@@ -18,8 +18,8 @@
*/
.thread {
- #tag-name {
- /* Ensure ridiculous length tags get wrapped */
- word-wrap: anywhere;
- }
+ #tag-name {
+ /* Ensure ridiculous length tags get wrapped */
+ word-wrap: anywhere;
+ }
}
diff --git a/web/source/frontend/index.js b/web/source/frontend/index.js
index 310d149ed..d45420255 100644
--- a/web/source/frontend/index.js
+++ b/web/source/frontend/index.js
@@ -40,6 +40,9 @@ const lightbox = new PhotoswipeLightbox({
gallery: '.photoswipe-gallery',
children: '.photoswipe-slide',
pswpModule: Photoswipe,
+ // Bit darker than default 0.8.
+ bgOpacity: 0.9,
+ loop: false,
});
new PhotoswipeCaptionPlugin(lightbox, {
@@ -71,7 +74,9 @@ lightbox.addFilter('itemData', (item) => {
}
},
width: parseInt(el.dataset.pswpWidth),
- height: parseInt(el.dataset.pswpHeight)
+ height: parseInt(el.dataset.pswpHeight),
+ parentStatus: el.dataset.pswpParentStatus,
+ attachmentId: el.dataset.pswpAttachmentId,
};
}
return item;
@@ -98,6 +103,26 @@ lightbox.on("close", function () {
}
});
+lightbox.on('uiRegister', function() {
+ lightbox.pswp.ui.registerElement({
+ name: 'open-post-link',
+ ariaLabel: 'Open post',
+ order: 8,
+ isButton: true,
+ tagName: "a",
+ html: '<span title="Open post"><span class="sr-only">Open post</span><i class="fa fa-lg fa-external-link-square" aria-hidden="true"></i></span>',
+ onInit: (el, pswp) => {
+ el.setAttribute('target', '_blank');
+ el.setAttribute('rel', 'noopener');
+ pswp.on('change', () => {
+ el.href = pswp.currSlide.data.parentStatus
+ ? pswp.currSlide.data.parentStatus
+ : pswp.currSlide.data.element.dataset.pswpParentStatus;
+ });
+ }
+ });
+});
+
lightbox.init();
function dynamicSpoiler(className, updateFunc) {
@@ -156,22 +181,40 @@ Array.from(document.getElementsByClassName("plyr-video")).forEach((video) => {
let player = new Plyr(video, {
title: video.title,
- settings: ["loop"],
+ settings: [],
+ controls: ['play-large', 'play', 'progress', 'current-time', 'volume', 'mute', 'fullscreen'],
disableContextMenu: false,
hideControls: false,
- tooltips: { contrors: true, seek: true },
+ tooltips: { controls: true, seek: true },
iconUrl: "/assets/plyr.svg",
+ invertTime: false,
listeners: {
fullscreen: () => {
- if (player.playing) {
- setTimeout(() => {
- player.play();
- }, 1);
+ // Check if the photoswipe lightbox is
+ // open with this as the current slide.
+ const alreadyInLightbox = (
+ lightbox.pswp !== undefined &&
+ video.dataset.pswpAttachmentId === lightbox.pswp.currSlide.data.attachmentId
+ );
+
+ if (alreadyInLightbox) {
+ // If this video is already open as the
+ // current photoswipe slide, the fullscreen
+ // button toggles proper fullscreen.
+ player.fullscreen.toggle();
+ } else {
+ // Otherwise the fullscreen button opens
+ // the video as current photoswipe slide.
+ //
+ // (Don't pause the video while it's
+ // being transitioned to a slide.)
+ if (player.playing) {
+ setTimeout(() => player.play(), 1);
+ }
+ lightbox.loadAndOpen(parseInt(video.dataset.pswpIndex), {
+ gallery: video.closest(".photoswipe-gallery")
+ });
}
- lightbox.loadAndOpen(parseInt(video.dataset.pswpIndex), {
- gallery: video.closest(".photoswipe-gallery")
- });
-
return false;
}
}
diff --git a/web/source/package.json b/web/source/package.json
index ea90137c8..7b11d7eb2 100644
--- a/web/source/package.json
+++ b/web/source/package.json
@@ -24,7 +24,7 @@
"object-to-formdata": "^4.4.2",
"papaparse": "^5.3.2",
"parse-link-header": "^2.0.0",
- "photoswipe": "^5.3.3",
+ "photoswipe": "^5.4.4",
"photoswipe-dynamic-caption-plugin": "^1.2.7",
"plyr": "^3.7.8",
"psl": "^1.9.0",
diff --git a/web/source/settings/lib/types/account.ts b/web/source/settings/lib/types/account.ts
index 76055ba53..f5238a7d3 100644
--- a/web/source/settings/lib/types/account.ts
+++ b/web/source/settings/lib/types/account.ts
@@ -79,6 +79,8 @@ export interface AccountSource {
privacy: string;
sensitive: boolean;
status_content_type: string;
+ web_visibility: string;
+ web_layout: string;
}
export interface SearchAccountParams {
diff --git a/web/source/settings/views/user/profile.tsx b/web/source/settings/views/user/profile.tsx
index 80be3c878..9ea948337 100644
--- a/web/source/settings/views/user/profile.tsx
+++ b/web/source/settings/views/user/profile.tsx
@@ -61,20 +61,6 @@ interface UserProfileFormProps {
}
function UserProfileForm({ data: profile }: UserProfileFormProps) {
- /*
- User profile update form keys
- - bool bot
- - bool locked
- - string display_name
- - string note
- - file avatar
- - file header
- - bool enable_rss
- - bool hide_collections
- - string custom_css (if enabled)
- - string theme
- */
-
const { data: instance } = useInstanceV1Query();
const instanceConfig = React.useMemo(() => {
return {
@@ -120,7 +106,8 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
discoverable: useBoolInput("discoverable", { source: profile}),
enableRSS: useBoolInput("enable_rss", { source: profile }),
hideCollections: useBoolInput("hide_collections", { source: profile }),
- webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p) => p.source?.web_visibility }),
+ webVisibility: useTextInput("web_visibility", { source: profile, valueSelector: (p: Account) => p.source?.web_visibility }),
+ webLayout: useTextInput("web_layout", { source: profile, valueSelector: (p: Account) => p.source?.web_layout }),
fields: useFieldArrayInput("fields_attributes", {
defaultValue: profile?.source?.fields,
length: instanceConfig.maxPinnedFields
@@ -185,18 +172,24 @@ function UserProfileForm({ data: profile }: UserProfileFormProps) {
/>
</fieldset>
- <div className="theme">
- <div>
- <b id="theme-label">Theme</b>
- <br/>
- <span>After choosing theme and saving, <a href={profile.url} target="_blank">open your profile</a> and refresh to see changes.</span>
- </div>
- <Select
- aria-labelledby="theme-label"
- field={form.theme}
- options={<>{themeOptions}</>}
- />
- </div>
+ <span>After choosing theme or layout and saving, <a href={profile.url} target="_blank">open your profile</a> and refresh to see changes.</span>
+
+ <Select
+ label="Theme for the web view of your profile"
+ field={form.theme}
+ options={<>{themeOptions}</>}
+ />
+
+ <Select
+ field={form.webLayout}
+ label="Layout for the web view of your profile"
+ options={
+ <>
+ <option value="microblog">Classic microblog layout (show recent + pinned posts; media shown alongside its parent post)</option>
+ <option value="gallery">'Gram-style gallery layout (show recent + pinned media; parent posts still accessible by link)</option>
+ </>
+ }
+ />
</div>
<div className="form-section-docs">
diff --git a/web/source/yarn.lock b/web/source/yarn.lock
index 826aaaed0..f3f3a5752 100644
--- a/web/source/yarn.lock
+++ b/web/source/yarn.lock
@@ -5399,10 +5399,10 @@ photoswipe-dynamic-caption-plugin@^1.2.7:
resolved "https://registry.yarnpkg.com/photoswipe-dynamic-caption-plugin/-/photoswipe-dynamic-caption-plugin-1.2.7.tgz#53aa5059f1c4dccc8aa36196ff3e09baa5e537c2"
integrity sha512-5XXdXLf2381nwe7KqQvcyStiUBi9TitYXppUQTrzPwYAi4lZsmWNnNKMclM7I4QGlX6fXo42v3bgb6rlK9pY1Q==
-photoswipe@^5.3.3:
- version "5.4.2"
- resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.2.tgz#bed976c27f876bd9c86085a022701a8cea484f7e"
- integrity sha512-z5hr36nAIPOZbHJPbCJ/mQ3+ZlizttF9za5gKXKH/us1k4KNHaRbC63K1Px5sVVKUtGb/2+ixHpKqtwl0WAwvA==
+photoswipe@^5.4.4:
+ version "5.4.4"
+ resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.4.tgz#e045dc036453493188d5c8665b0e8f1000ac4d6e"
+ integrity sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==
picocolors@^1.0.0:
version "1.0.0"
diff --git a/web/template/profile-gallery.tmpl b/web/template/profile-gallery.tmpl
new file mode 100644
index 000000000..badb04615
--- /dev/null
+++ b/web/template/profile-gallery.tmpl
@@ -0,0 +1,87 @@
+{{- /*
+// 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/>.
+*/ -}}
+
+{{- with . }}
+<main class="profile h-card">
+ {{- with . }}
+ {{- include "profile_header.tmpl" . | indent 1 }}
+ {{- end }}
+ {{- with . }}
+ {{- include "profile_about_user.tmpl" . | indent 1 }}
+ {{- end }}
+ <div
+ class="media-galleries-wrapper"
+ role="region"
+ aria-label="Media by {{ .account.Username -}}"
+ >
+ {{- if .pinned_statuses }}
+ <section class="pinned h-feed" aria-labelledby="pinned">
+ <div class="col-header">
+ <h3 class="p-name" id="pinned">Pinned media</h3>
+ <a href="#recent">jump to recent</a>
+ </div>
+ <div
+ class="media-gallery photoswipe-gallery"
+ role="group"
+ >
+ {{- range $index, $attachment := .pinnedGalleryItems }}
+ {{- includeIndex "status_attachment.tmpl" $attachment $index | indent 4 }}
+ {{- end }}
+ </div>
+ </section>
+ {{- end }}
+ <section class="recent h-feed" aria-labelledby="recent">
+ <div class="col-header">
+ <h3 id="recent p-name" tabindex="-1">Recent media</h3>
+ {{- if .rssFeed }}
+ <a href="{{- .rssFeed -}}" class="rss-icon" aria-label="RSS feed">
+ <i class="fa fa-rss-square" aria-hidden="true"></i>
+ </a>
+ {{- end }}
+ </div>
+ {{- if not .galleryItems }}
+ <div data-nosnippet class="nothinghere">
+ {{- if .show_back_to_top }}
+ Reached the end of visible media!
+ {{- else }}
+ Nothing to see here! {{ .account.Username }} has either not posted any public media yet, or has opted not to make posts visible via the World Wide Web.
+ {{- end }}
+ </div>
+ {{- else }}
+ <div
+ class="media-gallery photoswipe-gallery"
+ role="group"
+ >
+ {{- range $index, $attachment := .galleryItems }}
+ {{- includeIndex "status_attachment.tmpl" $attachment $index | indent 4 }}
+ {{- end }}
+ </div>
+ {{- end }}
+ <nav class="backnextlinks">
+ {{- if .show_back_to_top }}
+ <a href="/@{{- .account.Username -}}">Back to top</a>
+ {{- end }}
+ {{- if .statuses_next }}
+ <a href="{{- .statuses_next -}}" class="next">Show older</a>
+ {{- end }}
+ </nav>
+ </section>
+ </div>
+</main>
+{{- end }} \ No newline at end of file
diff --git a/web/template/profile.tmpl b/web/template/profile.tmpl
index 6b486eb74..d7d24cb64 100644
--- a/web/template/profile.tmpl
+++ b/web/template/profile.tmpl
@@ -17,200 +17,15 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ -}}
-{{- define "profileMovedTo" -}}
-{{- with .account.Moved }}
-<div class="moved-to">
- <b>
- ℹ️ This account has permanently moved to
- <a
- href="{{ .URL }}"
- class="nounderline"
- rel="nofollow noreferrer noopener"
- target="_blank"
- >
- @{{ .Username }}
- </a>
- </b>
-</div>
-{{- end }}
-{{- end -}}
-
-{{- define "defaultAvatarDimension" -}}
-{{- /* 136 is the default width/height for 8.5rem avatars, double it to get a good look when expanded. */ -}}
-272
-{{- end -}}
-
-{{- define "avatarWidth" -}}
-{{- with .account }}
- {{- if isNil .AvatarAttachment -}}
- {{- template "defaultAvatarDimension" . -}}
- {{- else -}}
- {{- /* Use the avatar's proper dimensions. */ -}}
- {{- .AvatarAttachment.Meta.Original.Width -}}
- {{- end -}}
-{{- end }}
-{{- end -}}
-
-{{- define "avatarHeight" -}}
-{{- with .account }}
- {{- if isNil .AvatarAttachment -}}
- {{- template "defaultAvatarDimension" . -}}
- {{- else -}}
- {{- /* Use the avatar's proper dimensions. */ -}}
- {{- .AvatarAttachment.Meta.Original.Height -}}
- {{- end -}}
-{{- end }}
-{{- end -}}
-
-{{- define "avatarAlt" -}}
- Avatar for {{ .account.Username -}}
- {{- if .account.AvatarDescription }}
- {{- /* Add the avatar's image description. */ -}}
- : {{ .account.AvatarDescription -}}
- {{- end -}}
-{{- end -}}
-
-{{- define "headerAlt" -}}
- Header for {{ .account.Username -}}
- {{- if .account.HeaderDescription }}
- {{- /* Add the header's image description. */ -}}
- : {{ .account.HeaderDescription -}}
- {{- end -}}
-{{- end -}}
-
-{{- define "avatar" -}}
-{{- with . }}
-<div
- class="media photoswipe-gallery odd single avatar-image-wrapper"
- role="group"
->
- <a
- class="photoswipe-slide"
- href="{{- .account.Avatar -}}"
- target="_blank"
- data-pswp-width="{{- template "avatarWidth" . -}}px"
- data-pswp-height="{{- template "avatarHeight" . -}}px"
- data-cropped="true"
- alt="{{- template "avatarAlt" . -}}"
- title="{{- template "avatarAlt" . -}}"
- >
- <picture
- aria-hidden="true"
- >
- {{- if .account.AvatarAttachment }}
- <source
- class="avatar"
- srcset="{{- .account.AvatarStatic -}}"
- type="{{- .account.AvatarAttachment.PreviewMIMEType -}}"
- media="(prefers-reduced-motion: reduce)"
- />
- {{- end }}
- <img
- class="avatar u-photo"
- src="{{- .account.Avatar -}}"
- alt="{{- template "avatarAlt" . -}}"
- title="{{- template "avatarAlt" . -}}"
- width="{{- template "avatarWidth" . -}}"
- height="{{- template "avatarHeight" . -}}"
- />
- </picture>
- </a>
-</div>
-{{- end }}
-{{- end -}}
-
{{- with . }}
<main class="profile h-card">
- <h2 class="sr-only">Profile for {{ .account.Username -}}</h2>
- <section class="profile-header" role="region" aria-label="Basic info">
- {{- if .account.Moved }}
- {{- include "profileMovedTo" . | indent 2 }}
- {{- end }}
- <div class="header-image-wrapper">
- <picture>
- {{- if .account.HeaderAttachment }}
- <source
- srcset="{{- .account.HeaderStatic -}}"
- type="{{- .account.HeaderAttachment.PreviewMIMEType -}}"
- media="(prefers-reduced-motion: reduce)"
- />
- {{- end }}
- <img
- src="{{- .account.Header -}}"
- alt="{{- template "headerAlt" . -}}"
- title="{{- template "headerAlt" . -}}"
- />
- </picture>
- </div>
- <div class="basic-info">
- {{- with . }}
- {{- include "avatar" . | indent 3 }}
- {{- end }}
- <dl class="namerole">
- <dt class="sr-only">Display name</dt>
- <dd class="displayname text-cutoff p-name">
- {{- if .account.DisplayName -}}
- {{- emojify .account.Emojis (escape .account.DisplayName) -}}
- {{- else -}}
- {{- .account.Username -}}
- {{- end -}}
- </dd>
- <div class="bot-username-wrapper">
- {{- if .account.Bot }}
- <dt class="sr-only">Bot account</dt>
- <dd>
- <span class="sr-only">true</span>
- <div
- class="bot-legend-wrapper"
- aria-hidden="true"
- title="This is a bot account."
- >
- <i class="bot-icon fa fa-microchip"></i>
- <span class="bot-legend">bot</span>
- </div>
- </dd>
- {{- end }}
- <dt class="sr-only">Username</dt>
- <dd class="username text-cutoff p-nickname">@{{- .account.Username -}}@{{- .instance.AccountDomain -}}</dd>
- </div>
- {{- if .account.Roles }}
- <dt class="sr-only">Role</dt>
- {{- range .account.Roles }}
- <dd class="role {{ .Name -}}">{{- .Name -}}</dd>
- {{- end }}
- {{- end }}
- </dl>
- <a class="u-url u-uid hidden" rel="me" href="/@{{- .account.Username -}}"></a>
- </div>
- </section>
+ {{- with . }}
+ {{- include "profile_header.tmpl" . | indent 1 }}
+ {{- end }}
<div class="column-split">
- <section class="about-user" role="region" aria-labelledby="about-header">
- <div class="col-header">
- <h3 id="about-header">About<span class="sr-only">&nbsp;{{- .account.Username -}}</span></h3>
- </div>
- {{- if .account.Fields }}
- {{- include "profile_fields.tmpl" . | indent 3 }}
- {{- end }}
- <h4 class="sr-only">Bio</h4>
- <div class="bio p-note">
- {{- if .account.Note }}
- {{ emojify .account.Emojis (noescape .account.Note) }}
- {{- else }}
- <p>This GoToSocial user hasn't written a bio yet!</p>
- {{- end }}
- </div>
- <h4 class="sr-only">Stats</h4>
- <dl class="accountstats">
- <dt>Joined</dt>
- <dd><time datetime="{{- .account.CreatedAt -}}">{{- .account.CreatedAt | timestampVague -}}</time></dd>
- <dt>Posts</dt>
- <dd>{{- .account.StatusesCount -}}</dd>
- <dt>Followed by</dt>
- <dd>{{- if .account.HideCollections -}}<i>hidden</i>{{- else -}}{{- .account.FollowersCount -}}{{- end -}}</dd>
- <dt>Following</dt>
- <dd>{{- if .account.HideCollections -}}<i>hidden</i>{{- else -}}{{- .account.FollowingCount -}}{{- end -}}</dd>
- </dl>
- </section>
+ {{- with . }}
+ {{- include "profile_about_user.tmpl" . | indent 2 }}
+ {{- end }}
<div class="statuses-wrapper" role="region" aria-label="Posts by {{ .account.Username -}}">
{{- if .pinned_statuses }}
<section class="pinned statuses h-feed" aria-labelledby="pinned">
diff --git a/web/template/profile_about_user.tmpl b/web/template/profile_about_user.tmpl
new file mode 100644
index 000000000..7f3ce0e97
--- /dev/null
+++ b/web/template/profile_about_user.tmpl
@@ -0,0 +1,56 @@
+{{- /*
+// 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/>.
+*/ -}}
+
+{{- with . }}
+<section class="about-user" role="region" aria-labelledby="about-header">
+ <div class="col-header">
+ <h3 id="about-header">About<span class="sr-only">&nbsp;{{- .account.Username -}}</span></h3>
+ </div>
+ {{- if .account.Fields }}
+ {{- include "profile_fields.tmpl" . | indent 1 }}
+ {{- end }}
+ <h4 class="sr-only">Bio</h4>
+ <div class="bio p-note">
+ {{- if .account.Note }}
+ {{ emojify .account.Emojis (noescape .account.Note) }}
+ {{- else }}
+ <p>This GoToSocial user hasn't written a bio yet!</p>
+ {{- end }}
+ </div>
+ <h4 class="sr-only">Stats</h4>
+ <dl class="accountstats">
+ <div class="stats-item">
+ <dt class="joineddt text-cutoff">Joined</dt>
+ <dd class="joineddd text-cutoff"><time datetime="{{- .account.CreatedAt -}}">{{- .account.CreatedAt | timestampVague -}}</time></dd>
+ </div>
+ <div class="stats-item">
+ <dt class="postsdt text-cutoff">Posts</dt>
+ <dd class="postsdd text-cutoff">{{- .account.StatusesCount -}}</dd>
+ </div>
+ <div class="stats-item">
+ <dt class="followeddt text-cutoff">Followed by</dt>
+ <dd class="followeddd text-cutoff">{{- if .account.HideCollections -}}<i>hidden</i>{{- else -}}{{- .account.FollowersCount -}}{{- end -}}</dd>
+ </div>
+ <div class="stats-item">
+ <dt class="followingdt text-cutoff">Following</dt>
+ <dd class="followingdd text-cutoff">{{- if .account.HideCollections -}}<i>hidden</i>{{- else -}}{{- .account.FollowingCount -}}{{- end -}}</dd>
+ </div>
+ </dl>
+</section>
+{{- end }} \ No newline at end of file
diff --git a/web/template/profile_header.tmpl b/web/template/profile_header.tmpl
new file mode 100644
index 000000000..4be900287
--- /dev/null
+++ b/web/template/profile_header.tmpl
@@ -0,0 +1,185 @@
+{{- /*
+// 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/>.
+*/ -}}
+
+{{- define "profileMovedTo" -}}
+{{- with .account.Moved }}
+<div class="moved-to">
+ <b>
+ ℹ️ This account has permanently moved to
+ <a
+ href="{{ .URL }}"
+ class="nounderline"
+ rel="nofollow noreferrer noopener"
+ target="_blank"
+ >
+ @{{ .Username }}
+ </a>
+ </b>
+</div>
+{{- end }}
+{{- end -}}
+
+{{- define "defaultAvatarDimension" -}}
+{{- /* 136 is the default width/height for 8.5rem avatars, double it to get a good look when expanded. */ -}}
+272
+{{- end -}}
+
+{{- define "avatarWidth" -}}
+{{- with .account }}
+ {{- if isNil .AvatarAttachment -}}
+ {{- template "defaultAvatarDimension" . -}}
+ {{- else -}}
+ {{- /* Use the avatar's proper dimensions. */ -}}
+ {{- .AvatarAttachment.Meta.Original.Width -}}
+ {{- end -}}
+{{- end }}
+{{- end -}}
+
+{{- define "avatarHeight" -}}
+{{- with .account }}
+ {{- if isNil .AvatarAttachment -}}
+ {{- template "defaultAvatarDimension" . -}}
+ {{- else -}}
+ {{- /* Use the avatar's proper dimensions. */ -}}
+ {{- .AvatarAttachment.Meta.Original.Height -}}
+ {{- end -}}
+{{- end }}
+{{- end -}}
+
+{{- define "avatarAlt" -}}
+ Avatar for {{ .account.Username -}}
+ {{- if .account.AvatarDescription }}
+ {{- /* Add the avatar's image description. */ -}}
+ : {{ .account.AvatarDescription -}}
+ {{- end -}}
+{{- end -}}
+
+{{- define "headerAlt" -}}
+ Header for {{ .account.Username -}}
+ {{- if .account.HeaderDescription }}
+ {{- /* Add the header's image description. */ -}}
+ : {{ .account.HeaderDescription -}}
+ {{- end -}}
+{{- end -}}
+
+{{- define "avatar" -}}
+{{- with . }}
+<div
+ class="photoswipe-gallery odd single avatar-image-wrapper"
+ role="group"
+>
+ <a
+ class="photoswipe-slide"
+ href="{{- .account.Avatar -}}"
+ target="_blank"
+ data-pswp-width="{{- template "avatarWidth" . -}}px"
+ data-pswp-height="{{- template "avatarHeight" . -}}px"
+ data-cropped="true"
+ alt="{{- template "avatarAlt" . -}}"
+ title="{{- template "avatarAlt" . -}}"
+ >
+ <picture
+ aria-hidden="true"
+ >
+ {{- if .account.AvatarAttachment }}
+ <source
+ class="avatar"
+ srcset="{{- .account.AvatarStatic -}}"
+ type="{{- .account.AvatarAttachment.PreviewMIMEType -}}"
+ media="(prefers-reduced-motion: reduce)"
+ />
+ {{- end }}
+ <img
+ class="avatar u-photo"
+ src="{{- .account.Avatar -}}"
+ alt="{{- template "avatarAlt" . -}}"
+ title="{{- template "avatarAlt" . -}}"
+ width="{{- template "avatarWidth" . -}}"
+ height="{{- template "avatarHeight" . -}}"
+ />
+ </picture>
+ </a>
+</div>
+{{- end }}
+{{- end -}}
+
+{{- with . }}
+<h2 class="sr-only">Profile for {{ .account.Username -}}</h2>
+<section class="profile-header" role="region" aria-label="Basic info">
+ {{- if .account.Moved }}
+ {{- include "profileMovedTo" . | indent 2 }}
+ {{- end }}
+ <div class="header-image-wrapper">
+ <picture>
+ {{- if .account.HeaderAttachment }}
+ <source
+ srcset="{{- .account.HeaderStatic -}}"
+ type="{{- .account.HeaderAttachment.PreviewMIMEType -}}"
+ media="(prefers-reduced-motion: reduce)"
+ />
+ {{- end }}
+ <img
+ src="{{- .account.Header -}}"
+ alt="{{- template "headerAlt" . -}}"
+ title="{{- template "headerAlt" . -}}"
+ />
+ </picture>
+ </div>
+ <div class="basic-info">
+ {{- with . }}
+ {{- include "avatar" . | indent 3 }}
+ {{- end }}
+ <dl class="namerole">
+ <dt class="sr-only">Display name</dt>
+ <dd class="displayname text-cutoff p-name">
+ {{- if .account.DisplayName -}}
+ {{- emojify .account.Emojis (escape .account.DisplayName) -}}
+ {{- else -}}
+ {{- .account.Username -}}
+ {{- end -}}
+ </dd>
+ <div class="bot-username-wrapper">
+ {{- if .account.Bot }}
+ <dt class="sr-only">Bot account</dt>
+ <dd>
+ <span class="sr-only">true</span>
+ <div
+ class="bot-legend-wrapper"
+ aria-hidden="true"
+ title="This is a bot account."
+ >
+ <i class="bot-icon fa fa-microchip"></i>
+ <span class="bot-legend">bot</span>
+ </div>
+ </dd>
+ {{- end }}
+ <dt class="sr-only">Username</dt>
+ <dd class="username text-cutoff p-nickname">@{{- .account.Username -}}@{{- .instance.AccountDomain -}}</dd>
+ </div>
+ {{- if .account.Roles }}
+ <dt class="sr-only">Role</dt>
+ {{- range .account.Roles }}
+ <dd class="role {{ .Name -}}">{{- .Name -}}</dd>
+ {{- end }}
+ {{- end }}
+ </dl>
+ <a class="u-url u-uid hidden" rel="me" href="/@{{- .account.Username -}}"></a>
+ </div>
+</section>
+{{- end }} \ No newline at end of file
diff --git a/web/template/status.tmpl b/web/template/status.tmpl
index 85000fd72..872b784ed 100644
--- a/web/template/status.tmpl
+++ b/web/template/status.tmpl
@@ -30,6 +30,16 @@
it in an appropriate <article></article>!
*/ -}}
+{{- /* Produces something like "1 attachment", "2 attachments", etc */ -}}
+{{- define "attachmentsLength" -}}
+{{- (len .) }}{{- if eq (len .) 1 }} attachment{{- else }} attachments{{- end -}}
+{{- end -}}
+
+{{- /* Produces something like "media photoswipe-gallery odd single" */ -}}
+{{- define "galleryClass" -}}
+media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{ else if eq (len .) 2 }}double{{ end }}
+{{- end -}}
+
{{- with . }}
<header class="status-header">
{{- include "status_header.tmpl" . | indent 1 }}
@@ -63,7 +73,15 @@
</div>
{{- end }}
{{- if .MediaAttachments }}
- {{- include "status_attachments.tmpl" . | indent 1 }}
+ <div
+ class="{{- template "galleryClass" .MediaAttachments -}}"
+ role="group"
+ aria-label="{{- template "attachmentsLength" .MediaAttachments -}}"
+ >
+ {{- range $index, $media := .MediaAttachments }}
+ {{- includeIndex "status_attachment.tmpl" $media $index | indent 2 }}
+ {{- end }}
+ </div>
{{- end }}
</div>
<aside class="status-info" aria-hidden="true">
diff --git a/web/template/status_attachment.tmpl b/web/template/status_attachment.tmpl
new file mode 100644
index 000000000..bdfafa96f
--- /dev/null
+++ b/web/template/status_attachment.tmpl
@@ -0,0 +1,179 @@
+{{- /*
+// 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/>.
+*/ -}}
+
+{{- define "imagePreview" }}
+<img
+ src="{{- .PreviewURL -}}"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="{{- .Meta.Original.Width -}}"
+ height="{{- .Meta.Original.Height -}}"
+/>
+{{- end }}
+
+{{- define "videoPreview" }}
+<img
+ src="{{- .PreviewURL -}}"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="{{- .Meta.Small.Width -}}"
+ height="{{- .Meta.Small.Height -}}"
+/>
+{{- end }}
+
+{{- define "audioPreview" }}
+{{- if and .PreviewURL .Meta.Small.Width }}
+<img
+ src="{{- .PreviewURL -}}"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="{{- .Meta.Small.Width -}}"
+ height="{{- .Meta.Small.Height -}}"
+/>
+{{- else }}
+<img
+ src="/assets/logo.webp"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="518"
+ height="460"
+/>
+{{- end }}
+{{- end }}
+
+{{- with . }}
+<div class="media-wrapper">
+ <details class="{{- .Item.Type -}}-spoiler media-spoiler" {{- if not .Item.Sensitive }} open{{- end -}}>
+ <summary>
+ <div class="show sensitive button" aria-hidden="true">Show sensitive</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 or (eq .Item.Type "video") (eq .Item.Type "gifv") }}
+ {{- include "videoPreview" .Item | indent 3 }}
+ {{- else if eq .Item.Type "image" }}
+ {{- include "imagePreview" .Item | indent 3 }}
+ {{- else if eq .Item.Type "audio" }}
+ {{- include "audioPreview" .Item | indent 3 }}
+ {{- end }}
+ </summary>
+ {{- if or (eq .Item.Type "video") (eq .Item.Type "gifv") }}
+ <video
+ {{- if eq .Item.Type "video" }}
+ preload="none"
+ {{- else }}
+ preload="auto"
+ muted
+ {{- end }}
+ class="plyr-video photoswipe-slide{{- if eq .Item.Type "gifv" }} gifv{{ end }}"
+ controls
+ playsinline
+ data-pswp-index="{{- .Index -}}"
+ data-pswp-parent-status="{{- .Item.ParentStatusLink -}}"
+ data-pswp-attachment-id="{{- .Item.ID -}}"
+ poster="{{- .Item.PreviewURL -}}"
+ data-pswp-width="{{- .Item.Meta.Original.Width -}}px"
+ data-pswp-height="{{- .Item.Meta.Original.Height -}}px"
+ {{- if .Item.Description }}
+ alt="{{- .Item.Description -}}"
+ title="{{- .Item.Description -}}"
+ {{- end }}
+ >
+ <source type="{{- .Item.MIMEType -}}" src="{{- .Item.URL -}}"/>
+ </video>
+ {{- else if eq .Item.Type "audio" }}
+ <video
+ preload="none"
+ class="plyr-video photoswipe-slide"
+ controls
+ playsinline
+ data-pswp-index="{{- .Index -}}"
+ data-pswp-parent-status="{{- .Item.ParentStatusLink -}}"
+ data-pswp-attachment-id="{{- .Item.ID -}}"
+ {{- if and .Item.PreviewURL .Item.Meta.Small.Width }}
+ poster="{{- .Item.PreviewURL -}}"
+ data-pswp-width="{{- .Item.Meta.Small.Width -}}px"
+ data-pswp-height="{{- .Item.Meta.Small.Height -}}px"
+ {{- else }}
+ poster="/assets/logo.webp"
+ width="518px"
+ height="460px"
+ {{- end }}
+ {{- if .Item.Description }}
+ alt="{{- .Item.Description -}}"
+ title="{{- .Item.Description -}}"
+ {{- end }}
+ >
+ <source type="{{- .Item.MIMEType -}}" src="{{- .Item.URL -}}"/>
+ </video>
+ {{- else if eq .Item.Type "image" }}
+ <a
+ class="photoswipe-slide"
+ data-pswp-index="{{- .Index -}}"
+ data-pswp-parent-status="{{- .Item.ParentStatusLink -}}"
+ data-pswp-attachment-id="{{- .Item.ID -}}"
+ href="{{- .Item.URL -}}"
+ target="_blank"
+ data-pswp-width="{{- .Item.Meta.Original.Width -}}px"
+ data-pswp-height="{{- .Item.Meta.Original.Height -}}px"
+ data-cropped="true"
+ {{- if .Item.Description }}
+ alt="{{- .Item.Description -}}"
+ title="{{- .Item.Description -}}"
+ {{- end }}
+ >
+ {{- with .Item }}
+ {{- include "imagePreview" . | indent 3 }}
+ {{- end }}
+ </a>
+ {{- else }}
+ <a
+ class="unknown-attachment"
+ href="{{- .Item.RemoteURL -}}"
+ rel="nofollow noreferrer noopener"
+ target="_blank"
+ {{- if .Item.Description }}
+ title="Open external media: {{ .Item.Description -}}&#10;&#13;{{- .Item.RemoteURL -}}"
+ {{- else }}
+ title="Open external media.&#10;&#13;{{- .Item.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 }} \ No newline at end of file
diff --git a/web/template/status_attachments.tmpl b/web/template/status_attachments.tmpl
deleted file mode 100644
index ee564d934..000000000
--- a/web/template/status_attachments.tmpl
+++ /dev/null
@@ -1,195 +0,0 @@
-{{- /*
-// 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.
-*/ -}}
-
-{{- define "imagePreview" }}
-<img
- src="{{- .PreviewURL -}}"
- loading="lazy"
- {{- if .Description }}
- alt="{{- .Description -}}"
- title="{{- .Description -}}"
- {{- end }}
- width="{{- .Meta.Original.Width -}}"
- height="{{- .Meta.Original.Height -}}"
-/>
-{{- end }}
-
-{{- define "videoPreview" }}
-<img
- src="{{- .PreviewURL -}}"
- loading="lazy"
- {{- if .Description }}
- alt="{{- .Description -}}"
- title="{{- .Description -}}"
- {{- end }}
- width="{{- .Meta.Small.Width -}}"
- height="{{- .Meta.Small.Height -}}"
-/>
-{{- end }}
-
-{{- define "audioPreview" }}
-{{- if and .PreviewURL .Meta.Small.Width }}
-<img
- src="{{- .PreviewURL -}}"
- loading="lazy"
- {{- if .Description }}
- alt="{{- .Description -}}"
- title="{{- .Description -}}"
- {{- end }}
- width="{{- .Meta.Small.Width -}}"
- height="{{- .Meta.Small.Height -}}"
-/>
-{{- else }}
-<img
- src="/assets/logo.webp"
- loading="lazy"
- {{- if .Description }}
- alt="{{- .Description -}}"
- title="{{- .Description -}}"
- {{- end }}
- width="518"
- height="460"
-/>
-{{- end }}
-{{- end }}
-
-{{- /* Produces something like "1 attachment", "2 attachments", etc */ -}}
-{{- define "attachmentsLength" -}}
-{{- (len .) }}{{- if eq (len .) 1 }} attachment{{- else }} attachments{{- end -}}
-{{- end -}}
-
-{{- /* Produces something like "media photoswipe-gallery odd single" */ -}}
-{{- define "galleryClass" -}}
-media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{ else if eq (len .) 2 }}double{{ end }}
-{{- end -}}
-
-{{- with .MediaAttachments }}
-<div
- class="{{- template "galleryClass" . -}}"
- role="group"
- aria-label="{{- template "attachmentsLength" . -}}"
->
- {{- 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 or (eq .Type "video") (eq .Type "gifv") }}
- {{- include "videoPreview" $media | indent 4 }}
- {{- else if eq .Type "image" }}
- {{- include "imagePreview" $media | indent 4 }}
- {{- else if eq .Type "audio" }}
- {{- include "audioPreview" $media | indent 4 }}
- {{- end }}
- </summary>
- {{- if or (eq .Type "video") (eq .Type "gifv") }}
- <video
- {{- if eq .Type "video" }}
- preload="none"
- {{- else }}
- preload="auto"
- muted
- {{- end }}
- class="plyr-video photoswipe-slide{{- if eq .Type "gifv" }} gifv{{ end }}"
- controls
- playsinline
- data-pswp-index="{{- $index -}}"
- poster="{{- .PreviewURL -}}"
- data-pswp-width="{{- $media.Meta.Small.Width -}}px"
- data-pswp-height="{{- $media.Meta.Small.Height -}}px"
- {{- if .Description }}
- alt="{{- $media.Description -}}"
- title="{{- $media.Description -}}"
- {{- end }}
- >
- <source type="{{- $media.MIMEType -}}" src="{{- $media.URL -}}"/>
- </video>
- {{- else if eq .Type "audio" }}
- <video
- preload="none"
- class="plyr-video photoswipe-slide"
- controls
- playsinline
- data-pswp-index="{{- $index -}}"
- {{- if and $media.PreviewURL $media.Meta.Small.Width }}
- poster="{{- .PreviewURL -}}"
- data-pswp-width="{{- $media.Meta.Small.Width -}}px"
- data-pswp-height="{{- $media.Meta.Small.Height -}}px"
- {{- else }}
- poster="/assets/logo.webp"
- width="518px"
- height="460px"
- {{- end }}
- {{- if .Description }}
- alt="{{- $media.Description -}}"
- title="{{- $media.Description -}}"
- {{- end }}
- >
- <source type="{{- $media.MIMEType -}}" 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 }}
- alt="{{- $media.Description -}}"
- title="{{- $media.Description -}}"
- {{- end }}
- >
- {{- with $media }}
- {{- include "imagePreview" . | indent 4 }}
- {{- end }}
- </a>
- {{- else }}
- <a
- class="unknown-attachment"
- href="{{- $media.RemoteURL -}}"
- rel="nofollow noreferrer noopener"
- target="_blank"
- {{- if .Description }}
- title="Open external media: {{ $media.Description -}}&#10;&#13;{{- $media.RemoteURL -}}"
- {{- else }}
- title="Open 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 }} \ No newline at end of file
diff --git a/web/template/status_info.tmpl b/web/template/status_info.tmpl
index 5d26811d7..194a799f5 100644
--- a/web/template/status_info.tmpl
+++ b/web/template/status_info.tmpl
@@ -17,36 +17,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ -}}
-{{- define "visibility_icon" -}}
- {{- if eq .Visibility "public" -}}
- globe
- {{- else if eq .Visibility "unlisted" -}}
- unlock
- {{- else -}}
- question
- {{- end -}}
-{{- end -}}
-
-{{- define "visibility_title" -}}
- {{- if eq .Visibility "public" -}}
- Public
- {{- else if eq .Visibility "unlisted" -}}
- Unlisted
- {{- else -}}
- Unknown
- {{- end -}}
-{{- 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="stats-item published-at text-cutoff">
<dt class="sr-only">Published</dt>
<dd>
@@ -57,7 +30,7 @@
<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>
+ (edited <time class="dt-updated" datetime="{{- .EditedAt -}}">{{- .EditedAt | timestampPrecise -}}</time>)
</dd>
</div>
{{ end }}