--- /dev/null
- prepend_str = [
+ # frozen_string_literal: true
+
+ module AccountsHelper
+ def display_name(account, **options)
+ if options[:custom_emojify]
+ Formatter.instance.format_display_name(account, options)
+ else
+ account.display_name.presence || account.username
+ end
+ end
+
+ def acct(account)
+ if account.local?
+ "@#{account.acct}@#{Rails.configuration.x.local_domain}"
+ else
+ "@#{account.acct}"
+ end
+ end
+
+ def account_action_button(account)
+ if user_signed_in?
+ if account.id == current_user.account_id
+ link_to settings_profile_url, class: 'button logo-button' do
+ safe_join([svg_logo, t('settings.edit_profile')])
+ end
+ elsif current_account.following?(account) || current_account.requested?(account)
+ link_to account_unfollow_path(account), class: 'button logo-button button--destructive', data: { method: :post } do
+ safe_join([svg_logo, t('accounts.unfollow')])
+ end
+ elsif !(account.memorial? || account.moved?)
+ link_to account_follow_path(account), class: "button logo-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post } do
+ safe_join([svg_logo, t('accounts.follow')])
+ end
+ end
+ elsif !(account.memorial? || account.moved?)
+ link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
+ safe_join([svg_logo, t('accounts.follow')])
+ end
+ end
+ end
+
+ def minimal_account_action_button(account)
+ if user_signed_in?
+ return if account.id == current_user.account_id
+
+ if current_account.following?(account) || current_account.requested?(account)
+ link_to account_unfollow_path(account), class: 'icon-button active', data: { method: :post }, title: t('accounts.unfollow') do
+ fa_icon('user-times fw')
+ end
+ elsif !(account.memorial? || account.moved?)
+ link_to account_follow_path(account), class: "icon-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post }, title: t('accounts.follow') do
+ fa_icon('user-plus fw')
+ end
+ end
+ elsif !(account.memorial? || account.moved?)
+ link_to account_remote_follow_path(account), class: 'icon-button modal-button', target: '_new', title: t('accounts.follow') do
+ fa_icon('user-plus fw')
+ end
+ end
+ end
+
+ def account_badge(account, all: false)
+ if account.bot?
+ content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
+ elsif (Setting.show_staff_badge && account.user_staff?) || all
+ content_tag(:div, class: 'roles') do
+ if all && !account.user_staff?
+ content_tag(:div, t('admin.accounts.roles.user'), class: 'account-role')
+ elsif account.user_admin?
+ content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
+ elsif account.user_moderator?
+ content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
+ end
+ end
+ end
+ end
+
++ def hide_followers_count?(account)
++ Setting.hide_followers_count || account.user&.setting_hide_followers_count
++ end
++
+ def account_description(account)
- [
++ prepend_stats = [
+ [
+ number_to_human(account.statuses_count, strip_insignificant_zeros: true),
+ I18n.t('accounts.posts', count: account.statuses_count),
+ ].join(' '),
+
+ [
+ number_to_human(account.following_count, strip_insignificant_zeros: true),
+ I18n.t('accounts.following', count: account.following_count),
+ ].join(' '),
++ ]
+
- ].join(' '),
- ].join(', ')
++ unless hide_followers_count?(account)
++ prepend_stats << [
+ number_to_human(account.followers_count, strip_insignificant_zeros: true),
+ I18n.t('accounts.followers', count: account.followers_count),
- [prepend_str, account.note].join(' · ')
++ ].join(' ')
++ end
+
++ [prepend_stats.join(', '), account.note].join(' · ')
+ end
+
+ def svg_logo
+ content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976')
+ end
+
+ def svg_logo_full
+ content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo-full'), 'viewBox' => '0 0 713.35878 175.8678')
+ end
+ end
Extractor.remove_overlapping_entities(special + standard)
end
+ def html_friendly_extractor(html, options = {})
+ gaps = []
+ total_offset = 0
+
+ escaped = html.gsub(/<[^>]*>|&#[0-9]+;/) do |match|
+ total_offset += match.length - 1
+ end_offset = Regexp.last_match.end(0)
+ gaps << [end_offset - total_offset, total_offset]
+ "\u200b"
+ end
+
+ entities = Extractor.extract_hashtags_with_indices(escaped, :check_url_overlap => false) +
+ Extractor.extract_mentions_or_lists_with_indices(escaped)
+ Extractor.remove_overlapping_entities(entities).map do |extract|
+ pos = extract[:indices].first
+ offset_idx = gaps.rindex { |gap| gap.first <= pos }
+ offset = offset_idx.nil? ? 0 : gaps[offset_idx].last
+ next extract.merge(
+ :indices => [extract[:indices].first + offset, extract[:indices].last + offset]
+ )
+ end
+ end
+
def link_to_url(entity, options = {})
url = Addressable::URI.parse(entity[:url])
- html_attrs = { target: '_blank', rel: 'nofollow noopener' }
+ html_attrs = { target: '_blank', rel: 'nofollow noopener noreferrer' }
html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
add_attributes: {
'a' => {
- 'rel' => 'nofollow noopener tag',
- 'rel' => 'nofollow noopener noreferrer',
++ 'rel' => 'nofollow noopener tag noreferrer',
'target' => '_blank',
},
},
has_many :session_activations, dependent: :destroy
- delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
- :reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
+ delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
+ :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_network, :hide_followers_count,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
- :advanced_layout, :use_blurhash, :use_pending_items, :trends,
+ :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
+ :default_content_type, :system_emoji_font,
to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:is_staff] = object.current_account.user.staff?
store[:trends] = Setting.trends && object.current_account.user.setting_trends
+ store[:default_content_type] = object.current_account.user.setting_default_content_type
+ store[:system_emoji_font] = object.current_account.user.setting_system_emoji_font
+ store[:crop_images] = object.current_account.user.setting_crop_images
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media
= f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
+ = f.input :setting_system_emoji_font, as: :boolean, wrapper: :with_label
+ %h4= t 'appearance.toot_layout'
+
+ .fields-group
+ = f.input :setting_crop_images, as: :boolean, wrapper: :with_label
+
%h4= t 'appearance.discovery'
.fields-group
setting_aggregate_reblogs: Group boosts in timelines
setting_auto_play_gif: Auto-play animated GIFs
setting_boost_modal: Show confirmation dialog before boosting
+ setting_crop_images: Crop images in non-expanded toots to 16x9
+ setting_default_content_type: Default format for toots
+ setting_default_content_type_html: HTML
+ setting_default_content_type_markdown: Markdown
+ setting_default_content_type_plain: Plain text
setting_default_language: Posting language
setting_default_privacy: Posting privacy
setting_default_sensitive: Always mark media as sensitive
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.5.4",
- "@clusterws/cws": "^0.15.0",
+ "@clusterws/cws": "^0.15.2",
"array-includes": "^3.0.3",
+ "atrament": "^0.2.3",
"autoprefixer": "^9.6.1",
"axios": "^0.19.0",
"babel-loader": "^8.0.6",