Change public accounts pages to mount the web UI (#19319)
* Change public accounts pages to mount the web UI * Fix handling of remote usernames in routes - When logged in, serve web app - When logged out, redirect to permalink - Fix `app-body` class not being set sometimes due to name conflict * Fix missing `multiColumn` prop * Fix failing test * Use `discoverable` attribute to control indexing directives * Fix `<ColumnLoading />` not using `multiColumn` * Add `noindex` to accounts in REST API * Change noindex directive to not be rendered by default before a route is mounted * Add loading indicator for detailed status in web UI * Fix missing indicator appearing while account is loading in web UI
This commit is contained in:
parent
b0e3f0312c
commit
839f893168
101 changed files with 393 additions and 2468 deletions
|
@ -1,4 +1,7 @@
|
|||
- content_for :page_title do
|
||||
= t('about.title')
|
||||
|
||||
- content_for :header_tags do
|
||||
= render partial: 'shared/og'
|
||||
|
||||
= render partial: 'shared/web_app'
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
- fields = account.fields
|
||||
|
||||
.public-account-bio
|
||||
- unless fields.empty?
|
||||
.account__header__fields
|
||||
- fields.each do |field|
|
||||
%dl
|
||||
%dt.emojify{ title: field.name }= prerender_custom_emojis(h(field.name), account.emojis)
|
||||
%dd{ title: field.value, class: custom_field_classes(field) }
|
||||
- if field.verified?
|
||||
%span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
|
||||
= fa_icon 'check'
|
||||
= prerender_custom_emojis(account_field_value_format(field), account.emojis)
|
||||
|
||||
= account_badge(account)
|
||||
|
||||
- if account.note.present?
|
||||
.account__header__content.emojify= prerender_custom_emojis(account_bio_format(account), account.emojis)
|
||||
|
||||
.public-account-bio__extra
|
||||
= t 'accounts.joined', date: l(account.created_at, format: :month)
|
|
@ -1,43 +0,0 @@
|
|||
.public-account-header{:class => ("inactive" if account.moved?)}
|
||||
.public-account-header__image
|
||||
= image_tag (prefers_autoplay? ? account.header_original_url : account.header_static_url), class: 'parallax'
|
||||
.public-account-header__bar
|
||||
= link_to short_account_url(account), class: 'avatar' do
|
||||
= image_tag (prefers_autoplay? ? account.avatar_original_url : account.avatar_static_url), id: 'profile_page_avatar', data: { original: full_asset_url(account.avatar_original_url), static: full_asset_url(account.avatar_static_url), autoplay: prefers_autoplay? }
|
||||
.public-account-header__tabs
|
||||
.public-account-header__tabs__name
|
||||
%h1
|
||||
= display_name(account, custom_emojify: true)
|
||||
%small
|
||||
= acct(account)
|
||||
= fa_icon('lock') if account.locked?
|
||||
.public-account-header__tabs__tabs
|
||||
.details-counters
|
||||
.counter{ class: active_nav_class(short_account_url(account), short_account_with_replies_url(account), short_account_media_url(account)) }
|
||||
= link_to short_account_url(account), class: 'u-url u-uid', title: number_with_delimiter(account.statuses_count) do
|
||||
%span.counter-number= friendly_number_to_human account.statuses_count
|
||||
%span.counter-label= t('accounts.posts', count: account.statuses_count)
|
||||
|
||||
.counter{ class: active_nav_class(account_following_index_url(account)) }
|
||||
= link_to account_following_index_url(account), title: number_with_delimiter(account.following_count) do
|
||||
%span.counter-number= friendly_number_to_human account.following_count
|
||||
%span.counter-label= t('accounts.following', count: account.following_count)
|
||||
|
||||
.counter{ class: active_nav_class(account_followers_url(account)) }
|
||||
= link_to account_followers_url(account), title: number_with_delimiter(account.followers_count) do
|
||||
%span.counter-number= friendly_number_to_human account.followers_count
|
||||
%span.counter-label= t('accounts.followers', count: account.followers_count)
|
||||
.spacer
|
||||
.public-account-header__tabs__tabs__buttons
|
||||
= account_action_button(account)
|
||||
|
||||
.public-account-header__extra
|
||||
= render 'accounts/bio', account: account
|
||||
|
||||
.public-account-header__extra__links
|
||||
= link_to account_following_index_url(account) do
|
||||
%strong= friendly_number_to_human account.following_count
|
||||
= t('accounts.following', count: account.following_count)
|
||||
= link_to account_followers_url(account) do
|
||||
%strong= friendly_number_to_human account.followers_count
|
||||
= t('accounts.followers', count: account.followers_count)
|
|
@ -1,20 +0,0 @@
|
|||
- moved_to_account = account.moved_to_account
|
||||
|
||||
.moved-account-widget
|
||||
.moved-account-widget__message
|
||||
= fa_icon 'suitcase'
|
||||
= t('accounts.moved_html', name: content_tag(:bdi, content_tag(:strong, display_name(account, custom_emojify: true), class: :emojify)), new_profile_link: link_to(content_tag(:strong, safe_join(['@', content_tag(:span, moved_to_account.pretty_acct)])), ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'mention'))
|
||||
|
||||
.moved-account-widget__card
|
||||
= link_to ActivityPub::TagManager.instance.url_for(moved_to_account), class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'me noopener noreferrer' do
|
||||
.detailed-status__display-avatar
|
||||
.account__avatar-overlay
|
||||
.account__avatar-overlay-base
|
||||
= image_tag moved_to_account.avatar_static_url
|
||||
.account__avatar-overlay-overlay
|
||||
= image_tag account.avatar_static_url
|
||||
|
||||
%span.display-name
|
||||
%bdi
|
||||
%strong.emojify= display_name(moved_to_account, custom_emojify: true)
|
||||
%span @#{moved_to_account.pretty_acct}
|
|
@ -2,85 +2,13 @@
|
|||
= "#{display_name(@account)} (#{acct(@account)})"
|
||||
|
||||
- content_for :header_tags do
|
||||
- if @account.user&.setting_noindex
|
||||
- if @account.user_prefers_noindex?
|
||||
%meta{ name: 'robots', content: 'noindex, noarchive' }/
|
||||
|
||||
%link{ rel: 'alternate', type: 'application/rss+xml', href: @rss_url }/
|
||||
%link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@account) }/
|
||||
|
||||
- if @older_url
|
||||
%link{ rel: 'next', href: @older_url }/
|
||||
- if @newer_url
|
||||
%link{ rel: 'prev', href: @newer_url }/
|
||||
|
||||
= opengraph 'og:type', 'profile'
|
||||
= render 'og', account: @account, url: short_account_url(@account, only_path: false)
|
||||
|
||||
|
||||
= render 'header', account: @account, with_bio: true
|
||||
|
||||
.grid
|
||||
.column-0
|
||||
.h-feed
|
||||
%data.p-name{ value: "#{@account.username} on #{site_hostname}" }/
|
||||
|
||||
.account__section-headline
|
||||
= active_link_to t('accounts.posts_tab_heading'), short_account_url(@account)
|
||||
= active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
|
||||
= active_link_to t('accounts.media'), short_account_media_url(@account)
|
||||
|
||||
- if user_signed_in? && @account.blocking?(current_account)
|
||||
.nothing-here.nothing-here--under-tabs= t('accounts.unavailable')
|
||||
- elsif @statuses.empty?
|
||||
= nothing_here 'nothing-here--under-tabs'
|
||||
- else
|
||||
.activity-stream.activity-stream--under-tabs
|
||||
- if params[:page].to_i.zero?
|
||||
= render partial: 'statuses/status', collection: @pinned_statuses, as: :status, locals: { pinned: true }
|
||||
|
||||
- if @newer_url
|
||||
.entry= link_to_newer @newer_url
|
||||
|
||||
= render partial: 'statuses/status', collection: @statuses, as: :status
|
||||
|
||||
- if @older_url
|
||||
.entry= link_to_older @older_url
|
||||
|
||||
.column-1
|
||||
- if @account.memorial?
|
||||
.memoriam-widget= t('in_memoriam_html')
|
||||
- elsif @account.moved?
|
||||
= render 'moved', account: @account
|
||||
|
||||
= render 'bio', account: @account
|
||||
|
||||
- if @endorsed_accounts.empty? && @account.id == current_account&.id
|
||||
.placeholder-widget= t('accounts.endorsements_hint')
|
||||
- elsif !@endorsed_accounts.empty?
|
||||
.endorsements-widget
|
||||
%h4= t 'accounts.choices_html', name: content_tag(:bdi, display_name(@account, custom_emojify: true))
|
||||
|
||||
- @endorsed_accounts.each do |account|
|
||||
= account_link_to account
|
||||
|
||||
- if @featured_hashtags.empty? && @account.id == current_account&.id
|
||||
.placeholder-widget
|
||||
= t('accounts.featured_tags_hint')
|
||||
= link_to settings_featured_tags_path do
|
||||
= t('featured_tags.add_new')
|
||||
= fa_icon 'chevron-right fw'
|
||||
- else
|
||||
- @featured_hashtags.each do |featured_tag|
|
||||
.directory__tag{ class: params[:tag] == featured_tag.name ? 'active' : nil }
|
||||
= link_to short_account_tag_path(@account, featured_tag.tag) do
|
||||
%h4
|
||||
= fa_icon 'hashtag'
|
||||
= featured_tag.display_name
|
||||
%small
|
||||
- if featured_tag.last_status_at.nil?
|
||||
= t('accounts.nothing_here')
|
||||
- else
|
||||
%time.formatted{ datetime: featured_tag.last_status_at.iso8601, title: l(featured_tag.last_status_at) }= l featured_tag.last_status_at
|
||||
.trends__item__current= friendly_number_to_human featured_tag.statuses_count
|
||||
|
||||
= render 'application/sidebar'
|
||||
= render partial: 'shared/web_app'
|
||||
|
|
|
@ -1,20 +1,6 @@
|
|||
- content_for :page_title do
|
||||
= t('accounts.people_who_follow', name: display_name(@account))
|
||||
|
||||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
= render 'accounts/og', account: @account, url: account_followers_url(@account, only_path: false)
|
||||
|
||||
= render 'accounts/header', account: @account
|
||||
|
||||
- if @account.hide_collections?
|
||||
.nothing-here= t('accounts.network_hidden')
|
||||
- elsif user_signed_in? && @account.blocking?(current_account)
|
||||
.nothing-here= t('accounts.unavailable')
|
||||
- elsif @follows.empty?
|
||||
= nothing_here
|
||||
- else
|
||||
.card-grid
|
||||
= render partial: 'application/card', collection: @follows.map(&:account), as: :account
|
||||
|
||||
= paginate @follows
|
||||
= render 'shared/web_app'
|
||||
|
|
|
@ -1,20 +1,6 @@
|
|||
- content_for :page_title do
|
||||
= t('accounts.people_followed_by', name: display_name(@account))
|
||||
|
||||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
= render 'accounts/og', account: @account, url: account_followers_url(@account, only_path: false)
|
||||
|
||||
= render 'accounts/header', account: @account
|
||||
|
||||
- if @account.hide_collections?
|
||||
.nothing-here= t('accounts.network_hidden')
|
||||
- elsif user_signed_in? && @account.blocking?(current_account)
|
||||
.nothing-here= t('accounts.unavailable')
|
||||
- elsif @follows.empty?
|
||||
= nothing_here
|
||||
- else
|
||||
.card-grid
|
||||
= render partial: 'application/card', collection: @follows.map(&:target_account), as: :account
|
||||
|
||||
= paginate @follows
|
||||
= render 'shared/web_app'
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
- content_for :header_tags do
|
||||
- unless request.path == '/'
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
= render partial: 'shared/og'
|
||||
|
||||
= render 'shared/web_app'
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
- content_for :header_tags do
|
||||
= render_initial_state
|
||||
= javascript_pack_tag 'public', crossorigin: 'anonymous'
|
||||
|
||||
- content_for :content do
|
||||
.public-layout
|
||||
- unless @hide_navbar
|
||||
.container
|
||||
%nav.header
|
||||
.nav-left
|
||||
= link_to root_url, class: 'brand' do
|
||||
= logo_as_symbol(:wordmark)
|
||||
|
||||
- unless whitelist_mode?
|
||||
= link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
|
||||
= link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
|
||||
|
||||
.nav-center
|
||||
|
||||
.nav-right
|
||||
- if user_signed_in?
|
||||
= link_to t('settings.back'), root_url, class: 'nav-link nav-button webapp-btn'
|
||||
- else
|
||||
= link_to_login t('auth.login'), class: 'webapp-btn nav-link nav-button'
|
||||
= link_to t('auth.register'), available_sign_up_path, class: 'webapp-btn nav-link nav-button'
|
||||
|
||||
.container= yield
|
||||
|
||||
.container
|
||||
.footer
|
||||
.grid
|
||||
.column-0
|
||||
%h4= t 'footer.resources'
|
||||
%ul
|
||||
%li= link_to t('about.privacy_policy'), privacy_policy_path
|
||||
.column-1
|
||||
%h4= t 'footer.developers'
|
||||
%ul
|
||||
%li= link_to t('about.documentation'), 'https://docs.joinmastodon.org/'
|
||||
%li= link_to t('about.api'), 'https://docs.joinmastodon.org/client/intro/'
|
||||
.column-2
|
||||
%h4= link_to t('about.what_is_mastodon'), 'https://joinmastodon.org/'
|
||||
= link_to logo_as_symbol, root_url, class: 'brand'
|
||||
.column-3
|
||||
%h4= site_hostname
|
||||
%ul
|
||||
- unless whitelist_mode?
|
||||
%li= link_to t('about.about_this'), about_more_path
|
||||
%li= "v#{Mastodon::Version.to_s}"
|
||||
.column-4
|
||||
%h4= t 'footer.more'
|
||||
%ul
|
||||
%li= link_to t('about.source_code'), Mastodon::Version.source_url
|
||||
%li= link_to t('about.apps'), 'https://joinmastodon.org/apps'
|
||||
.legal-xs
|
||||
= link_to "v#{Mastodon::Version.to_s}", Mastodon::Version.source_url
|
||||
·
|
||||
= link_to t('about.privacy_policy'), privacy_policy_path
|
||||
|
||||
= render template: 'layouts/application'
|
|
@ -1,4 +1,7 @@
|
|||
- content_for :page_title do
|
||||
= t('privacy_policy.title')
|
||||
|
||||
- content_for :header_tags do
|
||||
= render partial: 'shared/og'
|
||||
|
||||
= render 'shared/web_app'
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
.form-container
|
||||
.follow-prompt
|
||||
%h2= t('remote_follow.prompt')
|
||||
|
||||
= render partial: 'application/card', locals: { account: @account }
|
||||
|
||||
= simple_form_for @remote_follow, as: :remote_follow, url: account_remote_follow_path(@account) do |f|
|
||||
= render 'shared/error_messages', object: @remote_follow
|
||||
|
||||
= f.input :acct, placeholder: t('remote_follow.acct'), input_html: { autocapitalize: 'none', autocorrect: 'off' }
|
||||
|
||||
.actions
|
||||
= f.button :button, t('remote_follow.proceed'), type: :submit
|
||||
|
||||
%p.hint.subtle-hint
|
||||
= t('remote_follow.reason_html', instance: site_hostname)
|
||||
= t('remote_follow.no_account_html', sign_up_path: available_sign_up_path)
|
|
@ -1,24 +0,0 @@
|
|||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
.form-container
|
||||
.follow-prompt
|
||||
%h2= t("remote_interaction.#{@interaction_type}.prompt")
|
||||
|
||||
.public-layout
|
||||
.activity-stream.activity-stream--highlighted
|
||||
= render 'statuses/status', status: @status
|
||||
|
||||
= simple_form_for @remote_follow, as: :remote_follow, url: remote_interaction_path(@status) do |f|
|
||||
= render 'shared/error_messages', object: @remote_follow
|
||||
|
||||
= hidden_field_tag :type, @interaction_type
|
||||
|
||||
= f.input :acct, placeholder: t('remote_follow.acct'), input_html: { autocapitalize: 'none', autocorrect: 'off' }
|
||||
|
||||
.actions
|
||||
= f.button :button, t("remote_interaction.#{@interaction_type}.proceed"), type: :submit
|
||||
|
||||
%p.hint.subtle-hint
|
||||
= t('remote_follow.reason_html', instance: site_hostname)
|
||||
= t('remote_follow.no_account_html', sign_up_path: available_sign_up_path)
|
|
@ -56,7 +56,7 @@
|
|||
- else
|
||||
= link_to status.application.name, status.application.website, class: 'detailed-status__application', target: '_blank', rel: 'noopener noreferrer'
|
||||
·
|
||||
= link_to remote_interaction_path(status, type: :reply), class: 'modal-button detailed-status__link' do
|
||||
%span.detailed-status__link
|
||||
- if status.in_reply_to_id.nil?
|
||||
= fa_icon('reply')
|
||||
- else
|
||||
|
@ -65,12 +65,12 @@
|
|||
= " "
|
||||
·
|
||||
- if status.public_visibility? || status.unlisted_visibility?
|
||||
= link_to remote_interaction_path(status, type: :reblog), class: 'modal-button detailed-status__link' do
|
||||
%span.detailed-status__link
|
||||
= fa_icon('retweet')
|
||||
%span.detailed-status__reblogs>= friendly_number_to_human status.reblogs_count
|
||||
= " "
|
||||
·
|
||||
= link_to remote_interaction_path(status, type: :favourite), class: 'modal-button detailed-status__link' do
|
||||
%span.detailed-status__link
|
||||
= fa_icon('star')
|
||||
%span.detailed-status__favorites>= friendly_number_to_human status.favourites_count
|
||||
= " "
|
||||
|
|
|
@ -53,18 +53,18 @@
|
|||
= t 'statuses.show_thread'
|
||||
|
||||
.status__action-bar
|
||||
= link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button icon-button--with-counter modal-button' do
|
||||
%span.status__action-bar-button.icon-button.icon-button--with-counter
|
||||
- if status.in_reply_to_id.nil?
|
||||
= fa_icon 'reply fw'
|
||||
- else
|
||||
= fa_icon 'reply-all fw'
|
||||
%span.icon-button__counter= obscured_counter status.replies_count
|
||||
= link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button' do
|
||||
%span.status__action-bar-button.icon-button
|
||||
- if status.distributable?
|
||||
= fa_icon 'retweet fw'
|
||||
- elsif status.private_visibility? || status.limited_visibility?
|
||||
= fa_icon 'lock fw'
|
||||
- else
|
||||
= fa_icon 'at fw'
|
||||
= link_to remote_interaction_path(status, type: :favourite), class: 'status__action-bar-button icon-button modal-button' do
|
||||
%span.status__action-bar-button.icon-button
|
||||
= fa_icon 'star fw'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
= t('statuses.title', name: display_name(@account), quote: truncate(@status.spoiler_text.presence || @status.text, length: 50, omission: '…', escape: false))
|
||||
|
||||
- content_for :header_tags do
|
||||
- if @account.user&.setting_noindex
|
||||
- if @account.user_prefers_noindex?
|
||||
%meta{ name: 'robots', content: 'noindex, noarchive' }/
|
||||
|
||||
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: short_account_status_url(@account, @status), format: 'json') }/
|
||||
|
|
5
app/views/tags/show.html.haml
Normal file
5
app/views/tags/show.html.haml
Normal file
|
@ -0,0 +1,5 @@
|
|||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
= render partial: 'shared/og'
|
||||
|
||||
= render partial: 'shared/web_app'
|
Loading…
Add table
Add a link
Reference in a new issue