diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 80cff6982..d80c050b8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -113,6 +113,7 @@ module ApplicationHelper end def material_symbol(icon, attributes = {}) + whitespace = attributes.delete(:whitespace) { true } safe_join( [ inline_svg_tag( @@ -121,7 +122,7 @@ module ApplicationHelper role: :img, data: attributes[:data] ), - ' ', + whitespace ? ' ' : '', ] ) end diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index 68e9b1304..84dea96fa 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -46,6 +46,14 @@ module StatusesHelper status.preloadable_poll.options.map { |o| "[ ] #{o}" }.join("\n") end + def status_classnames(status, is_quote) + if is_quote + 'status--is-quote' + elsif status.quote.present? + 'status--has-quote' + end + end + def status_description(status) components = [[media_summary(status), status_text_summary(status)].compact_blank.join(' · ')] diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index b76d7ef84..cf0e18675 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1955,60 +1955,77 @@ a.sparkline { box-sizing: border-box; min-height: 100%; + &.status--has-quote { + .quote-inline { + display: none; + } + } + + .status__quote & { + // Remove the border from the .status__card within .status__quote + border: none; + + .display-name__account { + line-height: inherit; + } + + .status__avatar, + .status__avatar .account__avatar { + width: 32px; + height: 32px; + } + } + .status__prepend { padding: 0 0 15px; gap: 4px; align-items: center; } - .status__content { - padding-top: 0; + > details { + summary { + display: block; + box-sizing: border-box; + background: var(--nested-card-background); + color: var(--nested-card-text); + border: var(--nested-card-border); + border-radius: 8px; + padding: 8px 13px; + position: relative; + font-size: 15px; + line-height: 22px; + cursor: pointer; - > details { - summary { + &::after { + content: attr(data-show, 'Show more'); + margin-top: 8px; display: block; - box-sizing: border-box; - background: var(--nested-card-background); - color: var(--nested-card-text); - border: var(--nested-card-border); - border-radius: 8px; - padding: 8px 13px; - position: relative; font-size: 15px; - line-height: 22px; + line-height: 20px; + color: $highlight-text-color; cursor: pointer; - - &::after { - content: attr(data-show, 'Show more'); - margin-top: 8px; - display: block; - font-size: 15px; - line-height: 20px; - color: $highlight-text-color; - cursor: pointer; - border: 0; - background: transparent; - padding: 0; - text-decoration: none; - font-weight: 500; - } - - &:hover, - &:focus-visible { - &::after { - text-decoration: underline !important; - } - } + border: 0; + background: transparent; + padding: 0; + text-decoration: none; + font-weight: 500; } - &[open] summary { - margin-bottom: 16px; - + &:hover, + &:focus-visible { &::after { - content: attr(data-hide, 'Hide post'); + text-decoration: underline !important; } } } + + &[open] summary { + margin-bottom: 16px; + + &::after { + content: attr(data-hide, 'Hide post'); + } + } } .preview-card { @@ -2065,6 +2082,14 @@ a.sparkline { } } } + + .detailed-status__meta { + .detailed-status__application, + .detailed-status__datetime, + .detailed-status__link { + color: inherit; + } + } } .admin { diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 3489d22e5..e82f2b551 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -356,7 +356,7 @@ a.table-action-link { // Reset the status card to not have borders, background or padding when // inline in the table of statuses - .status__card { + .batch-table__row__content > .status__card { border: none; background: none; padding: 0; diff --git a/app/views/admin/shared/_status.html.haml b/app/views/admin/shared/_status.html.haml index c042fd7a2..9b5880ab4 100644 --- a/app/views/admin/shared/_status.html.haml +++ b/app/views/admin/shared/_status.html.haml @@ -1,6 +1,5 @@ --# locals: (status:) - -.status__card>< +-# locals: (status:, is_quote: false) +.status__card{ class: status_classnames(status, is_quote) } - if status.reblog? .status__prepend = material_symbol('repeat') @@ -10,31 +9,48 @@ = material_symbol('reply') = t('admin.statuses.replied_to_html', acct_link: admin_account_inline_link_to(status.in_reply_to_account, path: status.thread.present? ? admin_account_status_path(status.thread.account_id, status.in_reply_to_id) : nil)) - = render partial: 'admin/shared/status_content', locals: { status: status.proper } + - if is_quote + .status__info + = conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status), class: 'status__relative-time' do + %span.status__visibility-icon{ title: t("statuses.visibilities.#{status.visibility}") }>< + = material_symbol(visibility_icon(status), whitespace: false) + %time.relative-formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }><= l(status.created_at) + = link_to admin_account_path(status.account.id), class: 'status__display-name' do + .status__avatar + .account__avatar + = image_tag status.account.avatar.url(:original), alt: '', width: 46, height: 46, class: 'avatar' + .display-name + %bdi + %strong.display-name__html.emojify.p-name= display_name(status.account, custom_emojify: true) + %span.display-name__account + = acct(status.account) - .detailed-status__meta - - if status.application - = status.application.name - · - = conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status), class: 'detailed-status__datetime' do - %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }><= l(status.created_at) - - if status.edited? -  · - = conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status, { anchor: 'history' }), class: 'detailed-status__datetime' do - %span><= t('statuses.edited_at_html', date: content_tag(:time, l(status.edited_at), datetime: status.edited_at.iso8601, title: l(status.edited_at), class: 'relative-formatted')) - - if status.discarded? -  · - %span.negative-hint= t('admin.statuses.deleted') - - unless status.reblog? -  · - %span< - = material_symbol(visibility_icon(status)) - = t("statuses.visibilities.#{status.visibility}") - - if status.proper.sensitive? -  · - = material_symbol('visibility_off') - = t('stream_entries.sensitive_content') - - unless status.direct_visibility? -  · - = link_to ActivityPub::TagManager.instance.url_for(status.proper), class: 'detailed-status__link', target: 'blank', rel: 'noopener' do - = t('admin.statuses.view_publicly') + = render partial: 'admin/shared/status_content', locals: { status: status.proper, is_quote: is_quote } + + - unless is_quote + .detailed-status__meta + = conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status), class: 'detailed-status__datetime' do + %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }><= l(status.created_at) + - if status.edited? +  · + = conditional_link_to can?(:show, status), admin_account_status_path(status.account.id, status, { anchor: 'history' }), class: 'detailed-status__datetime' do + %span><= t('statuses.edited_at_html', date: content_tag(:time, l(status.edited_at), datetime: status.edited_at.iso8601, title: l(status.edited_at), class: 'relative-formatted')) + - if status.discarded? +  · + %span.negative-hint= t('admin.statuses.deleted') + - if status.application +  · + = status.application.name + - unless status.reblog? +  · + %span< + = material_symbol(visibility_icon(status)) + = t("statuses.visibilities.#{status.visibility}") + - if status.proper.sensitive? +  · + = material_symbol('visibility_off') + = t('stream_entries.sensitive_content') + - unless status.direct_visibility? +  · + = link_to ActivityPub::TagManager.instance.url_for(status.proper), class: 'detailed-status__link', target: 'blank', rel: 'noopener' do + = t('admin.statuses.view_publicly') diff --git a/app/views/admin/shared/_status_attachments.html.haml b/app/views/admin/shared/_status_attachments.html.haml index 8fca4add5..c627027bb 100644 --- a/app/views/admin/shared/_status_attachments.html.haml +++ b/app/views/admin/shared/_status_attachments.html.haml @@ -1,3 +1,4 @@ +-# locals: (status:, is_quote: false) - if status.with_poll? .poll %ul @@ -23,3 +24,24 @@ = render_audio_component(status) - else = render_media_gallery_component(status, visible: false) + +- if status.quote + - if status.quote.accepted? && status.quote.quoted_status.present? + - if is_quote + .status__quote-author-button + %span= t('statuses.quote_post_author', acct: acct(status.account)) + - else + .status__quote + = render partial: 'admin/shared/status', object: status.quote.quoted_status, locals: { is_quote: true } + - else + .status__quote.status__quote--error + - if status.quote.pending? + %span= t('statuses.quote_error.pending_approval') + - elsif status.quote.revoked? + %span= t('statuses.quote_error.revoked') + - else + %span= t('statuses.quote_error.not_available') + + - if status.quote.quoted_status.present? && can?(:show, status.quote.quoted_status) + = link_to admin_account_status_path(status.quote.quoted_status.account.id, status.quote.quoted_status), class: 'link-button' do + = t('admin.statuses.view_quoted_post') diff --git a/app/views/admin/shared/_status_content.html.haml b/app/views/admin/shared/_status_content.html.haml index 465696fe5..53e79b152 100644 --- a/app/views/admin/shared/_status_content.html.haml +++ b/app/views/admin/shared/_status_content.html.haml @@ -1,16 +1,18 @@ -.status__content>< - - if status.spoiler_text.present? - %details< - %summary{ - data: { - show: t('statuses.content_warnings.show'), - hide: t('statuses.content_warnings.hide'), - } - }>< - %strong> - = prerender_custom_emojis(h(status.spoiler_text), status.emojis) +-# locals: (status:, is_quote: false) +- if status.spoiler_text.present? + %details< + %summary{ + data: { + show: t('statuses.content_warnings.show'), + hide: t('statuses.content_warnings.hide'), + } + }>< + %strong> + = prerender_custom_emojis(h(status.spoiler_text), status.emojis) + .status__content>< = prerender_custom_emojis(status_content_format(status), status.emojis) - = render partial: 'admin/shared/status_attachments', locals: { status: status.proper } - - else + = render partial: 'admin/shared/status_attachments', locals: { status: status.proper, is_quote: is_quote } +- else + .status__content>< = prerender_custom_emojis(status_content_format(status), status.emojis) - = render partial: 'admin/shared/status_attachments', locals: { status: status.proper } + = render partial: 'admin/shared/status_attachments', locals: { status: status.proper, is_quote: is_quote } diff --git a/app/views/admin/statuses/show.html.haml b/app/views/admin/statuses/show.html.haml index ba5ba8198..27af69fb5 100644 --- a/app/views/admin/statuses/show.html.haml +++ b/app/views/admin/statuses/show.html.haml @@ -45,6 +45,9 @@ %tr %th= t('admin.statuses.reblogs') %td= friendly_number_to_human @status.reblogs_count + %tr + %th= t('admin.statuses.quotes') + %td= friendly_number_to_human @status.quotes_count %tr %th= t('admin.statuses.favourites') %td= friendly_number_to_human @status.favourites_count diff --git a/app/views/auth/registrations/_session.html.haml b/app/views/auth/registrations/_session.html.haml index 92e514759..b347d21e4 100644 --- a/app/views/auth/registrations/_session.html.haml +++ b/app/views/auth/registrations/_session.html.haml @@ -1,7 +1,7 @@ %tr %td %span{ title: session.user_agent }< - = material_symbol session_device_icon(session), 'aria-label': session_device_icon(session) + = material_symbol session_device_icon(session)   = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}", default: session.browser.to_s), diff --git a/app/views/settings/verifications/show.html.haml b/app/views/settings/verifications/show.html.haml index 0243e3b80..ac8778a7e 100644 --- a/app/views/settings/verifications/show.html.haml +++ b/app/views/settings/verifications/show.html.haml @@ -28,7 +28,7 @@ - @verified_links.each do |field| %li %span.verified-badge - = material_symbol 'check', class: 'verified-badge__mark' + = material_symbol 'check', { class: 'verified-badge__mark' } %span= field.value = simple_form_for @account, url: settings_verification_path, html: { class: 'form-section' } do |f| diff --git a/config/locales/en.yml b/config/locales/en.yml index e21698b58..538f2d65d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -905,6 +905,7 @@ en: no_status_selected: No posts were changed as none were selected open: Open post original_status: Original post + quotes: Quotes reblogs: Reblogs replied_to_html: Replied to %{acct_link} status_changed: Post changed @@ -912,6 +913,7 @@ en: title: Account posts - @%{name} trending: Trending view_publicly: View publicly + view_quoted_post: View quoted post visibility: Visibility with_media: With media strikes: @@ -1925,10 +1927,15 @@ en: limit: You have already pinned the maximum number of posts ownership: Someone else's post cannot be pinned reblog: A boost cannot be pinned + quote_error: + not_available: Post unavailable + pending_approval: Post pending + revoked: Post removed by author quote_policies: followers: Followers only nobody: Just me public: Anyone + quote_post_author: Quoted a post by %{acct} title: '%{name}: "%{quote}"' visibilities: direct: Private mention