プロジェクト

全般

プロフィール

提案 #1309 » load_more_timeline.patch

cob odo, 2018-12-31 01:50

差分を表示:

core/mui/cairo_timeline.rb
179 179
    # ==== Args
180 180
    # [iter] 削除するレコード(Gtk::TreeIter)
181 181
    def tl_model_remove(iter)
182
      Plugin.call(:gui_timeline_message_removed, @tl.imaginary, iter[Gtk::TimeLine::InnerTL::MESSAGE])
182 183
      iter[InnerTL::MIRACLE_PAINTER].destroy
183 184
      @tl.model.remove(iter) end end
184 185

  
core/plugin/extract_load_more/.mikutter.yml
1
---
2
name: extract_load more
3
slug: extract_load_more
4
description: 抽出タブの遡り機能を実装します。
5
depends:
6
  mikutter: '3.8'
7
  plugin:
8
  - extract
9
  - load_more_timeline
10
  - gui
11
version: '1.0'
12
author: cobodo
13

  
core/plugin/extract_load_more/extract_load_more.rb
1
Plugin.create(:extract_load_more) do
2
  # データソース単位のload moreイベント
3
  # 第1引数: timeline slug
4
  # 第2引数: datasource slug
5
  # 第3引数: 抽出タブ内にある、そのdatasourceの最古のDiva::Model。
6
  defevent :extract_load_more_datasource, prototype: [Symbol, Symbol, Diva::Model]
7

  
8
  # 特定のデータソースにDiva::Model群を追加し、必要に応じてtimeline_maxの拡大も行なう。
9
  # 第1引数: timeline slug
10
  # 第2引数: datasource slug
11
  # 第3引数: datasourceに追加するDiva::Modelの配列
12
  defevent :extract_load_more_messages, prototype: [Symbol, Symbol, [Diva::Model]]
13

  
14
  def tl_uris
15
    @tl_uris ||= Hash.new # timeline_slug -> datasource_slug -> URI -> time
16
  end
17
  tl_uris
18

  
19
  def extract_tabs
20
    Plugin[:extract].extract_tabs
21
  end
22

  
23
  on_extract_receive_message do |source, messages|
24
    tabs = extract_tabs.values.select{ |r| r.sources && r.using?(source) }
25
    next if tabs.empty?
26
    tabs.each do |record|
27
      @tl_uris[record.slug] ||= Hash.new
28
      @tl_uris[record.slug][source] ||= Hash.new
29
      messages.each do |m|
30
        if m && m.uri
31
          unless m.retweet_source && tl_uris[record.slug][source].has_key?(m.retweet_source.uri.to_s)
32
            @tl_uris[record.slug][source][m.uri.to_s] = m.retweet_source ? m.modified : m.created
33
          end
34
        end
35
      end
36
    end
37
  end
38

  
39
  # n=TLあたりのメッセージ保持数、m=タブあたりの平均データソース数として、O(nm)
40
  on_gui_timeline_message_removed do |i_timeline, message|
41
    next unless (message && message.uri)
42
    @tl_uris[i_timeline.slug] ||= Hash.new
43
    @tl_uris[i_timeline.slug].keys.each do |source|
44
      @tl_uris[i_timeline.slug][source].delete(message.uri.to_s)
45
    end
46
  end
47

  
48
  # O(n)
49
  on_load_more_timeline do |tl_slug|
50
    next unless @tl_uris[tl_slug]
51
    @tl_uris[tl_slug].keys.each do |source|
52
      pair = @tl_uris[tl_slug][source].to_a.min{|a, b| a[1] <=> b[1] }
53
      oldest = timeline(tl_slug).find {|m| m && m.uri && m.uri == pair[0] }
54
      Plugin.call(:extract_load_more_datasource, tl_slug, source, oldest) if oldest
55
    end
56
  end
57

  
58
  on_extract_load_more_messages do |tl_slug, source, messages|
59
    pp "on_extract_load_more_messages: #{tl_slug} #{source} #{messages}"
60
    i_tl = timeline(tl_slug)
61
    i_tl.timeline_max = [messages.size + i_tl.size, i_tl.timeline_max].max
62
    @tl_uris[tl_slug] ||= Hash.new
63
    @tl_uris[tl_slug][source] ||= Hash.new
64
    messages.each do |m|
65
      @tl_uris[tl_slug][source][m.uri] = m.retweet_source ? m.modified : m.created
66
    end
67
    i_tl << messages
68
  end
69
end
70

  
core/plugin/gtk/gtk.rb
611 611
  # _widget_ に対応するウィジェットオブジェクトまたは偽
612 612
  def find_implement_widget_by_gtkwidget(widget)
613 613
    @slug_dictionary.imaginally_by_gtk(widget) end
614

  
615
  # timeline_maxを取得するフィルタ
616
  filter_gui_timeline_get_timeline_max do |i_tl, _|
617
    [i_tl, widgetof(i_tl).timeline_max]
618
  end
619

  
620
  # timeline_maxを設定するフィルタ
621
  filter_gui_timeline_set_timeline_max do |i_tl, n|
622
    widgetof(i_tl).timeline_max = n
623
    [i_tl, n]
624
  end
625

  
626
  # タイムラインのメッセージを順に処理するフィルタ
627
  filter_gui_timeline_each_messages do |i_tl, y|
628
    widgetof(i_tl).each do |m|
629
      y << m
630
    end
631
    [i_tl, y]
632
  end
633

  
614 634
end
615 635

  
616 636
module Plugin::Gtk
core/plugin/gui/gui.rb
152 152
    [(set || {}).merge(Plugin::GUI::Tab.cuscaded)]
153 153
  end
154 154

  
155
  # timeline_maxを取得するフィルタ
156
  defevent :gui_timeline_get_timeline_max, prototype: [Plugin::GUI::Timeline, Integer]
157

  
158
  # timeline_maxを設定するフィルタ
159
  defevent :gui_timeline_set_timeline_max, prototype: [Plugin::GUI::Timeline, Integer]
160

  
161
  # タイムラインのメッセージを順に処理するフィルタ
162
  defevent :gui_timeline_each_messages, prototype: [Plugin::GUI::Timeline, :<<]
163

  
164
  # タイムラインからメッセージが除去された際に発生させるイベント
165
  defevent :gui_timeline_message_removed, prototype: [Plugin::GUI::Timeline, Diva::Model]
166

  
155 167
end
core/plugin/gui/timeline.rb
13 13
  include Plugin::GUI::HierarchyParent
14 14
  include Plugin::GUI::Widget
15 15

  
16
  include Enumerable
17

  
16 18
  role :timeline
17 19

  
18 20
  set_parent_event :gui_timeline_join_tab
......
132 134
    Plugin.call(:gui_timeline_set_order, self, block)
133 135
  end
134 136

  
137
  # このタイムライン内の _message_ を繰り返し処理する
138
  def each
139
    enum = Enumerator.new do |y|
140
      Plugin.filtering(:gui_timeline_each_messages, self, y)
141
    end.lazy
142
    return enum unless block_given?
143

  
144
    enum.each do |m|
145
      yield m
146
    end
147
  end
148

  
149
  def size
150
    to_a.size
151
  end
152

  
153
  # timeline_maxを取得する
154
  def timeline_max
155
    Plugin.filtering(:gui_timeline_get_timeline_max, self, nil)[1] || UserConfig[:timeline_max]
156
  end
157

  
158
  # timeline_maxを設定する
159
  def timeline_max=(n)
160
    Plugin.filtering(:gui_timeline_set_timeline_max, self, n)
161
  end
135 162
end
core/plugin/load_more_timeline/.mikutter.yml
1
---
2
name: load more timeline
3
slug: load_more_timeline
4
description: mikutterのタイムラインを遡るためのイベントを定義し、コマンドを提供します。
5
depends:
6
  mikutter: '3.8'
7
  plugin:
8
  - gui
9
version: '1.0'
10
author: cobodo
11

  
core/plugin/load_more_timeline/load_more_timeline.rb
1
Plugin.create(:load_more_timeline) do
2
  defevent :load_more_timeline, prototype: [Symbol]
3

  
4
  # 遡るコマンド
5
  # プラグインは、on_load_more_timelineに渡されたタイムラインslugを見て遡ることができる。
6
  command(:load_more_timeline,
7
          name: _('タイムラインを遡る'),
8
          condition: lambda{ |opt| true },
9
          visible: true,
10
          role: :timeline) do |opt|
11
    i_tl = opt.widget
12
    Plugin.call(:load_more_timeline, i_tl.slug)
13
  end
14
end
15

  
core/plugin/twitter_load_more/.mikutter.yml
1
---
2
name: twitter_load more
3
slug: twitter_load_more
4
description: twitterのHome Timeline、リプライ、ユーザータイムライン、リストの遡り機能を実装します。
5
depends:
6
  mikutter: '3.8'
7
  plugin:
8
  - twitter
9
  - list
10
  - load_more_timeline
11
  - extract_load_more
12
  - gui
13
  - world
14
version: '1.0'
15
author: cobodo
16

  
core/plugin/twitter_load_more/twitter_load_more.rb
1
Plugin.create(:twitter_load_more) do
2
  def load_more(tl_slug, source = nil, oldest = nil, &adder)
3
    notice "twitter_load_more: tl_slug=#{tl_slug} source=#{source} oldest=#{oldest}"
4
    world = Plugin.filtering(:worlds, []).first.find{|w| w.class.slug == :twitter }
5
    if tl_slug == :home_timeline
6
      oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
7
      params = {
8
        count: 200,
9
        max_id: oldest.id - 1,
10
        tweet_mode: 'extended'.freeze
11
      }
12
      world.home_timeline(params).next(&adder).terminate("twitter_load_more: home_timeline の追加取得に失敗しました")
13
    elsif tl_slug == :mentions
14
      oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
15
      params = {
16
        count: 200,
17
        max_id: oldest.id - 1,
18
        tweet_mode: 'extended'.freeze
19
      }
20
      world.mentions(params).next(&adder).terminate("twitter_load_more: reply の追加取得に失敗しました")
21
    elsif timeline(tl_slug).parent.is_a?(Plugin::GUI::Fragment)
22
      oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
23
      fragment_slug = timeline(tl_slug).parent.slug
24
      %r!\Ausertimeline_(.+)_[0-9]+_[0-9A-Fa-f]{8}_[0-9A-Fa-f]{8}\z!.match(fragment_slug.to_s) do |m|
25
        params = {
26
          # profile-http://twitter.com/username -> username
27
          screen_name: m[1].split('/').last,
28
          max_id: oldest.id - 1,
29
          count: 200,
30
          include_rts: 1,
31
          tweet_mode: 'extended'.freeze
32
        }
33
        world.user_timeline(params).next(&adder).terminate("twitter_load_more: usertimeline の追加取得に失敗しました")
34
      end
35
    elsif source
36
      list = Plugin[:list].using_lists.find{|list| Plugin[:list].datasource_slug(list) == source }
37
      return unless list
38

  
39
      world = Plugin.filtering(:worlds, []).first.find{|w| w.class.slug == :twitter }
40
      params = {
41
        count: 200,
42
        max_id: oldest.id - 1,
43
        list_id: list[:id],
44
        cache: :keep
45
      }
46
      world.list_statuses(params).next(&adder).terminate("twitter_load_more: home_timeline の追加取得に失敗しました")
47
    end
48
  end
49

  
50
  on_load_more_timeline do |tl_slug|
51
    notice "twitter_load_more: on_load_more_timeline: tl_slug=#{tl_slug}"
52
    load_more(tl_slug) do |messages|
53
      notice "on_load_more_timeline: add #{messages} to #{tl_slug}"
54
      i_tl = timeline(tl_slug)
55
      i_tl.timeline_max = [messages.size + i_tl.size, i_tl.timeline_max].max
56
      i_tl << messages
57
    end
58
  end
59

  
60
  on_extract_load_more_datasource do |tl_slug, source, oldest|
61
    notice "twitter_load_more: on_extract_load_more_datasource: tl_slug=#{tl_slug} source=#{source} oldest=#{oldest}"
62
    load_more(tl_slug, source, oldest) do |messages|
63
      notice "on_extract_load_more_datasource: add #{messages} to #{tl_slug}, #{source}"
64
      Plugin.call(:extract_load_more_messages, tl_slug, source, messages)
65
    end
66
  end
67
end
68

  
(1-1/2)