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 |
||||
|
||||
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 |
||||
|
@ -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