プロジェクト

全般

プロフィール

提案 #1309 » load_more_timeline.patch

cob odo, 2018-12-31 01:50

差分を表示:

core/mui/cairo_timeline.rb
# ==== Args
# [iter] 削除するレコード(Gtk::TreeIter)
def tl_model_remove(iter)
Plugin.call(:gui_timeline_message_removed, @tl.imaginary, iter[Gtk::TimeLine::InnerTL::MESSAGE])
iter[InnerTL::MIRACLE_PAINTER].destroy
@tl.model.remove(iter) end end
core/plugin/extract_load_more/.mikutter.yml
---
name: extract_load more
slug: extract_load_more
description: 抽出タブの遡り機能を実装します。
depends:
mikutter: '3.8'
plugin:
- extract
- load_more_timeline
- gui
version: '1.0'
author: cobodo
core/plugin/extract_load_more/extract_load_more.rb
Plugin.create(:extract_load_more) do
# データソース単位のload moreイベント
# 第1引数: timeline slug
# 第2引数: datasource slug
# 第3引数: 抽出タブ内にある、そのdatasourceの最古のDiva::Model。
defevent :extract_load_more_datasource, prototype: [Symbol, Symbol, Diva::Model]
# 特定のデータソースにDiva::Model群を追加し、必要に応じてtimeline_maxの拡大も行なう。
# 第1引数: timeline slug
# 第2引数: datasource slug
# 第3引数: datasourceに追加するDiva::Modelの配列
defevent :extract_load_more_messages, prototype: [Symbol, Symbol, [Diva::Model]]
def tl_uris
@tl_uris ||= Hash.new # timeline_slug -> datasource_slug -> URI -> time
end
tl_uris
def extract_tabs
Plugin[:extract].extract_tabs
end
on_extract_receive_message do |source, messages|
tabs = extract_tabs.values.select{ |r| r.sources && r.using?(source) }
next if tabs.empty?
tabs.each do |record|
@tl_uris[record.slug] ||= Hash.new
@tl_uris[record.slug][source] ||= Hash.new
messages.each do |m|
if m && m.uri
unless m.retweet_source && tl_uris[record.slug][source].has_key?(m.retweet_source.uri.to_s)
@tl_uris[record.slug][source][m.uri.to_s] = m.retweet_source ? m.modified : m.created
end
end
end
end
end
# n=TLあたりのメッセージ保持数、m=タブあたりの平均データソース数として、O(nm)
on_gui_timeline_message_removed do |i_timeline, message|
next unless (message && message.uri)
@tl_uris[i_timeline.slug] ||= Hash.new
@tl_uris[i_timeline.slug].keys.each do |source|
@tl_uris[i_timeline.slug][source].delete(message.uri.to_s)
end
end
# O(n)
on_load_more_timeline do |tl_slug|
next unless @tl_uris[tl_slug]
@tl_uris[tl_slug].keys.each do |source|
pair = @tl_uris[tl_slug][source].to_a.min{|a, b| a[1] <=> b[1] }
oldest = timeline(tl_slug).find {|m| m && m.uri && m.uri == pair[0] }
Plugin.call(:extract_load_more_datasource, tl_slug, source, oldest) if oldest
end
end
on_extract_load_more_messages do |tl_slug, source, messages|
pp "on_extract_load_more_messages: #{tl_slug} #{source} #{messages}"
i_tl = timeline(tl_slug)
i_tl.timeline_max = [messages.size + i_tl.size, i_tl.timeline_max].max
@tl_uris[tl_slug] ||= Hash.new
@tl_uris[tl_slug][source] ||= Hash.new
messages.each do |m|
@tl_uris[tl_slug][source][m.uri] = m.retweet_source ? m.modified : m.created
end
i_tl << messages
end
end
core/plugin/gtk/gtk.rb
# _widget_ に対応するウィジェットオブジェクトまたは偽
def find_implement_widget_by_gtkwidget(widget)
@slug_dictionary.imaginally_by_gtk(widget) end
# timeline_maxを取得するフィルタ
filter_gui_timeline_get_timeline_max do |i_tl, _|
[i_tl, widgetof(i_tl).timeline_max]
end
# timeline_maxを設定するフィルタ
filter_gui_timeline_set_timeline_max do |i_tl, n|
widgetof(i_tl).timeline_max = n
[i_tl, n]
end
# タイムラインのメッセージを順に処理するフィルタ
filter_gui_timeline_each_messages do |i_tl, y|
widgetof(i_tl).each do |m|
y << m
end
[i_tl, y]
end
end
module Plugin::Gtk
core/plugin/gui/gui.rb
[(set || {}).merge(Plugin::GUI::Tab.cuscaded)]
end
# timeline_maxを取得するフィルタ
defevent :gui_timeline_get_timeline_max, prototype: [Plugin::GUI::Timeline, Integer]
# timeline_maxを設定するフィルタ
defevent :gui_timeline_set_timeline_max, prototype: [Plugin::GUI::Timeline, Integer]
# タイムラインのメッセージを順に処理するフィルタ
defevent :gui_timeline_each_messages, prototype: [Plugin::GUI::Timeline, :<<]
# タイムラインからメッセージが除去された際に発生させるイベント
defevent :gui_timeline_message_removed, prototype: [Plugin::GUI::Timeline, Diva::Model]
end
core/plugin/gui/timeline.rb
include Plugin::GUI::HierarchyParent
include Plugin::GUI::Widget
include Enumerable
role :timeline
set_parent_event :gui_timeline_join_tab
......
Plugin.call(:gui_timeline_set_order, self, block)
end
# このタイムライン内の _message_ を繰り返し処理する
def each
enum = Enumerator.new do |y|
Plugin.filtering(:gui_timeline_each_messages, self, y)
end.lazy
return enum unless block_given?
enum.each do |m|
yield m
end
end
def size
to_a.size
end
# timeline_maxを取得する
def timeline_max
Plugin.filtering(:gui_timeline_get_timeline_max, self, nil)[1] || UserConfig[:timeline_max]
end
# timeline_maxを設定する
def timeline_max=(n)
Plugin.filtering(:gui_timeline_set_timeline_max, self, n)
end
end
core/plugin/load_more_timeline/.mikutter.yml
---
name: load more timeline
slug: load_more_timeline
description: mikutterのタイムラインを遡るためのイベントを定義し、コマンドを提供します。
depends:
mikutter: '3.8'
plugin:
- gui
version: '1.0'
author: cobodo
core/plugin/load_more_timeline/load_more_timeline.rb
Plugin.create(:load_more_timeline) do
defevent :load_more_timeline, prototype: [Symbol]
# 遡るコマンド
# プラグインは、on_load_more_timelineに渡されたタイムラインslugを見て遡ることができる。
command(:load_more_timeline,
name: _('タイムラインを遡る'),
condition: lambda{ |opt| true },
visible: true,
role: :timeline) do |opt|
i_tl = opt.widget
Plugin.call(:load_more_timeline, i_tl.slug)
end
end
core/plugin/twitter_load_more/.mikutter.yml
---
name: twitter_load more
slug: twitter_load_more
description: twitterのHome Timeline、リプライ、ユーザータイムライン、リストの遡り機能を実装します。
depends:
mikutter: '3.8'
plugin:
- twitter
- list
- load_more_timeline
- extract_load_more
- gui
- world
version: '1.0'
author: cobodo
core/plugin/twitter_load_more/twitter_load_more.rb
Plugin.create(:twitter_load_more) do
def load_more(tl_slug, source = nil, oldest = nil, &adder)
notice "twitter_load_more: tl_slug=#{tl_slug} source=#{source} oldest=#{oldest}"
world = Plugin.filtering(:worlds, []).first.find{|w| w.class.slug == :twitter }
if tl_slug == :home_timeline
oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
params = {
count: 200,
max_id: oldest.id - 1,
tweet_mode: 'extended'.freeze
}
world.home_timeline(params).next(&adder).terminate("twitter_load_more: home_timeline の追加取得に失敗しました")
elsif tl_slug == :mentions
oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
params = {
count: 200,
max_id: oldest.id - 1,
tweet_mode: 'extended'.freeze
}
world.mentions(params).next(&adder).terminate("twitter_load_more: reply の追加取得に失敗しました")
elsif timeline(tl_slug).parent.is_a?(Plugin::GUI::Fragment)
oldest = timeline(tl_slug).min {|a, b| a.modified.to_i <=> b.modified.to_i }
fragment_slug = timeline(tl_slug).parent.slug
%r!\Ausertimeline_(.+)_[0-9]+_[0-9A-Fa-f]{8}_[0-9A-Fa-f]{8}\z!.match(fragment_slug.to_s) do |m|
params = {
# profile-http://twitter.com/username -> username
screen_name: m[1].split('/').last,
max_id: oldest.id - 1,
count: 200,
include_rts: 1,
tweet_mode: 'extended'.freeze
}
world.user_timeline(params).next(&adder).terminate("twitter_load_more: usertimeline の追加取得に失敗しました")
end
elsif source
list = Plugin[:list].using_lists.find{|list| Plugin[:list].datasource_slug(list) == source }
return unless list
world = Plugin.filtering(:worlds, []).first.find{|w| w.class.slug == :twitter }
params = {
count: 200,
max_id: oldest.id - 1,
list_id: list[:id],
cache: :keep
}
world.list_statuses(params).next(&adder).terminate("twitter_load_more: home_timeline の追加取得に失敗しました")
end
end
on_load_more_timeline do |tl_slug|
notice "twitter_load_more: on_load_more_timeline: tl_slug=#{tl_slug}"
load_more(tl_slug) do |messages|
notice "on_load_more_timeline: add #{messages} to #{tl_slug}"
i_tl = timeline(tl_slug)
i_tl.timeline_max = [messages.size + i_tl.size, i_tl.timeline_max].max
i_tl << messages
end
end
on_extract_load_more_datasource do |tl_slug, source, oldest|
notice "twitter_load_more: on_extract_load_more_datasource: tl_slug=#{tl_slug} source=#{source} oldest=#{oldest}"
load_more(tl_slug, source, oldest) do |messages|
notice "on_extract_load_more_datasource: add #{messages} to #{tl_slug}, #{source}"
Plugin.call(:extract_load_more_messages, tl_slug, source, messages)
end
end
end
(1-1/2)