~cytrogen/masto-fe

ref: cc4560d95bae267a8f9281f9f559f8a08b87bd85 masto-fe/db/migrate/20181024224956_migrate_account_conversations.rb -rw-r--r-- 4.3 KiB
cc4560d9 — Claire Change “privacy and reach” settings so that unchecking boxes always increase privacy and checking them always increase reach (#26508) 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

require_relative '../../lib/mastodon/migration_warning'

class MigrateAccountConversations < ActiveRecord::Migration[5.2]
  include Mastodon::MigrationWarning

  disable_ddl_transaction!

  class MigrationAccount < ApplicationRecord
    self.table_name = :accounts
    has_many :mentions, inverse_of: :account, dependent: :destroy, class_name: 'MigrationMention', foreign_key: :account_id
  end

  class MigrationConversation < ApplicationRecord
    self.table_name = :conversations
  end

  class MigrationStatus < ApplicationRecord
    self.table_name = :statuses
    belongs_to :account, class_name: 'MigrationAccount'
    has_many :mentions, dependent: :destroy, inverse_of: :status, class_name: 'MigrationMention', foreign_key: :status_id
    scope :local, -> { where(local: true).or(where(uri: nil)) }
    enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, _suffix: :visibility
    has_many :active_mentions, -> { active }, class_name: 'MigrationMention', inverse_of: :status, foreign_key: :status_id
  end

  class MigrationMention < ApplicationRecord
    self.table_name = :mentions
    belongs_to :account, inverse_of: :mentions, class_name: 'MigrationAccount'
    belongs_to :status, -> { unscope(where: :deleted_at) }, class_name: 'MigrationStatus'
    scope :active, -> { where(silent: false) }

    delegate(
      :username,
      :acct,
      to: :account,
      prefix: true
    )
  end

  class MigrationNotification < ApplicationRecord
    self.table_name = :notifications
    belongs_to :account, optional: true, class_name: 'MigrationAccount'
    belongs_to :activity, polymorphic: true, optional: true

    belongs_to :status,  foreign_key: 'activity_id', optional: true, class_name: 'MigrationStatus'
    belongs_to :mention, foreign_key: 'activity_id', optional: true, class_name: 'MigrationMention'

    def target_status
      mention&.status
    end
  end

  class MigrationAccountConversation < ApplicationRecord
    self.table_name = :account_conversations
    belongs_to :account, class_name: 'MigrationAccount'
    belongs_to :conversation, class_name: 'MigrationConversation'
    belongs_to :last_status, -> { unscope(where: :deleted_at) }, class_name: 'MigrationStatus'

    before_validation :set_last_status

    class << self
      def add_status(recipient, status)
        conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))

        return conversation if conversation.status_ids.include?(status.id)

        conversation.status_ids << status.id
        conversation.unread = status.account_id != recipient.id
        conversation.save
        conversation
      rescue ActiveRecord::StaleObjectError
        retry
      end

      private

      def participants_from_status(recipient, status)
        ((status.active_mentions.pluck(:account_id) + [status.account_id]).uniq - [recipient.id]).sort
      end
    end

    private

    def set_last_status
      self.status_ids     = status_ids.sort
      self.last_status_id = status_ids.last
    end
  end

  def up
    migration_duration_warning

    migrated  = 0
    last_time = Time.zone.now

    local_direct_statuses.includes(:account, mentions: :account).find_each do |status|
      MigrationAccountConversation.add_status(status.account, status)
      migrated += 1

      if Time.zone.now - last_time > 1
        say_progress(migrated)
        last_time = Time.zone.now
      end
    end

    notifications_about_direct_statuses.includes(:account, mention: { status: [:account, mentions: :account] }).find_each do |notification|
      MigrationAccountConversation.add_status(notification.account, notification.target_status)
      migrated += 1

      if Time.zone.now - last_time > 1
        say_progress(migrated)
        last_time = Time.zone.now
      end
    end
  end

  def down; end

  private

  def say_progress(migrated)
    say "Migrated #{migrated} rows", true
  end

  def local_direct_statuses
    MigrationStatus.unscoped.local.where(visibility: :direct)
  end

  def notifications_about_direct_statuses
    MigrationNotification.joins('INNER JOIN mentions ON mentions.id = notifications.activity_id INNER JOIN statuses ON statuses.id = mentions.status_id').where(activity_type: 'Mention', statuses: { visibility: :direct })
  end
end