Merge branch 'master' into msg-replies

This commit is contained in:
Alexander Zveruk 2020-05-19 23:36:50 +03:00
commit 1441cd2197
4 changed files with 85 additions and 55 deletions

View file

@ -7,6 +7,7 @@ DEFAULT_CONFIG = os.path.expanduser("~/.config/tg/tg.conf")
DEFAULT_FILES = os.path.expanduser("~/.cache/tg/")
max_download_size = "10MB"
record_cmd = None
long_msg_cmd = "vim -c 'startinsert' {file_path}"
def get_cfg(config=DEFAULT_CONFIG):

View file

@ -1,7 +1,9 @@
import curses
import logging
import os
import threading
from datetime import datetime
from signal import SIGWINCH, signal
from tempfile import NamedTemporaryFile
from typing import Any, Dict, Optional
@ -60,6 +62,8 @@ class Controller:
"updateMessageSendSucceeded": self.update_msg_send_succeeded,
"updateNewMessage": self.update_new_msg,
}
self.chat_size = 0.5
signal(SIGWINCH, self.resize_handler)
def send_file(self, send_file_fun, *args, **kwargs):
file_path = self.view.status.get_input()
@ -118,35 +122,64 @@ class Controller:
log.info("Opening file: %s", path)
s.open_file(path)
def write_long_msg(self):
file_path = "/tmp/tg-msg.txt"
with suspend(self.view) as s:
s.call(config.long_msg_cmd.format(file_path=file_path))
if not os.path.isfile(file_path):
return
with open(file_path) as f:
msg = f.read().strip()
os.remove(file_path)
if msg:
self.model.send_message(text=msg)
with self.lock:
self.view.status.draw("Message sent")
def resize_handler(self, signum, frame):
curses.endwin()
self.view.stdscr.refresh()
self.resize()
def resize(self):
rows, cols = self.view.stdscr.getmaxyx()
# If we didn't clear the screen before doing this,
# the original window contents would remain on the screen
# and we would see the window text twice.
self.view.stdscr.erase()
self.view.stdscr.noutrefresh()
self.view.chats.resize(rows, cols, self.chat_size)
self.view.msgs.resize(rows, cols, 1 - self.chat_size)
self.view.status.resize(rows, cols)
self.render()
def handle_msgs(self) -> str:
self.view.chats.resize(0.2)
self.view.msgs.resize(0.8)
self.refresh_chats()
self.chat_size = 0.2
self.resize()
while True:
repeat_factor, keys = self.view.get_keys(
self.view.chats.h, self.view.chats.w
)
repeat_factor, keys = self.view.get_keys()
log.info("Pressed keys: %s", keys)
if keys == "q":
return "QUIT"
elif keys == "]":
if self.model.next_chat():
self.refresh_chats()
self.render()
elif keys == "[":
if self.model.prev_chat():
self.refresh_chats()
self.render()
elif keys == "J":
if self.model.next_msg(10):
self.refresh_msgs()
elif keys == "K":
if self.model.prev_msg(10):
self.refresh_msgs()
elif keys in ("j", "^P"):
elif keys in ("j", "^N"):
if self.model.next_msg(repeat_factor):
self.refresh_msgs()
elif keys in ("k", "^N"):
elif keys in ("k", "^P"):
if self.model.prev_msg(repeat_factor):
self.refresh_msgs()
elif keys == "G":
@ -198,15 +231,16 @@ class Controller:
# reply to this msg
# print to status line
pass
elif keys == "I":
# open vim or emacs to write long messages
pass
elif keys in ("i", "a"):
# write new message
msg = self.view.status.get_input()
if msg:
self.model.send_message(text=msg)
self.view.status.draw(f"Sent: {msg}")
with self.lock:
self.view.status.draw(f"Sent: {msg}")
elif keys in ("I", "A"):
self.write_long_msg()
elif keys in ("h", "^D"):
return "BACK"
@ -216,14 +250,12 @@ class Controller:
breakpoint()
def handle_chats(self) -> None:
self.view.chats.resize(0.5)
self.view.msgs.resize(0.5)
self.refresh_chats()
self.chat_size = 0.5
self.resize()
while True:
repeat_factor, keys = self.view.get_keys(
self.view.chats.h, self.view.chats.w
)
repeat_factor, keys = self.view.get_keys()
log.info("Pressed keys: %s", keys)
if keys == "q":
return
@ -231,29 +263,28 @@ class Controller:
rc = self.handle_msgs()
if rc == "QUIT":
return
self.view.chats.resize(0.5)
self.view.msgs.resize(0.5)
self.refresh_chats()
self.chat_size = 0.5
self.resize()
elif keys in ("j", "^N"):
if self.model.next_chat(repeat_factor):
self.refresh_chats()
self.render()
elif keys in ("k", "^P"):
if self.model.prev_chat(repeat_factor):
self.refresh_chats()
self.render()
elif keys in ("J",):
if self.model.next_chat(10):
self.refresh_chats()
self.render()
elif keys in ("K",):
if self.model.prev_chat(10):
self.refresh_chats()
self.render()
elif keys == "gg":
if self.model.first_chat():
self.refresh_chats()
self.render()
elif keys == "bp":
with suspend(self.view):
@ -292,11 +323,11 @@ class Controller:
chat_id, notification_settings
)
def refresh_chats(self) -> None:
def render(self) -> None:
with self.lock:
# using lock here, because refresh_chats is used from another
# using lock here, because render is used from another
# thread by tdlib python wrapper
page_size = self.view.msgs.h
page_size = self.view.chats.h
chats = self.model.get_chats(
self.model.current_chat, page_size, MSGS_LEFT_SCROLL_THRESHOLD
)
@ -451,7 +482,7 @@ class Controller:
if chat["id"] == current_chat_id:
self.model.current_chat = i
break
self.refresh_chats()
self.render()
@handle_exception
def update_chat_notification_settings(self, update):
@ -461,7 +492,7 @@ class Controller:
self.model.chats.update_chat(
chat_id, notification_settings=notification_settings
)
self.refresh_chats()
self.render()
@handle_exception
def update_msg_send_succeeded(self, update):

View file

@ -1,7 +1,6 @@
import logging
import logging.handlers
import signal
import sys
import threading
from curses import window, wrapper
from functools import partial
@ -171,6 +170,7 @@ def main():
cfg.get("max_download_size", config.max_download_size)
)
config.record_cmd = cfg.get("record_cmd")
config.long_msg_cmd = cfg.get("long_msg_cmd", config.long_msg_cmd)
tg.login()
wrapper(partial(run, tg))

View file

@ -35,6 +35,7 @@ class View:
curses.cbreak()
stdscr.keypad(True)
curses.curs_set(0)
curses.start_color()
curses.use_default_colors()
# init white color first to initialize colors correctly
@ -46,15 +47,15 @@ class View:
self.status = status_view
self.max_read = 2048
def get_keys(self, y: int, x: int) -> Tuple[int, str]:
def get_keys(self) -> Tuple[int, str]:
keys = repeat_factor = ""
for _ in range(MAX_KEYBINDING_LENGTH):
ch = self.stdscr.getch(y, x)
ch = self.stdscr.getch()
log.info("raw ch without unctrl: %s", ch)
try:
key = curses.unctrl(ch).decode()
except UnicodeDecodeError:
except Exception:
log.warning("cant uncrtl: %s", ch)
break
if key.isdigit():
@ -77,14 +78,15 @@ class StatusView:
self.w = curses.COLS
self.y = curses.LINES - 1
self.x = 0
self.stdscr = stdscr
self.win = stdscr.subwin(self.h, self.w, self.y, self.x)
self._refresh = self.win.refresh
def resize(self):
self.w = curses.COLS
self.y = curses.LINES - 1
def resize(self, rows: int, cols: int):
self.w = cols
self.y = rows - 1
self.win.resize(self.h, self.w)
self.win.wmove(self.y, self.x)
self.win.mvwin(self.y, self.x)
def draw(self, msg: Optional[str] = None) -> None:
self.win.clear()
@ -125,14 +127,15 @@ class StatusView:
class ChatView:
def __init__(self, stdscr: window, p: float = 0.5) -> None:
self.stdscr = stdscr
self.h = 0
self.w = 0
self.win = stdscr.subwin(self.h, self.w, 0, 0)
self._refresh = self.win.refresh
def resize(self, p: float = 0.25) -> None:
self.h = curses.LINES - 1
self.w = round(curses.COLS * p)
def resize(self, rows: int, cols: int, p: float = 0.25) -> None:
self.h = rows - 1
self.w = round(cols * p)
self.win.resize(self.h, self.w)
def _msg_color(self, is_selected: bool = False) -> int:
@ -174,21 +177,16 @@ class ChatView:
for attr, elem in zip(
self._chat_attributes(is_selected), [f"{date} ", title]
):
if offset > self.w:
break
self.win.addstr(
i,
offset,
truncate_to_len(elem, self.w - offset - 1),
truncate_to_len(elem, max(0, self.w - offset - 1)),
attr,
)
offset += len(elem)
if offset >= self.w:
break
last_msg = " " + last_msg.replace("\n", " ")
last_msg = truncate_to_len(last_msg, self.w - offset)
last_msg = truncate_to_len(last_msg, max(0, self.w - offset))
if last_msg.strip():
self.win.addstr(
i, offset, last_msg, self._msg_color(is_selected)
@ -238,10 +236,10 @@ class MsgView:
self.win = self.stdscr.subwin(self.h, self.w, 0, self.x)
self._refresh = self.win.refresh
def resize(self, p: float = 0.5) -> None:
self.h = curses.LINES - 1
self.w = round(curses.COLS * p)
self.x = curses.COLS - self.w
def resize(self, rows: int, cols: int, p: float = 0.5) -> None:
self.h = rows - 1
self.w = round(cols * p)
self.x = cols - self.w
self.win.resize(self.h, self.w)
self.win.mvwin(0, self.x)