amatriain/feedbunch

View on GitHub
FeedBunch-app/lib/entry_state_manager.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

require 'subscriptions_manager'

##
# Class with methods related to changing the read/unread state of entries.

class EntryStateManager

  ##
  # Change the read or unread state of an entry, for a given user.
  #
  # Receives as arguments:
  # - the entry to be changed state
  # - the state in which to put it. Supported values are only "read" and "unread"; this method
  # does nothing if a different value is passed
  # - the user for which the state will be set.
  # - whole_feed (optional): boolean to indicate whether other entries in the same feed **older** than
  # the one passed as argument are to be changed state as well.
  # - whole_folder (optional): boolean to indicate whether other entries in the same folder **older** than
  # the one passed as argument are to be changed state as well.
  # - all_entries (optional): boolean to indicate whether **ALL** entries from all subscribed feeds **older**
  # than the one passed as argument are to be changed state as well.
  #
  # If the update_feed or update_folder optional named arguments are passed as true,
  # entries in the same feed/folder as the passed entry which either:
  # - have an older publish date than the passed entry
  # - or have the same publish date but a smaller id
  # are considered to be older and therefore set as read or unread depending on the "state" argument.

  def self.change_entries_state(entry, state, user, whole_feed: false, whole_folder: false, all_entries: false)
    if state == 'read'
      read = true
    elsif state == 'unread'
      read = false
    else
      return nil
    end

    if !whole_feed && !whole_folder && !all_entries
      # Update a single entry
      entry_state = EntryState.find_by user_id: user.id, entry_id: entry.id
      entry_state.update read: read
      # Update unread entries count for the feed
      if read
        SubscriptionsManager.feed_decrement_count entry.feed, user
      else
        SubscriptionsManager.feed_increment_count entry.feed, user
      end
    else
      change_feed_entries_state entry, read, user if whole_feed
      change_folder_entries_state entry, read, user if whole_folder
      change_all_entries_state entry, read, user if all_entries
    end

    return nil
  end

  #############################
  # PRIVATE CLASS METHODS
  #############################

  ##
  # Change the read/unread state for all entries in a feed older than the passed entry.
  #
  # Receives as arguments:
  # - entry: this entry, and all entries in the same feed older than this one, will be marked as read.
  # - read: boolean argument indicating if entries will be marked as read (true) or unread (false).
  # - user: user for whom the read/unread state will be set.

  def self.change_feed_entries_state(entry, read, user)
    # Join with entry_states to select only those entries that don't already have the desired state.
    EntryState.joins(:entry).
      where(entry_states: {user_id: user.id, read: !read}).
      where('entries.feed_id=? AND (entries.published<? OR (entries.published=? AND entries.created_at<?) OR (entries.published=? AND entries.created_at=? AND entries.id<=?))',
            entry.feed_id, entry.published, entry.published, entry.created_at, entry.published, entry.created_at, entry.id).
      update_all read: read

    # Update unread entries count for the feed
    SubscriptionsManager.recalculate_unread_count entry.feed, user
  end
  private_class_method :change_feed_entries_state

  ##
  # Change the read/unread state for all entries in a folder older than the passed entry.
  #
  # Receives as arguments:
  # - entry: this entry, and all entries in the same feed older than this one, will be marked as read.
  # - read: boolean argument indicating if entries will be marked as read (true) or unread (false).
  # - user: user for whom the read/unread state will be set.

  def self.change_folder_entries_state(entry, read, user)
    folder = entry.feed.user_folder user
    # Join with entry_states to select only those entries that don't already have the desired state.
    EntryState.joins(entry: {feed: :folders}).
      where(entry_states: {user_id: user.id, read: !read}, folders: {id: folder.id}).
      where('entries.published<? OR (entries.published=? AND entries.created_at<?) OR (entries.published=? AND entries.created_at=? AND entries.id<=?)',
            entry.published, entry.published, entry.created_at, entry.published, entry.created_at, entry.id).
      update_all read: read

    # Update unread entries count for the feeds
    folder.feeds.find_each do |f|
      SubscriptionsManager.recalculate_unread_count f, user
    end
  end
  private_class_method :change_folder_entries_state

  ##
  # Change the read/unread state for all entries in all subscribed feeds.
  #
  # Receives as arguments:
  # - entry: this entry, and all entries in subscribed feeds older than this one, will be marked as read.
  # - read: boolean argument indicating if entries will be marked as read (true) or unread (false).
  # - user: user for whom the read/unread state will be set.

  def self.change_all_entries_state(entry, read, user)
    # Join with entry_states to select only those entries that don't already have the desired state.
    EntryState.joins(:entry).where(entry_states: {user_id: user.id, read: !read}).
      where('entries.published<? OR (entries.published=? AND entries.created_at<?) OR (entries.published=? AND entries.created_at=? AND entries.id<=?)',
            entry.published, entry.published, entry.created_at, entry.published, entry.created_at, entry.id).
      update_all read: read

    # Update unread entries count for the feeds
    user.feeds.find_each do |f|
      SubscriptionsManager.recalculate_unread_count f, user
    end
  end
  private_class_method :change_all_entries_state
end