aboutsummaryrefslogtreecommitdiffstats
path: root/core/plugin/saved_search/saved_search.rb
blob: f0c7e930d8602e4f41ffcbb367f174722f5a94f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
100
101
102
103
104
105
106
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
154
155
156
157
158
159
160
161
# -*- coding: utf-8 -*-

module Plugin::SavedSearch
  SavedSearch = Struct.new(:id,      # Saved Search ID (Twitter APIが採番するもの)
                           :query,   # 検索クエリ文字列
                           :name,    # 検索の名前
                           :slug,    # Timeline, Tabのスラッグ
                           :service) # この検索を作成したService
  DEFAULT_ICON = Skin.get('savedsearch.png').freeze
end

Plugin.create :saved_search do

  counter = gen_counter

  on_period do |service|
    if counter.call >= UserConfig[:retrieve_interval_search]
      counter = gen_counter
      refresh end end

  on_saved_search_register do |id, query, service|
    add_tab(Plugin::SavedSearch::SavedSearch.new(id,
                                                 query,
                                                 query,
                                                 :"savedsearch_#{id.to_s}",
                                                 service)) end

  command(:saved_search_destroy,
          name: _('保存した検索を削除'),
          condition: lambda{ |opt| timelines.values.any?{ |s| s.slug == opt.widget.slug } },
          visible: true,
          role: :tab) do |opt|
    saved_search = timelines.values.find{ |s| s.slug == opt.widget.slug }
    if saved_search
      saved_search.service.search_destroy(id: saved_search.id)
      opt.widget.destroy end end

  on_gui_destroy do |i_tab|
    if i_tab.is_a? Plugin::GUI::Tab
      saved_search = timelines.values.find{ |s| s.slug == i_tab.slug }
      if saved_search
        delete_cache(saved_search.id) end end end

  # id => SavedSearch
  def timelines
    @timelines ||= {} end

  # タブを保存する
  # ==== Args
  # [saved_search] saved search
  def add_tab(saved_search)
    type_strict saved_search => Plugin::SavedSearch::SavedSearch
    tab(saved_search.slug, saved_search.name) do
      set_icon Plugin::SavedSearch::DEFAULT_ICON
      timeline saved_search.slug end
    register_cache(saved_search)
    timelines[saved_search.id] = saved_search end

  # idに対応するタブを削除
  # ==== Args
  # [id] saved search の ID
  def delete_tab(id)
    type_strict id => Integer
    saved_search = timelines[id]
    timelines.delete(id)
    tab(saved_search.slug).destroy if saved_search.slug end

  # タイムラインを更新する
  # ==== Args
  # [saved_search] saved search
  def rewind_timeline(saved_search)
    type_strict saved_search => Plugin::SavedSearch::SavedSearch
    saved_search.service.search(q: saved_search.query, count: 100).next{ |res|
      timeline(saved_search.slug) << res if res.is_a? Array
    }.trap{ |e|
      timeline(saved_search.slug) << Message.new(message: _("更新中にエラーが発生しました (%{error})") % {error: e.to_s}, system: true) } end

  # 全 Service について saved search を取得する
  # ==== Args
  # [cache] キャッシュの利用方法
  # ==== Return
  # deferred
  def refresh(cache=:keep)
    Deferred.when(*Service.map { |service| refresh_for_service(service, cache) }) end

  # あるServiceに対してのみ saved search 一覧を取得する
  # ==== Args
  # [service] Service 対象となるService
  # [cache] キャッシュの利用方法
  # ==== Return
  # deferred
  def refresh_for_service(service, cache=:keep)
    service.saved_searches(cache: cache).next{ |res|
      if res
        saved_searches = {}
        res.each{ |record|
          saved_searches[record[:id]] = Plugin::SavedSearch::SavedSearch.new(record[:id],
                                                                             URI.decode(record[:query]),
                                                                             URI.decode(record[:name]),
                                                                             :"savedsearch_#{record[:id]}",
                                                                             service) }
        new_ids = saved_searches.keys
        old_ids = timelines.values.select{|s| s.service == service }.map(&:id)
        (new_ids - old_ids).each{ |id| add_tab(saved_searches[id]) }
        (old_ids - new_ids).each{ |id| delete_tab(id) }
        new_ids.each{ |id| rewind_timeline(saved_searches[id]) } end }.terminate end

  # 保存した検索の情報をキャッシュに登録する
  # ==== Args
  # [saved_search] 保存した検索
  def register_cache(saved_search)
    type_strict saved_search => Plugin::SavedSearch::SavedSearch
    cache = at(:last_saved_search_state, {}).melt
    cache[saved_search.id] = {
      id: saved_search.id,
      query: saved_search.query,
      name: saved_search.name,
      slug: saved_search.slug,
      service_id: saved_search.service.user_obj.id
    }
    store(:last_saved_search_state, cache) end

  # 保存した検索の情報をキャッシュから削除
  # ==== Args
  # [id] 削除するID
  def delete_cache(id)
    cache = at(:last_saved_search_state, {}).melt
    cache.delete(id)
    store(:last_saved_search_state, cache) end

  # ユーザIDから対応する Service を返す
  # ==== Args
  # [user_id] ユーザID
  # ==== Return
  # Service か、見つからなければnil
  def service_by_user_id(user_id)
    Service.find{ |service| service.user_obj.id == user_id } end

  at(:last_saved_search_state, {}).values.each{ |s|
    service = service_by_user_id(s[:service_id])
    if service
      add_tab(Plugin::SavedSearch::SavedSearch.new(s[:id],
                                                   URI.decode(s[:query]),
                                                   URI.decode(s[:name]),
                                                   s[:slug],
                                                   service))
    elsif s[:slug]
      zombie_tab = tab(s[:slug])
      zombie_tab.destroy if zombie_tab end }

  Delayer.new{ refresh(true) }

end