From 5622de4a785e6b7c9a4946af3efaf8b4c2bc5755 Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Sun, 23 Aug 2020 09:38:27 -0500 Subject: [Feature] Support Misskey-compatible boosts with attached content notes --- app/controllers/activitypub/outboxes_controller.rb | 1 - .../flavours/glitch/components/status.js | 2 +- .../flavours/glitch/components/status_content.js | 38 ++++++++++++----- app/javascript/flavours/glitch/selectors/index.js | 5 +++ .../styles/monsterfork/components/status.scss | 24 +++++++---- app/lib/activitypub/activity/create.rb | 49 +++++++++++++++++++++- app/lib/activitypub/adapter.rb | 14 ++++--- app/lib/activitypub/case_transform.rb | 4 +- app/lib/command_tag/command/status_tools.rb | 14 +++++++ app/lib/command_tag/processor.rb | 15 +++++++ app/lib/formatter.rb | 18 ++++---- app/presenters/activitypub/activity_presenter.rb | 6 +-- app/serializers/activitypub/note_serializer.rb | 16 ++++++- app/services/process_mentions_service.rb | 4 +- app/services/reblog_service.rb | 2 +- app/services/remove_status_service.rb | 2 +- app/views/statuses/_detailed_status.html.haml | 15 ++++--- app/views/statuses/_simple_status.html.haml | 21 ++++++---- app/views/statuses/_status.html.haml | 2 +- lib/mastodon/version.rb | 2 +- 20 files changed, 193 insertions(+), 61 deletions(-) diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb index 5c4a15051..0346dcb1d 100644 --- a/app/controllers/activitypub/outboxes_controller.rb +++ b/app/controllers/activitypub/outboxes_controller.rb @@ -34,7 +34,6 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController ActivityPub::CollectionPresenter.new( id: account_outbox_url(@account), type: :ordered, - size: @statuses.count, first: account_outbox_url(@account, page: true), last: account_outbox_url(@account, page: true, min_id: 0) ) diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index a94142452..69f93a2f1 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -370,7 +370,7 @@ class Status extends ImmutablePureComponent { } handleExpandedToggle = () => { - if (this.props.status.get('spoiler_text')) { + if (this.props.status.get('spoiler_text') || this.props.status.get('reblogSpoilerHtml')) { this.setExpansion(!this.state.isExpanded); } }; diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js index 40413b07b..e0a759499 100644 --- a/app/javascript/flavours/glitch/components/status_content.js +++ b/app/javascript/flavours/glitch/components/status_content.js @@ -422,19 +422,35 @@ export default class StatusContent extends React.PureComponent { ); + const reblog_spoiler_html = status.get('reblogSpoilerPresent') && { __html: status.get('reblogSpoilerHtml') }; + const reblog_spoiler = reblog_spoiler_html && ( +
+ + +
+ ); + + const spoiler_html = status.get('spoiler_text').length > 0 && { __html: status.get('spoilerHtml') }; + const spoiler = spoiler_html && ( +
+ + +
+ ); + + const spoiler_present = status.get('spoiler_text').length > 0 || status.get('reblogSpoilerPresent'); const content = { __html: article ? status.get('articleHtml') : status.get('contentHtml') }; - const spoilerContent = { __html: status.get('spoilerHtml') }; const directionStyle = { direction: 'ltr' }; const classNames = classnames('status__content', { 'status__content--with-action': parseClick && !disabled, - 'status__content--with-spoiler': status.get('spoiler_text').length > 0, + 'status__content--with-spoiler': spoiler_present, }); if (isRtl(status.get('search_index'))) { directionStyle.direction = 'rtl'; } - if (status.get('spoiler_text').length > 0) { + if (spoiler_present) { let mentionsPlaceholder = ''; const mentionLinks = status.get('mentions').map(item => ( @@ -486,15 +502,17 @@ export default class StatusContent extends React.PureComponent { return (
{status_notice_wrapper} - + {reblog_spoiler} + {spoiler} +
+ +
+
{mentionsPlaceholder} diff --git a/app/javascript/flavours/glitch/selectors/index.js b/app/javascript/flavours/glitch/selectors/index.js index bb9180d12..3571aea3e 100644 --- a/app/javascript/flavours/glitch/selectors/index.js +++ b/app/javascript/flavours/glitch/selectors/index.js @@ -141,6 +141,11 @@ export const makeGetStatus = () => { } } + if (statusReblog) { + statusReblog = statusReblog.set('reblogSpoilerPresent', statusBase.get('spoiler_text').length > 0); + statusReblog = statusReblog.set('reblogSpoilerHtml', statusBase.get('spoilerHtml')); + } + return statusBase.withMutations(map => { map.set('reblog', statusReblog); map.set('account', accountBase); diff --git a/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss b/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss index 0486c9a80..65ac9286d 100644 --- a/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss +++ b/app/javascript/flavours/glitch/styles/monsterfork/components/status.scss @@ -142,15 +142,25 @@ div[data-nest-deep="true"] { margin-bottom: 0px; } - p:first-child, - pre:first-child, - blockquote:first-child, - div.status__notice-wrapper + p { - margin-top: 0px; + .status__content__spoiler { + .status__content__spoiler--visible { + margin-top: 1em; + } } - .status__content__spoiler.status__content__spoiler--visible { - margin-top: 1em; + .spoiler { + & > i { + padding-right: 8px; + color: lighten($dark-text-color, 4%); + } + } + + .reblog-spoiler { + font-style: italic; + + & > span { + color: lighten($ui-highlight-color, 8%); + } } } diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 69480ccf6..9ab60107b 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -85,7 +85,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @params = {} unless @status.nil? - process_status_update_params + reblog_uri.blank? ? process_status_update_params : process_reblog_update_params process_tags process_audience @@ -95,7 +95,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity return @status end - process_status_params + reblog_uri.blank? ? process_status_params : process_reblog_params process_tags process_audience @@ -128,6 +128,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity language: detected_language, spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), title: text_from_title, + reblog: reblogged_status, created_at: @object['published'], override_timestamps: @options[:override_timestamps], reply: @object['inReplyTo'].present?, @@ -155,6 +156,40 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end end + def process_reblog_params + @params = begin + { + uri: object_uri, + url: object_url || object_uri, + account: @account, + text: text_from_content || '', + language: detected_language, + spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), + title: text_from_title, + reblog: reblogged_status, + created_at: @object['published'], + override_timestamps: @options[:override_timestamps], + reply: @object['inReplyTo'].present?, + sensitive: @object['sensitive'] || false, + visibility: visibility_from_audience, + thread: replied_to_status, + } + end + end + + def process_reblog_update_params + @params = begin + { + text: text_from_content || '', + language: detected_language, + spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), + title: text_from_title, + sensitive: @object['sensitive'] || false, + visibility: visibility_from_audience, + } + end + end + def process_audience (as_array(audience_to) + as_array(audience_cc)).uniq.each do |audience| next if audience == ActivityPub::TagManager::COLLECTIONS[:public] @@ -459,6 +494,16 @@ class ActivityPub::Activity::Create < ActivityPub::Activity value_or_id(@object['inReplyTo']) end + def reblogged_status + FetchRemoteStatusService.new.call(reblog_uri) if reblog_uri.present? + end + + def reblog_uri + return @reblog_uri if defined?(@reblog_uri) + + @reblog_uri = @object['reblog'].presence || @object['_misskey_quote'].presence + end + def text_from_content return Formatter.instance.linkify([[text_from_name, text_from_summary.presence].compact.join("\n\n"), object_url || object_uri].join(' ')) if converted_object_type? diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 107b93b44..33fa47d63 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -8,15 +8,17 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base CONTEXT_EXTENSION_MAP = { direct_message: { 'litepub': 'http://litepub.social/ns#', 'directMessage': 'litepub:directMessage' }, - edited: { 'mp' => 'http://the.monsterpit.net/ns#', 'edited' => 'mp:edited' }, - require_dereference: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireDereference' => 'mp:requireDereference' }, - show_replies: { 'mp' => 'http://the.monsterpit.net/ns#', 'showReplies' => 'mp:showReplies' }, - show_unlisted: { 'mp' => 'http://the.monsterpit.net/ns#', 'showUnlisted' => 'mp:showUnlisted' }, - private: { 'mp' => 'http://the.monsterpit.net/ns#', 'private' => 'mp:private' }, - require_auth: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireAuth' => 'mp:requireAuth' }, + edited: { 'mp' => 'https://the.monsterpit.net/ns#', 'edited' => 'mp:edited' }, + require_dereference: { 'mp' => 'https://the.monsterpit.net/ns#', 'requireDereference' => 'mp:requireDereference' }, + show_replies: { 'mp' => 'https://the.monsterpit.net/ns#', 'showReplies' => 'mp:showReplies' }, + show_unlisted: { 'mp' => 'https://the.monsterpit.net/ns#', 'showUnlisted' => 'mp:showUnlisted' }, + private: { 'mp' => 'https://the.monsterpit.net/ns#', 'private' => 'mp:private' }, + require_auth: { 'mp' => 'https://the.monsterpit.net/ns#', 'requireAuth' => 'mp:requireAuth' }, metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'metadata' => { '@id' => 'mp:metadata', '@type' => '@id' } }, server_metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'serverMetadata' => { '@id' => 'mp:serverMetadata', '@type' => '@id' } }, root: { 'mp' => 'https://the.monsterpit.net/ns#', 'root' => { '@id' => 'mp:root', '@type' => '@id' } }, + reblog: { 'mp' => 'https://the.monsterpit.net/ns#', 'reblog' => { '@id' => 'mp:reblog', '@type' => '@id' }, + 'misskey' => 'https://misskey.io/ns#', '_misskey_quote' => { '@id' => 'misskey:_misskey_quote', '@type' => '@id' } }, manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' }, sensitive: { 'sensitive' => 'as:sensitive' }, hashtag: { 'Hashtag' => 'as:Hashtag' }, diff --git a/app/lib/activitypub/case_transform.rb b/app/lib/activitypub/case_transform.rb index 7f716f862..7f31fabda 100644 --- a/app/lib/activitypub/case_transform.rb +++ b/app/lib/activitypub/case_transform.rb @@ -14,8 +14,10 @@ module ActivityPub::CaseTransform when String camel_lower_cache[value] ||= if value.start_with?('_:') '_:' + value.gsub(/\A_:/, '').underscore.camelize(:lower) - else + elsif value != '_misskey_quote' value.underscore.camelize(:lower) + else + value end else value end diff --git a/app/lib/command_tag/command/status_tools.rb b/app/lib/command_tag/command/status_tools.rb index 1cdb90e4a..5cc11dde2 100644 --- a/app/lib/command_tag/command/status_tools.rb +++ b/app/lib/command_tag/command/status_tools.rb @@ -1,5 +1,19 @@ # frozen_string_literal: true module CommandTag::Command::StatusTools + def handle_boost_once_at_start(args) + return unless @parent.present? && StatusPolicy.new(@account, @parent).reblog? + + status = ReblogService.new.call( + @account, @parent, + visibility: @status.visibility, + spoiler_text: args.join(' ').presence || @status.spoiler_text + ) + end + + alias handle_reblog_at_start handle_boost_once_at_start + alias handle_rb_at_start handle_boost_once_at_start + alias handle_rt_at_start handle_boost_once_at_start + def handle_article_before_save(args) return unless author_of_status? && args.present? diff --git a/app/lib/command_tag/processor.rb b/app/lib/command_tag/processor.rb index 2c33b5f83..9edcb58ba 100644 --- a/app/lib/command_tag/processor.rb +++ b/app/lib/command_tag/processor.rb @@ -12,6 +12,12 @@ require_relative 'commands' +class CommandTag::Break < Mastodon::Error + def initialize(msg = 'A handler stopped execution.') + super + end +end + class CommandTag::Processor include Redisable include ImgProxyHelper @@ -83,6 +89,8 @@ class CommandTag::Processor execute_statements(:at_end) all_handlers!(:shutdown) + rescue CommandTag::Break + nil rescue StandardError => e @status.update(published: false) @status.destroy @@ -247,6 +255,13 @@ class CommandTag::Processor @status.destroy end + def replace_status!(new_status) + return if new_status.blank? + + destroy_status! + @status = new_status + end + def normalize(text) text.to_s.strip.downcase end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 5559ddb73..6673f4b4b 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -32,15 +32,8 @@ class Formatter include ActionView::Helpers::TextHelper def format(status, **options) - if status.reblog? - prepend_reblog = status.reblog.account.acct - status = status.proper - else - prepend_reblog = false - end - summary = nil - raw_content = status.text + raw_content = status.proper.text summary_mode = false if status.title.present? @@ -56,8 +49,13 @@ class Formatter return '' if raw_content.blank? return format_remote_content(raw_content, status.emojis, summary: summary, **options) unless status.local? - html = raw_content - html = "RT @#{prepend_reblog} #{html}" if prepend_reblog + if status.reblog? + html = "šŸ” @#{status.reblog.account.acct}\nšŸ”— #{ActivityPub::TagManager.instance.url_for(status.reblog)}" + html += "\nā„¹ļø #{status.reblog.spoiler_text}" if status.reblog.spoiler_text.present? + else + html = raw_content + end + html = "šŸ“„ #{html}" if summary_mode return html if options[:plaintext] diff --git a/app/presenters/activitypub/activity_presenter.rb b/app/presenters/activitypub/activity_presenter.rb index 4d5d28611..7a19cc96a 100644 --- a/app/presenters/activitypub/activity_presenter.rb +++ b/app/presenters/activitypub/activity_presenter.rb @@ -8,7 +8,7 @@ class ActivityPub::ActivityPresenter < ActiveModelSerializers::Model new.tap do |presenter| default_activity = update && status.edited.positive? ? 'Update' : 'Create' presenter.id = ActivityPub::TagManager.instance.activity_uri_for(status) - presenter.type = status.reblog? ? 'Announce' : default_activity + presenter.type = (status.reblog? && status.spoiler_text.blank? ? 'Announce' : default_activity) presenter.actor = ActivityPub::TagManager.instance.uri_for(status.account) presenter.published = status.created_at presenter.to = ActivityPub::TagManager.instance.to(status) @@ -20,14 +20,14 @@ class ActivityPub::ActivityPresenter < ActiveModelSerializers::Model end presenter.virtual_object = begin - if status.reblog? + if status.reblog? && status.spoiler_text.blank? if status.account == status.proper.account && status.proper.private_visibility? && status.local? status.proper else ActivityPub::TagManager.instance.uri_for(status.proper) end else - status.proper + status end end end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index acadce1b2..1ab2ed35a 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -3,7 +3,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :direct_message - context_extensions :edited, :server_metadata, :root + context_extensions :edited, :server_metadata, :root, :reblog attributes :id, :type, :summary, :in_reply_to, :published, :url, @@ -13,6 +13,8 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer attributes :updated, :root attribute :title, key: :name, if: :title_present? + attribute :reblog, if: :reblog_present? + attribute :renote, key: '_misskey_quote', if: :reblog_present? attribute :content attribute :content_map, if: :language? @@ -208,6 +210,18 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer Mastodon::Version.server_metadata_json end + def reblog + ActivityPub::TagManager.instance.uri_for(object.reblog) + end + + def renote + ActivityPub::TagManager.instance.uri_for(object.reblog) + end + + def reblog_present? + object.reblog_of_id.present? + end + class MediaAttachmentSerializer < ActivityPub::Serializer context_extensions :blurhash, :focal_point diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index 2ec21f22b..ba61e4464 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -9,8 +9,8 @@ class ProcessMentionsService < BaseService # @param [Status] status # @option [Enumerable] :mentions Mentions to include # @option [Boolean] :deliver Deliver mention notifications - def call(status, mentions: [], reveal_implicit_mentions: true, deliver: true) - return unless status.local? + def call(status, mentions: [], deliver: true) + return unless status.local? && !status.frozen? @status = status @status.text, mentions = ResolveMentionsService.new.call(@status, mentions: mentions) diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb index f4280e9b2..ddd22e379 100644 --- a/app/services/reblog_service.rb +++ b/app/services/reblog_service.rb @@ -28,7 +28,7 @@ class ReblogService < BaseService end end - reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility, rate_limit: options[:with_rate_limit]) + reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility, rate_limit: options[:with_rate_limit], sensitive: true, spoiler_text: options[:spoiler_text] || '', published: true) DistributionWorker.perform_async(reblog.id) ActivityPub::DistributionWorker.perform_async(reblog.id) unless reblogged_status.local_only? diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index 72ef6a9ec..f5983fc40 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -121,7 +121,7 @@ class RemoveStatusService < BaseService end def signed_activity_json - @signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account)) + @signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? && @status.spoiler_text.blank? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account)) end def remove_reblogs diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index d3c538368..ade03df39 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -15,10 +15,13 @@ = 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.title? || status.spoiler_text?) } - if status.title? || status.spoiler_text? - %p< - %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  + %div.spoiler + = fa_icon 'info-circle fw' + %span.p-summary= Formatter.instance.format_spoiler(status, autoplay: autoplay) + - if status.title? || status.spoiler_text? || parent_status&.spoiler_text? + %div %button.status__content__spoiler-link= t('statuses.show_more') .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' } = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay, article_content: true) @@ -29,17 +32,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive? || parent_status&.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 670, height: 380, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 380, sensitive: status.sensitive? || parent_status&.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: status.sensitive? || parent_status&.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index f76d57ae4..d826d38e6 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -21,10 +21,17 @@ %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?) }< - - if status.spoiler_text? - %p< - %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}  + .status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.title? || status.spoiler_text? || parent_status&.spoiler_text?) }< + - if parent_status&.spoiler_text? + %div.spoiler.reblog-spoiler + = fa_icon 'retweet fw' + %span.p-summary= Formatter.instance.format_spoiler(parent_status, autoplay: autoplay) + - if status.title? || status.spoiler_text? + %div.spoiler + = fa_icon 'info-circle fw' + %span.p-summary= Formatter.instance.format_spoiler(status, autoplay: autoplay) + - if status.title? || status.spoiler_text? || parent_status&.spoiler_text? + %div %button.status__content__spoiler-link= t('statuses.show_more') .e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }< = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay, article_content: true) @@ -35,17 +42,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive? || parent_status&.sensitive?, width: 610, height: 343, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 610, height: 343, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 343, sensitive: status.sensitive? || parent_status&.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: status.sensitive? || parent_status&.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 5923316b4..1c8acadf2 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -32,7 +32,7 @@ .status__prepend-icon-wrapper %i.status__prepend-icon.fa.fa-fw.fa-thumb-tack - = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay + = render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay, parent_status: status - if include_threads - if @since_descendant_thread_id diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 0cad7a273..a8ffaf9ac 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -69,7 +69,7 @@ module Mastodon '@context': { 'schema': 'http://schema.org/', name: 'schema:name', value: 'schema:value' }, type: 'PropertyValue', name: 'monsterpit:extensions', - value: '2020.08.06.1', + value: '2020.08.23.1', }, { '@context': { 'schema': 'http://schema.org/', name: 'schema:name', value: 'schema:value' }, -- cgit