Skip to content

Commit ff789a7

Browse files
ClearlyClaireGargron
authored andcommitted
Fix boosting & unboosting preventing a boost from appearing in the TL (mastodon#11405)
* Fix boosting & unboosting preventing a boost from appearing in the TL * Add tests * Avoids side effects when aggregate_reblogs isn't true
1 parent 648cdbc commit ff789a7

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

app/lib/feed_manager.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def push_to_home(account, status)
3535
end
3636

3737
def unpush_from_home(account, status)
38-
return false unless remove_from_feed(:home, account.id, status)
38+
return false unless remove_from_feed(:home, account.id, status, account.user&.aggregates_reblogs?)
3939
redis.publish("timeline:#{account.id}", Oj.dump(event: :delete, payload: status.id.to_s))
4040
true
4141
end
@@ -53,7 +53,7 @@ def push_to_list(list, status)
5353
end
5454

5555
def unpush_from_list(list, status)
56-
return false unless remove_from_feed(:list, list.id, status)
56+
return false unless remove_from_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
5757
redis.publish("timeline:list:#{list.id}", Oj.dump(event: :delete, payload: status.id.to_s))
5858
true
5959
end
@@ -105,7 +105,7 @@ def unmerge_from_timeline(from_account, into_account)
105105
oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
106106

107107
from_account.statuses.select('id, reblog_of_id').where('id > ?', oldest_home_score).reorder(nil).find_each do |status|
108-
remove_from_feed(:home, into_account.id, status)
108+
remove_from_feed(:home, into_account.id, status, into_account.user&.aggregates_reblogs?)
109109
end
110110
end
111111

@@ -275,10 +275,11 @@ def add_to_feed(timeline_type, account_id, status, aggregate_reblogs = true)
275275
# with reblogs, and returning true if a status was removed. As with
276276
# `add_to_feed`, this does not trigger push updates, so callers must
277277
# do so if appropriate.
278-
def remove_from_feed(timeline_type, account_id, status)
278+
def remove_from_feed(timeline_type, account_id, status, aggregate_reblogs = true)
279279
timeline_key = key(timeline_type, account_id)
280+
reblog_key = key(timeline_type, account_id, 'reblogs')
280281

281-
if status.reblog?
282+
if status.reblog? && (aggregate_reblogs.nil? || aggregate_reblogs)
282283
# 1. If the reblogging status is not in the feed, stop.
283284
status_rank = redis.zrevrank(timeline_key, status.id)
284285
return false if status_rank.nil?
@@ -287,19 +288,22 @@ def remove_from_feed(timeline_type, account_id, status)
287288
reblog_set_key = key(timeline_type, account_id, "reblogs:#{status.reblog_of_id}")
288289

289290
redis.srem(reblog_set_key, status.id)
291+
redis.zrem(reblog_key, status.reblog_of_id)
290292
# 3. Re-insert another reblog or original into the feed if one
291293
# remains in the set. We could pick a random element, but this
292294
# set should generally be small, and it seems ideal to show the
293295
# oldest potential such reblog.
294296
other_reblog = redis.smembers(reblog_set_key).map(&:to_i).min
295297

296298
redis.zadd(timeline_key, other_reblog, other_reblog) if other_reblog
299+
redis.zadd(reblog_key, other_reblog, status.reblog_of_id) if other_reblog
297300

298301
# 4. Remove the reblogging status from the feed (as normal)
299302
# (outside conditional)
300303
else
301304
# If the original is getting deleted, no use for reblog references
302305
redis.del(key(timeline_type, account_id, "reblogs:#{status.id}"))
306+
redis.srem(reblog_key, status.id)
303307
end
304308

305309
redis.zrem(timeline_key, status.id)

spec/lib/feed_manager_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,23 @@
247247
expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be false
248248
end
249249

250+
it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do
251+
account = Fabricate(:account)
252+
reblogged = Fabricate(:status)
253+
old_reblog = Fabricate(:status, reblog: reblogged)
254+
255+
# The first reblog should be accepted
256+
expect(FeedManager.instance.push_to_home(account, old_reblog)).to be true
257+
258+
# The first reblog should be successfully removed
259+
expect(FeedManager.instance.unpush_from_home(account, old_reblog)).to be true
260+
261+
reblog = Fabricate(:status, reblog: reblogged)
262+
263+
# The second reblog should be accepted
264+
expect(FeedManager.instance.push_to_home(account, reblog)).to be true
265+
end
266+
250267
it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
251268
account = Fabricate(:account)
252269
reblogged = Fabricate(:status)

0 commit comments

Comments
 (0)