Merge branch 'main' into glitch-soc/merge-upstream

This commit is contained in:
Claire 2022-03-04 01:07:49 +01:00
commit d9c3109bb6
24 changed files with 66 additions and 57 deletions

View file

@ -2,9 +2,9 @@
class Api::V1::AccountsController < Api::BaseController class Api::V1::AccountsController < Api::BaseController
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers]
before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute]
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock] before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, only: [:block, :unblock]
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create] before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create]
before_action :require_user!, except: [:show, :create] before_action :require_user!, except: [:show, :create]

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::BlocksController < Api::BaseController class Api::V1::BlocksController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' } before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }
before_action :require_user! before_action :require_user!
after_action :insert_pagination_headers after_action :insert_pagination_headers

View file

@ -3,8 +3,8 @@
class Api::V1::DomainBlocksController < Api::BaseController class Api::V1::DomainBlocksController < Api::BaseController
BLOCK_LIMIT = 100 BLOCK_LIMIT = 100
before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }, only: :show
before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, except: :show
before_action :require_user! before_action :require_user!
after_action :insert_pagination_headers, only: :show after_action :insert_pagination_headers, only: :show

View file

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::FollowRequestsController < Api::BaseController class Api::V1::FollowRequestsController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index before_action -> { doorkeeper_authorize! :follow, :read, :'read:follows' }, only: :index
before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, except: :index
before_action :require_user! before_action :require_user!
after_action :insert_pagination_headers, only: :index after_action :insert_pagination_headers, only: :index
@ -13,7 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
def authorize def authorize
AuthorizeFollowService.new.call(account, current_account) AuthorizeFollowService.new.call(account, current_account)
NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account)) LocalNotificationWorker.perform_async(current_account.id, Follow.find_by(account: account, target_account: current_account).id, 'Follow', 'follow')
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
end end

View file

@ -31,7 +31,7 @@ class Api::V1::MediaController < Api::BaseController
end end
def set_media_attachment def set_media_attachment
@media_attachment = current_account.media_attachments.unattached.find(params[:id]) @media_attachment = current_account.media_attachments.where(status_id: nil).find(params[:id])
end end
def check_processing def check_processing

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::MutesController < Api::BaseController class Api::V1::MutesController < Api::BaseController
before_action -> { doorkeeper_authorize! :follow, :'read:mutes' } before_action -> { doorkeeper_authorize! :follow, :read, :'read:mutes' }
before_action :require_user! before_action :require_user!
after_action :insert_pagination_headers after_action :insert_pagination_headers

View file

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, profile_directory, showTrends } from '../../initial_state'; import { me, showTrends } from '../../initial_state';
import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import NavigationContainer from '../compose/containers/navigation_container'; import NavigationContainer from '../compose/containers/navigation_container';
@ -35,7 +35,6 @@ const messages = defineMessages({
personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' }, personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' },
security: { id: 'navigation_bar.security', defaultMessage: 'Security' }, security: { id: 'navigation_bar.security', defaultMessage: 'Security' },
menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
profile_directory: { id: 'getting_started.directory', defaultMessage: 'Profile directory' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -104,25 +103,11 @@ class GettingStarted extends ImmutablePureComponent {
height += 34 + 48*2; height += 34 + 48*2;
if (profile_directory) {
navItems.push(
<ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
);
height += 48;
}
navItems.push( navItems.push(
<ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />, <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />,
); );
height += 34; height += 34;
} else if (profile_directory) {
navItems.push(
<ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
);
height += 48;
} }
if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) { if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) {

View file

@ -3,7 +3,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { invitesEnabled, limitedFederationMode, version, repository, source_url } from 'mastodon/initial_state'; import { invitesEnabled, limitedFederationMode, version, repository, source_url, profile_directory as profileDirectory } from 'mastodon/initial_state';
import { logOut } from 'mastodon/utils/log_out'; import { logOut } from 'mastodon/utils/log_out';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
@ -52,6 +52,7 @@ class LinkFooter extends React.PureComponent {
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>} {withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li> <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
{!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>} {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
{profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>}
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li> <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li> <li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li> <li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>

View file

@ -2,7 +2,7 @@ import React from 'react';
import { NavLink, withRouter } from 'react-router-dom'; import { NavLink, withRouter } from 'react-router-dom';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { profile_directory, showTrends } from 'mastodon/initial_state'; import { showTrends } from 'mastodon/initial_state';
import NotificationsCounterIcon from './notifications_counter_icon'; import NotificationsCounterIcon from './notifications_counter_icon';
import FollowRequestsNavLink from './follow_requests_nav_link'; import FollowRequestsNavLink from './follow_requests_nav_link';
import ListPanel from './list_panel'; import ListPanel from './list_panel';
@ -20,7 +20,6 @@ const NavigationPanel = () => (
<NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink> <NavLink className='column-link column-link--transparent' to='/favourites'><Icon className='column-link__icon' id='star' fixedWidth /><FormattedMessage id='navigation_bar.favourites' defaultMessage='Favourites' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink> <NavLink className='column-link column-link--transparent' to='/bookmarks'><Icon className='column-link__icon' id='bookmark' fixedWidth /><FormattedMessage id='navigation_bar.bookmarks' defaultMessage='Bookmarks' /></NavLink>
<NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink> <NavLink className='column-link column-link--transparent' to='/lists'><Icon className='column-link__icon' id='list-ul' fixedWidth /><FormattedMessage id='navigation_bar.lists' defaultMessage='Lists' /></NavLink>
{profile_directory && <NavLink className='column-link column-link--transparent' to='/directory'><Icon className='column-link__icon' id='address-book-o' fixedWidth /><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></NavLink>}
<ListPanel /> <ListPanel />

View file

@ -35,7 +35,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
def distribute def distribute
# Notify the author of the original status if that status is local # Notify the author of the original status if that status is local
NotifyService.new.call(@status.reblog.account, :reblog, @status) if reblog_of_local_account?(@status) && !reblog_by_following_group_account?(@status) LocalNotificationWorker.perform_async(@status.reblog.account_id, @status.id, 'Status', 'reblog') if reblog_of_local_account?(@status) && !reblog_by_following_group_account?(@status)
# Distribute into home and list feeds # Distribute into home and list feeds
::DistributionWorker.perform_async(@status.id) if @options[:override_timestamps] || @status.within_realtime_window? ::DistributionWorker.perform_async(@status.id) if @options[:override_timestamps] || @status.within_realtime_window?

View file

@ -31,10 +31,10 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity
follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id'])
if target_account.locked? || @account.silenced? if target_account.locked? || @account.silenced?
NotifyService.new.call(target_account, :follow_request, follow_request) LocalNotificationWorker.perform_async(target_account.id, follow_request.id, 'FollowRequest', 'follow_request')
else else
AuthorizeFollowService.new.call(@account, target_account) AuthorizeFollowService.new.call(@account, target_account)
NotifyService.new.call(target_account, :follow, ::Follow.find_by(account: @account, target_account: target_account)) LocalNotificationWorker.perform_async(target_account.id, ::Follow.find_by(account: @account, target_account: target_account).id, 'Follow', 'follow')
end end
end end

View file

@ -8,7 +8,7 @@ class ActivityPub::Activity::Like < ActivityPub::Activity
favourite = original_status.favourites.create!(account: @account) favourite = original_status.favourites.create!(account: @account)
NotifyService.new.call(original_status.account, :favourite, favourite) LocalNotificationWorker.perform_async(original_status.account_id, favourite.id, 'Favourite', 'favourite')
Trends.statuses.register(original_status) Trends.statuses.register(original_status)
end end
end end

View file

@ -26,11 +26,6 @@ class ActivityPub::Activity::Update < ActivityPub::Activity
return if @status.nil? return if @status.nil?
forwarder.forward! if forwarder.forwardable?
ActivityPub::ProcessStatusUpdateService.new.call(@status, @object) ActivityPub::ProcessStatusUpdateService.new.call(@status, @object)
end end
def forwarder
@forwarder ||= ActivityPub::Forwarder.new(@account, @json, @status)
end
end end

View file

@ -90,6 +90,8 @@ class Formatter
end end
def simplified_format(account, **options) def simplified_format(account, **options)
return '' if account.note.blank?
html = account.local? ? linkify(account.note) : reformat(account.note) html = account.local? ? linkify(account.note) : reformat(account.note)
html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify] html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
html.html_safe # rubocop:disable Rails/OutputSafety html.html_safe # rubocop:disable Rails/OutputSafety

View file

@ -23,6 +23,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord
include Redisable include Redisable
ALLOWED_MIN_STATUS_AGE = [ ALLOWED_MIN_STATUS_AGE = [
1.week.seconds,
2.weeks.seconds, 2.weeks.seconds,
1.month.seconds, 1.month.seconds,
2.months.seconds, 2.months.seconds,

View file

@ -18,7 +18,7 @@ class BootstrapTimelineService < BaseService
def notify_staff! def notify_staff!
User.staff.includes(:account).find_each do |user| User.staff.includes(:account).find_each do |user|
NotifyService.new.call(user.account, :'admin.sign_up', @source_account) LocalNotificationWorker.perform_async(user.account_id, @source_account.id, 'Account', 'admin.sign_up')
end end
end end
end end

View file

@ -31,7 +31,7 @@ class FavouriteService < BaseService
status = favourite.status status = favourite.status
if status.account.local? if status.account.local?
NotifyService.new.call(status.account, :favourite, favourite) LocalNotificationWorker.perform_async(status.account_id, favourite.id, 'Favourite', 'favourite')
elsif status.account.activitypub? elsif status.account.activitypub?
ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url) ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url)
end end

View file

@ -0,0 +1,10 @@
.simple_form
%p.lead= t('about.logged_in_as_html', username: content_tag(:strong, current_account.username))
.actions
= link_to t('about.continue_to_web'), root_url, class: 'button button-primary'
.form-footer
%ul.no-list
%li= link_to t('about.get_apps'), 'https://joinmastodon.org/apps', target: '_blank', rel: 'noopener noreferrer'
%li= link_to t('auth.logout'), destroy_user_session_path, data: { method: :delete }

View file

@ -1,17 +1,20 @@
.simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' } - disabled = closed_registrations? || omniauth_only? || current_account.present?
- show_message = disabled && (current_user.present? || @instance_presenter.closed_registrations_message.present?)
.simple_form__overlay-area{ class: show_message ? 'simple_form__overlay-area__blurred' : '' }
= simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f| = simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f|
%p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname))
.fields-group .fields-group
= f.simple_fields_for :account do |account_fields| = f.simple_fields_for :account do |account_fields|
= account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: disabled
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: disabled
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations? = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: disabled
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: disabled
= f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? = f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: disabled
= f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? = f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: disabled
- if approved_registrations? - if approved_registrations?
.fields-group .fields-group
@ -19,13 +22,16 @@
= invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text
.fields-group .fields-group
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: disabled
.actions .actions
= f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: disabled
- if closed_registrations? && @instance_presenter.closed_registrations_message.present? - if show_message
.simple_form__overlay-area__overlay .simple_form__overlay-area__overlay
.simple_form__overlay-area__overlay__content.rich-formatting .simple_form__overlay-area__overlay__content.rich-formatting
.block-icon= fa_icon 'warning' .block-icon= fa_icon 'warning'
= @instance_presenter.closed_registrations_message.html_safe - if current_account.present?
= t('about.logout_before_registering')
- else
= @instance_presenter.closed_registrations_message.html_safe

View file

@ -46,7 +46,10 @@
.landing__grid__column.landing__grid__column-login .landing__grid__column.landing__grid__column-login
.box-widget .box-widget
= render 'login' - if current_user.present?
= render 'logged_in'
- else
= render 'login'
.hero-widget .hero-widget
.hero-widget__img .hero-widget__img

View file

@ -74,7 +74,7 @@ class FeedInsertWorker
end end
def perform_notify def perform_notify
NotifyService.new.call(@follower, :status, @status) LocalNotificationWorker.perform_async(@follower.id, @status.id, 'Status', 'status')
end end
def update? def update?

View file

@ -38,12 +38,14 @@ class PollExpirationNotifyWorker
def notify_remote_voters_and_owner! def notify_remote_voters_and_owner!
ActivityPub::DistributePollUpdateWorker.perform_async(@poll.status.id) ActivityPub::DistributePollUpdateWorker.perform_async(@poll.status.id)
NotifyService.new.call(@poll.account, :poll, @poll) LocalNotificationWorker.perform_async(@poll.account_id, @poll.id, 'Poll', 'poll')
end end
def notify_local_voters! def notify_local_voters!
@poll.voters.merge(Account.local).find_each do |account| @poll.voters.merge(Account.local).select(:id).find_in_batches do |accounts|
NotifyService.new.call(account, :poll, @poll) LocalNotificationWorker.push_bulk(accounts) do |account|
[account.id, @poll.id, 'Poll', 'poll']
end
end end
end end
end end

View file

@ -16,6 +16,7 @@ en:
contact: Contact contact: Contact
contact_missing: Not set contact_missing: Not set
contact_unavailable: N/A contact_unavailable: N/A
continue_to_web: Continue to web app
discover_users: Discover users discover_users: Discover users
documentation: Documentation documentation: Documentation
federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond. federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond.
@ -25,6 +26,8 @@ en:
This account is a virtual actor used to represent the server itself and not any individual user. This account is a virtual actor used to represent the server itself and not any individual user.
It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block. It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block.
learn_more: Learn more learn_more: Learn more
logged_in_as_html: You are currently logged in as %{username}.
logout_before_registering: You are already logged in.
privacy_policy: Privacy policy privacy_policy: Privacy policy
rules: Server rules rules: Server rules
rules_html: 'Below is a summary of rules you need to follow if you want to have an account on this server of Mastodon:' rules_html: 'Below is a summary of rules you need to follow if you want to have an account on this server of Mastodon:'
@ -1484,6 +1487,7 @@ en:
'2629746': 1 month '2629746': 1 month
'31556952': 1 year '31556952': 1 year
'5259492': 2 months '5259492': 2 months
'604800': 1 week
'63113904': 2 years '63113904': 2 years
'7889238': 3 months '7889238': 3 months
min_age_label: Age threshold min_age_label: Age threshold

View file

@ -10,6 +10,7 @@ describe 'about/show.html.haml', without_verify_partial_doubles: true do
allow(view).to receive(:site_title).and_return('example site') allow(view).to receive(:site_title).and_return('example site')
allow(view).to receive(:new_user).and_return(User.new) allow(view).to receive(:new_user).and_return(User.new)
allow(view).to receive(:use_seamless_external_login?).and_return(false) allow(view).to receive(:use_seamless_external_login?).and_return(false)
allow(view).to receive(:current_account).and_return(nil)
end end
it 'has valid open graph tags' do it 'has valid open graph tags' do