Archived
1
0
Fork 0
This repository has been archived on 2023-03-27. You can view files and clone it, but cannot push or open issues or pull requests.
cli-old/lib/main.rb

422 lines
10 KiB
Ruby
Raw Normal View History

2017-07-21 02:48:04 -04:00
# frozen_string_literal: true
2017-07-25 19:06:37 -04:00
require 'tox'
2017-07-21 02:48:04 -04:00
require 'thread'
2017-07-21 11:23:43 -04:00
require 'faker'
2017-07-24 08:48:22 -04:00
require 'screen'
2017-07-21 10:00:01 -04:00
2017-07-21 02:48:04 -04:00
class Main
2017-07-25 19:13:06 -04:00
SAVEDATA_FILENAME = File.expand_path '../savedata', __dir__
2017-07-21 02:48:04 -04:00
def self.inherited(_base)
raise "#{self} is final"
end
def self.mutex
(@mutex ||= Mutex.new).tap { freeze }
end
def initialize
raise "#{self.class} is singleton" unless self.class.mutex.try_lock
call
end
private
def call
before_loop
2017-07-25 19:06:37 -04:00
before_iteration
@tox_client.run
2017-07-25 19:13:06 -04:00
ensure
2017-07-21 02:48:04 -04:00
after_loop
end
def before_loop
2017-07-25 19:13:06 -04:00
tox_options = Tox::Options.new
tox_options.savedata = File.binread SAVEDATA_FILENAME if File.exist? SAVEDATA_FILENAME
@tox_client = Tox::Client.new tox_options
2017-07-25 19:06:37 -04:00
2017-07-25 19:26:38 -04:00
on_friends_load @tox_client.friends
2017-07-25 19:06:37 -04:00
@tox_client.on_iteration do
after_iteration
before_iteration
end
2017-07-25 19:15:10 -04:00
@tox_client.on_friend_request do |public_key|
2017-07-25 19:43:52 -04:00
on_friend_add @tox_client.friend_add_norequest public_key
2017-07-25 19:15:10 -04:00
end
2017-07-25 21:17:18 -04:00
@tox_client.on_friend_message(&method(:on_friend_message))
2017-07-24 08:48:22 -04:00
@screen = Screen.new
2017-07-22 05:46:15 -04:00
Style.default = Style.new
2017-07-21 02:48:04 -04:00
end
def after_loop
2017-07-25 19:13:06 -04:00
@screen&.close
File.binwrite SAVEDATA_FILENAME, @tox_client.savedata if @tox_client
2017-07-21 02:48:04 -04:00
end
2017-07-21 07:41:05 -04:00
def before_iteration
2017-07-24 18:38:23 -04:00
@screen.props = state
2017-07-24 08:48:22 -04:00
@screen.render
2017-07-21 07:41:05 -04:00
end
2017-07-21 02:48:04 -04:00
def after_iteration
2017-07-24 08:48:22 -04:00
@screen.poll
2017-07-21 08:03:53 -04:00
end
2017-07-24 18:38:23 -04:00
def state
@state ||= {
2017-07-25 09:46:31 -04:00
x: 0,
y: 0,
width: Curses.stdscr.maxx,
height: Curses.stdscr.maxy,
2017-07-24 18:38:23 -04:00
focus: :sidebar,
2017-07-25 09:46:31 -04:00
focused: true,
2017-07-25 12:22:38 -04:00
on_window_left: method(:on_window_left),
on_window_right: method(:on_window_right),
2017-07-25 12:22:38 -04:00
on_menu_up: method(:on_menu_up),
on_menu_down: method(:on_menu_down),
2017-07-25 12:36:19 -04:00
on_new_message_putc: method(:on_new_message_putc),
2017-07-25 13:36:05 -04:00
on_new_message_left: method(:on_new_message_left),
on_new_message_right: method(:on_new_message_right),
on_new_message_home: method(:on_new_message_home),
on_new_message_end: method(:on_new_message_end),
2017-07-25 13:41:55 -04:00
on_new_message_backspace: method(:on_new_message_backspace),
on_new_message_delete: method(:on_new_message_delete),
2017-07-25 21:46:41 -04:00
active_friend_number: nil,
friends: {}.freeze,
2017-07-24 18:38:23 -04:00
sidebar: {
2017-07-25 09:46:31 -04:00
x: 0,
y: 0,
width: Widgets::Logo::WIDTH,
height: Curses.stdscr.maxy,
2017-07-24 18:38:23 -04:00
focus: :menu,
2017-07-25 09:46:31 -04:00
focused: true,
2017-07-25 10:29:01 -04:00
logo: {
2017-07-25 09:46:31 -04:00
x: 0,
y: 0,
width: Widgets::Logo::WIDTH,
2017-07-25 10:29:01 -04:00
height: Widgets::Logo::HEIGHT,
}.freeze,
menu: {
x: 0,
y: Widgets::Logo::HEIGHT,
width: Widgets::Logo::WIDTH,
2017-07-25 09:46:31 -04:00
height: Curses.stdscr.maxy - Widgets::Logo::HEIGHT,
2017-07-24 18:38:23 -04:00
focused: true,
2017-07-25 09:46:31 -04:00
2017-07-24 18:38:23 -04:00
active: 0,
top: 0,
}.freeze,
}.freeze,
2017-07-25 09:46:31 -04:00
2017-07-24 18:38:23 -04:00
chat: {
2017-07-25 10:29:01 -04:00
x: Widgets::Logo::WIDTH,
2017-07-25 09:46:31 -04:00
y: 0,
width: Curses.stdscr.maxx - Widgets::Logo::WIDTH,
height: Curses.stdscr.maxy,
2017-07-24 18:38:23 -04:00
focus: :new_message,
2017-07-25 09:46:31 -04:00
focused: false,
2017-07-24 18:38:23 -04:00
info: {
2017-07-25 10:29:01 -04:00
x: Widgets::Logo::WIDTH,
2017-07-25 09:46:31 -04:00
y: 0,
width: Curses.stdscr.maxx - Widgets::Logo::WIDTH,
height: 2,
focused: false,
2017-07-24 18:38:23 -04:00
}.freeze,
2017-07-25 09:46:31 -04:00
2017-07-24 18:38:23 -04:00
new_message: {
2017-07-25 10:29:01 -04:00
x: Widgets::Logo::WIDTH,
y: Curses.stdscr.maxy - 1,
2017-07-25 09:46:31 -04:00
width: Curses.stdscr.maxx - Widgets::Logo::WIDTH,
2017-07-25 10:29:01 -04:00
height: 1,
2017-07-25 09:46:31 -04:00
focused: false,
2017-07-24 18:38:23 -04:00
text: '',
cursor_pos: 0,
}.freeze,
2017-07-25 09:46:31 -04:00
2017-07-24 18:38:23 -04:00
history: {
2017-07-25 10:29:01 -04:00
x: Widgets::Logo::WIDTH,
y: 2,
2017-07-25 09:46:31 -04:00
width: Curses.stdscr.maxx - Widgets::Logo::WIDTH,
2017-07-25 10:29:01 -04:00
height: Curses.stdscr.maxy - 3,
2017-07-25 09:46:31 -04:00
focused: true,
2017-07-24 18:38:23 -04:00
}.freeze,
}.freeze,
}.freeze
end
2017-07-25 19:26:38 -04:00
def on_friends_load(friends)
@state = state.merge(
active_friend_number: friends.empty? ? nil : friends.first.number,
friends: friends.map do |friend|
[
friend.number,
public_key: friend.public_key.to_hex.freeze,
name: friend.name.freeze,
status: friend.status,
status_message: friend.status_message.freeze,
history: [].freeze,
]
end.to_h.freeze,
2017-07-25 19:43:52 -04:00
).freeze
end
def on_friend_add(friend)
@state = state.merge(
friends: state[:friends].merge(
friend.number => {
public_key: friend.public_key.to_hex.freeze,
name: friend.name.freeze,
status: friend.status,
status_message: friend.status_message.freeze,
history: [].freeze,
}.freeze,
2017-07-25 19:26:38 -04:00
).freeze,
).freeze
end
2017-07-25 21:17:18 -04:00
def on_friend_message(friend, text)
@state = state.merge(
friends: state[:friends].merge(
friend.number => state[:friends][friend.number].merge(
history: (state[:friends][friend.number][:history] + [
out: false,
time: Time.now.utc.freeze,
name: friend.name.freeze,
text: text.freeze,
2017-07-25 21:17:18 -04:00
]).freeze,
).freeze,
).freeze,
).freeze
end
def on_window_left
@state = state.merge(
focus: :sidebar,
sidebar: state[:sidebar].merge(
focused: true,
logo: state[:sidebar][:logo].merge(focused: state[:sidebar][:focus] == :logo).freeze,
menu: state[:sidebar][:menu].merge(focused: state[:sidebar][:focus] == :menu).freeze,
).freeze,
chat: state[:chat].merge(
focused: false,
info: state[:chat][:info].merge(focused: false).freeze,
new_message: state[:chat][:new_message].merge(focused: false).freeze,
history: state[:chat][:history].merge(focused: false).freeze,
).freeze,
).freeze
end
def on_window_right
@state = state.merge(
2017-07-25 12:36:19 -04:00
focus: :chat,
sidebar: state[:sidebar].merge(
focused: true,
logo: state[:sidebar][:logo].merge(focused: false).freeze,
menu: state[:sidebar][:menu].merge(focused: false).freeze,
).freeze,
chat: state[:chat].merge(
focused: false,
info: state[:chat][:info].merge(focused: state[:chat][:focus] == :info).freeze,
new_message: state[:chat][:new_message].merge(focused: state[:chat][:focus] == :new_message).freeze,
history: state[:chat][:history].merge(focused: state[:chat][:focus] == :history).freeze,
).freeze,
).freeze
end
2017-07-25 12:22:38 -04:00
def on_menu_up
@state = state.merge(
sidebar: state[:sidebar].merge(
2017-07-25 13:21:15 -04:00
menu: self.class.update_menu(
state[:sidebar][:menu],
state[:friends].count,
active: state[:sidebar][:menu][:active] - 1,
2017-07-25 13:21:15 -04:00
),
2017-07-25 12:22:38 -04:00
).freeze,
)
@state[:active_friend_number] = state[:friends].keys[@state[:sidebar][:menu][:active]]
@state.freeze
2017-07-25 12:22:38 -04:00
end
def on_menu_down
@state = state.merge(
sidebar: state[:sidebar].merge(
2017-07-25 13:21:15 -04:00
menu: self.class.update_menu(
state[:sidebar][:menu],
state[:friends].count,
active: state[:sidebar][:menu][:active] + 1,
2017-07-25 13:21:15 -04:00
),
2017-07-25 12:22:38 -04:00
).freeze,
)
@state[:active_friend_number] = state[:friends].keys[@state[:sidebar][:menu][:active]]
@state.freeze
2017-07-25 12:22:38 -04:00
end
2017-07-25 12:36:19 -04:00
def on_new_message_putc(char)
text = state[:chat][:new_message][:text]
cursor_pos = state[:chat][:new_message][:cursor_pos]
@state = state.merge(
chat: state[:chat].merge(
2017-07-25 13:27:25 -04:00
new_message: self.class.update_new_message(
state[:chat][:new_message],
2017-07-25 12:36:19 -04:00
text: "#{text[0...cursor_pos]}#{char}#{text[cursor_pos..-1]}",
cursor_pos: cursor_pos + 1,
2017-07-25 13:27:25 -04:00
),
2017-07-25 12:36:19 -04:00
).freeze,
).freeze
end
2017-07-25 13:21:15 -04:00
2017-07-25 13:36:05 -04:00
def on_new_message_left
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: state[:chat][:new_message][:text],
cursor_pos: state[:chat][:new_message][:cursor_pos] - 1,
),
).freeze,
).freeze
end
def on_new_message_right
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: state[:chat][:new_message][:text],
cursor_pos: state[:chat][:new_message][:cursor_pos] + 1,
),
).freeze,
).freeze
end
def on_new_message_home
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: state[:chat][:new_message][:text],
cursor_pos: 0,
),
).freeze,
).freeze
end
def on_new_message_end
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: state[:chat][:new_message][:text],
cursor_pos: state[:chat][:new_message][:text].length,
),
).freeze,
).freeze
end
2017-07-25 13:41:55 -04:00
def on_new_message_backspace
text = state[:chat][:new_message][:text]
cursor_pos = state[:chat][:new_message][:cursor_pos]
return unless cursor_pos.positive?
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: "#{text[0...(cursor_pos - 1)]}#{text[cursor_pos..-1]}",
cursor_pos: cursor_pos - 1,
),
).freeze,
).freeze
end
def on_new_message_delete
text = state[:chat][:new_message][:text]
cursor_pos = state[:chat][:new_message][:cursor_pos]
return if cursor_pos > text.length
@state = state.merge(
chat: state[:chat].merge(
new_message: self.class.update_new_message(
state[:chat][:new_message],
text: "#{text[0...cursor_pos]}#{text[(cursor_pos + 1)..-1]}",
cursor_pos: cursor_pos,
),
).freeze,
).freeze
end
2017-07-25 13:21:15 -04:00
class << self
def update_menu(state, items_count, active:)
2017-07-25 13:21:15 -04:00
top = state[:top]
if active.negative?
active = items_count - 1
elsif active >= items_count
2017-07-25 13:21:15 -04:00
active = 0
end
if active < state[:top]
top = active
elsif active >= state[:top] + state[:height]
top = active - state[:height] + 1
end
state.merge(
active: active,
top: top,
).freeze
end
2017-07-25 13:27:25 -04:00
def update_new_message(state, text:, cursor_pos:)
if cursor_pos.negative?
cursor_pos = 0
elsif cursor_pos > text.length
cursor_pos = text.length
end
state.merge(
text: text,
cursor_pos: cursor_pos,
).freeze
end
2017-07-25 13:21:15 -04:00
end
end