add color options

pull/4/head
Blake DeMarcy 2017-04-10 09:02:08 -05:00
parent 88f43d023a
commit 31ccd5c232
4 changed files with 183 additions and 47 deletions

View File

@ -67,7 +67,7 @@ class BBJ(object):
self.user_name = self.user_auth = None self.user_name = self.user_auth = None
self.send_auth = True self.send_auth = True
try: try:
self("get_me") self.user = self("get_me")["data"]
except URLError: except URLError:
raise URLError("Cannot connect to %s (is the server down?)" % self.base[0:-2]) raise URLError("Cannot connect to %s (is the server down?)" % self.base[0:-2])
@ -275,6 +275,7 @@ class BBJ(object):
self.user_auth = user_auth self.user_auth = user_auth
self.user_name = user_name self.user_name = user_name
self.user = self("get_me")["data"]
return True return True
@ -331,7 +332,6 @@ class BBJ(object):
return response["data"] return response["data"]
def user_register(self, user_name, user_auth, hash_auth=True, set_as_user=True): def user_register(self, user_name, user_auth, hash_auth=True, set_as_user=True):
""" """
Register user_name into the system with user_auth. Unless hash_auth Register user_name into the system with user_auth. Unless hash_auth
@ -368,12 +368,20 @@ class BBJ(object):
]) ])
if set_as_user: if set_as_user:
self.user_name = user_name self.set_credentials(user_name, user_auth, False)
self.user_auth = user_auth
return response return response
def user_update(self, **params):
"""
Update the user's data on the server.
"""
response = self("user_update", **params)
self.user = self("get_me")["data"]
return response["data"]
def thread_index(self): def thread_index(self):
""" """
Returns a tuple where [0] is a list of all threads ordered by Returns a tuple where [0] is a list of all threads ordered by

View File

@ -46,11 +46,10 @@ colors = [
] ]
editors = ["nano", "emacs", "vim", "micro", "ed", "joe"] editors = ["nano", "vim", "emacs", "vim -u NONE", "emacs -Q", "micro", "ed", "joe"]
# defaults to internal editor, integrates the above as well # defaults to internal editor, integrates the above as well
default_prefs = { default_prefs = {
# well, it WILL default to the internal editor when I write it =) "editor": None,
"editor": "vim",
"integrate_external_editor": True, "integrate_external_editor": True,
"dramatic_exit": True, "dramatic_exit": True,
"date": "%Y/%m/%d", "date": "%Y/%m/%d",
@ -63,24 +62,26 @@ default_prefs = {
class App(object): class App(object):
def __init__(self): def __init__(self):
self.bars = { self.bars = {
"index": "[C]ompose [R]efresh [/]Search [O]ptions [?]Help/More [Q]uit", "index": "[C]ompose [R]efresh [O]ptions [?]Help/More [Q]uit",
"thread": "[C]ompose [Q]Back [R]efresh [E]dit [/]Search [B/T]End [?]Help/More" "thread": "[C]ompose [Q]Back [R]efresh [Enter]Post Options [/]Search [B/T]End [?]Help/More"
} }
colors = [ colors = [
("default", "default", "default"), ("default", "default", "default"),
("bar", "light magenta", "default"), ("bar", "light magenta", "default"),
("button", "light red", "default"), ("button", "light red", "default"),
("hover", "light cyan", "default"),
("dim", "dark gray", "default"), ("dim", "dark gray", "default"),
# map the bbj api color values for display # map the bbj api color values for display
("0", "default", "default"), ("0", "default", "default"),
("1", "light red", "default"), ("1", "dark red", "default"),
("2", "yellow", "default"), # sounds ugly but brown is as close as we can get to yellow without being bold
("3", "light green", "default"), ("2", "brown", "default"),
("4", "light blue", "default"), ("3", "dark green", "default"),
("5", "light cyan", "default"), ("4", "dark blue", "default"),
("6", "light magenta", "default") ("5", "dark cyan", "default"),
("6", "dark magenta", "default")
] ]
self.mode = None self.mode = None
@ -124,6 +125,33 @@ class App(object):
self.loop.widget.footer = urwid.AttrMap(urwid.Text(string), "bar") self.loop.widget.footer = urwid.AttrMap(urwid.Text(string), "bar")
def set_default_header(self):
"""
Sets the header to the default for the current screen.
"""
if self.mode == "thread":
name = self.usermap[self.thread["author"]]["user_name"]
self.set_header("~{}: {}", name, self.thread["title"])
else:
self.set_header("{} threads", len(self.walker))
def set_default_footer(self):
"""
Sets the footer to the default for the current screen.
"""
self.set_footer(self.bars[self.mode])
def set_bars(self):
"""
Sets both the footer and header to their default values
for the current mode.
"""
self.set_default_header()
self.set_default_footer()
def close_editor(self): def close_editor(self):
""" """
Close whatever editing widget is open and restore proper Close whatever editing widget is open and restore proper
@ -133,10 +161,9 @@ class App(object):
self.window_split = False self.window_split = False
self.loop.widget.focus_position = "body" self.loop.widget.focus_position = "body"
self.set_footer(self.bars["thread"]) 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]
self.set_default_header()
def switch_editor(self): def switch_editor(self):
@ -188,6 +215,10 @@ class App(object):
def make_message_body(self, message): def make_message_body(self, message):
"""
Returns the widgets that comprise a message in a thread, including the
text body, author info and the action button
"""
info = "@ " + self.timestring(message["created"]) info = "@ " + self.timestring(message["created"])
if message["edited"]: if message["edited"]:
info += " [edited]" info += " [edited]"
@ -195,7 +226,7 @@ class App(object):
name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"])) name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"]))
post = str(message["post_id"]) post = str(message["post_id"])
head = urwid.Columns([ head = urwid.Columns([
(2 + len(post), urwid.AttrMap(cute_button(">" + post), "button")), (2 + len(post), urwid.AttrMap(cute_button(">" + post), "button", "hover")),
(len(name._text) + 1, (len(name._text) + 1,
urwid.AttrMap(name, str(self.usermap[message["author"]]["color"]))), urwid.AttrMap(name, str(self.usermap[message["author"]]["color"]))),
urwid.AttrMap(urwid.Text(info), "dim") urwid.AttrMap(urwid.Text(info), "dim")
@ -212,6 +243,9 @@ class App(object):
def timestring(self, epoch, mode="both"): def timestring(self, epoch, mode="both"):
"""
Returns a string of time representing a given epoch and mode.
"""
if mode == "delta": if mode == "delta":
return self.readable_delta(epoch) return self.readable_delta(epoch)
@ -226,19 +260,24 @@ class App(object):
def make_thread_body(self, thread): def make_thread_body(self, thread):
"""
Returns the pile widget that comprises a thread in the index.
"""
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"])
dateline = "by ~{} @ {}".format( user = self.usermap[thread["author"]]
self.usermap[thread["author"]]["user_name"], dateline = [
self.timestring(thread["created"]) ("default", "by "),
) (str(user["color"]), "~%s " % user["user_name"]),
("dim", "@ %s" % self.timestring(thread["created"]))
]
infoline = "%d replies; active %s" % ( infoline = "%d replies; active %s" % (
thread["reply_count"], self.timestring(thread["last_mod"], "delta")) 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", "hover")), title]),
urwid.AttrMap(urwid.Text(dateline), "dim"), urwid.Text(dateline),
urwid.AttrMap(urwid.Text(infoline), "dim"), urwid.AttrMap(urwid.Text(infoline), "dim"),
urwid.AttrMap(urwid.Divider("-"), "dim") urwid.AttrMap(urwid.Divider("-"), "dim")
]) ])
@ -256,11 +295,10 @@ class App(object):
self.window_split = False self.window_split = False
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_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.set_bars()
try: 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: except IndexError:
pass pass
@ -277,14 +315,9 @@ class App(object):
self.usermap.update(usermap) self.usermap.update(usermap)
self.thread = thread self.thread = thread
self.walker.clear() self.walker.clear()
self.set_footer(self.bars["thread"])
self.set_header("~{}: {}",
usermap[thread["author"]]["user_name"],
thread["title"])
for message in thread["messages"]: for message in thread["messages"]:
self.walker += self.make_message_body(message) self.walker += self.make_message_body(message)
self.set_bars()
def refresh(self, bottom=False): def refresh(self, bottom=False):
@ -303,6 +336,9 @@ class App(object):
def set_new_editor(self, button, value, key): def set_new_editor(self, button, value, key):
"""
Callback for the option radio buttons to set the the text editor.
"""
if value == False: if value == False:
return return
elif key == "internal": elif key == "internal":
@ -312,13 +348,78 @@ class App(object):
def set_editor_mode(self, button, value): def set_editor_mode(self, button, value):
"""
Callback for the editor mode radio buttons in the options.
"""
self.prefs["integrate_external_editor"] = value self.prefs["integrate_external_editor"] = value
bbjrc("update", **self.prefs) bbjrc("update", **self.prefs)
def relog(self, *_, **__):
"""
Options menu callback to log the user in again.
Drops back to text mode because im too lazy to
write a responsive urwid thing for this.
"""
self.loop.widget = self.loop.widget[0]
self.loop.stop()
run("clear", shell=True)
print(welcome)
log_in()
self.loop.start()
self.set_default_header()
self.options_menu()
def unlog(self, *_, **__):
"""
Options menu callback to anonymize the user and
then redisplay the options menu.
"""
network.user_name = network.user_auth = None
self.loop.widget = self.loop.widget[0]
self.set_default_header()
self.options_menu()
def set_color(self, button, value, color):
if value == False:
return
network.user_update(color=color)
def options_menu(self): def options_menu(self):
"""
Create a popup for the user to configure their account and
display settings.
"""
editor_buttons = [] editor_buttons = []
edit_mode = [] edit_mode = []
user_colors = []
if network.user_auth:
account_message = "Logged in as %s." % network.user_name
colors = ["None", "Red", "Yellow", "Green", "Blue", "Cyan", "Magenta"]
for index, color in enumerate(colors):
urwid.RadioButton(
user_colors, color,
network.user["color"] == index,
self.set_color, index)
account_stuff = [
urwid.Button("Change login.", on_press=self.relog),
urwid.Button("Go anonymous.", on_press=self.unlog),
urwid.Divider(),
urwid.Text(("button", "Your color:")),
*user_colors
]
else:
account_message = "You're browsing anonymously, and cannot set account preferences."
account_stuff = [
urwid.Button("Login/Register", on_press=self.relog)
]
urwid.RadioButton( urwid.RadioButton(
editor_buttons, "Internal", editor_buttons, "Internal",
@ -328,7 +429,7 @@ class App(object):
for editor in editors: for editor in editors:
urwid.RadioButton( urwid.RadioButton(
editor_buttons, editor.title(), editor_buttons, editor,
state=self.prefs["editor"] == editor, state=self.prefs["editor"] == editor,
on_state_change=self.set_new_editor, on_state_change=self.set_new_editor,
user_data=editor) user_data=editor)
@ -345,11 +446,16 @@ class App(object):
widget = OptionsMenu( widget = OptionsMenu(
urwid.Filler( urwid.Filler(
urwid.Pile([ urwid.Pile([
urwid.AttrMap(urwid.Text("Text editor:"), "button"), urwid.Text(("button", "Your account:")),
urwid.Text(account_message),
urwid.Divider(),
*account_stuff,
urwid.Divider("-"),
urwid.Text(("button", "Text editor:")),
urwid.Divider(), urwid.Divider(),
*editor_buttons, *editor_buttons,
urwid.Divider("-"), urwid.Divider("-"),
urwid.AttrMap(urwid.Text("External text editor mode:"), "button"), urwid.Text(("button", "External text editor mode:")),
urwid.Text("If you have problems using an external text editor, " urwid.Text("If you have problems using an external text editor, "
"set this to Overthrow."), "set this to Overthrow."),
urwid.Divider(), urwid.Divider(),
@ -654,6 +760,15 @@ def cute_button(label, callback=None, data=None):
return button return button
def urwid_rainbows(string):
"""
Same as below, but instead of printing rainbow text, returns
a markup list suitable for urwid's Text contructor.
"""
colors = [str(x) for x in range(1, 7)]
return urwid.Text([(choice(colors), char) for char in string])
def motherfucking_rainbows(string, inputmode=False, end="\n"): def motherfucking_rainbows(string, inputmode=False, end="\n"):
""" """
I cANtT FeELLE MyYE FACECsEE ANYrrMOROeeee I cANtT FeELLE MyYE FACECsEE ANYrrMOROeeee
@ -777,6 +892,7 @@ def log_in():
password = password_loop("Enter a password. It can be empty if you want") password = password_loop("Enter a password. It can be empty if you want")
network.user_register(name, password) network.user_register(name, password)
motherfucking_rainbows("~~welcome to the party, %s!~~" % network.user_name) motherfucking_rainbows("~~welcome to the party, %s!~~" % network.user_name)
sleep(0.8) # let that confirmation message shine
def frame_theme(mode="default"): def frame_theme(mode="default"):
@ -824,10 +940,10 @@ def main():
motherfucking_rainbows(obnoxious_logo) motherfucking_rainbows(obnoxious_logo)
print(welcome) print(welcome)
log_in() log_in()
sleep(0.8) # let that confirmation message shine
if __name__ == "__main__": if __name__ == "__main__":
# global app # global app
main() main()
app = App() app = App()
app.loop.run() app.loop.run()
# app.loop.widget.keypress(app.loop.screen_size, "up")

View File

@ -268,7 +268,8 @@ def user_update(connection, user_object, parameters):
user_id = user_object["user_id"] user_id = user_object["user_id"]
for key in ("user_name", "auth_hash", "quip", "bio", "color"): for key in ("user_name", "auth_hash", "quip", "bio", "color"):
value = parameters.get(key) value = parameters.get(key)
if value: # bool(0) == False hur hur hurrrrrr ::drools::
if value == 0 or value:
validate([(key, value)]) validate([(key, value)])
user_object[key] = value user_object[key] = value
@ -383,7 +384,7 @@ def validate(keys_and_values):
elif key == "color": elif key == "color":
if value in range(0, 6): if value in range(0, 7):
continue continue
raise BBJParameterError( raise BBJParameterError(
"Color specification out of range (int 0-6)") "Color specification out of range (int 0-6)")

View File

@ -16,13 +16,11 @@ markup = [
"bold", "italic", "underline", "strike" "bold", "italic", "underline", "strike"
] ]
tokens = re.compile( tokens = re.compile(r"\[(%s): (.+?)]" % "|".join(colors + markup),
r"\[({}): (.+?)]".format( flags=re.DOTALL)
"|".join(colors + markup)),
flags=re.DOTALL)
quotes = re.compile(">>([0-9]+)") quotes = re.compile(">>([0-9]+)")
linequotes = re.compile("^(>.+)$", linequotes = re.compile("^(>.+)$",
flags=re.MULTILINE) flags=re.MULTILINE)
@ -31,9 +29,8 @@ def apply_formatting(msg_obj, formatter):
Receives a messages object from a thread and returns it with Receives a messages object from a thread and returns it with
all the message bodies passed through FORMATTER. all the message bodies passed through FORMATTER.
""" """
for x in msg_obj["messages"].keys(): for x in range(len(msg_obj)):
msg_obj["messages"][x]["body"] = \ msg_obj[x]["body"] = formatter(msg_obj[x]["body"])
formatter(msg_obj["messages"][x]["body"])
return msg_obj return msg_obj
@ -44,6 +41,20 @@ def raw(text):
return text return text
def strip(text):
"""
Returns the text with all formatting directives removed.
Not to be confused with `raw`.
"""
def entities(text):
"""
Returns a tuple where [0] is raw text
"""
def html(text): def html(text):
""" """
Returns messages in html format, after being sent through markdown. Returns messages in html format, after being sent through markdown.