Module: Thredded::UserTopicReadStateCommon::ClassMethods

Defined in:
app/models/concerns/thredded/user_topic_read_state_common.rb

Instance Method Summary collapse

Instance Method Details

#read_on_first_post!(user, topic) ⇒ Object



37
38
39
# File 'app/models/concerns/thredded/user_topic_read_state_common.rb', line 37

def read_on_first_post!(user, topic)
  create!(user: user, postable: topic, read_at: Time.zone.now)
end

#topic_classObject



92
93
94
# File 'app/models/concerns/thredded/user_topic_read_state_common.rb', line 92

def topic_class
  reflect_on_association(:postable).klass
end

#touch!(user_id, topic_id, post) ⇒ Object

Parameters:



28
29
30
31
32
33
34
35
# File 'app/models/concerns/thredded/user_topic_read_state_common.rb', line 28

def touch!(user_id, topic_id, post)
  # TODO: Switch to upsert once Travis supports PostgreSQL 9.5.
  # Travis issue: https://github.com/travis-ci/travis-ci/issues/4264
  # Upsert gem: https://github.com/seamusabshere/upsert
  state = find_or_initialize_by(user_id: user_id, postable_id: topic_id)
  return unless !state.read_at? || state.read_at < post.created_at
  state.update!(read_at: post.created_at)
end

#with_page_info(posts_per_page: topic_class.default_per_page, posts_scope: post_class.all) ⇒ Object

Adds ‘first_unread_post_page` and `last_read_post_page` columns onto the scope. Skips the records that have no read posts.



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
# File 'app/models/concerns/thredded/user_topic_read_state_common.rb', line 43

def with_page_info( # rubocop:disable Metrics/MethodLength
  posts_per_page: topic_class.default_per_page, posts_scope: post_class.all
)
  states = arel_table
  self_relation = is_a?(ActiveRecord::Relation) ? self : all
  if self_relation == unscoped
    states_select_manager = states
  else
    # Using the relation here is redundant but massively improves performance.
    states_select_manager = Thredded::ArelCompat.new_arel_select_manager(
      Arel::Nodes::TableAlias.new(Thredded::ArelCompat.relation_to_arel(self_relation), table_name)
    )
  end
  read = if posts_scope == post_class.unscoped
           post_class.arel_table
         else
           posts_subquery = Thredded::ArelCompat.relation_to_arel(posts_scope)
           Arel::Nodes::TableAlias.new(posts_subquery, 'read_posts')
         end
  unread_topics = topic_class.arel_table
  page_info =
    states_select_manager
      .project(
        states[:id],
        Arel::Nodes::Case.new(unread_topics[:id].not_eq(nil))
          .when(Thredded::ArelCompat.true_value(self)).then(
            Arel::Nodes::Addition.new(
              Thredded::ArelCompat.integer_division(self, read[:id].count, posts_per_page), 1
            )
          ).else(nil)
          .as('first_unread_post_page'),
        Arel::Nodes::Addition.new(
          Thredded::ArelCompat.integer_division(self, read[:id].count, posts_per_page),
          Arel::Nodes::Case.new(Arel::Nodes::InfixOperation.new(:%, read[:id].count, posts_per_page))
            .when(0).then(0).else(1)
        ).as('last_read_post_page')
      )
      .join(read)
      .on(read[:postable_id].eq(states[:postable_id]).and(read[:created_at].lteq(states[:read_at])))
      .outer_join(unread_topics)
      .on(states[:postable_id].eq(unread_topics[:id]).and(unread_topics[:last_post_at].gt(states[:read_at])))
      .group(states[:id], unread_topics[:id])
      .as('id_and_page_info')

  # We use a subquery because selected fields must appear in the GROUP BY or be used in an aggregate function.
  select(states[Arel.star], page_info[:first_unread_post_page], page_info[:last_read_post_page])
    .joins(states.join(page_info).on(states[:id].eq(page_info[:id])).join_sources)
end