end
def follow
- FollowService.new.call(current_user.account, @account.acct, reblogs: truthy_param?(:reblogs))
+ FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs))
options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } }
export function followAccount(id, reblogs = true) {
return (dispatch, getState) => {
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
- dispatch(followAccountRequest(id));
+ const locked = getState().getIn(['accounts', id, 'locked'], false);
+
+ dispatch(followAccountRequest(id, locked));
api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then(response => {
dispatch(followAccountSuccess(response.data, alreadyFollowing));
}).catch(error => {
- dispatch(followAccountFail(error));
+ dispatch(followAccountFail(error, locked));
});
};
};
};
};
-export function followAccountRequest(id) {
+export function followAccountRequest(id, locked) {
return {
type: ACCOUNT_FOLLOW_REQUEST,
id,
+ locked,
+ skipLoading: true,
};
};
type: ACCOUNT_FOLLOW_SUCCESS,
relationship,
alreadyFollowing,
+ skipLoading: true,
};
};
-export function followAccountFail(error) {
+export function followAccountFail(error, locked) {
return {
type: ACCOUNT_FOLLOW_FAIL,
error,
+ locked,
+ skipLoading: true,
};
};
return {
type: ACCOUNT_UNFOLLOW_REQUEST,
id,
+ skipLoading: true,
};
};
type: ACCOUNT_UNFOLLOW_SUCCESS,
relationship,
statuses,
+ skipLoading: true,
};
};
return {
type: ACCOUNT_UNFOLLOW_FAIL,
error,
+ skipLoading: true,
};
};
import {
ACCOUNT_FOLLOW_SUCCESS,
+ ACCOUNT_FOLLOW_REQUEST,
+ ACCOUNT_FOLLOW_FAIL,
ACCOUNT_UNFOLLOW_SUCCESS,
+ ACCOUNT_UNFOLLOW_REQUEST,
+ ACCOUNT_UNFOLLOW_FAIL,
ACCOUNT_BLOCK_SUCCESS,
ACCOUNT_UNBLOCK_SUCCESS,
ACCOUNT_MUTE_SUCCESS,
export default function relationships(state = initialState, action) {
switch(action.type) {
+ case ACCOUNT_FOLLOW_REQUEST:
+ return state.setIn([action.id, action.locked ? 'requested' : 'following'], true);
+ case ACCOUNT_FOLLOW_FAIL:
+ return state.setIn([action.id, action.locked ? 'requested' : 'following'], false);
+ case ACCOUNT_UNFOLLOW_REQUEST:
+ return state.setIn([action.id, 'following'], false);
+ case ACCOUNT_UNFOLLOW_FAIL:
+ return state.setIn([action.id, 'following'], true);
case ACCOUNT_FOLLOW_SUCCESS:
case ACCOUNT_UNFOLLOW_SUCCESS:
case ACCOUNT_BLOCK_SUCCESS:
acct = "#{username}@#{domain}"
end
- ResolveAccountService.new.call(acct, update_profile)
+ ResolveAccountService.new.call(acct, update_profile: update_profile)
end
end
# @param [Account] source_account From which to follow
# @param [String, Account] uri User URI to follow in the form of username@domain (or account record)
# @param [true, false, nil] reblogs Whether or not to show reblogs, defaults to true
- def call(source_account, uri, reblogs: nil)
+ def call(source_account, target_account, reblogs: nil)
reblogs = true if reblogs.nil?
- target_account = uri.is_a?(Account) ? uri : ResolveAccountService.new.call(uri)
+ target_account = ResolveAccountService.new.call(target_account, skip_webfinger: true)
raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
raise Mastodon::NotPermittedError if target_account.blocking?(source_account) || source_account.blocking?(target_account)
follow_request = FollowRequest.create!(account: source_account, target_account: target_account, show_reblogs: reblogs)
if target_account.local?
- NotifyService.new.call(target_account, follow_request)
+ LocalNotificationWorker.perform_async(target_account.id, follow_request.id, follow_request.class.name)
elsif target_account.ostatus?
NotificationWorker.perform_async(build_follow_request_xml(follow_request), source_account.id, target_account.id)
AfterRemoteFollowRequestWorker.perform_async(follow_request.id)
follow = source_account.follow!(target_account, reblogs: reblogs)
if target_account.local?
- NotifyService.new.call(target_account, follow)
+ LocalNotificationWorker.perform_async(target_account.id, follow.id, follow.class.name)
else
Pubsubhubbub::SubscribeWorker.perform_async(target_account.id) unless target_account.subscribed?
NotificationWorker.perform_async(build_follow_xml(follow), source_account.id, target_account.id)
mentioned_account = mention.account
if mentioned_account.local?
- LocalNotificationWorker.perform_async(mention.id)
+ LocalNotificationWorker.perform_async(mentioned_account.id, mention.id, mention.class.name)
elsif mentioned_account.ostatus? && !@status.stream_entry.hidden?
NotificationWorker.perform_async(ostatus_xml, @status.account_id, mentioned_account.id)
elsif mentioned_account.activitypub?
# Find or create a local account for a remote user.
# When creating, look up the user's webfinger and fetch all
# important information from their feed
- # @param [String] uri User URI in the form of username@domain
+ # @param [String, Account] uri User URI in the form of username@domain
+ # @param [Hash] options
# @return [Account]
- def call(uri, update_profile = true, redirected = nil)
- @username, @domain = uri.split('@')
- @update_profile = update_profile
+ def call(uri, options = {})
+ @options = options
- return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
+ if uri.is_a?(Account)
+ @account = uri
+ @username = @account.username
+ @domain = @account.domain
+
+ return @account if @account.local? || !webfinger_update_due?
+ else
+ @username, @domain = uri.split('@')
- @account = Account.find_remote(@username, @domain)
+ return Account.find_local(@username) if TagManager.instance.local_domain?(@domain)
- return @account unless webfinger_update_due?
+ @account = Account.find_remote(@username, @domain)
+
+ return @account unless webfinger_update_due?
+ end
Rails.logger.debug "Looking up webfinger for #{uri}"
if confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
@username = confirmed_username
@domain = confirmed_domain
- elsif redirected.nil?
- return call("#{confirmed_username}@#{confirmed_domain}", update_profile, true)
+ elsif options[:redirected].nil?
+ return call("#{confirmed_username}@#{confirmed_domain}", options.merge(redirected: true))
else
Rails.logger.debug 'Requested and returned acct URIs do not match'
return
end
def webfinger_update_due?
- @account.nil? || @account.possibly_stale?
+ @account.nil? || ((!@options[:skip_webfinger] || @account.ostatus?) && @account.possibly_stale?)
end
def activitypub_ready?
end
def update_profile?
- @update_profile
+ @options[:update_profile]
end
def handle_activitypub
class LocalNotificationWorker
include Sidekiq::Worker
- def perform(mention_id)
- mention = Mention.find(mention_id)
- NotifyService.new.call(mention.account, mention)
+ def perform(receiver_account_id, activity_id = nil, activity_class_name = nil)
+ if activity_id.nil? && activity_class_name.nil?
+ activity = Mention.find(receiver_account_id)
+ receiver = activity.account
+ else
+ receiver = Account.find(receiver_account_id)
+ activity = activity_class_name.constantize.find(activity_id)
+ end
+
+ NotifyService.new.call(receiver, activity)
rescue ActiveRecord::RecordNotFound
true
end
allow(ResolveAccountService).to receive(:new).and_return(service)
allow(service).to receive(:call).with('user@hostname').and_return(target_account)
+ allow(service).to receive(:call).with(target_account, skip_webfinger: true).and_return(target_account)
+
post :create, params: { acct: 'acct:user@hostname' }
- expect(service).to have_received(:call).with('user@hostname')
+ expect(service).to have_received(:call).with(target_account, skip_webfinger: true)
expect(account.following?(target_account)).to be true
expect(response).to render_template(:success)
end