mastodon-sakura/app/javascript/mastodon/features/about/index.jsx
neatchee 4691b0068c
Merge latest upstream from glitch-soc/mastodon/main (#70)
* Remove the search button from UI header when logged out (#25631)

* Change account search to match by text when opted-in (#25599)

Co-authored-by: Eugen Rochko <eugen@zeonfederated.com>

* Fix ResolveURLService not resolving local URLs for remote content (#25637)

* Remove `pkg-config` gem dependency (#25615)

* Update Crowdin configuration file

* Fix onboarding prompt being displayed because of disconnection gaps (#25617)

* Use an Immutable Record as the root state (#25584)

* Add index to backups on `user_id` column (#25647)

* Fix rails `rewhere` deprecation warning in directories api controller (#25625)

* Remove unused routes (#25578)

* Fixing an issue with a missing argument (#2261)

undefined

* Update uri to version 0.12.2 (CVE fix) (#25657)

* Change local and federated timelines to be in a single firehose column (#25641)

* Fix HTTP 500 in `/api/v1/emails/check_confirmation` (#25595)

* Rails 7 update (#24241)

* Change dropdown icon above compose form from ellipsis to bars in web UI (#25661)

* Prevent duplicate concurrent calls of `/api/*/instance` in web UI (#25663)

* Revert "Rails 7 update" (#25667)

* [Glitch] Remove the search button from UI header when logged out

Port 285a691936 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* [Glitch] Fix onboarding prompt being displayed because of disconnection gaps

Port 9934949fc4 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* [Glitch] Use an Immutable Record as the root state

Port 78ba12f0bf to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* [Glitch] Change local and federated timelines to be in a single firehose column

Port cea9db5a0b to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* [Glitch] Change dropdown icon above compose form from ellipsis to bars in web UI

Port 0512537eb6 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* [Glitch] Prevent duplicate concurrent calls of `/api/*/instance` in web UI

Port 5b46345459 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>

* Show local-only posts in “All” by default, and add back option to toggle it

* Fix showing local only toots in "All" (#2265)

* Fix warnings about missing dependency in hooks

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

* Add `allowLocalOnly` to timelineId

Without this local-only toots will never be loaded.

feedType is checked to be public to not show local-only toots in the "Remote" tab.

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

---------

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

* Add regex filter back to firehose (#2266)

* Add regex filter back to firehose

The regex filter will apply to all tabs and not be automatically applied when pinned.

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

* Keep regex when pinned

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

---------

Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>

---------

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
Signed-off-by: Plastikmensch <plastikmensch@users.noreply.github.com>
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Co-authored-by: jsgoldstein <jakegoldstein95@gmail.com>
Co-authored-by: Eugen Rochko <eugen@zeonfederated.com>
Co-authored-by: Renaud Chaput <renchap@gmail.com>
Co-authored-by: Matt Jankowski <matt@jankowski.online>
Co-authored-by: Vivianne <puttabutta@gmail.com>
Co-authored-by: Daniel M Brasil <danielmbrasil@protonmail.com>
Co-authored-by: mogaminsk <mgmnjp@icloud.com>
Co-authored-by: Plastikmensch <Plastikmensch@users.noreply.github.com>
2023-07-03 09:12:00 -07:00

224 lines
8.9 KiB
JavaScript

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 ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
import Column from 'mastodon/components/column';
import { Icon } from 'mastodon/components/icon';
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
import { Skeleton } from 'mastodon/components/skeleton';
import Account from 'mastodon/containers/account_container';
import LinkFooter from 'mastodon/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']),
extendedDescription: state.getIn(['server', 'extendedDescription']),
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 ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title}
</div>
{!collapsed && (
<div className='about__section__body'>{children}</div>
)}
</div>
);
}
}
class About extends PureComponent {
static propTypes = {
server: ImmutablePropTypes.map,
extendedDescription: 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());
dispatch(fetchExtendedDescription());
}
handleDomainBlocksOpen = () => {
const { dispatch } = this.props;
dispatch(fetchDomainBlocks());
};
render () {
const { multiColumn, intl, server, extendedDescription, 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 {mastodon}' values={{ mastodon: <a href='https://joinmastodon.org' className='about__mail' target='_blank'>Mastodon</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} minimal />
</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)}>
{extendedDescription.get('isLoading') ? (
<>
<Skeleton width='100%' />
<br />
<Skeleton width='100%' />
<br />
<Skeleton width='100%' />
<br />
<Skeleton width='70%' />
</>
) : (extendedDescription.get('content')?.length > 0 ? (
<div
className='prose'
dangerouslySetInnerHTML={{ __html: extendedDescription.get('content') }}
/>
) : (
<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', []).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.disclaimer' defaultMessage='Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.' /></p>
</div>
</div>
<Helmet>
<title>{intl.formatMessage(messages.title)}</title>
<meta name='robots' content='all' />
</Helmet>
</Column>
);
}
}
export default connect(mapStateToProps)(injectIntl(About));