Replies and new post functionality added

This commit is contained in:
asdf 2021-08-15 11:47:26 +10:00
parent aea4572121
commit 8b6072c316
2 changed files with 184 additions and 64 deletions

View File

@ -141,7 +141,7 @@ class LinkData:
def add(self, record) -> int:
"""Add a record to the data file, and to link_data. Returns a new post
ID, if record is a post, or -1"""
ID, if record is a post, or None"""
if os.path.exists(config.USER.datafile):
append_write = "a" # append if already exists
else:
@ -157,7 +157,7 @@ class LinkData:
)
)
new_post_id = -1
new_post_id = None
if record.category:
if self.link_data:
new_post_id = (

View File

@ -225,34 +225,62 @@ def open_link_in_browser(url):
call([config.USER.browser, url])
def new_reply(stdscr, post_id):
# TODO
pass
def command_bar():
"""Uses defined stdscr to display a command input prompt. returns command text"""
# draw the prompt in its own window
prompt_win = curses.newwin(1, curses.COLS, curses.LINES - 1, 0)
prompt_win.addch(0, 0, ":")
prompt_win.clrtoeol()
prompt_win.refresh()
# draw the command field and return gathered text
command_win = curses.newwin(1, curses.COLS, curses.LINES - 1, 1)
command_field = textpad.Textbox(command_win)
command_field.stripspaces = True
curses.curs_set(1)
command_field.edit()
command_text = command_field.gather()
command_text = command_text.strip()
curses.curs_set(0)
return command_text
# def reply(parent_id):
# """Prompt for reply, validate input, save validated input to disk and update
# link_data. Calls view_thread when complete."""
# while True:
# comment = input("Enter your comment (or leave empty to abort): ")
# if comment == "":
# input("Reply aborted. Hit [Enter] to continue.")
# break
# if not is_valid_input(comment):
# print(
# "Entries consisting of whitespace, or containing pipes, '|', are "
# "not valid.Please try again."
# )
# else:
# record = data.LinkDataRecord(
# username=getpass.getuser(),
# timestamp=str(time()),
# parent_id=parent_id,
# link_title_or_comment=comment,
# )
# LinkData.add(record)
# input("Reply added. Hit [Enter] to return to thread.")
# break
def new_reply():
# draw the prompt in its own window
reply_prompt = curses.newwin(1, curses.COLS, curses.LINES - 5, 0)
reply_prompt.addstr(
0, 0, "Enter your comment (or leave empty to cancel). CTRL+G to submit."
)
reply_prompt.clrtoeol()
reply_prompt.refresh()
# loop until valid input can be gathered
while True:
reply_window = curses.newwin(4, curses.COLS, curses.LINES - 4, 0)
reply_field = textpad.Textbox(reply_window)
reply_field.stripspaces = True
curses.curs_set(1)
reply_field.edit(complete_on_enter)
reply_text = reply_field.gather()
reply_text = reply_text.strip()
curses.curs_set(0)
if is_valid_input(reply_text):
break
else:
reply_prompt.addstr(0, 0, "Sorry, replies can't contain pipes '|'")
reply_prompt.clrtoeol()
reply_prompt.refresh()
return reply_text
def complete_on_enter(char):
# https://stackoverflow.com/questions/36121802/python-curses-make-enter-key-terminate-textbox
# # if enter, return CTRL+G equivalent
if char == 10:
char = 7
return char
def is_valid_input(entry: str) -> bool:
@ -267,11 +295,58 @@ def is_valid_input(entry: str) -> bool:
"""
if "|" in entry:
return False
if entry.strip() == "":
return False
# if entry.strip() == "":
# return False
return True
def new_post():
# draw the prompt in its own window
new_post_title = curses.newwin(8, curses.COLS, 4, 0)
new_post_title.addstr(
0, 0, "Enter the requested details, to cancel submit a blank field"
)
new_post_title.clrtoeol()
new_post_title.addstr(1, 0, "URL:")
new_post_title.clrtoeol()
new_post_title.addstr(2, 0, "Title:")
new_post_title.clrtoeol()
new_post_title.addstr(3, 0, "Category:")
new_post_title.clrtobot()
new_post_title.refresh()
# loop until valid input can be gathered
post_data = {}
items = ["url", "title", "category"]
for i, item in enumerate(items):
startrow = i + 5
while True:
item_window = curses.newwin(1, curses.COLS, startrow, 10)
item_field = textpad.Textbox(item_window)
item_field.stripspaces = True
curses.curs_set(1)
item_field.edit()
item_text = item_field.gather()
item_text = item_text.strip()
curses.curs_set(0)
if not item_text:
return None
if is_valid_input(item_text):
break
else:
new_post_title.addstr(
0, 0, "Sorry, {} can't contain pipes '|'".format(item)
)
new_post_title.clrtoeol()
new_post_title.refresh()
post_data[item] = item_text
return post_data
def is_correct_category(entry: str) -> bool:
"""Make sure the user purposefully created a new category and not by
accident (mistyped, tried to use category number instead of name)"""
@ -435,7 +510,9 @@ class MenuHierarchy:
) = view_categories(self.active.data, curses.COLS)
elif self.active.name == "links":
# reinitialise lower-level menu
self.primary_hierarchy[self.primary_index + 1].data = None
# set data
self.active.data = LinkData.get_links_by_category_name(
self.active.selected_key
)
@ -451,7 +528,8 @@ class MenuHierarchy:
elif self.active.name == "search_results":
pass
elif self.active.name == "search_results_thread_details":
pass
self.active.data = LinkData.get_post(self.active.selected_key)
self.active.output.content = view_post(self.active.data, curses.COLS)
else:
raise ValueError("This shouldn't happen?")
@ -490,6 +568,7 @@ def menu_system(stdscr):
status.action = ""
while True:
curses.set_escdelay(25)
curses.update_lines_cols()
# TODO: consider stdscr.erase()
@ -553,23 +632,34 @@ def menu_system(stdscr):
if k == "KEY_RESIZE":
term_resize(stdscr)
elif k in [":", " "]:
stdscr.addch(curses.LINES - 1, 0, ":")
stdscr.clrtoeol()
stdscr.refresh()
iwin = curses.newwin(1, curses.COLS, curses.LINES - 1, 1)
itxt = textpad.Textbox(iwin)
itxt.stripspaces = True
curses.curs_set(1)
itxt.edit()
action = itxt.gather()
action = command_bar()
handle_action(menus, action, status)
curses.curs_set(0)
del itxt
del iwin
else:
stdscr.erase()
stdscr.refresh()
handle_action(menus, k, status)
# menu loop
# action = get action from input
# return valid action or error message
# actions:
# navigate menu
# get data for menu change
# back/forward
# screen change
# set screen data
# scroll up/down
# get new post or reply input
# send data for input
# search
# perform search
# return search results
# open external program
# shutdown curses, start program, restart curses on return
# show help screen
def handle_action(menus, action, status):
int_action = None
try:
@ -579,15 +669,9 @@ def handle_action(menus, action, status):
pass
if int_action:
try:
navigate(menus, int_action)
except Exception as e:
status.message = str(e)
navigate(menus, int_action)
else:
try:
do_command(menus, action)
except Exception as e:
status.message = str(e)
do_command(menus, action)
def navigate(menus, int_action):
@ -629,9 +713,9 @@ def do_command(menus, action):
elif action in ["k", "KEY_UP"]:
if menus.active.output.scrollminy > 0:
menus.active.output.scrollminy -= 1
elif action in ["b", "back"]:
elif action in ["b", "back", "KEY_LEFT"]:
menus.back()
elif action in ["f", "forward"]:
elif action in ["f", "forward", "KEY_RIGHT"]:
menus.forward()
elif action in ["?", "help"]:
pass
@ -639,13 +723,39 @@ def do_command(menus, action):
elif action in ["q", "quit", "exit"]:
graceful_exit()
elif action in ["c", "create"]:
# post_id = post_link()
# if post_id >= 0:
# TODO: create new post
# set category_details to the relevant category
# set the selected index to the index of the new post in category_details
# set menu level to thread index
pass
new_post_data = new_post()
if new_post_data:
new_post_record = data.LinkDataRecord(
username=getpass.getuser(),
timestamp=str(time()),
category=new_post_data["category"],
link_URL=new_post_data["url"],
link_title_or_comment=new_post_data["title"],
)
new_post_id = LinkData.add(new_post_record)
if new_post_id:
# set categories screen
menus.primary_index = 0
menus.active = menus.primary_hierarchy[menus.primary_index]
menus.set_active_menu_data(LinkData)
menus.active.output.calc_dimensions()
# set links screen
menus.primary_index = 1
menus.active = menus.primary_hierarchy[menus.primary_index]
menus.active.selected_key = new_post_data["category"]
menus.set_active_menu_data(LinkData)
menus.active.output.calc_dimensions()
# set post screen
menus.primary_index = 2
menus.active = menus.primary_hierarchy[menus.primary_index]
menus.active.selected_key = new_post_id
menus.set_active_menu_data(LinkData)
menus.active.output.calc_dimensions()
elif action in ["s", "search"]:
pass
# search()
@ -655,10 +765,20 @@ def do_command(menus, action):
elif action in ["r", "reply"] and menus.active.name in [
"search_result_thread_details",
"thread_details",
"post",
]:
# TODO: need stdscr here
new_reply(menus.active.selected_key)
reply_text = new_reply()
if reply_text:
reply_record = data.LinkDataRecord(
username=getpass.getuser(),
timestamp=str(time()),
parent_id=menus.active.data["parent_id"],
link_title_or_comment=reply_text,
)
_ = LinkData.add(reply_record)
menus.set_active_menu_data(LinkData)
menus.active.output.calc_dimensions()
elif action in ["o", "open"] and menus.active.name in [
"search_result_thread_details",
"thread_details",
@ -669,7 +789,7 @@ def do_command(menus, action):
# is o the right command?
## GENERAL
# GENERAL
def style_text(text: str, is_input: bool, *args) -> str: