amatriain/feedbunch

View on GitHub
FeedBunch-app/app/assets/javascripts/ng-services/ng-LoadEntriesSvc.js.coffee.erb

Summary

Maintainability
Test Coverage
########################################################
# AngularJS service to read entries in a feed or folder
########################################################

angular.module('feedbunch').service 'loadEntriesSvc',
['$rootScope', '$timeout', 'csrfTokenSvc', 'timerFlagSvc', 'openFolderSvc',
'entriesPaginationSvc', 'openEntrySvc', 'feedsFoldersTimerSvc', 'favicoSvc', 'lazyLoadingSvc', 'startPageSvc',
'findSvc', 'changeUnreadCountSvc', 'highlightedEntrySvc',
($rootScope, $timeout, csrfTokenSvc, timerFlagSvc, openFolderSvc,
entriesPaginationSvc, openEntrySvc, feedsFoldersTimerSvc, favicoSvc, lazyLoadingSvc, startPageSvc,
findSvc, changeUnreadCountSvc, highlightedEntrySvc)->

  # Maximum number of entries in each page.
  # This MUST match the entries page size set in the server!
  entries_page_size = 25

  # Constants for the different operations the web worker can perform
  LOAD_FEED_ENTRIES = 'load_feed_entries'
  LOAD_FOLDER_ENTRIES = 'load_folder_entries'

  # CSRF token
  token = csrfTokenSvc.get_token()

  # Web worker to load entries
  worker = new Worker '<%= asset_path 'workers/load_entries_worker' %>'
  worker.onmessage = (e) ->
    if e.data.status == 200 || e.data.status == 304
      if e.data.operation == LOAD_FEED_ENTRIES
        feed_entries_loaded e.data.params, e.data.response
      else if e.data.operation == LOAD_FOLDER_ENTRIES
        folder_entries_loaded e.data.params, e.data.response
    else if e.data.status == 401 || e.data.status == 422
      $window.location.href = '/login'
    else
      if e.data.operation == LOAD_FEED_ENTRIES
        feed_entries_loading_error e.data.params, e.data.status
      else if e.data.operation == LOAD_FOLDER_ENTRIES
        folder_entries_loading_error e.data.params, e.data.status
    $rootScope.$digest()

  #--------------------------------------------
  # PRIVATE FUNCTION: Operations after entries of a feed have been loaded
  #--------------------------------------------
  feed_entries_loaded = (params, response)->
    # Check that the entries received are the expected ones
    # If receiving the first page of entries, entries list must be empty. This ensures that the first page of entries
    # appears only once even if the user clicks rapidly on the feed link.
    page = entriesPaginationSvc.get_entries_page()
    if $rootScope.current_feed?.id == params.feed_id && page == params.page && (page != 1 || $rootScope.entries.length == 0)
      entriesPaginationSvc.set_busy false

      # If entries list is empty, populate it with received entries and highlight first entry.
      # Otherwise concatenate the received page of entries to the list of entries.
      if !$rootScope.entries || $rootScope.entries?.length == 0
        $rootScope.entries = response.slice()
        highlightedEntrySvc.reset()
      else
        $rootScope.entries = $rootScope.entries.concat response.slice()

      # Set correct state (open or closed) for new entries, based on user configuration
      openEntrySvc.add_entries response.slice()

      # If the user has selected the "open all entries by default" option, lazy load images
      if $rootScope.open_all_entries
        $timeout ->
          lazyLoadingSvc.load_viewport_images()
        , 250

      feed = findSvc.find_feed params.feed_id
      # If less than a full page of entries is received, this is the last page of entries available.
      if response.length < entries_page_size
        entriesPaginationSvc.set_more_entries_available false
        correct_feed_unread_counts feed
      else if entriesPaginationSvc.is_first_page()
        # After loading the first page of entries, load a second one to ensure the list is fully populated
        load_feed_entries feed

  #--------------------------------------------
  # PRIVATE FUNCTION: Operations after loading entries from a single feed finishes with an error
  #--------------------------------------------
  feed_entries_loading_error = (params, status)->
    entriesPaginationSvc.set_busy false
    entriesPaginationSvc.set_more_entries_available false
    if status == 404
      feed = findSvc.find_feed params.feed_id
      correct_feed_unread_counts feed
      if entriesPaginationSvc.is_first_page()
        entriesPaginationSvc.set_error_no_entries true
        feed.unread_entries = 0
    else
      startPageSvc.show_start_page()
      timerFlagSvc.start 'error_loading_entries'

  #--------------------------------------------
  # PRIVATE FUNCTION: Operations after entries of a folder have been loaded
  #--------------------------------------------
  folder_entries_loaded = (params, response)->
    # Check that the entries received are the expected ones
    # If receiving the first page of entries, entries list must be empty. This ensures that the first page of entries
    # appears only once even if the user clicks rapidly on the folder link.
    page = entriesPaginationSvc.get_entries_page()
    if $rootScope.current_folder?.id == params.folder_id && page == params.page && (page != 1 || $rootScope.entries.length == 0)

      entriesPaginationSvc.set_busy false

      # If entries list is empty, populate it with received entries and highlight first entry.
      # Otherwise concatenate the received page of entries to the list of entries.
      if !$rootScope.entries || $rootScope.entries?.length == 0
        $rootScope.entries = response.slice()
        highlightedEntrySvc.reset()
      else
        $rootScope.entries = $rootScope.entries.concat response.slice()

      # Set correct state (open or closed) for new entries, based on user configuration
      openEntrySvc.add_entries response.slice()

      # If the user has selected the "open all entries by default" option, lazy load images
      if $rootScope.open_all_entries
        $timeout ->
          lazyLoadingSvc.load_viewport_images()
        , 250

      folder = findSvc.find_folder params.folder_id
      # If less than a full page of entries is received, this is the last page of entries available.
      if response.length < entries_page_size
        entriesPaginationSvc.set_more_entries_available false
        correct_folder_unread_counts folder
      else if entriesPaginationSvc.is_first_page()
        # After loading the first page of entries, load a second one to ensure the list is fully populated
        load_folder_entries folder

  #--------------------------------------------
  # PRIVATE FUNCTION: Operations after loading entries from a folder finishes with an error
  #--------------------------------------------
  folder_entries_loading_error = (params, status)->
    entriesPaginationSvc.set_busy false
    entriesPaginationSvc.set_more_entries_available false
    if status == 404
      folder = findSvc.find_folder params.folder_id
      correct_folder_unread_counts folder
      if entriesPaginationSvc.is_first_page()
        entriesPaginationSvc.set_error_no_entries true
    else
      startPageSvc.show_start_page()
      timerFlagSvc.start 'error_loading_entries'

  #--------------------------------------------
  # PRIVATE FUNCTION: Validations and setup that runs every time entries are loaded.
  # Returns true if the load process can continue, false if it must be cancelled.
  #--------------------------------------------
  load_entries_setup = ->
    # If a 404 has been received in a previous page (no more entries available), do nothing
    return false if !entriesPaginationSvc.more_entries_available()

    # Reset the timer that updates feeds every minute
    feedsFoldersTimerSvc.reset_refresh_timer()
    # Increment the results page
    entriesPaginationSvc.increment_entries_page()
    # Indicate that AJAX request/response cycle is busy so no more calls are done until finished
    entriesPaginationSvc.set_busy true

    return true

  #--------------------------------------------
  # PRIVATE FUNCTION: Load entries in the feed passed as argument.
  #--------------------------------------------
  load_feed_entries = (feed)->
    return if load_entries_setup() == false

    url = "/api/feeds/#{feed.id}/entries.json"
    worker.postMessage {operation: LOAD_FEED_ENTRIES, token: token, feed_id: feed.id, include_read: $rootScope.show_read, page: entriesPaginationSvc.get_entries_page()}

  #--------------------------------------------
  # PRIVATE FUNCTION: After all entries in a feed have been received, set the unread count for the feed
  # to the number of unread entries actually present.
  #--------------------------------------------
  correct_feed_unread_counts = (feed)->
    entries = findSvc.find_feed_unread_entries feed
    if entries
      changeUnreadCountSvc.set_feed_count feed, entries.length
    else
      changeUnreadCountSvc.set_feed_count feed, 0

  #--------------------------------------------
  # PRIVATE FUNCTION: Load entries in the folder passed as argument.
  #--------------------------------------------
  load_folder_entries = (folder)->
    return if load_entries_setup() == false

    url = "/api/folders/#{folder.id}/entries.json"
    worker.postMessage {operation: LOAD_FOLDER_ENTRIES, token: token, folder_id: folder.id, include_read: $rootScope.show_read, page: entriesPaginationSvc.get_entries_page()}

  #--------------------------------------------
  # PRIVATE FUNCTION: After retrieving entries in a folder, set to zero the unread count of
  # feeds for which no entries have been received.
  #--------------------------------------------
  correct_folder_unread_counts = (folder)->
    feeds = findSvc.find_folder_feeds folder
    if feeds && feeds?.length > 0
      for f in feeds
        correct_feed_unread_counts f

  service =

    #---------------------------------------------
    # Load a page of entries for the currently selected feed or folder
    #---------------------------------------------
    read_entries_page: ->
      # The fill block is reset to zero height on each entries page load. This fill block is only
      # necessary for the autoscroll when opening an entry to work correctly (positioning the open entry at the top of
      # the list), the rest of the time its height should be zero.
      $('#entries-fill-block').height 0

      current_feed = $rootScope.current_feed
      current_folder = $rootScope.current_folder
      if current_feed
        load_feed_entries current_feed
      else if current_folder
        load_folder_entries current_folder

  return service
]