Commit 17412d19 authored by Jeffrey Phillips Freeman's avatar Jeffrey Phillips Freeman 💥
Browse files

Render rich text in feed and add option to strip it.

parent 4deb41cb
......@@ -66,6 +66,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_show_quote_button,
:setting_show_bookmark_button,
:setting_show_target,
:setting_strip_formatting,
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)
......
......@@ -61,6 +61,17 @@ module StatusesHelper
embedded_view? ? '_blank' : nil
end
def text_formatting_classes
case current_user&.setting_strip_formatting
when 'none', nil
'rich-text rich-blocks'
when 'blocks'
'rich-text'
when 'all'
nil
end
end
def style_classes(status, is_predecessor, is_successor, include_threads)
classes = ['entry']
classes << 'entry-predecessor' if is_predecessor
......
......@@ -8,6 +8,7 @@ import classnames from 'classnames';
import PollContainer from 'mastodon/containers/poll_container';
import Icon from 'mastodon/components/icon';
import { autoPlayGif } from 'mastodon/initial_state';
import { stripFormatting } from 'mastodon/initial_state';
const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
......@@ -210,6 +211,8 @@ export default class StatusContent extends React.PureComponent {
'status__content--with-action': this.props.onClick && this.context.router,
'status__content--with-spoiler': status.get('spoiler_text').length > 0,
'status__content--collapsed': renderReadMore,
'rich-text': stripFormatting !== 'all',
'rich-blocks': stripFormatting === 'none',
});
if (isRtl(status.get('search_index'))) {
......
......@@ -35,5 +35,6 @@ export const show_navigation_panel = getMeta('show_navigation_panel');
export const show_quote_button = getMeta('show_quote_button');
export const show_bookmark_button = getMeta('show_bookmark_button');
export const show_target = getMeta('show_target');
export const stripFormatting = getMeta('strip_formatting');
export default initialState;
......@@ -843,10 +843,6 @@
&.status__content--with-spoiler {
white-space: normal;
.status__content__text {
white-space: pre-wrap;
}
}
.emojione {
......@@ -855,7 +851,9 @@
margin: -3px 0 0;
}
p {
p,
pre,
blockquote {
margin-bottom: 20px;
white-space: pre-wrap;
......@@ -864,6 +862,175 @@
}
}
h1,
h2,
h3,
h4,
h5 {
margin-bottom: 20px;
}
blockquote {
white-space: normal;
p:last-child {
margin-bottom: 0;
}
}
ul,
ol {
p {
margin-bottom: 0;
}
}
&:not(.rich-text) {
del {
text-decoration: none;
&::before,
&::after {
content: '~~';
}
}
code {
font-family: inherit;
}
u {
text-decoration: none;
&::before,
&::after {
content: '__';
}
}
h1::before {
content: '# ';
}
h2::before {
content: '## ';
}
h3::before {
content: '### ';
}
h4::before {
content: '#### ';
}
h5::before {
content: '##### ';
}
b,
strong {
&::before,
&::after {
content: '**';
}
}
em,
i {
&::before,
&::after {
content: '*';
}
}
}
&:not(.rich-blocks) {
blockquote {
position: relative;
padding-left: 1em;
overflow: hidden;
}
blockquote::before {
position: absolute;
content: '>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a>\a';
white-space: pre-wrap;
left: 0;
top: 0;
}
li::before {
position: absolute;
content: '*';
left: 0;
top: 0;
}
li {
position: relative;
padding-left: 1em;
}
}
&.rich-text {
h1,
h2 {
font-weight: 700;
}
h3,
h4,
h5 {
font-weight: 500;
}
b,
strong {
font-weight: 700;
}
em,
i {
font-style: italic;
}
sub {
font-size: smaller;
text-align: sub;
}
}
&.rich-blocks {
h1,
h2 {
font-size: 18px;
}
h2 {
font-size: 16px;
}
blockquote {
padding-left: 10px;
border-left: 3px solid $darker-text-color;
color: $darker-text-color;
}
ul,
ol {
margin-left: 1em;
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
}
a {
color: $secondary-text-color;
text-decoration: none;
......
......@@ -72,11 +72,13 @@ class Sanitize
end
MASTODON_STRICT ||= freeze_config(
elements: %w(p br span a),
elements: %w(p br span a abbr del pre blockquote code b strong u sub i em h1 h2 h3 h4 h5 ul ol li),
attributes: {
'a' => %w(href rel class),
'span' => %w(class),
'a' => %w(href rel class title),
'span' => %w(class),
'abbr' => %w(title),
'blockquote' => %w(cite),
},
add_attributes: {
......@@ -86,7 +88,10 @@ class Sanitize
},
},
protocols: {},
protocols: {
'a' => { 'href' => HTTP_PROTOCOLS },
'blockquote' => { 'cite' => HTTP_PROTOCOLS },
},
transformers: [
CLASS_WHITELIST_TRANSFORMER,
......
......@@ -50,6 +50,7 @@ class UserSettingsDecorator
user.settings['show_quote_button'] = show_quote_button_preference if change?('setting_show_quote_button')
user.settings['show_bookmark_button'] = show_bookmark_button_preference if change?('setting_show_bookmark_button')
user.settings['show_target'] = show_target_preference if change?('setting_show_target')
user.settings['strip_formatting'] = strip_formatting_preference if change?('setting_strip_formatting')
end
def merged_notification_emails
......@@ -71,7 +72,7 @@ class UserSettingsDecorator
def default_federation_preference
boolean_cast_setting 'setting_default_federation'
end
def default_content_type_preference
settings['setting_default_content_type']
end
......@@ -192,6 +193,10 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_show_target'
end
def strip_formatting_preference
settings['setting_strip_formatting']
end
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end
......
......@@ -120,7 +120,7 @@ class User < ApplicationRecord
:show_follow_button_on_timeline, :show_subscribe_button_on_timeline, :show_target,
:show_follow_button_on_timeline, :show_subscribe_button_on_timeline, :show_followed_by, :show_target,
:follow_button_to_list_adder, :show_navigation_panel, :show_quote_button, :show_bookmark_button,
to: :settings, prefix: :setting, allow_nil: false
:strip_formatting, to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code, :sign_in_token_attempt
attr_writer :external
......
......@@ -43,6 +43,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:is_staff] = object.current_account.user.staff?
store[:strip_formatting] = object.current_account.user.setting_strip_formatting
store[:trends] = Setting.trends && object.current_account.user.setting_trends
store[:crop_images] = object.current_account.user.setting_crop_images
store[:show_follow_button_on_timeline] = object.current_account.user.setting_show_follow_button_on_timeline
......
......@@ -7,6 +7,7 @@ class REST::PreferencesSerializer < ActiveModel::Serializer
attribute :reading_default_sensitive_media, key: 'reading:expand:media'
attribute :reading_default_sensitive_text, key: 'reading:expand:spoilers'
attribute :reading_strip_formatting, key: 'reading:formatting:strip'
def posting_default_privacy
object.user.setting_default_privacy
......@@ -27,4 +28,8 @@ class REST::PreferencesSerializer < ActiveModel::Serializer
def reading_default_sensitive_text
object.user.setting_expand_spoilers
end
def reading_strip_formatting
object.user.setting_strip_formatting
end
end
......@@ -61,6 +61,9 @@
.fields-group
= f.input :setting_show_bookmark_button, as: :boolean, wrapper: :with_label
.fields-group
= f.input :setting_strip_formatting, collection: ['none', 'blocks', 'all'], wrapper: :with_floating_label, include_blank: false, label_method: lambda { |value| safe_join([I18n.t("statuses.strip_formatting.#{value}"), content_tag(:span, I18n.t("statuses.strip_formatting.#{value}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', hint: false
-# .fields-group
-# = f.input :setting_show_target, as: :boolean, wrapper: :with_label
......
......@@ -15,7 +15,7 @@
= account_action_button(status.account)
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?), class: text_formatting_classes }<
- if status.spoiler_text?
%p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
......
......@@ -11,14 +11,14 @@
= acct(status.account)
= fa_icon('lock') if status.account.locked?
.status__content.emojify<
.status__content.emojify{ class: text_formatting_classes }<
- if status.spoiler_text?
%p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
%span.p-summary> #{Formatter.instance.format_spoiler(status)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
= Formatter.instance.format_in_quote(status, custom_emojify: true)
- if !status.media_attachments.empty?
- if status.media_attachments.first.video?
- video = status.media_attachments.first
......
......@@ -21,7 +21,7 @@
%span.display-name__account
= acct(status.account)
= fa_icon('lock') if status.account.locked?
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?), class: text_formatting_classes }<
- if status.spoiler_text?
%p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
......
......@@ -1302,6 +1302,13 @@ en:
show_more: Show more
show_thread: Show thread
sign_in_to_participate: Sign in to participate in the conversation
strip_formatting:
all: All
all_long: Strip all advanced formatting
blocks: Block elements
blocks_long: Strip formatting for title headers and block quotes
none: None
none_long: Do not strip any formatting supported by Mastodon
title: '%{name}: "%{quote}"'
visibilities:
direct: Direct
......
......@@ -187,6 +187,7 @@ en:
setting_show_subscribe_button_on_timeline: Show subscribe button on timeline
setting_show_followed_by: Reflect the following status on the follow button
setting_show_target: Enable targeting features
setting_strip_formatting: Strip advanced formatting from toots
setting_system_font_ui: Use system's default font
setting_theme: Site theme
setting_trends: Show today's trends
......
......@@ -35,7 +35,6 @@ defaults: &defaults
noindex: false
theme: 'default'
aggregate_reblogs: true
advanced_layout: false
use_blurhash: true
use_pending_items: false
trends: true
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment