new time and bar display methods

pull/4/head
Blake DeMarcy 2017-04-08 05:09:50 -05:00
parent 5358639307
commit 1b67057527
1 changed files with 90 additions and 55 deletions

View File

@ -1,6 +1,7 @@
# -*- fill-column: 72 -*- # -*- fill-column: 72 -*-
from time import time, sleep, localtime from time import time, sleep, localtime
from datetime import datetime
from network import BBJ, URLError from network import BBJ, URLError
from string import punctuation from string import punctuation
from subprocess import run from subprocess import run
@ -49,17 +50,19 @@ editors = ["nano", "emacs", "vim", "micro", "ed", "joe"]
default_prefs = { default_prefs = {
# well, it WILL default to the internal editor when I write it =) # well, it WILL default to the internal editor when I write it =)
"editor": "vim", "editor": "vim",
"dramatic_exit": True "dramatic_exit": True,
"date": "%Y/%m/%d",
"time": "%H:%M"
} }
class App(object): class App(object):
def __init__(self): def __init__(self):
self.mode = None self.bars = {
self.thread = None "index": "[C]ompose [R]efresh [/]Search [O]ptions [?]Help/More [Q]uit",
self.usermap = {} "thread": "[C]ompose [Q]Back [R]efresh [E]dit [/]Search [B/T]End [?]Help/More"
self.prefs = bbjrc("load") }
self.window_split = False
colors = [ colors = [
("bar", "light magenta", "default"), ("bar", "light magenta", "default"),
("button", "light red", "default"), ("button", "light red", "default"),
@ -74,16 +77,22 @@ class App(object):
("5", "light cyan", "default"), ("5", "light cyan", "default"),
("6", "light magenta", "default") ("6", "light magenta", "default")
] ]
self.loop = urwid.MainLoop(urwid.Frame(
urwid.LineBox(ActionBox(urwid.SimpleFocusListWalker([])),
title="> > T I L D E T O W N < <",
tlcorner="@", tline="=", lline="|", rline="|",
bline="=", trcorner="@", brcorner="@", blcorner="@"
)), colors) self.mode = None
self.walker = self.loop.widget.body.base_widget.body self.thread = None
self.usermap = {}
self.prefs = bbjrc("load")
self.window_split = False
self.last_pos = 0 self.last_pos = 0
self.date_format = "{1}/{2}/{0}"
self.walker = urwid.SimpleFocusListWalker([])
self.loop = urwid.MainLoop(urwid.Frame(
urwid.LineBox(ActionBox(self.walker),
title="> > T I L D E T O W N < <",
tlcorner="@", trcorner="@", blcorner="@", brcorner="@",
tline="=", bline="=", lline="|", rline="|"
)), colors)
self.index() self.index()
@ -99,42 +108,48 @@ class App(object):
), "bar") ), "bar")
def set_footer(self, *controls, static_string=""): def set_footer(self, string):
""" """
Sets the footer, emphasizing the first character of each string Sets the footer to display `string`, applying bar formatting.
argument passed to it. Used to show controls to the user. Applies Other than setting the color, `string` is shown verbatim.
bar formatting.
""" """
text = str() self.loop.widget.footer = \
for control in controls: urwid.AttrMap(urwid.Text(string), "bar")
text += "[{}]{} ".format(control[0], control[1:])
text += static_string
self.loop.widget.footer = urwid.AttrMap(urwid.Text(text), "bar")
def close_editor(self): def close_editor(self):
"""
Close whatever editing widget is open and restore proper
state back the walker.
"""
if self.window_split: if self.window_split:
self.window_split = False self.window_split = False
self.loop.widget.focus_position = "body" self.loop.widget.focus_position = "body"
self.set_footer("lmao") self.set_footer(self.bars["thread"])
name = self.usermap[self.thread["author"]]["user_name"]
self.set_header("~{}: {}", name, self.thread["title"])
else: else:
self.loop.widget = self.loop.widget[0] self.loop.widget = self.loop.widget[0]
def switch_editor(self): def switch_editor(self):
"""
Switch focus between the thread viewer and the open editor
"""
if not self.window_split: if not self.window_split:
return return
elif self.loop.widget.focus_position == "body": elif self.loop.widget.focus_position == "body":
self.loop.widget.focus_position = "footer" self.loop.widget.focus_position = "footer"
focus = "[focused on editor]" focus = "[focused on editor]"
else: else:
self.loop.widget.focus_position = "body" self.loop.widget.focus_position = "body"
focus = "[focused on thread]" focus = "[focused on thread]"
if self.window_split:
control = ("" if self.prefs["editor"] else " [F3]Send") control = "[save/quit to send]" if self.prefs["editor"] else "[F3]Send"
self.loop.widget.footer[0].set_text( self.loop.widget.footer[0].set_text(
"[F1]Abort [F2]Swap%s %s" % (control, focus) "[F1]Abort [F2]Swap %s %s" % (control, focus))
)
def readable_delta(self, modified): def readable_delta(self, modified):
@ -145,7 +160,7 @@ class App(object):
delta = time() - modified delta = time() - modified
hours, remainder = divmod(delta, 3600) hours, remainder = divmod(delta, 3600)
if hours > 48: if hours > 48:
return self.date_format.format(*localtime(modified)) return self.timestring(modified)
elif hours > 1: elif hours > 1:
return "%d hours ago" % hours return "%d hours ago" % hours
elif hours == 1: elif hours == 1:
@ -158,7 +173,7 @@ class App(object):
def make_message_body(self, message): def make_message_body(self, message):
name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"])) name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"]))
info = "@ " + self.date_format.format(*localtime(message["created"])) info = "@ " + self.timestring(message["created"])
if message["edited"]: if message["edited"]:
info += " [edited]" info += " [edited]"
@ -180,17 +195,34 @@ class App(object):
return pile return pile
def timestring(self, epoch, mode="both"):
if mode == "delta":
return self.readable_delta(epoch)
date = datetime.fromtimestamp(epoch)
if mode == "time":
directive = self.prefs["time"]
elif mode == "date":
directive = self.prefs["date"]
else:
directive = "%s %s" % (self.prefs["date"], self.prefs["time"])
return date.strftime(directive)
def make_thread_body(self, thread): def make_thread_body(self, thread):
button = cute_button(">>", self.thread_load, thread["thread_id"]) button = cute_button(">>", self.thread_load, thread["thread_id"])
title = urwid.Text(thread["title"]) title = urwid.Text(thread["title"])
infoline = "by ~{} @ {} | last active {}".format( dateline = "by ~{} @ {}".format(
self.usermap[thread["author"]]["user_name"], self.usermap[thread["author"]]["user_name"],
self.date_format.format(*localtime(thread["created"])), self.timestring(thread["created"])
self.readable_delta(thread["last_mod"])
) )
infoline = "%d replies; active %s" % (
thread["reply_count"], self.timestring(thread["last_mod"], "delta"))
pile = urwid.Pile([ pile = urwid.Pile([
urwid.Columns([(3, urwid.AttrMap(button, "button")), title]), urwid.Columns([(3, urwid.AttrMap(button, "button")), title]),
urwid.AttrMap(urwid.Text(dateline), "dim"),
urwid.AttrMap(urwid.Text(infoline), "dim"), urwid.AttrMap(urwid.Text(infoline), "dim"),
urwid.AttrMap(urwid.Divider("-"), "dim") urwid.AttrMap(urwid.Divider("-"), "dim")
]) ])
@ -209,11 +241,13 @@ class App(object):
threads, usermap = network.thread_index() threads, usermap = network.thread_index()
self.usermap.update(usermap) self.usermap.update(usermap)
self.set_header("{} threads", len(threads)) self.set_header("{} threads", len(threads))
self.set_footer("Refresh", "Compose", "Quit", "/Search", "?Help") self.set_footer(self.bars["index"])
self.walker.clear() self.walker.clear()
for thread in threads: for thread in threads:
self.walker.append(self.make_thread_body(thread)) self.walker.append(self.make_thread_body(thread))
self.loop.widget.body.base_widget.set_focus(self.last_pos) try: self.loop.widget.body.base_widget.set_focus(self.last_pos)
except IndexError:
pass
def thread_load(self, button, thread_id): def thread_load(self, button, thread_id):
@ -229,11 +263,7 @@ class App(object):
self.walker.clear() self.walker.clear()
self.set_header("~{}: {}", self.set_header("~{}: {}",
usermap[thread["author"]]["user_name"], thread["title"]) usermap[thread["author"]]["user_name"], thread["title"])
self.set_footer( self.set_footer(self.bars["thread"])
"Compose", "Refresh",
"\"Quote", "/Search",
"Top", "Bottom", "QBack"
)
for message in thread["messages"]: for message in thread["messages"]:
self.walker.append(self.make_message_body(message)) self.walker.append(self.make_message_body(message))
@ -241,7 +271,9 @@ class App(object):
def refresh(self): def refresh(self):
if self.mode == "index": if self.mode == "index":
self.index() return self.index()
self.thread_load(None, self.thread["thread_id"])
self.loop.widget.body.base_widget.set_focus(len(self.walker) - 1)
def back(self): def back(self):
@ -278,8 +310,7 @@ class App(object):
"Title", self.compose, extra_text=e.description) "Title", self.compose, extra_text=e.description)
self.set_header('Composing "{}"', title) self.set_header('Composing "{}"', title)
self.set_footer(static_string= self.set_footer("[F1]Abort [Save and quit to submit your thread]")
"[F1]Abort [Save and quit to submit your thread]")
self.loop.widget = urwid.Overlay( self.loop.widget = urwid.Overlay(
urwid.LineBox( urwid.LineBox(
@ -294,14 +325,14 @@ class App(object):
self.set_header('Replying to "{}"', self.thread["title"]) self.set_header('Replying to "{}"', self.thread["title"])
self.loop.widget.footer = urwid.Pile([ self.loop.widget.footer = urwid.Pile([
urwid.AttrMap(urwid.Text(""), "bar"), urwid.AttrMap(urwid.Text(""), "bar"),
urwid.BoxAdapter(urwid.LineBox(editor("thread_reply", thread_id=self.thread["thread_id"])), 15), urwid.BoxAdapter(urwid.LineBox(editor("thread_reply",
thread_id=self.thread["thread_id"])),
self.loop.screen_size[1] // 2),
]) ])
self.switch_editor() self.switch_editor()
class MessageBody(urwid.Text): class MessageBody(urwid.Text):
pass pass
@ -317,7 +348,7 @@ class FootPrompt(urwid.Edit):
if key != "enter": if key != "enter":
return super(FootPrompt, self).keypress(size, key) return super(FootPrompt, self).keypress(size, key)
app.loop.widget.focus_position = "body" app.loop.widget.focus_position = "body"
app.set_footer() app.set_footer(app.bars[app.mode])
self.callback(self.get_edit_text(), *self.args) self.callback(self.get_edit_text(), *self.args)
@ -580,20 +611,24 @@ def bbjrc(mode, **params):
values depending on `mode`. values depending on `mode`.
""" """
path = os.path.join(os.getenv("HOME"), ".bbjrc") path = os.path.join(os.getenv("HOME"), ".bbjrc")
try: try:
# load it up
with open(path, "r") as _in: with open(path, "r") as _in:
values = json.load(_in) values = json.load(_in)
# update it with new keys if necessary
for key, default_value in default_prefs.items():
if key not in values:
values[key] = default_value
# else make one
except FileNotFoundError: except FileNotFoundError:
values = default_prefs values = default_prefs
if mode == "update":
values.update(params)
with open(path, "w") as _out: with open(path, "w") as _out:
json.dump(values, _out) json.dump(values, _out)
if mode == "load":
return values
values.update(params)
with open(path, "w") as _out:
json.dump(values, _out)
return values return values
@ -603,7 +638,7 @@ def main():
motherfucking_rainbows(obnoxious_logo) motherfucking_rainbows(obnoxious_logo)
print(welcome) print(welcome)
log_in() log_in()
sleep(0.6) # let that confirmation message shine sleep(0.8) # let that confirmation message shine
if __name__ == "__main__": if __name__ == "__main__":
global app global app