Change about page to be mounted in the web UI (#19345)
This commit is contained in:
		| @@ -1,63 +1,11 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class AboutController < ApplicationController | ||||
|   include RegistrationSpamConcern | ||||
|   include WebAppControllerConcern | ||||
|  | ||||
|   layout 'public' | ||||
|   skip_before_action :require_functional! | ||||
|  | ||||
|   before_action :require_open_federation!, only: [:more] | ||||
|   before_action :set_body_classes, only: :show | ||||
|   before_action :set_instance_presenter | ||||
|   before_action :set_expires_in, only: [:more] | ||||
|  | ||||
|   skip_before_action :require_functional!, only: [:more] | ||||
|  | ||||
|   def more | ||||
|     flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor] | ||||
|  | ||||
|     toc_generator = TOCGenerator.new(@instance_presenter.extended_description) | ||||
|  | ||||
|     @rules             = Rule.ordered | ||||
|     @contents          = toc_generator.html | ||||
|     @table_of_contents = toc_generator.toc | ||||
|     @blocks            = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? | ||||
|   end | ||||
|  | ||||
|   helper_method :display_blocks? | ||||
|   helper_method :display_blocks_rationale? | ||||
|   helper_method :public_fetch_mode? | ||||
|   helper_method :new_user | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def require_open_federation! | ||||
|     not_found if whitelist_mode? | ||||
|   end | ||||
|  | ||||
|   def display_blocks? | ||||
|     Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?) | ||||
|   end | ||||
|  | ||||
|   def display_blocks_rationale? | ||||
|     Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?) | ||||
|   end | ||||
|  | ||||
|   def new_user | ||||
|     User.new.tap do |user| | ||||
|       user.build_account | ||||
|       user.build_invite_request | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def set_instance_presenter | ||||
|     @instance_presenter = InstancePresenter.new | ||||
|   end | ||||
|  | ||||
|   def set_body_classes | ||||
|     @hide_navbar = true | ||||
|   end | ||||
|  | ||||
|   def set_expires_in | ||||
|     expires_in 0, public: true | ||||
|   def show | ||||
|     expires_in 0, public: true unless user_signed_in? | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										23
									
								
								app/controllers/api/v1/instances/domain_blocks_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/controllers/api/v1/instances/domain_blocks_controller.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class Api::V1::Instances::DomainBlocksController < Api::BaseController | ||||
|   skip_before_action :require_authenticated_user!, unless: :whitelist_mode? | ||||
|  | ||||
|   before_action :require_enabled_api! | ||||
|   before_action :set_domain_blocks | ||||
|  | ||||
|   def index | ||||
|     expires_in 3.minutes, public: true | ||||
|     render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?)) | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def require_enabled_api! | ||||
|     head 404 unless Setting.show_domain_blocks == 'all' || (Setting.show_domain_blocks == 'users' && user_signed_in?) | ||||
|   end | ||||
|  | ||||
|   def set_domain_blocks | ||||
|     @domain_blocks = DomainBlock.with_user_facing_limitations.by_severity | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,18 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class Api::V1::Instances::ExtendedDescriptionsController < Api::BaseController | ||||
|   skip_before_action :require_authenticated_user!, unless: :whitelist_mode? | ||||
|  | ||||
|   before_action :set_extended_description | ||||
|  | ||||
|   def show | ||||
|     expires_in 3.minutes, public: true | ||||
|     render json: @extended_description, serializer: REST::ExtendedDescriptionSerializer | ||||
|   end | ||||
|  | ||||
|   private | ||||
|  | ||||
|   def set_extended_description | ||||
|     @extended_description = ExtendedDescription.current | ||||
|   end | ||||
| end | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -5,6 +5,14 @@ export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST'; | ||||
| export const SERVER_FETCH_SUCCESS = 'Server_FETCH_SUCCESS'; | ||||
| export const SERVER_FETCH_FAIL    = 'Server_FETCH_FAIL'; | ||||
|  | ||||
| export const EXTENDED_DESCRIPTION_REQUEST = 'EXTENDED_DESCRIPTION_REQUEST'; | ||||
| export const EXTENDED_DESCRIPTION_SUCCESS = 'EXTENDED_DESCRIPTION_SUCCESS'; | ||||
| export const EXTENDED_DESCRIPTION_FAIL    = 'EXTENDED_DESCRIPTION_FAIL'; | ||||
|  | ||||
| export const SERVER_DOMAIN_BLOCKS_FETCH_REQUEST = 'SERVER_DOMAIN_BLOCKS_FETCH_REQUEST'; | ||||
| export const SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS = 'SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS'; | ||||
| export const SERVER_DOMAIN_BLOCKS_FETCH_FAIL    = 'SERVER_DOMAIN_BLOCKS_FETCH_FAIL'; | ||||
|  | ||||
| export const fetchServer = () => (dispatch, getState) => { | ||||
|   dispatch(fetchServerRequest()); | ||||
|  | ||||
| @@ -28,3 +36,56 @@ const fetchServerFail = error => ({ | ||||
|   type: SERVER_FETCH_FAIL, | ||||
|   error, | ||||
| }); | ||||
|  | ||||
| export const fetchExtendedDescription = () => (dispatch, getState) => { | ||||
|   dispatch(fetchExtendedDescriptionRequest()); | ||||
|  | ||||
|   api(getState) | ||||
|     .get('/api/v1/instance/extended_description') | ||||
|     .then(({ data }) => dispatch(fetchExtendedDescriptionSuccess(data))) | ||||
|     .catch(err => dispatch(fetchExtendedDescriptionFail(err))); | ||||
| }; | ||||
|  | ||||
| const fetchExtendedDescriptionRequest = () => ({ | ||||
|   type: EXTENDED_DESCRIPTION_REQUEST, | ||||
| }); | ||||
|  | ||||
| const fetchExtendedDescriptionSuccess = description => ({ | ||||
|   type: EXTENDED_DESCRIPTION_SUCCESS, | ||||
|   description, | ||||
| }); | ||||
|  | ||||
| const fetchExtendedDescriptionFail = error => ({ | ||||
|   type: EXTENDED_DESCRIPTION_FAIL, | ||||
|   error, | ||||
| }); | ||||
|  | ||||
| export const fetchDomainBlocks = () => (dispatch, getState) => { | ||||
|   dispatch(fetchDomainBlocksRequest()); | ||||
|  | ||||
|   api(getState) | ||||
|     .get('/api/v1/instance/domain_blocks') | ||||
|     .then(({ data }) => dispatch(fetchDomainBlocksSuccess(true, data))) | ||||
|     .catch(err => { | ||||
|       if (err.response.status === 404) { | ||||
|         dispatch(fetchDomainBlocksSuccess(false, [])); | ||||
|       } else { | ||||
|         dispatch(fetchDomainBlocksFail(err)); | ||||
|       } | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const fetchDomainBlocksRequest = () => ({ | ||||
|   type: SERVER_DOMAIN_BLOCKS_FETCH_REQUEST, | ||||
| }); | ||||
|  | ||||
| const fetchDomainBlocksSuccess = (isAvailable, blocks) => ({ | ||||
|   type: SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS, | ||||
|   isAvailable, | ||||
|   blocks, | ||||
| }); | ||||
|  | ||||
| const fetchDomainBlocksFail = error => ({ | ||||
|   type: SERVER_DOMAIN_BLOCKS_FETCH_FAIL, | ||||
|   error, | ||||
| }); | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/javascript/mastodon/components/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/javascript/mastodon/components/image.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import Blurhash from './blurhash'; | ||||
| import classNames from 'classnames'; | ||||
|  | ||||
| export default class Image extends React.PureComponent { | ||||
|  | ||||
|   static propTypes = { | ||||
|     src: PropTypes.string, | ||||
|     srcSet: PropTypes.string, | ||||
|     blurhash: PropTypes.string, | ||||
|     className: PropTypes.string, | ||||
|   }; | ||||
|  | ||||
|   state = { | ||||
|     loaded: false, | ||||
|   }; | ||||
|  | ||||
|   handleLoad = () => this.setState({ loaded: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { src, srcSet, blurhash, className } = this.props; | ||||
|     const { loaded } = this.state; | ||||
|  | ||||
|     return ( | ||||
|       <div className={classNames('image', { loaded }, className)} role='presentation'> | ||||
|         {blurhash && <Blurhash hash={blurhash} className='image__preview' />} | ||||
|         <img src={src} srcSet={srcSet} alt='' onLoad={this.handleLoad} /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -7,13 +7,15 @@ import ShortNumber from 'mastodon/components/short_number'; | ||||
| import Skeleton from 'mastodon/components/skeleton'; | ||||
| import Account from 'mastodon/containers/account_container'; | ||||
| import { domain } from 'mastodon/initial_state'; | ||||
| import Image from 'mastodon/components/image'; | ||||
| import { Link } from 'react-router-dom'; | ||||
|  | ||||
| const messages = defineMessages({ | ||||
|   aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' }, | ||||
| }); | ||||
|  | ||||
| const mapStateToProps = state => ({ | ||||
|   server: state.get('server'), | ||||
|   server: state.getIn(['server', 'server']), | ||||
| }); | ||||
|  | ||||
| export default @connect(mapStateToProps) | ||||
| @@ -41,7 +43,7 @@ class ServerBanner extends React.PureComponent { | ||||
|           <FormattedMessage id='server_banner.introduction' defaultMessage='{domain} is part of the decentralized social network powered by {mastodon}.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} /> | ||||
|         </div> | ||||
|  | ||||
|         <img src={server.get('thumbnail')} alt={server.get('title')} className='server-banner__hero' /> | ||||
|         <Image blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' /> | ||||
|  | ||||
|         <div className='server-banner__description'> | ||||
|           {isLoading ? ( | ||||
| @@ -83,7 +85,7 @@ class ServerBanner extends React.PureComponent { | ||||
|  | ||||
|         <hr className='spacer' /> | ||||
|  | ||||
|         <a className='button button--block button-secondary' href='/about/more' target='_blank'><FormattedMessage id='server_banner.learn_more' defaultMessage='Learn more' /></a> | ||||
|         <Link className='button button--block button-secondary' to='/about'><FormattedMessage id='server_banner.learn_more' defaultMessage='Learn more' /></Link> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import PropTypes from 'prop-types'; | ||||
| const Skeleton = ({ width, height }) => <span className='skeleton' style={{ width, height }}>‌</span>; | ||||
|  | ||||
| Skeleton.propTypes = { | ||||
|   width: PropTypes.number, | ||||
|   height: PropTypes.number, | ||||
|   width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||||
|   height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | ||||
| }; | ||||
|  | ||||
| export default Skeleton; | ||||
|   | ||||
| @@ -1,27 +1,214 @@ | ||||
| import React from 'react'; | ||||
| import { defineMessages, injectIntl } from 'react-intl'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | ||||
| import { connect } from 'react-redux'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import Column from 'mastodon/components/column'; | ||||
| import LinkFooter from 'mastodon/features/ui/components/link_footer'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server'; | ||||
| import Account from 'mastodon/containers/account_container'; | ||||
| import Skeleton from 'mastodon/components/skeleton'; | ||||
| import Icon from 'mastodon/components/icon'; | ||||
| import classNames from 'classnames'; | ||||
| import Image from 'mastodon/components/image'; | ||||
|  | ||||
| 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.' }, | ||||
| }); | ||||
|  | ||||
| export default @injectIntl | ||||
| 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 React.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> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| export default @connect(mapStateToProps) | ||||
| @injectIntl | ||||
| class About extends React.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, | ||||
|   }; | ||||
|  | ||||
|   componentDidMount () { | ||||
|     const { dispatch } = this.props; | ||||
|     dispatch(fetchServer()); | ||||
|     dispatch(fetchExtendedDescription()); | ||||
|   } | ||||
|  | ||||
|   handleDomainBlocksOpen = () => { | ||||
|     const { dispatch } = this.props; | ||||
|     dispatch(fetchDomainBlocks()); | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { intl } = this.props; | ||||
|     const { intl, server, extendedDescription, domainBlocks } = this.props; | ||||
|     const isLoading = server.get('isLoading'); | ||||
|  | ||||
|     return ( | ||||
|       <Column> | ||||
|         <LinkFooter /> | ||||
|         <div className='scrollable about'> | ||||
|           <div className='about__header'> | ||||
|             <Image 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'])} /> | ||||
|             </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> | ||||
|  | ||||
|                 <table className='about__domain-blocks'> | ||||
|                   <thead> | ||||
|                     <tr> | ||||
|                       <th><FormattedMessage id='about.domain_blocks.domain' defaultMessage='Domain' /></th> | ||||
|                       <th><FormattedMessage id='about.domain_blocks.severity' defaultMessage='Severity' /></th> | ||||
|                       <th><FormattedMessage id='about.domain_blocks.comment' defaultMessage='Reason' /></th> | ||||
|                     </tr> | ||||
|                   </thead> | ||||
|  | ||||
|                   <tbody> | ||||
|                     {domainBlocks.get('items').map(block => ( | ||||
|                       <tr key={block.get('domain')}> | ||||
|                         <td><span title={`SHA-256: ${block.get('digest')}`}>{block.get('domain')}</span></td> | ||||
|                         <td><span title={intl.formatMessage(severityMessages[block.get('severity')].explanation)}>{intl.formatMessage(severityMessages[block.get('severity')].title)}</span></td> | ||||
|                         <td>{block.get('comment')}</td> | ||||
|                       </tr> | ||||
|                     ))} | ||||
|                   </tbody> | ||||
|                 </table> | ||||
|               </> | ||||
|             ) : ( | ||||
|               <p><FormattedMessage id='about.not_available' defaultMessage='This information has not been made available on this server.' /></p> | ||||
|             ))} | ||||
|           </Section> | ||||
|  | ||||
|           <LinkFooter /> | ||||
|         </div> | ||||
|  | ||||
|         <Helmet> | ||||
|           <title>{intl.formatMessage(messages.title)}</title> | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class PrivacyPolicy extends React.PureComponent { | ||||
|           </div> | ||||
|  | ||||
|           <div | ||||
|             className='privacy-policy__body' | ||||
|             className='privacy-policy__body prose' | ||||
|             dangerouslySetInnerHTML={{ __html: content }} | ||||
|           /> | ||||
|         </div> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ import Button from 'mastodon/components/button'; | ||||
| import Option from './components/option'; | ||||
|  | ||||
| const mapStateToProps = state => ({ | ||||
|   rules: state.getIn(['server', 'rules']), | ||||
|   rules: state.getIn(['server', 'server', 'rules']), | ||||
| }); | ||||
|  | ||||
| export default @connect(mapStateToProps) | ||||
|   | ||||
| @@ -51,7 +51,7 @@ class LinkFooter extends React.PureComponent { | ||||
|     const items = []; | ||||
|  | ||||
|     items.push(<a key='apps' href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Get the app' /></a>); | ||||
|     items.push(<a key='about' href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About' /></a>); | ||||
|     items.push(<Link key='about' to='/about'><FormattedMessage id='navigation_bar.info' defaultMessage='About' /></Link>); | ||||
|     items.push(<a key='mastodon' href='https://joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.what_is_mastodon' defaultMessage='About Mastodon' /></a>); | ||||
|     items.push(<a key='docs' href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a>); | ||||
|     items.push(<Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='getting_started.privacy_policy' defaultMessage='Privacy Policy' /></Link>); | ||||
|   | ||||
| @@ -1,18 +1,52 @@ | ||||
| import { SERVER_FETCH_REQUEST, SERVER_FETCH_SUCCESS, SERVER_FETCH_FAIL } from 'mastodon/actions/server'; | ||||
| import { Map as ImmutableMap, fromJS } from 'immutable'; | ||||
| import { | ||||
|   SERVER_FETCH_REQUEST, | ||||
|   SERVER_FETCH_SUCCESS, | ||||
|   SERVER_FETCH_FAIL, | ||||
|   EXTENDED_DESCRIPTION_REQUEST, | ||||
|   EXTENDED_DESCRIPTION_SUCCESS, | ||||
|   EXTENDED_DESCRIPTION_FAIL, | ||||
|   SERVER_DOMAIN_BLOCKS_FETCH_REQUEST, | ||||
|   SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS, | ||||
|   SERVER_DOMAIN_BLOCKS_FETCH_FAIL, | ||||
| } from 'mastodon/actions/server'; | ||||
| import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; | ||||
|  | ||||
| const initialState = ImmutableMap({ | ||||
|   isLoading: true, | ||||
|   server: ImmutableMap({ | ||||
|     isLoading: true, | ||||
|   }), | ||||
|  | ||||
|   extendedDescription: ImmutableMap({ | ||||
|     isLoading: true, | ||||
|   }), | ||||
|  | ||||
|   domainBlocks: ImmutableMap({ | ||||
|     isLoading: true, | ||||
|     isAvailable: true, | ||||
|     items: ImmutableList(), | ||||
|   }), | ||||
| }); | ||||
|  | ||||
| export default function server(state = initialState, action) { | ||||
|   switch (action.type) { | ||||
|   case SERVER_FETCH_REQUEST: | ||||
|     return state.set('isLoading', true); | ||||
|     return state.setIn(['server', 'isLoading'], true); | ||||
|   case SERVER_FETCH_SUCCESS: | ||||
|     return fromJS(action.server).set('isLoading', false); | ||||
|     return state.set('server', fromJS(action.server)).setIn(['server', 'isLoading'], false); | ||||
|   case SERVER_FETCH_FAIL: | ||||
|     return state.set('isLoading', false); | ||||
|     return state.setIn(['server', 'isLoading'], false); | ||||
|   case EXTENDED_DESCRIPTION_REQUEST: | ||||
|     return state.setIn(['extendedDescription', 'isLoading'], true); | ||||
|   case EXTENDED_DESCRIPTION_SUCCESS: | ||||
|     return state.set('extendedDescription', fromJS(action.description)).setIn(['extendedDescription', 'isLoading'], false); | ||||
|   case EXTENDED_DESCRIPTION_FAIL: | ||||
|     return state.setIn(['extendedDescription', 'isLoading'], false); | ||||
|   case SERVER_DOMAIN_BLOCKS_FETCH_REQUEST: | ||||
|     return state.setIn(['domainBlocks', 'isLoading'], true); | ||||
|   case SERVER_DOMAIN_BLOCKS_FETCH_SUCCESS: | ||||
|     return state.setIn(['domainBlocks', 'items'], fromJS(action.blocks)).setIn(['domainBlocks', 'isLoading'], false).setIn(['domainBlocks', 'isAvailable'], action.isAvailable); | ||||
|   case SERVER_DOMAIN_BLOCKS_FETCH_FAIL: | ||||
|     return state.setIn(['domainBlocks', 'isLoading'], false); | ||||
|   default: | ||||
|     return state; | ||||
|   } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
| @import 'mastodon/variables'; | ||||
| @import 'fonts/roboto'; | ||||
| @import 'fonts/roboto-mono'; | ||||
| @import 'fonts/montserrat'; | ||||
|  | ||||
| @import 'mastodon/reset'; | ||||
| @import 'mastodon/basics'; | ||||
| @@ -10,7 +9,6 @@ | ||||
| @import 'mastodon/containers'; | ||||
| @import 'mastodon/lists'; | ||||
| @import 'mastodon/footer'; | ||||
| @import 'mastodon/compact_header'; | ||||
| @import 'mastodon/widgets'; | ||||
| @import 'mastodon/forms'; | ||||
| @import 'mastodon/accounts'; | ||||
|   | ||||
| @@ -13,10 +13,6 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .rich-formatting a, | ||||
| .rich-formatting p a, | ||||
| .rich-formatting li a, | ||||
| .landing-page__short-description p a, | ||||
| .status__content a, | ||||
| .reply-indicator__content a { | ||||
|   color: lighten($ui-highlight-color, 12%); | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| @font-face { | ||||
|   font-family: mastodon-font-display; | ||||
|   src: | ||||
|     local('Montserrat'), | ||||
|     url('../fonts/montserrat/Montserrat-Regular.woff2') format('woff2'), | ||||
|     url('../fonts/montserrat/Montserrat-Regular.woff') format('woff'), | ||||
|     url('../fonts/montserrat/Montserrat-Regular.ttf') format('truetype'); | ||||
|   font-weight: 400; | ||||
|   font-display: swap; | ||||
|   font-style: normal; | ||||
| } | ||||
|  | ||||
| @font-face { | ||||
|   font-family: mastodon-font-display; | ||||
|   src: | ||||
|     local('Montserrat Medium'), | ||||
|     url('../fonts/montserrat/Montserrat-Medium.ttf') format('truetype'); | ||||
|   font-weight: 500; | ||||
|   font-display: swap; | ||||
|   font-style: normal; | ||||
| } | ||||
| @@ -36,6 +36,20 @@ html { | ||||
|   border-top: 0; | ||||
| } | ||||
|  | ||||
| .column > .scrollable.about { | ||||
|   border-top: 1px solid lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .about__meta, | ||||
| .about__section__title { | ||||
|   background: $white; | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .rules-list li::before { | ||||
|   background: $ui-highlight-color; | ||||
| } | ||||
|  | ||||
| .directory__card__img { | ||||
|   background: lighten($ui-base-color, 12%); | ||||
| } | ||||
| @@ -45,10 +59,6 @@ html { | ||||
|   border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .table-of-contents { | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .column-back-button, | ||||
| .column-header { | ||||
|   background: $white; | ||||
| @@ -138,11 +148,6 @@ html { | ||||
| .compose-form__poll-wrapper select, | ||||
| .search__input, | ||||
| .setting-text, | ||||
| .box-widget input[type="text"], | ||||
| .box-widget input[type="email"], | ||||
| .box-widget input[type="password"], | ||||
| .box-widget textarea, | ||||
| .statuses-grid .detailed-status, | ||||
| .report-dialog-modal__textarea, | ||||
| .audio-player { | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
| @@ -480,52 +485,16 @@ html { | ||||
|   background: $white; | ||||
| } | ||||
|  | ||||
| .tabs-bar { | ||||
|   background: $white; | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
|   border-bottom: 0; | ||||
|  | ||||
|   @media screen and (max-width: $no-gap-breakpoint) { | ||||
|     border-top: 0; | ||||
|   } | ||||
|  | ||||
|   &__link { | ||||
|     padding-bottom: 14px; | ||||
|     border-bottom-width: 1px; | ||||
|     border-bottom-color: lighten($ui-base-color, 8%); | ||||
|  | ||||
|     &:hover, | ||||
|     &:active, | ||||
|     &:focus { | ||||
|       background: $ui-base-color; | ||||
|     } | ||||
|  | ||||
|     &.active { | ||||
|       &:hover, | ||||
|       &:active, | ||||
|       &:focus { | ||||
|         background: transparent; | ||||
|         border-bottom-color: $ui-highlight-color; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Change the default colors used on some parts of the profile pages | ||||
| .activity-stream-tabs { | ||||
|   background: $account-background-color; | ||||
|   border-bottom-color: lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .box-widget, | ||||
| .nothing-here, | ||||
| .page-header, | ||||
| .directory__tag > a, | ||||
| .directory__tag > div, | ||||
| .landing-page__call-to-action, | ||||
| .contact-widget, | ||||
| .landing .hero-widget__text, | ||||
| .landing-page__information.contact-widget { | ||||
| .directory__tag > div { | ||||
|   background: $white; | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
|  | ||||
| @@ -536,11 +505,6 @@ html { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .landing .hero-widget__text { | ||||
|   border-top: 0; | ||||
|   border-bottom: 0; | ||||
| } | ||||
|  | ||||
| .simple_form { | ||||
|   input[type="text"], | ||||
|   input[type="number"], | ||||
| @@ -553,26 +517,12 @@ html { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .landing .hero-widget__footer { | ||||
|   background: $white; | ||||
|   border: 1px solid lighten($ui-base-color, 8%); | ||||
|   border-top: 0; | ||||
|  | ||||
|   @media screen and (max-width: $no-gap-breakpoint) { | ||||
|     border: 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .picture-in-picture-placeholder { | ||||
|   background: $white; | ||||
|   border-color: lighten($ui-base-color, 8%); | ||||
|   color: lighten($ui-base-color, 8%); | ||||
| } | ||||
|  | ||||
| .brand__tagline { | ||||
|   color: $ui-secondary-color; | ||||
| } | ||||
|  | ||||
| .directory__tag > a { | ||||
|   &:hover, | ||||
|   &:active, | ||||
| @@ -666,8 +616,7 @@ html { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .simple_form, | ||||
| .table-form { | ||||
| .simple_form { | ||||
|   .warning { | ||||
|     box-shadow: none; | ||||
|     background: rgba($error-red, 0.5); | ||||
| @@ -801,9 +750,6 @@ html { | ||||
| } | ||||
|  | ||||
| .hero-widget, | ||||
| .box-widget, | ||||
| .contact-widget, | ||||
| .landing-page__information.contact-widget, | ||||
| .moved-account-widget, | ||||
| .memoriam-widget, | ||||
| .activity-stream, | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| $maximum-width: 1235px; | ||||
| $fluid-breakpoint: $maximum-width + 20px; | ||||
| $column-breakpoint: 700px; | ||||
| $small-breakpoint: 960px; | ||||
|  | ||||
| .container { | ||||
|   box-sizing: border-box; | ||||
| @@ -15,892 +13,44 @@ $small-breakpoint: 960px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .rich-formatting { | ||||
|   font-family: $font-sans-serif, sans-serif; | ||||
|   font-size: 14px; | ||||
|   font-weight: 400; | ||||
|   line-height: 1.7; | ||||
|   word-wrap: break-word; | ||||
|   color: $darker-text-color; | ||||
|  | ||||
|   a { | ||||
|     color: $highlight-text-color; | ||||
|     text-decoration: underline; | ||||
|  | ||||
|     &:hover, | ||||
|     &:focus, | ||||
|     &:active { | ||||
|       text-decoration: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   p, | ||||
|   li { | ||||
|     color: $darker-text-color; | ||||
|   } | ||||
|  | ||||
|   p { | ||||
|     margin-top: 0; | ||||
|     margin-bottom: 0.85em; | ||||
|  | ||||
|     &:last-child { | ||||
|       margin-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   strong { | ||||
|     font-weight: 700; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   em { | ||||
|     font-style: italic; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   code { | ||||
|     font-size: 0.85em; | ||||
|     background: darken($ui-base-color, 8%); | ||||
|     border-radius: 4px; | ||||
|     padding: 0.2em 0.3em; | ||||
|   } | ||||
|  | ||||
|   h1, | ||||
|   h2, | ||||
|   h3, | ||||
|   h4, | ||||
|   h5, | ||||
|   h6 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     margin-top: 1.275em; | ||||
|     margin-bottom: 0.85em; | ||||
|     font-weight: 500; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   h1 { | ||||
|     font-size: 2em; | ||||
|   } | ||||
|  | ||||
|   h2 { | ||||
|     font-size: 1.75em; | ||||
|   } | ||||
|  | ||||
|   h3 { | ||||
|     font-size: 1.5em; | ||||
|   } | ||||
|  | ||||
|   h4 { | ||||
|     font-size: 1.25em; | ||||
|   } | ||||
|  | ||||
|   h5, | ||||
|   h6 { | ||||
|     font-size: 1em; | ||||
|   } | ||||
|  | ||||
|   ul { | ||||
|     list-style: disc; | ||||
|   } | ||||
|  | ||||
|   ol { | ||||
|     list-style: decimal; | ||||
|   } | ||||
|  | ||||
|   ul, | ||||
|   ol { | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     padding-left: 2em; | ||||
|     margin-bottom: 0.85em; | ||||
|  | ||||
|     &[type='a'] { | ||||
|       list-style-type: lower-alpha; | ||||
|     } | ||||
|  | ||||
|     &[type='i'] { | ||||
|       list-style-type: lower-roman; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   hr { | ||||
|     width: 100%; | ||||
|     height: 0; | ||||
|     border: 0; | ||||
|     border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|     margin: 1.7em 0; | ||||
|  | ||||
|     &.spacer { | ||||
|       height: 1px; | ||||
|       border: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   table { | ||||
|     width: 100%; | ||||
|     border-collapse: collapse; | ||||
|     break-inside: auto; | ||||
|     margin-top: 24px; | ||||
|     margin-bottom: 32px; | ||||
|  | ||||
|     thead tr, | ||||
|     tbody tr { | ||||
|       border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|       font-size: 1em; | ||||
|       line-height: 1.625; | ||||
|       font-weight: 400; | ||||
|       text-align: left; | ||||
|       color: $darker-text-color; | ||||
|     } | ||||
|  | ||||
|     thead tr { | ||||
|       border-bottom-width: 2px; | ||||
|       line-height: 1.5; | ||||
|       font-weight: 500; | ||||
|       color: $dark-text-color; | ||||
|     } | ||||
|  | ||||
|     th, | ||||
|     td { | ||||
|       padding: 8px; | ||||
|       align-self: flex-start; | ||||
|       align-items: flex-start; | ||||
|       word-break: break-all; | ||||
|  | ||||
|       &.nowrap { | ||||
|         width: 25%; | ||||
|         position: relative; | ||||
|  | ||||
|         &::before { | ||||
|           content: ' '; | ||||
|           visibility: hidden; | ||||
|         } | ||||
|  | ||||
|         span { | ||||
|           position: absolute; | ||||
|           left: 8px; | ||||
|           right: 8px; | ||||
|           white-space: nowrap; | ||||
|           overflow: hidden; | ||||
|           text-overflow: ellipsis; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   & > :first-child { | ||||
|     margin-top: 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .information-board { | ||||
|   background: darken($ui-base-color, 4%); | ||||
|   padding: 20px 0; | ||||
|  | ||||
|   .container-alt { | ||||
|     position: relative; | ||||
|     padding-right: 280px + 15px; | ||||
|   } | ||||
|  | ||||
|   &__sections { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     flex-wrap: wrap; | ||||
|   } | ||||
|  | ||||
|   &__section { | ||||
|     flex: 1 0 0; | ||||
|     font-family: $font-sans-serif, sans-serif; | ||||
|     font-size: 16px; | ||||
|     line-height: 28px; | ||||
|     color: $primary-text-color; | ||||
|     text-align: right; | ||||
|     padding: 10px 15px; | ||||
|  | ||||
|     span, | ||||
|     strong { | ||||
|       display: block; | ||||
|     } | ||||
|  | ||||
|     span { | ||||
|       &:last-child { | ||||
|         color: $secondary-text-color; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     strong { | ||||
|       font-family: $font-display, sans-serif; | ||||
|       font-weight: 500; | ||||
|       font-size: 32px; | ||||
|       line-height: 48px; | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $column-breakpoint) { | ||||
|       text-align: center; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .panel { | ||||
|     position: absolute; | ||||
|     width: 280px; | ||||
|     box-sizing: border-box; | ||||
|     background: darken($ui-base-color, 8%); | ||||
|     padding: 20px; | ||||
|     padding-top: 10px; | ||||
|     border-radius: 4px 4px 0 0; | ||||
|     right: 0; | ||||
|     bottom: -40px; | ||||
|  | ||||
|     .panel-header { | ||||
|       font-family: $font-display, sans-serif; | ||||
|       font-size: 14px; | ||||
|       line-height: 24px; | ||||
|       font-weight: 500; | ||||
|       color: $darker-text-color; | ||||
|       padding-bottom: 5px; | ||||
|       margin-bottom: 15px; | ||||
|       border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|       text-overflow: ellipsis; | ||||
|       white-space: nowrap; | ||||
|       overflow: hidden; | ||||
|  | ||||
|       a, | ||||
|       span { | ||||
|         font-weight: 400; | ||||
|         color: darken($darker-text-color, 10%); | ||||
|       } | ||||
|  | ||||
|       a { | ||||
|         text-decoration: none; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .owner { | ||||
|     text-align: center; | ||||
|  | ||||
|     .avatar { | ||||
|       width: 80px; | ||||
|       height: 80px; | ||||
|       margin: 0 auto; | ||||
|       margin-bottom: 15px; | ||||
|  | ||||
|       img { | ||||
|         display: block; | ||||
|         width: 80px; | ||||
|         height: 80px; | ||||
|         border-radius: 48px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .name { | ||||
|       font-size: 14px; | ||||
|  | ||||
|       a { | ||||
|         display: block; | ||||
|         color: $primary-text-color; | ||||
|         text-decoration: none; | ||||
|  | ||||
|         &:hover { | ||||
|           .display_name { | ||||
|             text-decoration: underline; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .username { | ||||
|         display: block; | ||||
|         color: $darker-text-color; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .landing-page { | ||||
|   p, | ||||
|   li { | ||||
|     font-family: $font-sans-serif, sans-serif; | ||||
|     font-size: 16px; | ||||
|     font-weight: 400; | ||||
|     line-height: 30px; | ||||
|     margin-bottom: 12px; | ||||
|     color: $darker-text-color; | ||||
|  | ||||
|     a { | ||||
|       color: $highlight-text-color; | ||||
|       text-decoration: underline; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   em { | ||||
|     display: inline; | ||||
|     margin: 0; | ||||
|     padding: 0; | ||||
|     font-weight: 700; | ||||
|     background: transparent; | ||||
|     font-family: inherit; | ||||
|     font-size: inherit; | ||||
|     line-height: inherit; | ||||
|     color: lighten($darker-text-color, 10%); | ||||
|   } | ||||
|  | ||||
|   h1 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 26px; | ||||
|     line-height: 30px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|  | ||||
|     small { | ||||
|       font-family: $font-sans-serif, sans-serif; | ||||
|       display: block; | ||||
|       font-size: 18px; | ||||
|       font-weight: 400; | ||||
|       color: lighten($darker-text-color, 10%); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   h2 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 22px; | ||||
|     line-height: 26px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   h3 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 18px; | ||||
|     line-height: 24px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   h4 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 16px; | ||||
|     line-height: 24px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   h5 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 14px; | ||||
|     line-height: 24px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   h6 { | ||||
|     font-family: $font-display, sans-serif; | ||||
|     font-size: 12px; | ||||
|     line-height: 24px; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     color: $secondary-text-color; | ||||
|   } | ||||
|  | ||||
|   ul, | ||||
|   ol { | ||||
|     margin-left: 20px; | ||||
|  | ||||
|     &[type='a'] { | ||||
|       list-style-type: lower-alpha; | ||||
|     } | ||||
|  | ||||
|     &[type='i'] { | ||||
|       list-style-type: lower-roman; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ul { | ||||
|     list-style: disc; | ||||
|   } | ||||
|  | ||||
|   ol { | ||||
|     list-style: decimal; | ||||
|   } | ||||
|  | ||||
|   li > ol, | ||||
|   li > ul { | ||||
|     margin-top: 6px; | ||||
|   } | ||||
|  | ||||
|   hr { | ||||
|     width: 100%; | ||||
|     height: 0; | ||||
|     border: 0; | ||||
|     border-bottom: 1px solid rgba($ui-base-lighter-color, 0.6); | ||||
|     margin: 20px 0; | ||||
|  | ||||
|     &.spacer { | ||||
|       height: 1px; | ||||
|       border: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__information, | ||||
|   &__forms { | ||||
|     padding: 20px; | ||||
|   } | ||||
|  | ||||
|   &__call-to-action { | ||||
|     background: $ui-base-color; | ||||
|     border-radius: 4px; | ||||
|     padding: 25px 40px; | ||||
|     overflow: hidden; | ||||
|     box-sizing: border-box; | ||||
|  | ||||
|     .row { | ||||
|       width: 100%; | ||||
|       display: flex; | ||||
|       flex-direction: row-reverse; | ||||
|       flex-wrap: nowrap; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|     } | ||||
|  | ||||
|     .row__information-board { | ||||
|       display: flex; | ||||
|       justify-content: flex-end; | ||||
|       align-items: flex-end; | ||||
|  | ||||
|       .information-board__section { | ||||
|         flex: 1 0 auto; | ||||
|         padding: 0 10px; | ||||
|       } | ||||
|  | ||||
|       @media screen and (max-width: $no-gap-breakpoint) { | ||||
|         width: 100%; | ||||
|         justify-content: space-between; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .row__mascot { | ||||
|       flex: 1; | ||||
|       margin: 10px -50px 0 0; | ||||
|  | ||||
|       @media screen and (max-width: $no-gap-breakpoint) { | ||||
|         display: none; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__logo { | ||||
|     margin-right: 20px; | ||||
|  | ||||
|     img { | ||||
|       height: 50px; | ||||
|       width: auto; | ||||
|       mix-blend-mode: lighten; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__information { | ||||
|     padding: 45px 40px; | ||||
|     margin-bottom: 10px; | ||||
|  | ||||
|     &:last-child { | ||||
|       margin-bottom: 0; | ||||
|     } | ||||
|  | ||||
|     strong { | ||||
|       font-weight: 500; | ||||
|       color: lighten($darker-text-color, 10%); | ||||
|     } | ||||
|  | ||||
|     .account { | ||||
|       border-bottom: 0; | ||||
|       padding: 0; | ||||
|  | ||||
|       &__display-name { | ||||
|         align-items: center; | ||||
|         display: flex; | ||||
|         margin-right: 5px; | ||||
|       } | ||||
|  | ||||
|       div.account__display-name { | ||||
|         &:hover { | ||||
|           .display-name strong { | ||||
|             text-decoration: none; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         .account__avatar { | ||||
|           cursor: default; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       &__avatar-wrapper { | ||||
|         margin-left: 0; | ||||
|         flex: 0 0 auto; | ||||
|       } | ||||
|  | ||||
|       .display-name { | ||||
|         font-size: 15px; | ||||
|  | ||||
|         &__account { | ||||
|           font-size: 14px; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $small-breakpoint) { | ||||
|       .contact { | ||||
|         margin-top: 30px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $column-breakpoint) { | ||||
|       padding: 25px 20px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__information, | ||||
|   &__forms, | ||||
|   #mastodon-timeline { | ||||
|     box-sizing: border-box; | ||||
|     background: $ui-base-color; | ||||
|     border-radius: 4px; | ||||
|     box-shadow: 0 0 6px rgba($black, 0.1); | ||||
|   } | ||||
|  | ||||
|   &__mascot { | ||||
|     height: 104px; | ||||
|     position: relative; | ||||
|     left: -40px; | ||||
|     bottom: 25px; | ||||
|  | ||||
|     img { | ||||
|       height: 190px; | ||||
|       width: auto; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__short-description { | ||||
|     .row { | ||||
|       display: flex; | ||||
|       flex-wrap: wrap; | ||||
|       align-items: center; | ||||
|       margin-bottom: 40px; | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $column-breakpoint) { | ||||
|       .row { | ||||
|         margin-bottom: 20px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     p a { | ||||
|       color: $secondary-text-color; | ||||
|     } | ||||
|  | ||||
|     h1 { | ||||
|       font-weight: 500; | ||||
|       color: $primary-text-color; | ||||
|       margin-bottom: 0; | ||||
|  | ||||
|       small { | ||||
|         color: $darker-text-color; | ||||
|  | ||||
|         span { | ||||
|           color: $secondary-text-color; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     p:last-child { | ||||
|       margin-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__hero { | ||||
|     margin-bottom: 10px; | ||||
|  | ||||
|     img { | ||||
|       display: block; | ||||
|       margin: 0; | ||||
|       max-width: 100%; | ||||
|       height: auto; | ||||
|       border-radius: 4px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @media screen and (max-width: 840px) { | ||||
|     .information-board { | ||||
|       .container-alt { | ||||
|         padding-right: 20px; | ||||
|       } | ||||
|  | ||||
|       .panel { | ||||
|         position: static; | ||||
|         margin-top: 20px; | ||||
|         width: 100%; | ||||
|         border-radius: 4px; | ||||
|  | ||||
|         .panel-header { | ||||
|           text-align: center; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @media screen and (max-width: 675px) { | ||||
|     .header-wrapper { | ||||
|       padding-top: 0; | ||||
|  | ||||
|       &.compact { | ||||
|         padding-bottom: 0; | ||||
|       } | ||||
|  | ||||
|       &.compact .hero .heading { | ||||
|         text-align: initial; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .header .container-alt, | ||||
|     .features .container-alt { | ||||
|       display: block; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .cta { | ||||
|     margin: 20px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .landing { | ||||
|   margin-bottom: 100px; | ||||
|  | ||||
|   @media screen and (max-width: 738px) { | ||||
|     margin-bottom: 0; | ||||
|   } | ||||
|  | ||||
|   &__brand { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|     padding: 50px; | ||||
|  | ||||
|     .logo { | ||||
|       fill: $primary-text-color; | ||||
|       height: 52px; | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $no-gap-breakpoint) { | ||||
|       padding: 0; | ||||
|       margin-bottom: 30px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .directory { | ||||
|     margin-top: 30px; | ||||
|     background: transparent; | ||||
|     box-shadow: none; | ||||
|     border-radius: 0; | ||||
|   } | ||||
|  | ||||
|   .hero-widget { | ||||
|     margin-top: 30px; | ||||
|     margin-bottom: 0; | ||||
|  | ||||
|     h4 { | ||||
|       padding: 10px; | ||||
|       text-transform: uppercase; | ||||
|       font-weight: 700; | ||||
|       font-size: 13px; | ||||
|       color: $darker-text-color; | ||||
|     } | ||||
|  | ||||
|     &__text { | ||||
|       border-radius: 0; | ||||
|       padding-bottom: 0; | ||||
|     } | ||||
|  | ||||
|     &__footer { | ||||
|       background: $ui-base-color; | ||||
|       padding: 10px; | ||||
|       border-radius: 0 0 4px 4px; | ||||
|       display: flex; | ||||
|  | ||||
|       &__column { | ||||
|         flex: 1 1 50%; | ||||
|         overflow-x: hidden; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .account { | ||||
|       padding: 10px 0; | ||||
|       border-bottom: 0; | ||||
|  | ||||
|       .account__display-name { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &__counters__wrapper { | ||||
|       display: flex; | ||||
|     } | ||||
|  | ||||
|     &__counter { | ||||
|       padding: 10px; | ||||
|       width: 50%; | ||||
|  | ||||
|       strong { | ||||
|         font-family: $font-display, sans-serif; | ||||
|         font-size: 15px; | ||||
|         font-weight: 700; | ||||
|         display: block; | ||||
|       } | ||||
|  | ||||
|       span { | ||||
|         font-size: 14px; | ||||
|         color: $darker-text-color; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .simple_form .user_agreement .label_input > label { | ||||
|     font-weight: 400; | ||||
|     color: $darker-text-color; | ||||
|   } | ||||
|  | ||||
|   .simple_form p.lead { | ||||
|     color: $darker-text-color; | ||||
|     font-size: 15px; | ||||
|     line-height: 20px; | ||||
|     font-weight: 400; | ||||
|     margin-bottom: 25px; | ||||
|   } | ||||
|  | ||||
|   &__grid { | ||||
|     max-width: 960px; | ||||
|     margin: 0 auto; | ||||
|     display: grid; | ||||
|     grid-template-columns: minmax(0, 50%) minmax(0, 50%); | ||||
|     grid-gap: 30px; | ||||
|  | ||||
|     @media screen and (max-width: 738px) { | ||||
|       grid-template-columns: minmax(0, 100%); | ||||
|       grid-gap: 10px; | ||||
|  | ||||
|       &__column-login { | ||||
|         grid-row: 1; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|  | ||||
|         .box-widget { | ||||
|           order: 2; | ||||
|           flex: 0 0 auto; | ||||
|         } | ||||
|  | ||||
|         .hero-widget { | ||||
|           margin-top: 0; | ||||
|           margin-bottom: 10px; | ||||
|           order: 1; | ||||
|           flex: 0 0 auto; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       &__column-registration { | ||||
|         grid-row: 2; | ||||
|       } | ||||
|  | ||||
|       .directory { | ||||
|         margin-top: 10px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $no-gap-breakpoint) { | ||||
|       grid-gap: 0; | ||||
|  | ||||
|       .hero-widget { | ||||
|         display: block; | ||||
|         margin-bottom: 0; | ||||
|         box-shadow: none; | ||||
|  | ||||
|         &__img, | ||||
|         &__img img, | ||||
|         &__footer { | ||||
|           border-radius: 0; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .hero-widget, | ||||
|       .box-widget, | ||||
|       .directory__tag { | ||||
|         border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
|       } | ||||
|  | ||||
|       .directory { | ||||
|         margin-top: 0; | ||||
|  | ||||
|         &__tag { | ||||
|           margin-bottom: 0; | ||||
|  | ||||
|           & > a, | ||||
|           & > div { | ||||
|             border-radius: 0; | ||||
|             box-shadow: none; | ||||
|           } | ||||
|  | ||||
|           &:last-child { | ||||
|             border-bottom: 0; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .brand { | ||||
|   position: relative; | ||||
|   text-decoration: none; | ||||
| } | ||||
|  | ||||
| .brand__tagline { | ||||
|   display: block; | ||||
|   position: absolute; | ||||
|   bottom: -10px; | ||||
|   left: 50px; | ||||
|   width: 300px; | ||||
|   color: $ui-primary-color; | ||||
|   text-decoration: none; | ||||
|   font-size: 14px; | ||||
|  | ||||
|   @media screen and (max-width: $no-gap-breakpoint) { | ||||
|     position: static; | ||||
|     width: auto; | ||||
|     margin-top: 20px; | ||||
|     color: $dark-text-color; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .rules-list { | ||||
|   background: darken($ui-base-color, 2%); | ||||
|   border: 1px solid darken($ui-base-color, 8%); | ||||
|   border-radius: 4px; | ||||
|   padding: 0.5em 2.5em !important; | ||||
|   margin-top: 1.85em !important; | ||||
|   font-size: 15px; | ||||
|   line-height: 22px; | ||||
|   color: $primary-text-color; | ||||
|   counter-reset: list-counter; | ||||
|  | ||||
|   li { | ||||
|     border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|     color: $dark-text-color; | ||||
|     padding: 1em; | ||||
|     position: relative; | ||||
|     border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
|     padding: 1em 1.75em; | ||||
|     padding-left: 3em; | ||||
|     font-weight: 500; | ||||
|     counter-increment: list-counter; | ||||
|  | ||||
|     &::before { | ||||
|       content: counter(list-counter); | ||||
|       position: absolute; | ||||
|       left: 0; | ||||
|       top: 50%; | ||||
|       transform: translateY(-50%); | ||||
|       background: $highlight-text-color; | ||||
|       color: $ui-base-color; | ||||
|       border-radius: 50%; | ||||
|       width: 4ch; | ||||
|       height: 4ch; | ||||
|       font-weight: 500; | ||||
|       display: flex; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|     } | ||||
|  | ||||
|     &:last-child { | ||||
|       border-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__text { | ||||
|     color: $primary-text-color; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,34 +0,0 @@ | ||||
| .compact-header { | ||||
|   h1 { | ||||
|     font-size: 24px; | ||||
|     line-height: 28px; | ||||
|     color: $darker-text-color; | ||||
|     font-weight: 500; | ||||
|     margin-bottom: 20px; | ||||
|     padding: 0 10px; | ||||
|     word-wrap: break-word; | ||||
|  | ||||
|     @media screen and (max-width: 740px) { | ||||
|       text-align: center; | ||||
|       padding: 20px 10px 0; | ||||
|     } | ||||
|  | ||||
|     a { | ||||
|       color: inherit; | ||||
|       text-decoration: none; | ||||
|     } | ||||
|  | ||||
|     small { | ||||
|       font-weight: 400; | ||||
|       color: $secondary-text-color; | ||||
|     } | ||||
|  | ||||
|     img { | ||||
|       display: inline-block; | ||||
|       margin-bottom: -5px; | ||||
|       margin-right: 15px; | ||||
|       width: 36px; | ||||
|       height: 36px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -3261,6 +3261,7 @@ $ui-header-height: 55px; | ||||
|     padding: 10px; | ||||
|     padding-top: 20px; | ||||
|     z-index: 1; | ||||
|     font-size: 13px; | ||||
|  | ||||
|     ul { | ||||
|       margin-bottom: 10px; | ||||
| @@ -3272,7 +3273,6 @@ $ui-header-height: 55px; | ||||
|  | ||||
|     p { | ||||
|       color: $dark-text-color; | ||||
|       font-size: 13px; | ||||
|       margin-bottom: 20px; | ||||
|  | ||||
|       a { | ||||
| @@ -8266,78 +8266,201 @@ noscript { | ||||
|  | ||||
|   &__body { | ||||
|     margin-top: 20px; | ||||
|     color: $secondary-text-color; | ||||
|     font-size: 15px; | ||||
|     line-height: 22px; | ||||
|   } | ||||
| } | ||||
|  | ||||
|     h1, | ||||
|     p, | ||||
|     ul, | ||||
|     ol { | ||||
|       margin-bottom: 20px; | ||||
| .prose { | ||||
|   color: $secondary-text-color; | ||||
|   font-size: 15px; | ||||
|   line-height: 22px; | ||||
|  | ||||
|   p, | ||||
|   ul, | ||||
|   ol { | ||||
|     margin-top: 1.25em; | ||||
|     margin-bottom: 1.25em; | ||||
|   } | ||||
|  | ||||
|   img { | ||||
|     margin-top: 2em; | ||||
|     margin-bottom: 2em; | ||||
|   } | ||||
|  | ||||
|   video { | ||||
|     margin-top: 2em; | ||||
|     margin-bottom: 2em; | ||||
|   } | ||||
|  | ||||
|   figure { | ||||
|     margin-top: 2em; | ||||
|     margin-bottom: 2em; | ||||
|  | ||||
|     figcaption { | ||||
|       font-size: 0.875em; | ||||
|       line-height: 1.4285714; | ||||
|       margin-top: 0.8571429em; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     ul { | ||||
|       list-style: disc; | ||||
|   figure > * { | ||||
|     margin-top: 0; | ||||
|     margin-bottom: 0; | ||||
|   } | ||||
|  | ||||
|   h1 { | ||||
|     font-size: 1.5em; | ||||
|     margin-top: 0; | ||||
|     margin-bottom: 1em; | ||||
|     line-height: 1.33; | ||||
|   } | ||||
|  | ||||
|   h2 { | ||||
|     font-size: 1.25em; | ||||
|     margin-top: 1.6em; | ||||
|     margin-bottom: 0.6em; | ||||
|     line-height: 1.6; | ||||
|   } | ||||
|  | ||||
|   h3, | ||||
|   h4, | ||||
|   h5, | ||||
|   h6 { | ||||
|     margin-top: 1.5em; | ||||
|     margin-bottom: 0.5em; | ||||
|     line-height: 1.5; | ||||
|   } | ||||
|  | ||||
|   ol { | ||||
|     counter-reset: list-counter; | ||||
|   } | ||||
|  | ||||
|   li { | ||||
|     margin-top: 0.5em; | ||||
|     margin-bottom: 0.5em; | ||||
|   } | ||||
|  | ||||
|   ol > li { | ||||
|     counter-increment: list-counter; | ||||
|  | ||||
|     &::before { | ||||
|       content: counter(list-counter) "."; | ||||
|       position: absolute; | ||||
|       left: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     ol { | ||||
|       list-style: decimal; | ||||
|   ul > li::before { | ||||
|     content: ""; | ||||
|     position: absolute; | ||||
|     background-color: $darker-text-color; | ||||
|     border-radius: 50%; | ||||
|     width: 0.375em; | ||||
|     height: 0.375em; | ||||
|     top: 0.5em; | ||||
|     left: 0.25em; | ||||
|   } | ||||
|  | ||||
|   ul > li, | ||||
|   ol > li { | ||||
|     position: relative; | ||||
|     padding-left: 1.75em; | ||||
|   } | ||||
|  | ||||
|   & > ul > li p { | ||||
|     margin-top: 0.75em; | ||||
|     margin-bottom: 0.75em; | ||||
|   } | ||||
|  | ||||
|   & > ul > li > *:first-child { | ||||
|     margin-top: 1.25em; | ||||
|   } | ||||
|  | ||||
|   & > ul > li > *:last-child { | ||||
|     margin-bottom: 1.25em; | ||||
|   } | ||||
|  | ||||
|   & > ol > li > *:first-child { | ||||
|     margin-top: 1.25em; | ||||
|   } | ||||
|  | ||||
|   & > ol > li > *:last-child { | ||||
|     margin-bottom: 1.25em; | ||||
|   } | ||||
|  | ||||
|   ul ul, | ||||
|   ul ol, | ||||
|   ol ul, | ||||
|   ol ol { | ||||
|     margin-top: 0.75em; | ||||
|     margin-bottom: 0.75em; | ||||
|   } | ||||
|  | ||||
|   h1, | ||||
|   h2, | ||||
|   h3, | ||||
|   h4, | ||||
|   h5, | ||||
|   h6, | ||||
|   strong, | ||||
|   b { | ||||
|     color: $primary-text-color; | ||||
|     font-weight: 700; | ||||
|   } | ||||
|  | ||||
|   em, | ||||
|   i { | ||||
|     font-style: italic; | ||||
|   } | ||||
|  | ||||
|   a { | ||||
|     color: $highlight-text-color; | ||||
|     text-decoration: underline; | ||||
|  | ||||
|     &:focus, | ||||
|     &:hover, | ||||
|     &:active { | ||||
|       text-decoration: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|     ul, | ||||
|     ol { | ||||
|       padding-left: 1em; | ||||
|     } | ||||
|   code { | ||||
|     font-size: 0.875em; | ||||
|     background: darken($ui-base-color, 8%); | ||||
|     border-radius: 4px; | ||||
|     padding: 0.2em 0.3em; | ||||
|   } | ||||
|  | ||||
|     li { | ||||
|       margin-bottom: 10px; | ||||
|   hr { | ||||
|     border: 0; | ||||
|     border-top: 1px solid lighten($ui-base-color, 4%); | ||||
|     margin-top: 3em; | ||||
|     margin-bottom: 3em; | ||||
|   } | ||||
|  | ||||
|       &::marker { | ||||
|         color: $darker-text-color; | ||||
|       } | ||||
|   hr + * { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|       &:last-child { | ||||
|         margin-bottom: 0; | ||||
|       } | ||||
|     } | ||||
|   h2 + * { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|     h1 { | ||||
|       color: $primary-text-color; | ||||
|       font-size: 19px; | ||||
|       line-height: 24px; | ||||
|       font-weight: 700; | ||||
|       margin-top: 30px; | ||||
|   h3 + * { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|       &:first-child { | ||||
|         margin-top: 0; | ||||
|       } | ||||
|     } | ||||
|   h4 + *, | ||||
|   h5 + *, | ||||
|   h6 + * { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|     strong { | ||||
|       font-weight: 700; | ||||
|       color: $primary-text-color; | ||||
|     } | ||||
|   & > :first-child { | ||||
|     margin-top: 0; | ||||
|   } | ||||
|  | ||||
|     em { | ||||
|       font-style: italic; | ||||
|     } | ||||
|  | ||||
|     a { | ||||
|       color: $highlight-text-color; | ||||
|       text-decoration: underline; | ||||
|  | ||||
|       &:focus, | ||||
|       &:hover, | ||||
|       &:active { | ||||
|         text-decoration: none; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     hr { | ||||
|       border: 1px solid lighten($ui-base-color, 4%); | ||||
|       margin: 30px 0; | ||||
|     } | ||||
|   & > :last-child { | ||||
|     margin-bottom: 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -8365,3 +8488,242 @@ noscript { | ||||
|     justify-content: center; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .image { | ||||
|   position: relative; | ||||
|   overflow: hidden; | ||||
|  | ||||
|   &__preview { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     object-fit: cover; | ||||
|   } | ||||
|  | ||||
|   &.loaded &__preview { | ||||
|     display: none; | ||||
|   } | ||||
|  | ||||
|   img { | ||||
|     display: block; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     object-fit: cover; | ||||
|     border: 0; | ||||
|     background: transparent; | ||||
|     opacity: 0; | ||||
|   } | ||||
|  | ||||
|   &.loaded img { | ||||
|     opacity: 1; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .about { | ||||
|   padding: 20px; | ||||
|  | ||||
|   @media screen and (min-width: $no-gap-breakpoint) { | ||||
|     border-radius: 4px; | ||||
|   } | ||||
|  | ||||
|   &__header { | ||||
|     margin-bottom: 30px; | ||||
|  | ||||
|     &__hero { | ||||
|       width: 100%; | ||||
|       height: auto; | ||||
|       aspect-ratio: 1.9; | ||||
|       background: lighten($ui-base-color, 4%); | ||||
|       border-radius: 8px; | ||||
|       margin-bottom: 30px; | ||||
|     } | ||||
|  | ||||
|     h1, | ||||
|     p { | ||||
|       text-align: center; | ||||
|     } | ||||
|  | ||||
|     h1 { | ||||
|       font-size: 24px; | ||||
|       line-height: 1.5; | ||||
|       font-weight: 700; | ||||
|       margin-bottom: 10px; | ||||
|     } | ||||
|  | ||||
|     p { | ||||
|       font-size: 16px; | ||||
|       line-height: 24px; | ||||
|       font-weight: 400; | ||||
|       color: $darker-text-color; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__meta { | ||||
|     background: lighten($ui-base-color, 4%); | ||||
|     border-radius: 4px; | ||||
|     display: flex; | ||||
|     margin-bottom: 30px; | ||||
|     font-size: 15px; | ||||
|  | ||||
|     &__column { | ||||
|       box-sizing: border-box; | ||||
|       width: 50%; | ||||
|       padding: 20px; | ||||
|     } | ||||
|  | ||||
|     &__divider { | ||||
|       width: 0; | ||||
|       border: 0; | ||||
|       border-style: solid; | ||||
|       border-color: lighten($ui-base-color, 8%); | ||||
|       border-left-width: 1px; | ||||
|       min-height: calc(100% - 60px); | ||||
|       flex: 0 0 auto; | ||||
|     } | ||||
|  | ||||
|     h4 { | ||||
|       font-size: 15px; | ||||
|       text-transform: uppercase; | ||||
|       color: $darker-text-color; | ||||
|       font-weight: 500; | ||||
|       margin-bottom: 20px; | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: 600px) { | ||||
|       display: block; | ||||
|  | ||||
|       h4 { | ||||
|         text-align: center; | ||||
|       } | ||||
|  | ||||
|       &__column { | ||||
|         width: 100%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|       } | ||||
|  | ||||
|       &__divider { | ||||
|         min-height: 0; | ||||
|         width: 100%; | ||||
|         border-left-width: 0; | ||||
|         border-top-width: 1px; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .layout-multiple-columns & { | ||||
|       display: block; | ||||
|  | ||||
|       h4 { | ||||
|         text-align: center; | ||||
|       } | ||||
|  | ||||
|       &__column { | ||||
|         width: 100%; | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|       } | ||||
|  | ||||
|       &__divider { | ||||
|         min-height: 0; | ||||
|         width: 100%; | ||||
|         border-left-width: 0; | ||||
|         border-top-width: 1px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__mail { | ||||
|     color: $primary-text-color; | ||||
|     text-decoration: none; | ||||
|     font-weight: 500; | ||||
|  | ||||
|     &:hover, | ||||
|     &:focus, | ||||
|     &:active { | ||||
|       text-decoration: underline; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .getting-started__footer { | ||||
|     padding: 0; | ||||
|     margin-top: 60px; | ||||
|     text-align: center; | ||||
|     font-size: 15px; | ||||
|     line-height: 22px; | ||||
|  | ||||
|     @media screen and (min-width: $no-gap-breakpoint) { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .account { | ||||
|     padding: 0; | ||||
|     border: 0; | ||||
|   } | ||||
|  | ||||
|   .account__avatar-wrapper { | ||||
|     margin-left: 0; | ||||
|   } | ||||
|  | ||||
|   .account__relationship { | ||||
|     display: none; | ||||
|   } | ||||
|  | ||||
|   &__section { | ||||
|     margin-bottom: 10px; | ||||
|  | ||||
|     &__title { | ||||
|       font-size: 17px; | ||||
|       font-weight: 600; | ||||
|       line-height: 22px; | ||||
|       padding: 20px; | ||||
|       border-radius: 4px; | ||||
|       background: lighten($ui-base-color, 4%); | ||||
|       color: $highlight-text-color; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|  | ||||
|     &.active &__title { | ||||
|       border-radius: 4px 4px 0 0; | ||||
|     } | ||||
|  | ||||
|     &__body { | ||||
|       border: 1px solid lighten($ui-base-color, 4%); | ||||
|       border-top: 0; | ||||
|       padding: 20px; | ||||
|       font-size: 15px; | ||||
|       line-height: 22px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__domain-blocks { | ||||
|     margin-top: 30px; | ||||
|     width: 100%; | ||||
|     border-collapse: collapse; | ||||
|     break-inside: auto; | ||||
|  | ||||
|     th { | ||||
|       text-align: left; | ||||
|       font-weight: 500; | ||||
|       color: $darker-text-color; | ||||
|     } | ||||
|  | ||||
|     thead tr, | ||||
|     tbody tr { | ||||
|       border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
|     } | ||||
|  | ||||
|     tbody tr:last-child { | ||||
|       border-bottom: 0; | ||||
|     } | ||||
|  | ||||
|     th, | ||||
|     td { | ||||
|       padding: 8px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -30,7 +30,6 @@ | ||||
|       outline: 0; | ||||
|       padding: 12px 16px; | ||||
|       line-height: 32px; | ||||
|       font-family: $font-display, sans-serif; | ||||
|       font-weight: 500; | ||||
|       font-size: 14px; | ||||
|     } | ||||
|   | ||||
| @@ -38,7 +38,6 @@ | ||||
|     font-weight: 500; | ||||
|     font-size: 24px; | ||||
|     color: $primary-text-color; | ||||
|     font-family: $font-display, sans-serif; | ||||
|     margin-bottom: 20px; | ||||
|     line-height: 30px; | ||||
|   } | ||||
|   | ||||
| @@ -139,18 +139,9 @@ code { | ||||
|   } | ||||
|  | ||||
|   .rules-list { | ||||
|     list-style: decimal; | ||||
|     font-size: 17px; | ||||
|     line-height: 22px; | ||||
|     font-weight: 500; | ||||
|     background: transparent; | ||||
|     border: 0; | ||||
|     padding: 0.5em 1em !important; | ||||
|     margin-bottom: 30px; | ||||
|  | ||||
|     li { | ||||
|       border-color: lighten($ui-base-color, 8%); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .hint { | ||||
| @@ -603,41 +594,6 @@ code { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &__overlay-area { | ||||
|     position: relative; | ||||
|  | ||||
|     &__blurred form { | ||||
|       filter: blur(2px); | ||||
|     } | ||||
|  | ||||
|     &__overlay { | ||||
|       position: absolute; | ||||
|       top: 0; | ||||
|       left: 0; | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|       display: flex; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|       background: rgba($ui-base-color, 0.65); | ||||
|       border-radius: 4px; | ||||
|       margin-left: -4px; | ||||
|       margin-top: -4px; | ||||
|       padding: 4px; | ||||
|  | ||||
|       &__content { | ||||
|         text-align: center; | ||||
|  | ||||
|         &.rich-formatting { | ||||
|           &, | ||||
|           p { | ||||
|             color: $primary-text-color; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .block-icon { | ||||
| @@ -908,24 +864,7 @@ code { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .table-form { | ||||
|   p { | ||||
|     margin-bottom: 15px; | ||||
|  | ||||
|     strong { | ||||
|       font-weight: 500; | ||||
|  | ||||
|       @each $lang in $cjk-langs { | ||||
|         &:lang(#{$lang}) { | ||||
|           font-weight: 700; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .simple_form, | ||||
| .table-form { | ||||
| .simple_form { | ||||
|   .warning { | ||||
|     box-sizing: border-box; | ||||
|     background: rgba($error-value-color, 0.5); | ||||
|   | ||||
| @@ -112,13 +112,6 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .box-widget { | ||||
|   padding: 20px; | ||||
|   border-radius: 4px; | ||||
|   background: $ui-base-color; | ||||
|   box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); | ||||
| } | ||||
|  | ||||
| .placeholder-widget { | ||||
|   padding: 16px; | ||||
|   border-radius: 4px; | ||||
| @@ -128,47 +121,6 @@ | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .contact-widget { | ||||
|   min-height: 100%; | ||||
|   font-size: 15px; | ||||
|   color: $darker-text-color; | ||||
|   line-height: 20px; | ||||
|   word-wrap: break-word; | ||||
|   font-weight: 400; | ||||
|   padding: 0; | ||||
|  | ||||
|   h4 { | ||||
|     padding: 10px; | ||||
|     text-transform: uppercase; | ||||
|     font-weight: 700; | ||||
|     font-size: 13px; | ||||
|     color: $darker-text-color; | ||||
|   } | ||||
|  | ||||
|   .account { | ||||
|     border-bottom: 0; | ||||
|     padding: 10px 0; | ||||
|     padding-top: 5px; | ||||
|   } | ||||
|  | ||||
|   & > a { | ||||
|     display: inline-block; | ||||
|     padding: 10px; | ||||
|     padding-top: 0; | ||||
|     color: $darker-text-color; | ||||
|     text-decoration: none; | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|  | ||||
|     &:hover, | ||||
|     &:focus, | ||||
|     &:active { | ||||
|       text-decoration: underline; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .moved-account-widget { | ||||
|   padding: 15px; | ||||
|   padding-bottom: 20px; | ||||
| @@ -249,37 +201,6 @@ | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .page-header { | ||||
|   background: lighten($ui-base-color, 8%); | ||||
|   box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); | ||||
|   border-radius: 4px; | ||||
|   padding: 60px 15px; | ||||
|   text-align: center; | ||||
|   margin: 10px 0; | ||||
|  | ||||
|   h1 { | ||||
|     color: $primary-text-color; | ||||
|     font-size: 36px; | ||||
|     line-height: 1.1; | ||||
|     font-weight: 700; | ||||
|     margin-bottom: 10px; | ||||
|   } | ||||
|  | ||||
|   p { | ||||
|     font-size: 15px; | ||||
|     color: $darker-text-color; | ||||
|   } | ||||
|  | ||||
|   @media screen and (max-width: $no-gap-breakpoint) { | ||||
|     margin-top: 0; | ||||
|     background: lighten($ui-base-color, 4%); | ||||
|  | ||||
|     h1 { | ||||
|       font-size: 24px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .directory { | ||||
|   background: $ui-base-color; | ||||
|   border-radius: 4px; | ||||
| @@ -366,34 +287,6 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| .avatar-stack { | ||||
|   display: flex; | ||||
|   justify-content: flex-end; | ||||
|  | ||||
|   .account__avatar { | ||||
|     flex: 0 0 auto; | ||||
|     width: 36px; | ||||
|     height: 36px; | ||||
|     border-radius: 50%; | ||||
|     position: relative; | ||||
|     margin-left: -10px; | ||||
|     background: darken($ui-base-color, 8%); | ||||
|     border: 2px solid $ui-base-color; | ||||
|  | ||||
|     &:nth-child(1) { | ||||
|       z-index: 1; | ||||
|     } | ||||
|  | ||||
|     &:nth-child(2) { | ||||
|       z-index: 2; | ||||
|     } | ||||
|  | ||||
|     &:nth-child(3) { | ||||
|       z-index: 3; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .accounts-table { | ||||
|   width: 100%; | ||||
|  | ||||
| @@ -495,11 +388,7 @@ | ||||
|  | ||||
| .moved-account-widget, | ||||
| .memoriam-widget, | ||||
| .box-widget, | ||||
| .contact-widget, | ||||
| .landing-page__information.contact-widget, | ||||
| .directory, | ||||
| .page-header { | ||||
| .directory { | ||||
|   @media screen and (max-width: $no-gap-breakpoint) { | ||||
|     margin-bottom: 0; | ||||
|     box-shadow: none; | ||||
| @@ -507,88 +396,6 @@ | ||||
|   } | ||||
| } | ||||
|  | ||||
| $maximum-width: 1235px; | ||||
| $fluid-breakpoint: $maximum-width + 20px; | ||||
|  | ||||
| .statuses-grid { | ||||
|   min-height: 600px; | ||||
|  | ||||
|   @media screen and (max-width: 640px) { | ||||
|     width: 100% !important; // Masonry layout is unnecessary at this width | ||||
|   } | ||||
|  | ||||
|   &__item { | ||||
|     width: math.div(960px - 20px, 3); | ||||
|  | ||||
|     @media screen and (max-width: $fluid-breakpoint) { | ||||
|       width: math.div(940px - 20px, 3); | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: 640px) { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     @media screen and (max-width: $no-gap-breakpoint) { | ||||
|       width: 100vw; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .detailed-status { | ||||
|     border-radius: 4px; | ||||
|  | ||||
|     @media screen and (max-width: $no-gap-breakpoint) { | ||||
|       border-top: 1px solid lighten($ui-base-color, 16%); | ||||
|     } | ||||
|  | ||||
|     &.compact { | ||||
|       .detailed-status__meta { | ||||
|         margin-top: 15px; | ||||
|       } | ||||
|  | ||||
|       .status__content { | ||||
|         font-size: 15px; | ||||
|         line-height: 20px; | ||||
|  | ||||
|         .emojione { | ||||
|           width: 20px; | ||||
|           height: 20px; | ||||
|           margin: -3px 0 0; | ||||
|         } | ||||
|  | ||||
|         .status__content__spoiler-link { | ||||
|           line-height: 20px; | ||||
|           margin: 0; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       .media-gallery, | ||||
|       .status-card, | ||||
|       .video-player { | ||||
|         margin-top: 15px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .notice-widget { | ||||
|   margin-bottom: 10px; | ||||
|   color: $darker-text-color; | ||||
|  | ||||
|   p { | ||||
|     margin-bottom: 10px; | ||||
|  | ||||
|     &:last-child { | ||||
|       margin-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   a { | ||||
|     font-size: 14px; | ||||
|     line-height: 20px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .notice-widget, | ||||
| .placeholder-widget { | ||||
|   a { | ||||
|     text-decoration: none; | ||||
| @@ -602,37 +409,3 @@ $fluid-breakpoint: $maximum-width + 20px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .table-of-contents { | ||||
|   background: darken($ui-base-color, 4%); | ||||
|   min-height: 100%; | ||||
|   font-size: 14px; | ||||
|   border-radius: 4px; | ||||
|  | ||||
|   li a { | ||||
|     display: block; | ||||
|     font-weight: 500; | ||||
|     padding: 15px; | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|     text-decoration: none; | ||||
|     color: $primary-text-color; | ||||
|     border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|  | ||||
|     &:hover, | ||||
|     &:focus, | ||||
|     &:active { | ||||
|       text-decoration: underline; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   li:last-child a { | ||||
|     border-bottom: 0; | ||||
|   } | ||||
|  | ||||
|   li ul { | ||||
|     padding-left: 20px; | ||||
|     border-bottom: 1px solid lighten($ui-base-color, 4%); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -28,8 +28,8 @@ class DomainBlock < ApplicationRecord | ||||
|   delegate :count, to: :accounts, prefix: true | ||||
|  | ||||
|   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } | ||||
|   scope :with_user_facing_limitations, -> { where(severity: [:silence, :suspend]).or(where(reject_media: true)) } | ||||
|   scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), reject_media, domain')) } | ||||
|   scope :with_user_facing_limitations, -> { where(severity: [:silence, :suspend]) } | ||||
|   scope :by_severity, -> { order(Arel.sql('(CASE severity WHEN 0 THEN 1 WHEN 1 THEN 2 WHEN 2 THEN 0 END), domain')) } | ||||
|  | ||||
|   def to_log_human_identifier | ||||
|     domain | ||||
|   | ||||
							
								
								
									
										15
									
								
								app/models/extended_description.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/models/extended_description.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class ExtendedDescription < ActiveModelSerializers::Model | ||||
|   attributes :updated_at, :text | ||||
|  | ||||
|   def self.current | ||||
|     custom = Setting.find_by(var: 'site_extended_description') | ||||
|  | ||||
|     if custom&.value.present? | ||||
|       new(text: custom.value, updated_at: custom.updated_at) | ||||
|     else | ||||
|       new | ||||
|     end | ||||
|   end | ||||
| end | ||||
							
								
								
									
										17
									
								
								app/serializers/rest/domain_block_serializer.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/serializers/rest/domain_block_serializer.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class REST::DomainBlockSerializer < ActiveModel::Serializer | ||||
|   attributes :domain, :digest, :severity, :comment | ||||
|  | ||||
|   def domain | ||||
|     object.public_domain | ||||
|   end | ||||
|  | ||||
|   def digest | ||||
|     object.domain_digest | ||||
|   end | ||||
|  | ||||
|   def comment | ||||
|     object.public_comment if instance_options[:with_comment] | ||||
|   end | ||||
| end | ||||
							
								
								
									
										13
									
								
								app/serializers/rest/extended_description_serializer.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/serializers/rest/extended_description_serializer.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| # frozen_string_literal: true | ||||
|  | ||||
| class REST::ExtendedDescriptionSerializer < ActiveModel::Serializer | ||||
|   attributes :updated_at, :content | ||||
|  | ||||
|   def updated_at | ||||
|     object.updated_at&.iso8601 | ||||
|   end | ||||
|  | ||||
|   def content | ||||
|     object.text | ||||
|   end | ||||
| end | ||||
| @@ -1,12 +0,0 @@ | ||||
| %table | ||||
|   %thead | ||||
|     %tr | ||||
|       %th= t('about.unavailable_content_description.domain') | ||||
|       %th= t('about.unavailable_content_description.reason') | ||||
|   %tbody | ||||
|     - domain_blocks.each do |domain_block| | ||||
|       %tr | ||||
|         %td.nowrap | ||||
|           %span{ title: "SHA-256: #{domain_block.domain_digest}" }= domain_block.public_domain | ||||
|         %td | ||||
|           = domain_block.public_comment if display_blocks_rationale? | ||||
| @@ -1,96 +0,0 @@ | ||||
| - content_for :page_title do | ||||
|   = site_hostname | ||||
|  | ||||
| - content_for :header_tags do | ||||
|   = javascript_pack_tag 'public', crossorigin: 'anonymous' | ||||
|   = render partial: 'shared/og' | ||||
|  | ||||
| .grid-4 | ||||
|   .column-0 | ||||
|     .public-account-header.public-account-header--no-bar | ||||
|       .public-account-header__image | ||||
|         = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.png'), alt: @instance_presenter.title, class: 'parallax' | ||||
|  | ||||
|   .column-1 | ||||
|     .landing-page__call-to-action{ dir: 'ltr' } | ||||
|       .row | ||||
|         .row__information-board | ||||
|           .information-board__section | ||||
|             %span= t 'about.user_count_before' | ||||
|             %strong= friendly_number_to_human @instance_presenter.user_count | ||||
|             %span= t 'about.user_count_after', count: @instance_presenter.user_count | ||||
|           .information-board__section | ||||
|             %span= t 'about.status_count_before' | ||||
|             %strong= friendly_number_to_human @instance_presenter.status_count | ||||
|             %span= t 'about.status_count_after', count: @instance_presenter.status_count | ||||
|         .row__mascot | ||||
|           .landing-page__mascot | ||||
|             = image_tag @instance_presenter.mascot&.file&.url || asset_pack_path('media/images/elephant_ui_plane.svg'), alt: '' | ||||
|  | ||||
|   .column-2 | ||||
|     .contact-widget | ||||
|       %h4= t 'about.administered_by' | ||||
|  | ||||
|       = account_link_to(@instance_presenter.contact.account) | ||||
|  | ||||
|       - if @instance_presenter.contact.email.present? | ||||
|         %h4 | ||||
|           = succeed ':' do | ||||
|             = t 'about.contact' | ||||
|  | ||||
|         = mail_to @instance_presenter.contact.email, nil, title: @instance_presenter.contact.email | ||||
|  | ||||
|   .column-3 | ||||
|     = render 'application/flashes' | ||||
|  | ||||
|     - if @contents.blank? && @rules.empty? && (!display_blocks? || @blocks&.empty?) | ||||
|       = nothing_here | ||||
|     - else | ||||
|       .box-widget | ||||
|         .rich-formatting | ||||
|           - unless @rules.empty? | ||||
|             %h2#rules= t('about.rules') | ||||
|  | ||||
|             %p= t('about.rules_html') | ||||
|  | ||||
|             %ol.rules-list | ||||
|               - @rules.each do |rule| | ||||
|                 %li | ||||
|                   .rules-list__text= rule.text | ||||
|  | ||||
|           = @contents.html_safe | ||||
|  | ||||
|           - if display_blocks? && !@blocks.empty? | ||||
|             %h2#unavailable-content= t('about.unavailable_content') | ||||
|  | ||||
|             %p= t('about.unavailable_content_html') | ||||
|  | ||||
|             - if (blocks = @blocks.select(&:reject_media?)) && !blocks.empty? | ||||
|               %h3= t('about.unavailable_content_description.rejecting_media_title') | ||||
|               %p= t('about.unavailable_content_description.rejecting_media') | ||||
|               = render partial: 'domain_blocks', locals: { domain_blocks: blocks } | ||||
|             - if (blocks = @blocks.select(&:silence?)) && !blocks.empty? | ||||
|               %h3= t('about.unavailable_content_description.silenced_title') | ||||
|               %p= t('about.unavailable_content_description.silenced') | ||||
|               = render partial: 'domain_blocks', locals: { domain_blocks: blocks } | ||||
|             - if (blocks = @blocks.select(&:suspend?)) && !blocks.empty? | ||||
|               %h3= t('about.unavailable_content_description.suspended_title') | ||||
|               %p= t('about.unavailable_content_description.suspended') | ||||
|               = render partial: 'domain_blocks', locals: { domain_blocks: blocks } | ||||
|  | ||||
|   .column-4 | ||||
|     %ul.table-of-contents | ||||
|       - unless @rules.empty? | ||||
|         %li= link_to t('about.rules'), '#rules' | ||||
|  | ||||
|       - @table_of_contents.each do |item| | ||||
|         %li | ||||
|           = link_to item.title, "##{item.anchor}" | ||||
|  | ||||
|           - unless item.children.empty? | ||||
|             %ul | ||||
|               - item.children.each do |sub_item| | ||||
|                 %li= link_to sub_item.title, "##{sub_item.anchor}" | ||||
|  | ||||
|       - if display_blocks? && !@blocks.empty? | ||||
|         %li= link_to t('about.unavailable_content'), '#unavailable-content' | ||||
							
								
								
									
										4
									
								
								app/views/about/show.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/views/about/show.html.haml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| - content_for :page_title do | ||||
|   = t('about.title') | ||||
|  | ||||
| = render partial: 'shared/web_app' | ||||
| @@ -2,41 +2,15 @@ | ||||
| en: | ||||
|   about: | ||||
|     about_mastodon_html: 'The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!' | ||||
|     about_this: About | ||||
|     administered_by: 'Administered by:' | ||||
|     api: API | ||||
|     apps: Mobile apps | ||||
|     contact: Contact | ||||
|     contact_missing: Not set | ||||
|     contact_unavailable: N/A | ||||
|     documentation: Documentation | ||||
|     hosted_on: Mastodon hosted on %{domain} | ||||
|     instance_actor_flash: | | ||||
|       This account is a virtual actor used to represent the server itself and not any individual user. | ||||
|       It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block. | ||||
|     privacy_policy: Privacy Policy | ||||
|     rules: Server rules | ||||
|     rules_html: 'Below is a summary of rules you need to follow if you want to have an account on this server of Mastodon:' | ||||
|     source_code: Source code | ||||
|     status_count_after: | ||||
|       one: post | ||||
|       other: posts | ||||
|     status_count_before: Who published | ||||
|     unavailable_content: Moderated servers | ||||
|     unavailable_content_description: | ||||
|       domain: Server | ||||
|       reason: Reason | ||||
|       rejecting_media: 'Media files from these servers will not be processed or stored, and no thumbnails will be displayed, requiring manual click-through to the original file:' | ||||
|       rejecting_media_title: Filtered media | ||||
|       silenced: 'Posts from these servers will be hidden in public timelines and conversations, and no notifications will be generated from their users interactions, unless you are following them:' | ||||
|       silenced_title: Limited servers | ||||
|       suspended: 'No data from these servers will be processed, stored or exchanged, making any interaction or communication with users from these servers impossible:' | ||||
|       suspended_title: Suspended servers | ||||
|     unavailable_content_html: 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. | ||||
|     user_count_after: | ||||
|       one: user | ||||
|       other: users | ||||
|     user_count_before: Home to | ||||
|     title: About | ||||
|     what_is_mastodon: What is Mastodon? | ||||
|   accounts: | ||||
|     choices_html: "%{name}'s choices:" | ||||
|   | ||||
| @@ -487,7 +487,9 @@ Rails.application.routes.draw do | ||||
|       resource :instance, only: [:show] do | ||||
|         resources :peers, only: [:index], controller: 'instances/peers' | ||||
|         resources :rules, only: [:index], controller: 'instances/rules' | ||||
|         resources :domain_blocks, only: [:index], controller: 'instances/domain_blocks' | ||||
|         resource :privacy_policy, only: [:show], controller: 'instances/privacy_policies' | ||||
|         resource :extended_description, only: [:show], controller: 'instances/extended_descriptions' | ||||
|         resource :activity, only: [:show], controller: 'instances/activity' | ||||
|       end | ||||
|  | ||||
| @@ -642,8 +644,8 @@ Rails.application.routes.draw do | ||||
|  | ||||
|   get '/web/(*any)', to: 'home#index', as: :web | ||||
|  | ||||
|   get '/about',        to: redirect('/') | ||||
|   get '/about/more',   to: 'about#more' | ||||
|   get '/about',        to: 'about#show' | ||||
|   get '/about/more',   to: redirect('/about') | ||||
|  | ||||
|   get '/privacy-policy', to: 'privacy#show', as: :privacy_policy | ||||
|   get '/terms',          to: redirect('/privacy-policy') | ||||
|   | ||||
| @@ -3,13 +3,9 @@ require 'rails_helper' | ||||
| RSpec.describe AboutController, type: :controller do | ||||
|   render_views | ||||
|  | ||||
|   describe 'GET #more' do | ||||
|   describe 'GET #show' do | ||||
|     before do | ||||
|       get :more | ||||
|     end | ||||
|  | ||||
|     it 'assigns @instance_presenter' do | ||||
|       expect(assigns(:instance_presenter)).to be_kind_of InstancePresenter | ||||
|       get :show | ||||
|     end | ||||
|  | ||||
|     it 'returns http success' do | ||||
|   | ||||
		Reference in New Issue
	
	Block a user