Module: Prime::AsyncTableMixin

Extended by:
MotionSupport::Concern
Defined in:
motion-prime/sections/_async_table_mixin.rb

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#async_data?Boolean

Returns true if table section have enabled async data. False by defaul.

Returns:

  • (Boolean)

    is async data enabled.



12
13
14
# File 'motion-prime/sections/_async_table_mixin.rb', line 12

def async_data?
  self.class.async_data_options
end

#height_for_index(index) ⇒ Object



34
35
36
37
38
39
40
41
42
# File 'motion-prime/sections/_async_table_mixin.rb', line 34

def height_for_index(index)
  section = cell_section_by_index(index)
  unless section
    Prime.logger.debug "could not find section with index #{index} for #{self.to_s}"
    return 0
  end
  preload_section_by_index(index)
  section.container_height
end

#on_async_data_loadedObject



49
# File 'motion-prime/sections/_async_table_mixin.rb', line 49

def on_async_data_loaded; end

#on_cell_section_preloaded(section, index) ⇒ Object



51
# File 'motion-prime/sections/_async_table_mixin.rb', line 51

def on_cell_section_preloaded(section, index); end

#on_queue_preloaded(queue_id, loaded_index) ⇒ Object



50
# File 'motion-prime/sections/_async_table_mixin.rb', line 50

def on_queue_preloaded(queue_id, loaded_index); end

#preload_sections_after(from_index, load_limit = nil) ⇒ NSIndexPath, Boolean

Preloads sections after rendering cell in current sheduled index or given index. TODO: probably should be in separate class.

Parameters:

  • from_index (NSIndexPath)

    Value of first index to load if current sheduled index not exists.

Returns:

  • (NSIndexPath, Boolean)

    Index of next sheduled index.



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
# File 'motion-prime/sections/_async_table_mixin.rb', line 58

def preload_sections_after(from_index, load_limit = nil)
  return unless async_data?
  service = preloader_index_service
  load_limit ||= self.class.async_data_options.try(:[], :preload_cells_count)

  if @preloader_next_starts_from
    index_to_start_preloading = service.sum_index(@preloader_next_starts_from, load_limit ? -load_limit/2 : 0)
    # should we start preload based on index of rendered cell
    return false if service.compare_indexes(from_index, index_to_start_preloading) < 0
  end

  # adjust start/finish points based on current queues
  current_group = from_index.section
  left_to_load_in_group = cell_sections_for_group(current_group).count - from_index.row
  load_count = [left_to_load_in_group, load_limit].compact.min

  to_index = service.sum_index(from_index, load_count - 1)
  @preloader_next_starts_from = to_index

  Array.wrap(@preloader_queue).each do |queue_info|
    # cancelled and dealloc are left from prev data
    next unless [:in_progress, :completed].include?(queue_info[:state])
    # filter by current group
    next unless queue_info[:from_index].section == current_group
    # reject not started threads
    next if queue_info[:to_index].nil? && queue_info[:state] != :in_progress

    if from_index.row >= queue_info[:from_index].row
      from_index = NSIndexPath.indexPathForRow([from_index.row, queue_info[:to_index].try(:row).try(:+, 1), (queue_info[:target_index] if queue_info[:state] == :in_progress).try(:row).try(:+, 1)].compact.max, inSection: current_group)
    else
      to_index = NSIndexPath.indexPathForRow([to_index.row, queue_info[:from_index].try(:row).try(:-, 1)].compact.min, inSection: current_group)
    end
  end

  load_count = to_index.row - from_index.row + 1
  preload_sections_schedule_from(from_index, load_count) if load_count > 0

  # quota_left = (load_limit || 0) - load_count
  # if quota_left > 0 && cell_sections_for_group(current_group + 1).any?
  #   preload_sections_after(NSIndexPath.indexPathForRow(0, inSection: current_group + 1), quota_left)
  # end
end

#preload_sections_schedule_from(index, load_count) ⇒ Integer

Schedules preloading sections starting with given index with given limit. TODO: probably should be in separate class.

Parameters:

  • index (NSIndexPath)

    Value of first index to load.

  • load_count (Integer)

    Count of sections to load.

Returns:

  • (Integer)

    Queue ID



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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'motion-prime/sections/_async_table_mixin.rb', line 107

def preload_sections_schedule_from(index, load_count)
  service = preloader_index_service

  @preloader_queue ||= []

  # TODO: we do we need to keep screen ref too?
  queue_id = @preloader_queue.count

  @preloader_queue[queue_id] = {
    state: :in_progress,
    target_index: service.sum_index(index, load_count-1),
    from_index: index
  }

  cell_refs = data.map &:weak_ref
  refs = strong_references
  BW::Reactor.schedule(queue_id) do |queue_id|
    result = load_count.times do |offset|
      break if @preloader_queue[queue_id][:state] == :cancelled
      unless refs.all?(&:weakref_alive?)
        @preloader_queue[queue_id][:state] = :dealloc
        break
      end


      cell_refs_by_group = flat_data? ? cell_refs : cell_refs[index.section]
      cell_ref = cell_refs_by_group[index.row]
      return unless cell_ref.weakref_alive?
      if section = preload_section_by_index(index)
        return unless section == cell_ref.strong_ref
        on_cell_section_preloaded(section, index)
      end

      @preloader_queue[queue_id][:to_index] = index
      unless offset == load_count - 1
        index = service.sum_index(index, 1)
      end
      true
    end

    if result
      @preloader_queue[queue_id][:state] = :completed
      on_queue_preloaded(queue_id, index)
    end
  end
  queue_id
end

#preloader_index_serviceObject



155
156
157
# File 'motion-prime/sections/_async_table_mixin.rb', line 155

def preloader_index_service
  TableDataIndexes.new(@data)
end

#render_cell(index) ⇒ Object



44
45
46
47
# File 'motion-prime/sections/_async_table_mixin.rb', line 44

def render_cell(index)
  preload_sections_after(index)
  super
end

#reset_collection_dataBoolean

Reset async loaded table data and preloader queue.

Returns:

  • (Boolean)

    true



27
28
29
30
31
32
# File 'motion-prime/sections/_async_table_mixin.rb', line 27

def reset_collection_data
  super # must be before to update fixed_collection_data
  @async_loaded_data = async_data? ? fixed_collection_data : nil
  Array.wrap(@preloader_queue).each { |queue| queue[:state] = :cancelled }
  @preloader_next_starts_from = nil
end

#table_element_optionsObject



16
17
18
19
20
21
22
# File 'motion-prime/sections/_async_table_mixin.rb', line 16

def table_element_options
  options = super
  if async_data? && self.class.async_data_options.has_key?(:estimated_cell_height)
    options[:estimated_cell_height] = self.class.async_data_options[:estimated_cell_height]
  end
  options
end