Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local only #7

Merged
merged 19 commits into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/javascript/mastodon/components/status_action_bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ export default class StatusActionBar extends ImmutablePureComponent {
reblogIcon = 'envelope';
} else if (status.get('visibility') === 'private') {
reblogIcon = 'lock';
} else if (status.get('visibility') === 'local') {
reblogIcon = 'users';
}

if (status.get('in_reply_to_id', null) === null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const messages = defineMessages({
public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
local_short: { id: 'privacy.local.short', defaultMessage: 'Local' },
local_long: { id: 'privacy.local.long', defaultMessage: 'Post to followers and the local timeline' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
Expand Down Expand Up @@ -215,6 +217,7 @@ export default class PrivacyDropdown extends React.PureComponent {
this.options = [
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
{ icon: 'users', value: 'local', text: formatMessage(messages.local_short), meta: formatMessage(messages.local_long) },
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
{ icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,9 @@ export default class ActionBar extends React.PureComponent {
let reblogIcon = 'retweet';
if (status.get('visibility') === 'direct') reblogIcon = 'envelope';
else if (status.get('visibility') === 'private') reblogIcon = 'lock';
else if (status.get('visibility') === 'local') reblogIcon = 'users';

let reblog_disabled = (status.get('visibility') === 'direct' || status.get('visibility') === 'private');
let reblog_disabled = (status.get('visibility') === 'direct' || status.get('visibility') === 'private' || status.get('visibility') === 'local');

return (
<div className='detailed-status__action-bar'>
Expand Down
10 changes: 9 additions & 1 deletion app/javascript/mastodon/locales/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,15 @@
"id": "privacy.unlisted.long"
},
{
"defaultMessage": "Followers-only",
"defaultMessage": "Local",
"id": "privacy.local.short"
},
{
"defaultMessage": "Post to local timeline and followers only",
"id": "privacy.local.long"
},
{
"defaultMessage": "Listeners Only",
"id": "privacy.private.short"
},
{
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@
"privacy.public.short": "Public",
"privacy.unlisted.long": "Do not post to public timelines",
"privacy.unlisted.short": "Unlisted",
"privacy.local.long": "Post to local timeline and followers only",
"privacy.local.short": "Local",
"regeneration_indicator.label": "Loading…",
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
"relative_time.days": "{number}d",
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/mastodon/reducers/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ const insertEmoji = (state, position, emojiData, needsSpace) => {
};

const privacyPreference = (a, b) => {
const order = ['public', 'unlisted', 'private', 'direct'];
const order = ['public', 'unlisted', 'local', 'private', 'direct'];
return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
};

Expand Down
15 changes: 11 additions & 4 deletions app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Status < ApplicationRecord

update_index('statuses#status', :proper) if Chewy.enabled?

enum visibility: [:public, :unlisted, :private, :direct], _suffix: :visibility
enum visibility: [:public, :unlisted, :private, :direct, :local], _suffix: :visibility

belongs_to :application, class_name: 'Doorkeeper::Application', optional: true

Expand Down Expand Up @@ -74,6 +74,7 @@ class Status < ApplicationRecord
scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
scope :with_public_visibility, -> { where(visibility: :public) }
scope :with_local_visibility, -> { where(visibility: :local).or(where(visibility: :public)) }
scope :tagged_with, ->(tag) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag }) }
scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: false }) }
scope :including_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced: true }) }
Expand Down Expand Up @@ -358,9 +359,15 @@ def permitted_for(target_account, account)

def timeline_scope(local_only = false)
starting_scope = local_only ? Status.local : Status
starting_scope
.with_public_visibility
.without_reblogs
if local_only
starting_scope
.with_local_visibility
.without_reblogs
else
starting_scope
.with_public_visibility
.without_reblogs
end
end

def apply_timeline_filters(query, account, local_only)
Expand Down
8 changes: 7 additions & 1 deletion app/policies/status_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ def index?
end

def show?
if direct?
if local?
current_account.nil? || current_account.local?
elsif direct?
owned? || mention_exists?
elsif private?
owned? || following_author? || mention_exists?
Expand Down Expand Up @@ -45,6 +47,10 @@ def direct?
record.direct_visibility?
end

def local?
record.local_visibility?
end

def owned?
author.id == current_account&.id
end
Expand Down
38 changes: 34 additions & 4 deletions app/services/fan_out_on_write_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ def call(status)
deliver_to_lists(status)
end

return if status.account.silenced? || !status.public_visibility? || status.reblog?
return if status.account.silenced? || status.reblog?
return if !status.public_visibility? && !status.local_visibility?

deliver_to_hashtags(status)
if status.local_visibility?
deliver_to_local_hashtags(status)
else
deliver_to_hashtags(status)
end

return if status.reply? && status.in_reply_to_account_id != status.account_id

deliver_to_public(status)
deliver_to_media(status) if status.media_attachments.any?
if status.local_visibility?
deliver_to_local(status)
deliver_to_local_media(status) if status.media_attachments.any?
else
deliver_to_public(status)
deliver_to_media(status) if status.media_attachments.any?
end
end

private
Expand Down Expand Up @@ -79,6 +89,14 @@ def deliver_to_hashtags(status)
end
end

def deliver_to_local_hashtags(status)
Rails.logger.debug "Delivering status #{status.id} to local hashtags"

status.tags.pluck(:name).each do |hashtag|
Redis.current.publish("timeline:hashtag:#{hashtag}:local", @payload) if status.local?
end
end

def deliver_to_public(status)
Rails.logger.debug "Delivering status #{status.id} to public timeline"

Expand All @@ -93,6 +111,18 @@ def deliver_to_media(status)
Redis.current.publish('timeline:public:local:media', @payload) if status.local?
end

def deliver_to_local(status)
Rails.logger.debug "Delivering status #{status.id} to local timeline"

Redis.current.publish('timeline:public:local', @payload) if status.local?
end

def deliver_to_local_media(status)
Rails.logger.debug "Delivering status #{status.id} to local media timeline"

Redis.current.publish('timeline:public:local:media', @payload) if status.local?
end

def deliver_to_direct_timelines(status)
Rails.logger.debug "Delivering status #{status.id} to direct timelines"

Expand Down
8 changes: 5 additions & 3 deletions app/services/post_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ def call(account, text, in_reply_to = nil, **options)

LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
DistributionWorker.perform_async(status.id)
Pubsubhubbub::DistributionWorker.perform_async(status.stream_entry.id)
ActivityPub::DistributionWorker.perform_async(status.id)
ActivityPub::ReplyDistributionWorker.perform_async(status.id) if status.reply? && status.thread.account.local?
unless status.local_visibility?
Pubsubhubbub::DistributionWorker.perform_async(status.stream_entry.id)
ActivityPub::DistributionWorker.perform_async(status.id)
ActivityPub::ReplyDistributionWorker.perform_async(status.id) if status.reply? && status.thread.account.local?
end

if options[:idempotency].present?
redis.setex("idempotency:status:#{account.id}:#{options[:idempotency]}", 3_600, status.id)
Expand Down
2 changes: 1 addition & 1 deletion app/services/process_hashtags_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def call(status, tags = [])
tags.map { |str| str.mb_chars.downcase }.uniq(&:to_s).each do |name|
tag = Tag.where(name: name).first_or_create(name: name)
status.tags << tag
TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility?
TrendingTags.record_use!(tag, status.account, status.created_at) if status.public_visibility? || status.local_visibility?
end
end
end
2 changes: 2 additions & 0 deletions app/services/process_mentions_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ def create_notification(mention)

if mentioned_account.local?
LocalNotificationWorker.perform_async(mention.id)
elsif @status.local_visibility?
return
elsif mentioned_account.ostatus? && !@status.stream_entry.hidden?
NotificationWorker.perform_async(ostatus_xml, @status.account_id, mentioned_account.id)
elsif mentioned_account.activitypub?
Expand Down
12 changes: 8 additions & 4 deletions app/services/reblog_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ReblogService < BaseService
# @return [Status]
def call(account, reblogged_status)
reblogged_status = reblogged_status.reblog if reblogged_status.reblog?
return if reblogged_status.local_visibility?

authorize_with account, reblogged_status, :reblog?

Expand All @@ -20,9 +21,12 @@ def call(account, reblogged_status)
reblog = account.statuses.create!(reblog: reblogged_status, text: '')

DistributionWorker.perform_async(reblog.id)
Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
ActivityPub::DistributionWorker.perform_async(reblog.id)


unless reblogged_status.local_visibility?
Pubsubhubbub::DistributionWorker.perform_async(reblog.stream_entry.id)
ActivityPub::DistributionWorker.perform_async(reblog.id)
end

create_notification(reblog)
bump_potential_friendship(account, reblog)

Expand All @@ -33,7 +37,7 @@ def call(account, reblogged_status)

def create_notification(reblog)
reblogged_status = reblog.reblog

if reblogged_status.account.local?
NotifyService.new.call(reblogged_status.account, reblog)
elsif reblogged_status.account.ostatus?
Expand Down
2 changes: 1 addition & 1 deletion app/workers/activitypub/distribution_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def perform(status_id)
private

def skip_distribution?
@status.direct_visibility?
@status.direct_visibility? || @status.direct_visibility?
end

def relayable?
Expand Down
2 changes: 1 addition & 1 deletion app/workers/activitypub/reply_distribution_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def perform(status_id)
private

def skip_distribution?
@status.private_visibility? || @status.direct_visibility?
@status.private_visibility? || @status.direct_visibility? || @status.local_visibility?
end

def inboxes
Expand Down
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,8 @@ en:
public_long: Everyone can see
unlisted: Unlisted
unlisted_long: Everyone can see, but not listed on public timelines
local: Local
local_long: Everyone can see on public account pages, but not sent to other sites.
stream_entries:
pinned: Pinned toot
reblogged: boosted
Expand Down