Change about page to be mounted in the web UI (#19345)
parent
b04633a961
commit
1bd00036c2
37 changed files with 900 additions and 1574 deletions
@ -1,63 +1,11 @@ |
|||||||
# frozen_string_literal: true |
# frozen_string_literal: true |
||||||
|
|
||||||
class AboutController < ApplicationController |
class AboutController < ApplicationController |
||||||
include RegistrationSpamConcern |
include WebAppControllerConcern |
||||||
|
|
||||||
layout 'public' |
skip_before_action :require_functional! |
||||||
|
|
||||||
before_action :require_open_federation!, only: [:more] |
def show |
||||||
before_action :set_body_classes, only: :show |
expires_in 0, public: true unless user_signed_in? |
||||||
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 |
|
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -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.
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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 |
@ -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 |
@ -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' |
|
@ -0,0 +1,4 @@ |
|||||||
|
- content_for :page_title do |
||||||
|
= t('about.title') |
||||||
|
|
||||||
|
= render partial: 'shared/web_app' |
Loading…
Reference in new issue