提案 #1309 » load_more_timeline.patch
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 |
|
|
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 |