6 Commits

Author SHA1 Message Date
Zoë Bijl 60731df277 [bugfix] show header logo + align header buttons
the logo wasn’t showing on the small screen layout. this fixes that. it also neatly aligns the buttons in the header area.
2025-10-24 10:44:30 +02:00
tobi ac7354ca62 [chore/docker] Remove reference to vanilla in Dockerfile (#114)
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/114
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-10-21 15:49:01 +02:00
Zoë Bijl 6e2caa1c2c [docs] fix typo 2025-10-21 15:16:52 +02:00
kipvandenbos 5447f63a3c [bugfix] Use correct branch name for next publication (#113)
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/113
Co-authored-by: kipvandenbos <kipvandenbos@noreply.codeberg.org>
Co-committed-by: kipvandenbos <kipvandenbos@noreply.codeberg.org>
2025-10-21 15:11:49 +02:00
tobi f5a55a64df [feature/cicd] Add next image publication on squerge to next branch (#112)
Build a `next` tagged docker image when we squerge something into next (makes my life a bit easier).

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/112
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Co-committed-by: tobi <tobi.smethurst@protonmail.com>
2025-10-21 15:08:31 +02:00
Zoë Bijl b976e9438f [bugfix] rewrite basic media-gallery css (#109)
Solves an issue where only four images would show up if more than four were added.

Fixes #34 & #107

Example of a post with many images:

![A post with thirteen images attached. they form a nice two column grid with the last item being two columns wide](/attachments/c44a4985-5297-4395-8964-15651b947d87)

---

@jacksonchen666 suggested we have a limit to the number of images and perhaps some sort of indicator to show that there are more images. I’ve made [an issue to track that](https://codeberg.org/superseriousbusiness/masto-fe-standalone/issues/110).

Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/109
Co-authored-by: Zoë Bijl <code@moiety.me>
Co-committed-by: Zoë Bijl <code@moiety.me>
2025-10-21 14:34:40 +02:00
18 changed files with 167 additions and 147 deletions
+24
View File
@@ -0,0 +1,24 @@
# https://woodpecker-ci.org/docs/usage/workflow-syntax#when---global-workflow-conditions
when:
- event: push
branch: next
# https://goreleaser.com/ci/woodpecker/
# https://woodpecker-ci.org/docs/usage/workflow-syntax#clone
clone:
git:
image: woodpeckerci/plugin-git
settings:
tags: true
# https://woodpecker-ci.org/plugins/docker-buildx
steps:
publish:
image: woodpeckerci/plugin-docker-buildx:5.2.2
settings:
pull_image: true
username: gotosocial
password:
from_secret: gts_docker_password
repo: superseriousbusiness/masto-fe-standalone
tag: next
+2 -1
View File
@@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added documentation for `yarn dev` (#75)
- Added an .editorconfig (#78)
- Added a CHANGELOG.md basaed on “Keep a Changelog” (#80)
- Added a CHANGELOG.md based on “Keep a Changelog” (#80)
### Changed
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated Yarn to 4.10.3 (#90)
- Updated Webpack to 5.102.1 (#83)
- Updated ESlint to 9.37.0 (#88)
- Fixed an issue where media items wouldnt show correctly (#109)
### Removed
-1
View File
@@ -27,7 +27,6 @@ RUN apk upgrade --update --no-cache
# Copy bigger nested stuff.
COPY --from=builder /build/public/packs/js/flavours/glitch /usr/share/nginx/html/packs/js/flavours/glitch
COPY --from=builder /build/public/packs/js/flavours/vanilla /usr/share/nginx/html/packs/js/flavours/vanilla
# Copy remaining files.
COPY --from=builder /build/public /usr/share/nginx/html/
@@ -1,13 +1,10 @@
import PropTypes from "prop-types";
import React from "react";
import classNames from "classnames";
import ImmutablePropTypes from "react-immutable-proptypes";
import { type List } from "immutable";
import { type List } from "immutable";
import { type Account } from "flavours/glitch/types/resources";
import { type Account } from "flavours/glitch/types/resources";
import { autoPlayGif } from "../initial_state";
@@ -18,15 +15,9 @@ interface Props {
others?: List<Account>,
localDomain?: string,
inline?: boolean,
nameLayout?: string,
}
export class DisplayName extends React.PureComponent<Props> {
static propTypes = {
onChange: PropTypes.func.isRequired,
nameLayout: ImmutablePropTypes.map.isRequired,
};
handleMouseEnter: React.ReactEventHandler<HTMLSpanElement> = ({
currentTarget,
}) => {
@@ -64,7 +55,7 @@ export class DisplayName extends React.PureComponent<Props> {
};
render() {
const { others, localDomain, inline, nameLayout } = this.props;
const { others, localDomain, inline } = this.props;
let displayName: React.ReactNode,
suffix: React.ReactNode,
@@ -82,7 +73,7 @@ export class DisplayName extends React.PureComponent<Props> {
.map((a) => (
<bdi key={a.get("id")}>
<strong
className="display-name__html"
className='display-name__html'
dangerouslySetInnerHTML={{ __html: a.get("display_name_html") }}
/>
</bdi>
@@ -102,39 +93,39 @@ export class DisplayName extends React.PureComponent<Props> {
displayName = (
<bdi>
<strong
className="display-name__html"
className='display-name__html'
dangerouslySetInnerHTML={{
__html: account.get("display_name_html"),
}}
/>
</bdi>
);
suffix = <span className="display-name__account">@{acct}</span>;
suffix = <span className='display-name__account'>@{acct}</span>;
} else {
displayName = (
<bdi>
<strong className="display-name__html">
<Skeleton width="10ch" />
<strong className='display-name__html'>
<Skeleton width='10ch' />
</strong>
</bdi>
);
suffix = (
<span className="display-name__account">
<Skeleton width="7ch" />
<span className='display-name__account'>
<Skeleton width='7ch' />
</span>
);
}
return (
<div
className={classNames("display-name", { inline }, nameLayout)}
<span
className={classNames("display-name", { inline })}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{displayName}
{inline ? " " : null}
{suffix}
</div>
</span>
);
}
}
@@ -826,7 +826,6 @@ class Status extends ImmutablePureComponent {
friend={account}
collapsed={isCollapsed}
parseClick={parseClick}
settings={settings}
/>
) : null}
<StatusIcons
@@ -15,7 +15,6 @@ export default class StatusHeader extends PureComponent {
status: ImmutablePropTypes.map.isRequired,
friend: ImmutablePropTypes.map,
parseClick: PropTypes.func.isRequired,
settings: ImmutablePropTypes.map.isRequired,
};
// Handles clicks on account name/image
@@ -34,7 +33,6 @@ export default class StatusHeader extends PureComponent {
const {
status,
friend,
settings,
} = this.props;
const account = status.get("account");
@@ -64,7 +62,7 @@ export default class StatusHeader extends PureComponent {
onClick={this.handleAccountClick}
rel='noopener noreferrer'
>
<DisplayName account={account} nameLayout={settings.get("name_layout")} />
<DisplayName account={account} />
</a>
</div>
);
@@ -33,10 +33,6 @@ const messages = defineMessages({
unlisted: { id: "privacy.unlisted.short", defaultMessage: "Unlisted" },
private: { id: "privacy.private.short", defaultMessage: "Followers only" },
direct: { id: "privacy.direct.short", defaultMessage: "Mentioned people only" },
namelayout_display: { id: "namelayout.display", defaultMessage: "Display name" },
namelayout_handle: { id: "namelayout.handle", defaultMessage: "Handle" },
namelayout_both_y: { id: "namelayout.both.vertical", defaultMessage: "Both (Vertical)" },
namelayout_both_x: { id: "namelayout.both.horizontal", defaultMessage: "Both (Horizontal)" },
});
class LocalSettingsPage extends PureComponent {
@@ -202,22 +198,6 @@ class LocalSettingsPage extends PureComponent {
<span className='hint'><FormattedMessage id='settings.wide_view_hint' defaultMessage='Stretches columns to better fill the available space.' /></span>
</LocalSettingsPageItem>
</section>
<section>
<h2><FormattedMessage id='settings.name_layout' defaultMessage='Name layout' /></h2>
<LocalSettingsPageItem
settings={settings}
item={["name_layout"]}
id='mastodon-settings--status-username-inline'
onChange={onChange}
options={[
{ value: "display", message: intl.formatMessage(messages.namelayout_display) },
{ value: "handle", message: intl.formatMessage(messages.namelayout_handle) },
{ value: "both_y", message: intl.formatMessage(messages.namelayout_both_y) },
{ value: "both_x", message: intl.formatMessage(messages.namelayout_both_x) },
]}
>
</LocalSettingsPageItem>
</section>
</div>
),
({ intl, onChange, settings }) => (
@@ -19,7 +19,7 @@ const Account = connect(state => ({
account: state.getIn(["accounts", me]),
}))(({ account }) => (
<Permalink href={account.get("url")} to={`/@${account.get("acct")}`} title={account.get("acct")}>
<Avatar account={account} size={35} />
<Avatar account={account} size={34} />
</Permalink>
));
@@ -108,8 +108,7 @@ class Header extends PureComponent {
return (
<div className='ui__header'>
<Link to='/' className='ui__header__logo'>
<WordmarkLogo />
<SymbolLogo />
<Icon id='gts-simple' />
</Link>
<div className='ui__header__links'>
@@ -53,7 +53,6 @@ const initialState = ImmutableMap({
pop_in_player : true,
pop_in_position : "right",
}),
name_layout : "both_y",
notifications : ImmutableMap({
favicon_badge : false,
tab_badge : true,
@@ -56,57 +56,6 @@
$ui-header-height: 55px;
.ui__header {
display: none;
box-sizing: border-box;
height: $ui-header-height;
position: sticky;
top: 0;
z-index: 3;
justify-content: space-between;
align-items: center;
&__logo {
display: inline-flex;
padding: 15px;
.logo {
height: $ui-header-height - 30px;
width: auto;
}
.logo--wordmark {
display: none;
}
@media screen and (width >= 320px) {
.logo--wordmark {
display: block;
}
.logo--icon {
display: none;
}
}
}
&__links {
display: flex;
align-items: center;
gap: 10px;
padding: 0 10px;
overflow: hidden;
.button {
flex: 0 0 auto;
}
.button-tertiary {
flex-shrink: 1;
}
}
}
.tabs-bar__wrapper {
background: darken($ui-base-color, 8%);
position: sticky;
@@ -64,29 +64,7 @@
line-height: 18px;
}
.media-gallery {
box-sizing: border-box;
overflow: hidden;
border-radius: 4px;
position: relative;
width: 100%;
min-height: 64px;
display: grid;
grid-template-columns: 50% 50%;
grid-template-rows: 50% 50%;
gap: 2px;
@include fullwidth-gallery;
}
.media-gallery__item {
border: 0;
box-sizing: border-box;
display: block;
position: relative;
border-radius: 4px;
overflow: hidden;
&--tall {
grid-row: span 2;
}
@@ -277,8 +277,8 @@
.layout-single-column .ui__header {
display: flex;
background: $ui-base-color;
border-bottom: 1px solid lighten($ui-base-color, 8%);
background: $ui-base-color;
}
.layout-single-column {
@@ -10,4 +10,6 @@
@import "sizes";
/* 🧩 Components */
@import "media-gallery";
@import "status";
@import "ui-header";
@@ -1 +1,73 @@
/* 🖼️ Media Gallery */
.media-gallery {
position: relative;
display: grid;
grid-template-columns: auto auto;
gap: var(--size-ui-gap);
overflow: hidden;
}
.media-gallery:not(.full-width) {
border-radius: var(--size-ui-radius);
}
.media-gallery.full-width {
margin-inline: -15px;
/* the `div` is there for specificity */
div.media-gallery__item {
border-radius: 0;
}
}
.media-gallery:not(.full-width) .spoiler-button__overlay {
border-radius: var(--size-ui-radius);
}
.media-gallery__item {
overflow: hidden;
}
/* Only one image */
.media-gallery__item:nth-child(2):nth-last-child(1) {
border-radius: var(--size-ui-radius);
}
/* First image in a set of two */
.media-gallery__item:nth-child(2):nth-last-child(2) {
border-top-left-radius: var(--size-ui-radius);
border-bottom-left-radius: var(--size-ui-radius);
}
/* Second image in a set of two */
.media-gallery__item:nth-child(3):nth-last-child(1) {
border-top-right-radius: var(--size-ui-radius);
border-bottom-right-radius: var(--size-ui-radius);
}
/* First image in a set of at least two */
.media-gallery__item:nth-child(2):not(:nth-last-child(1)) {
border-top-left-radius: var(--size-ui-radius);
}
/* Second image in a set of at least two */
.media-gallery__item:nth-child(3):not(:nth-last-child(1)) {
border-top-right-radius: var(--size-ui-radius);
}
/* Second to last image in an even set */
.media-gallery__item:nth-last-child(2):nth-child(even) {
border-bottom-left-radius: var(--size-ui-radius);
}
/* Last image in an even set */
.media-gallery__item:last-child:nth-child(odd) {
border-bottom-right-radius: var(--size-ui-radius);
}
/* Last image in an uneven set */
.media-gallery__item:last-child:nth-child(even) {
grid-column: 1/-1;
border-bottom-right-radius: var(--size-ui-radius);
border-bottom-left-radius: var(--size-ui-radius);
}
@@ -11,4 +11,5 @@
/* Components */
--size-avatar: 46px;
--size-icon: 20px;
--size-header-block: #{$ui-header-height};
}
@@ -31,9 +31,11 @@
color: var(--color-secondary-fg);
}
.status .mention,
.status .mention:hover {
text-decoration: none;
.status .mention {
&,
&:hover {
text-decoration: none;
}
}
.status .mention:is(:focus, :hover) span {
@@ -18,16 +18,6 @@
line-height: max(21px, 1.4em);
}
.display-name.both_x {
display: flex;
gap: var(--size-ui-gap);
}
.display-name.handle bdi,
.display-name.display .display-name__account {
display: none;
}
.display-name__html {
font-weight: 700;
}
@@ -37,14 +27,7 @@
font-weight: 400;
}
.display-name.handle .display-name__account {
color: currentcolor;
font-size: inherit;
font-weight: 700;
}
&:hover .display-name__html,
&:hover .display-name.handle .display-name__account {
&:hover .display-name__html {
text-decoration-line: underline;
text-decoration-color: currentColor;
text-decoration-thickness: 2px;
@@ -0,0 +1,43 @@
/* 🧑‍💻 UI: Header */
.ui__header {
position: sticky;
top: 0;
z-index: 3;
display: none;
align-items: center;
justify-content: space-between;
padding-inline-start: 7px;
height: var(--size-header-block);
box-sizing: border-box;
svg.gts-icon {
display: block;
}
}
.ui__header__logo {
--size-icon: 32px;
padding: 8px;
}
.ui__header__links {
display: flex;
align-items: center;
gap: 10px;
padding-inline: 10px;
overflow: hidden;
a {
display: flex;
align-items: center;
line-height: 20px;
}
.button {
flex: 0 0 auto;
}
.button-tertiary {
flex-shrink: 1;
}
}