new time and bar display methods
This commit is contained in:
		
							parent
							
								
									5358639307
								
							
						
					
					
						commit
						1b67057527
					
				@ -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
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user