Replies and new post functionality added
This commit is contained in:
parent
aea4572121
commit
8b6072c316
4
data.py
4
data.py
|
@ -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 = (
|
||||
|
|
244
linkulator.py
244
linkulator.py
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue