<commit_before># -*- coding: utf-8 -*-

Plugin.create :profile do
  UserConfig[:profile_show_tweet_once] ||= 20
  @counter = gen_counter
  plugin = self
  timeline_storage = {}                # {slug: {timeline, user}}

  Message::Entity.addlinkrule(:user_mentions, /(?:@|＠|〄|☯|⑨|♨|(?:\W|^)D )[a-zA-Z0-9_]+/){ |segment|
    idname = segment[:url].match(/^(?:@|＠|〄|☯|⑨|♨|(?:\W|^)D )?(.+)$/)[1]
    user = User.findbyidname(idname)
    if user
      Plugin.call(:show_profile, Service.primary, user)
    else
      Thread.new{
        user = service.scan(:user_show,
                            :no_auto_since_id => false,
                            :screen_name => idname)
        Plugin.call(:show_profile, Service.primary, user) if user } end }

  on_show_profile do |service, user|
    container = profile_head(user)
    i_profile = tab nil, "#{user[:name]} のプロフィール}" do
      set_icon user[:profile_image_url]
      set_deletable true
      shrink
      nativewidget container
      expand
      profile nil end
    Plugin.call(:profiletab, i_profile, user)
    i_profile.active!
  end

  profiletab :usertimeline, "最近のツイート" do
    set_icon MUI::Skin.get("timeline.png")
    i_timeline = timeline nil
    Service.primary.user_timeline(user_id: user[:id], include_rts: 1, count: [UserConfig[:profile_show_tweet_once], 200].min).next{ |tl|
      i_timeline << tl
    }.terminate("@#{user[:idname]} の最近のつぶやきが取得できませんでした。見るなってことですかね")
    timeline_storage[i_timeline.slug] = {timeline: i_timeline, user: user}.freeze end

  profiletab :aboutuser, "ユーザについて" do
    set_icon user[:profile_image_url]
    bio = Gtk::IntelligentTextview.new(user[:detail])
    ago = (Time.now - (user[:created] or 1)).to_i / (60 * 60 * 24)
    nativewidget Gtk::VBox.new.
      closeup(bio).
      closeup(Gtk::Label.new("Twitter開始: #{user[:created].strftime('%Y/%m/%d %H:%M:%S')} (#{ago == 0 ? user[:statuses_count] : (user[:statuses_count].to_f / ago).round_at(2)}tweet/day)\n").left).
      closeup(plugin.relation_bar(user)).
      closeup(plugin.mutebutton(user)).show_all end

  on_appear do |messages|
    timeline_storage.dup.deach{ |tl|
      messages.each{ |message|
        tl.timeline << message if message.user == tl.user } } end

  on_gui_destroy do |widget|
    if widget.is_a? Plugin::GUI::Timeline
      timeline_storage.delete(widget.slug) end end

  command(:aboutuser,
          name: lambda { |opt|
            if defined? opt.messages.first and opt.messages.first.repliable?
              u = opt.messages.first.user
              "#{u[:idname]}(#{u[:name]})について".gsub(/_/, '__')
            else
             "ユーザについて" end },
          condition: lambda{ |opt| opt.messages.first.repliable? },
          visible: true,
          role: :timeline) do |opt|
    Plugin.call(:show_profile, Service.primary, opt.messages.first.user) end

  def mutebutton(user)
    changer = lambda{ |new, widget|
      if new === nil
        UserConfig[:muted_users] and UserConfig[:muted_users].include?(user.idname)
      elsif new
        add_muted_user(user)
      else
        remove_muted_user(user)
      end
    }
    btn = Mtk::boolean(changer, 'ミュート')
  end

  def add_muted_user(user)
    type_strict user => User
    atomic{
      muted = (UserConfig[:muted_users] ||= []).melt
      muted << user.idname
      UserConfig[:muted_users] = muted } end

  def remove_muted_user(user)
    type_strict user => User
    atomic{
      muted = (UserConfig[:muted_users] ||= []).melt
      muted.delete(user.idname)
      UserConfig[:muted_users] = muted } end

  # フォロー関係を表示する
  # ==== Args
  # [user] 対象となるユーザ
  # ==== Return
  # リレーションバーのウィジェット(Gtk::VBox)
  def relation_bar(user)
    icon_size = Gdk::Rectangle.new(0, 0, 16, 16)
    container = Gtk::VBox.new(false, 4)
    Service.all.each{ |me|
      following = followed = nil
      w_following_label = Gtk::Label.new("関係を取得中")
      w_followed_label = Gtk::Label.new("")
      w_eventbox_image_following = Gtk::EventBox.new
      w_eventbox_image_followed = Gtk::EventBox.new
      followbutton = Gtk::Button.new
      followbutton.sensitive = false
      # フォローしている状態の更新
      m_following_refresh = lambda { |new|
        if not w_eventbox_image_following.destroyed?
          following = new
          if not w_eventbox_image_following.children.empty?
            w_eventbox_image_following.remove(w_eventbox_image_following.children.first) end
          w_eventbox_image_following.add(Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_following.png" : "arrow_notfollowing.png"), icon_size).show_all)
          w_following_label.text = new ? "ﾌｮﾛｰしている" : "ﾌｮﾛｰしていない"
          followbutton.label = new ? "解除" : "ﾌｮﾛｰ" end }
      # フォローされている状態の更新
      m_followed_refresh = lambda { |new|
        if not w_eventbox_image_followed.destroyed?
          followed = new
          if not w_eventbox_image_followed.children.empty?
            w_eventbox_image_followed.remove(w_eventbox_image_followed.children.first) end
          w_eventbox_image_followed.add(Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_followed.png" : "arrow_notfollowed.png"), icon_size).show_all)
          w_followed_label.text = new ? "ﾌｮﾛｰされている" : "ﾌｮﾛｰされていない" end }

      container.closeup(Gtk::HBox.new(false, icon_size.width).
                        closeup(Gtk::WebIcon.new(me.user_obj[:profile_image_url], icon_size)).
                        closeup(Gtk::HBox.new.
                                closeup(w_eventbox_image_following).
                                closeup(w_following_label)).
                        closeup(Gtk::HBox.new.
                                closeup(w_eventbox_image_followed).
                                closeup(w_followed_label)).
                        closeup(Gtk::WebIcon.new(user[:profile_image_url], icon_size)).
                        closeup(followbutton))
      Service.primary.friendship(target_id: user[:id], source_id: me.user_obj[:id]).next{ |rel|
        if rel and not(w_eventbox_image_following.destroyed?)
          m_following_refresh.call(rel[:following])
          m_followed_refresh.call(rel[:followed_by])
          handler_followings_created = on_followings_created do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(true) end end
          handler_followings_destroy = on_followings_destroy do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(false) end end
          followbutton.ssc(:clicked){
            followbutton.sensitive = false
            event = following ? :followings_destroy : :followings_created
            me.__send__(following ? :unfollow : :follow, user).next{ |msg|
              Plugin.call(event, me, [user])
              followbutton.sensitive = true unless followbutton.destroyed? }.
            terminate.trap{
              followbutton.sensitive = true unless followbutton.destroyed? }<commit_msg>フォロー関係の表示のレイアウト調整<commit_after># -*- coding: utf-8 -*-

Plugin.create :profile do
  UserConfig[:profile_show_tweet_once] ||= 20
  @counter = gen_counter
  plugin = self
  timeline_storage = {}                # {slug: {timeline, user}}

  Message::Entity.addlinkrule(:user_mentions, /(?:@|＠|〄|☯|⑨|♨|(?:\W|^)D )[a-zA-Z0-9_]+/){ |segment|
    idname = segment[:url].match(/^(?:@|＠|〄|☯|⑨|♨|(?:\W|^)D )?(.+)$/)[1]
    user = User.findbyidname(idname)
    if user
      Plugin.call(:show_profile, Service.primary, user)
    else
      Thread.new{
        user = service.scan(:user_show,
                            :no_auto_since_id => false,
                            :screen_name => idname)
        Plugin.call(:show_profile, Service.primary, user) if user } end }

  on_show_profile do |service, user|
    container = profile_head(user)
    i_profile = tab nil, "#{user[:name]} のプロフィール}" do
      set_icon user[:profile_image_url]
      set_deletable true
      shrink
      nativewidget container
      expand
      profile nil end
    Plugin.call(:profiletab, i_profile, user)
    i_profile.active!
  end

  profiletab :usertimeline, "最近のツイート" do
    set_icon MUI::Skin.get("timeline.png")
    i_timeline = timeline nil
    Service.primary.user_timeline(user_id: user[:id], include_rts: 1, count: [UserConfig[:profile_show_tweet_once], 200].min).next{ |tl|
      i_timeline << tl
    }.terminate("@#{user[:idname]} の最近のつぶやきが取得できませんでした。見るなってことですかね")
    timeline_storage[i_timeline.slug] = {timeline: i_timeline, user: user}.freeze end

  profiletab :aboutuser, "ユーザについて" do
    set_icon user[:profile_image_url]
    bio = Gtk::IntelligentTextview.new(user[:detail])
    ago = (Time.now - (user[:created] or 1)).to_i / (60 * 60 * 24)
    nativewidget Gtk::VBox.new.
      closeup(bio).
      closeup(Gtk::Label.new("Twitter開始: #{user[:created].strftime('%Y/%m/%d %H:%M:%S')} (#{ago == 0 ? user[:statuses_count] : (user[:statuses_count].to_f / ago).round_at(2)}tweet/day)\n").left).
      closeup(plugin.relation_bar(user)).
      closeup(plugin.mutebutton(user)).show_all end

  on_appear do |messages|
    timeline_storage.dup.deach{ |tl|
      messages.each{ |message|
        tl.timeline << message if message.user == tl.user } } end

  on_gui_destroy do |widget|
    if widget.is_a? Plugin::GUI::Timeline
      timeline_storage.delete(widget.slug) end end

  command(:aboutuser,
          name: lambda { |opt|
            if defined? opt.messages.first and opt.messages.first.repliable?
              u = opt.messages.first.user
              "#{u[:idname]}(#{u[:name]})について".gsub(/_/, '__')
            else
             "ユーザについて" end },
          condition: lambda{ |opt| opt.messages.first.repliable? },
          visible: true,
          role: :timeline) do |opt|
    Plugin.call(:show_profile, Service.primary, opt.messages.first.user) end

  def mutebutton(user)
    changer = lambda{ |new, widget|
      if new === nil
        UserConfig[:muted_users] and UserConfig[:muted_users].include?(user.idname)
      elsif new
        add_muted_user(user)
      else
        remove_muted_user(user)
      end
    }
    btn = Mtk::boolean(changer, 'ミュート')
  end

  def add_muted_user(user)
    type_strict user => User
    atomic{
      muted = (UserConfig[:muted_users] ||= []).melt
      muted << user.idname
      UserConfig[:muted_users] = muted } end

  def remove_muted_user(user)
    type_strict user => User
    atomic{
      muted = (UserConfig[:muted_users] ||= []).melt
      muted.delete(user.idname)
      UserConfig[:muted_users] = muted } end

  # フォロー関係を表示する
  # ==== Args
  # [user] 対象となるユーザ
  # ==== Return
  # リレーションバーのウィジェット(Gtk::VBox)
  def relation_bar(user)
    icon_size = Gdk::Rectangle.new(0, 0, 32, 32)
    arrow_size = Gdk::Rectangle.new(0, 0, 16, 16)
    container = Gtk::VBox.new(false, 4)
    Service.all.each{ |me|
      following = followed = nil
      w_following_label = Gtk::Label.new("関係を取得中")
      w_followed_label = Gtk::Label.new("")
      w_eventbox_image_following = Gtk::EventBox.new
      w_eventbox_image_followed = Gtk::EventBox.new
      followbutton = Gtk::Button.new
      followbutton.sensitive = false
      # フォローしている状態の更新
      m_following_refresh = lambda { |new|
        if not w_eventbox_image_following.destroyed?
          following = new
          if not w_eventbox_image_following.children.empty?
            w_eventbox_image_following.remove(w_eventbox_image_following.children.first) end
          w_eventbox_image_following.add(Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_following.png" : "arrow_notfollowing.png"), arrow_size).show_all)
          w_following_label.text = new ? "ﾌｮﾛｰしている" : "ﾌｮﾛｰしていない"
          followbutton.label = new ? "解除" : "ﾌｮﾛｰ" end }
      # フォローされている状態の更新
      m_followed_refresh = lambda { |new|
        if not w_eventbox_image_followed.destroyed?
          followed = new
          if not w_eventbox_image_followed.children.empty?
            w_eventbox_image_followed.remove(w_eventbox_image_followed.children.first) end
          w_eventbox_image_followed.add(Gtk::WebIcon.new(MUI::Skin.get(new ? "arrow_followed.png" : "arrow_notfollowed.png"), arrow_size).show_all)
          w_followed_label.text = new ? "ﾌｮﾛｰされている" : "ﾌｮﾛｰされていない" end }

      container.closeup(Gtk::HBox.new(false, icon_size.width/2).
                        closeup(Gtk::WebIcon.new(me.user_obj[:profile_image_url], icon_size)).
                        closeup(Gtk::VBox.new.
                                closeup(Gtk::HBox.new.
                                        closeup(w_eventbox_image_following).
                                        closeup(w_following_label)).
                                closeup(Gtk::HBox.new.
                                closeup(w_eventbox_image_followed).
                                closeup(w_followed_label))).
                        closeup(Gtk::WebIcon.new(user[:profile_image_url], icon_size)).
                        closeup(followbutton))
      Service.primary.friendship(target_id: user[:id], source_id: me.user_obj[:id]).next{ |rel|
        if rel and not(w_eventbox_image_following.destroyed?)
          m_following_refresh.call(rel[:following])
          m_followed_refresh.call(rel[:followed_by])
          handler_followings_created = on_followings_created do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(true) end end
          handler_followings_destroy = on_followings_destroy do |service, dst_users|
            if service == me and dst_users.include?(user)
              m_following_refresh.call(false) end end
          followbutton.ssc(:clicked){
            followbutton.sensitive = false
            event = following ? :followings_destroy : :followings_created
            me.__send__(following ? :unfollow : :follow, user).next{ |msg|
              Plugin.call(event, me, [user])
              followbutton.sensitive = true unless followbutton.destroyed? }.
            terminate.trap{
              followbutton.sensitive = true unless followbutton.destroyed? }
{'fraction_non_alphanumeric': 0.1266400456360525, 'fraction_numerical': 0.0042070735881346265, 'mean_word_length': 2.679171038824764, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2205029', 'n_tokens_mistral': 5077, 'n_tokens_neox': 4854, 'n_words': 778}