Files
Masto/app/javascript/flavours/glitch/features/about/index.jsx
T
Zoë Bijl d03bfe2ab8 [bugfix] further CSS fixes for Phosphor update
commit 242cfc4b06
Author: Zoë Bijl <code@moiety.me>
Date:   Tue Oct 21 00:22:46 2025 +0200

    [bugfix] tweak display name alignment

commit 23e5d9840f
Author: Zoë Bijl <code@moiety.me>
Date:   Tue Oct 21 00:00:59 2025 +0200

    [bugfix] improve display name alignment

commit 1664835c94
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 17:03:52 2025 +0200

    [feature] add text-decoration to usernames and hashtags

commit d4fdb18549
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 16:38:08 2025 +0200

    [bugfix] correct spacing in status__content__spoiler

commit c19307a115
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 16:28:30 2025 +0200

    [feature] remove giant logo in multi-column view

commit 51e2c6e1c3
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 16:24:52 2025 +0200

    [feature] add gts logo in the multi-column menu

commit edc83b0f54
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 16:23:26 2025 +0200

    [bugfix] fix width of fullwidth media gallery

commit 9923c1b6da
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 15:17:25 2025 +0200

    [chore] remove `fixedWith` from icons

commit 935d6b73ef
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 15:09:43 2025 +0200

    [chore] lint

commit 776f02bd5f
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 15:07:52 2025 +0200

    [bugfix] correctly align multiline column-header button

commit d988d35671
Author: Zoë Bijl <code@moiety.me>
Date:   Mon Oct 20 15:04:35 2025 +0200

    [feat] add new size variables

commit 34bcbb669d
Author: Zoë Bijl <code@moiety.me>
Date:   Sun Oct 19 23:47:20 2025 +0200

    [bugfix] re-enable hicolor privacy icons

commit 97f2cb8f69
Author: Zoë Bijl <code@moiety.me>
Date:   Sun Oct 19 23:26:16 2025 +0200

    [bugfix] correctly align “toot” buttons

commit 52bcd4b6d0
Author: Zoë Bijl <code@moiety.me>
Date:   Thu Oct 16 16:22:44 2025 +0200

    [bugfix] replace `--size` with global `--size-icon`

    BREAKING CHANGE: any user styles that overwrote `--size` in a `,gts-icon` class will need to be updated.

commit 9812a2611f
Author: Zoë Bijl <code@moiety.me>
Date:   Thu Oct 16 15:53:37 2025 +0200

    [bugfix] further tweaks to `.poll__footer` alignment

commit 798d6fbf79
Author: Zoë Bijl <code@moiety.me>
Date:   Thu Oct 16 15:38:45 2025 +0200

    [bugfix] correctly align .poll__footer
2025-10-21 00:31:41 +02:00

225 lines
8.8 KiB
React

import PropTypes from "prop-types";
import { PureComponent } from "react";
import { defineMessages, injectIntl, FormattedMessage } from "react-intl";
import classNames from "classnames";
import { Helmet } from "react-helmet";
import { List as ImmutableList } from "immutable";
import ImmutablePropTypes from "react-immutable-proptypes";
import { connect } from "react-redux";
import { fetchServer, fetchDomainBlocks } from "flavours/glitch/actions/server";
import Column from "flavours/glitch/components/column";
import { Icon } from "flavours/glitch/components/icon";
import { ServerHeroImage } from "flavours/glitch/components/server_hero_image";
import { Skeleton } from "flavours/glitch/components/skeleton";
import Account from "flavours/glitch/containers/account_container";
import LinkFooter from "flavours/glitch/features/ui/components/link_footer";
const messages = defineMessages({
title: { id: "column.about", defaultMessage: "About" },
rules: { id: "about.rules", defaultMessage: "Server rules" },
blocks: { id: "about.blocks", defaultMessage: "Moderated servers" },
silenced: { id: "about.domain_blocks.silenced.title", defaultMessage: "Limited" },
silencedExplanation: { id: "about.domain_blocks.silenced.explanation", defaultMessage: "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following." },
suspended: { id: "about.domain_blocks.suspended.title", defaultMessage: "Suspended" },
suspendedExplanation: { id: "about.domain_blocks.suspended.explanation", defaultMessage: "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible." },
});
const severityMessages = {
silence: {
title: messages.silenced,
explanation: messages.silencedExplanation,
},
suspend: {
title: messages.suspended,
explanation: messages.suspendedExplanation,
},
};
const mapStateToProps = state => ({
server: state.getIn(["server", "server"]),
domainBlocks: state.getIn(["server", "domainBlocks"]),
});
class Section extends PureComponent {
static propTypes = {
title: PropTypes.string,
children: PropTypes.node,
open: PropTypes.bool,
onOpen: PropTypes.func,
};
state = {
collapsed: !this.props.open,
};
handleClick = () => {
const { onOpen } = this.props;
const { collapsed } = this.state;
this.setState({ collapsed: !collapsed }, () => onOpen && onOpen());
};
render () {
const { title, children } = this.props;
const { collapsed } = this.state;
return (
<div className={classNames("about__section", { active: !collapsed })}>
<div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
<Icon id={collapsed ? "caret-right" : "caret-down"} /> {title}
</div>
{!collapsed && (
<div className='about__section__body'>{children}</div>
)}
</div>
);
}
}
class About extends PureComponent {
static propTypes = {
server: ImmutablePropTypes.map,
domainBlocks: ImmutablePropTypes.contains({
isLoading: PropTypes.bool,
isAvailable: PropTypes.bool,
items: ImmutablePropTypes.list,
}),
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool,
};
componentDidMount () {
const { dispatch } = this.props;
dispatch(fetchServer());
}
handleDomainBlocksOpen = () => {
const { dispatch } = this.props;
dispatch(fetchDomainBlocks());
};
render () {
const { multiColumn, intl, server, domainBlocks } = this.props;
const isLoading = server.get("isLoading");
return (
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<div className='scrollable about'>
<div className='about__header'>
<ServerHeroImage blurhash={server.getIn(["thumbnail", "blurhash"])} src={server.getIn(["thumbnail", "url"])} srcSet={server.getIn(["thumbnail", "versions"])?.map((value, key) => `${value} ${key.replace("@", "")}`).join(", ")} className='about__header__hero' />
<h1>{isLoading ? <Skeleton width='10ch' /> : server.get("domain")}</h1>
<p><FormattedMessage id='about.powered_by' defaultMessage='Decentralized social media powered by {torment}' values={{ torment: <a href='https://doom.fandom.com/wiki/Argent_Energy' className='about__mail' target='_blank' rel="noreferrer">torment</a> }} /></p>
</div>
<div className='about__meta'>
<div className='about__meta__column'>
<h4><FormattedMessage id='server_banner.administered_by' defaultMessage='Administered by:' /></h4>
<Account id={server.getIn(["contact", "account", "id"])} size={36} />
</div>
<hr className='about__meta__divider' />
<div className='about__meta__column'>
<h4><FormattedMessage id='about.contact' defaultMessage='Contact:' /></h4>
{isLoading ? <Skeleton width='10ch' /> : <a className='about__mail' href={`mailto:${server.getIn(["contact", "email"])}`}>{server.getIn(["contact", "email"])}</a>}
</div>
</div>
<Section open title={intl.formatMessage(messages.title)}>
{isLoading ? (
<>
<Skeleton width='100%' />
<br />
<Skeleton width='100%' />
<br />
<Skeleton width='100%' />
<br />
<Skeleton width='70%' />
</>
) : (server.get("description")?.length > 0 ? (
<div
className='prose'
dangerouslySetInnerHTML={{ __html: server.get("description") }}
/>
) : (
<p><FormattedMessage id='about.not_available' defaultMessage='This information has not been made available on this server.' /></p>
))}
</Section>
<Section title={intl.formatMessage(messages.rules)}>
{!isLoading && (server.get("rules", ImmutableList()).isEmpty() ? (
<p><FormattedMessage id='about.not_available' defaultMessage='This information has not been made available on this server.' /></p>
) : (
<ol className='rules-list'>
{server.get("rules").map(rule => (
<li key={rule.get("id")}>
<span className='rules-list__text'>{rule.get("text")}</span>
</li>
))}
</ol>
))}
</Section>
<Section title={intl.formatMessage(messages.blocks)} onOpen={this.handleDomainBlocksOpen}>
{domainBlocks.get("isLoading") ? (
<>
<Skeleton width='100%' />
<br />
<Skeleton width='70%' />
</>
) : (domainBlocks.get("isAvailable") ? (
<>
<p><FormattedMessage id='about.domain_blocks.preamble' defaultMessage='Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.' /></p>
<div className='about__domain-blocks'>
{domainBlocks.get("items").map(block => (
<div className='about__domain-blocks__domain' key={block.get("domain")}>
<div className='about__domain-blocks__domain__header'>
<h6><span title={`SHA-256: ${block.get("digest")}`}>{block.get("domain")}</span></h6>
<span className='about__domain-blocks__domain__type' title={intl.formatMessage(severityMessages[block.get("severity")].explanation)}>{intl.formatMessage(severityMessages[block.get("severity")].title)}</span>
</div>
<p>{(block.get("comment") || "").length > 0 ? block.get("comment") : <FormattedMessage id='about.domain_blocks.no_reason_available' defaultMessage='Reason not available' />}</p>
</div>
))}
</div>
</>
) : (
<p><FormattedMessage id='about.not_available' defaultMessage='This information has not been made available on this server.' /></p>
))}
</Section>
<LinkFooter />
<div className='about__footer'>
<p><FormattedMessage
id='about.fork_disclaimer'
defaultMessage='Masto-FE (🦥 flavour) is open source software forked from Mastodon via Glitch.'
/></p>
</div>
</div>
<Helmet>
<title>{intl.formatMessage(messages.title)}</title>
<meta name='robots' content='all' />
</Helmet>
</Column>
);
}
}
export default connect(mapStateToProps)(injectIntl(About));