retronews

a featureful fork of the luke8086/retronews hn+lobste.rs tui
Log | Files | Refs | README | LICENSE

commit f542517065e4a160f4e56c491e41e212e8a6449a
parent 9ab4ee7b85b8c5f14e98dab2d344d85d1b256566
Author: luke8086 <55237178+luke8086@users.noreply.github.com>
Date:   Sat, 23 Jul 2022 20:37:42 +0000

Initial support for persisting message flags

Diffstat:
Mretronews.py | 49++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 46 insertions(+), 3 deletions(-)

diff --git a/retronews.py b/retronews.py @@ -11,10 +11,12 @@ import curses import html.parser import json import logging +import os import re +import sqlite3 import sys import urllib.request -from dataclasses import dataclass, field +from dataclasses import asdict, dataclass, field from datetime import datetime from textwrap import wrap from typing import Any, Generator, List, Optional, TypedDict, TypeVar, Union @@ -77,6 +79,7 @@ class Message: class AppState: screen: "curses._CursesWindow" colors: Colors + db: sqlite3.Connection messages: List[Message] = field(default_factory=list) selected_message: Optional[Message] = None pager_visible: bool = False @@ -150,7 +153,8 @@ def list_get(lst: List[T], index: int, default: Optional[T] = None) -> Optional[ return lst[index] if 0 <= index < len(lst) else default -def cmd_quit(_: AppState): +def cmd_quit(app: AppState): + app.db.close() sys.exit(0) @@ -196,6 +200,7 @@ def cmd_close(app: AppState) -> None: def cmd_star(app: AppState) -> None: if (msg := app.selected_message) is not None: msg.flags.starred = not msg.flags.starred + db_save_message(app.db, msg) def cmd_toggle_raw_mode(app: AppState) -> None: @@ -221,6 +226,39 @@ KEY_BINDINGS = { } +def db_init() -> sqlite3.Connection: + path = os.path.expanduser("~/.retronews.db") + create_table_sql = """ + CREATE TABLE IF NOT EXISTS messages ( + id TEXT NOT NULL PRIMARY KEY, + story_id TEXT NOT NULL, + flags JSON NOT NULL) + """ + + db = sqlite3.connect(path) + db.row_factory = sqlite3.Row + db.execute(create_table_sql) + db.commit() + + return db + + +def db_save_message(db: sqlite3.Connection, message: Message) -> None: + sql = """INSERT OR REPLACE INTO messages (id, story_id, flags) VALUES (?, ?, ?)""" + db.execute(sql, (message.msg_id, message.story_id, json.dumps(asdict(message.flags)))) + db.commit() + + +def db_load_message_flags(db: sqlite3.Connection, messages: List[Message]) -> None: + messages_by_id = {msg.msg_id: msg for msg in messages} + message_ids = list(messages_by_id.keys()) + sql = f"SELECT * FROM messages WHERE id IN ({','.join('?' for _ in message_ids)})" + + for row in db.execute(sql, message_ids): + flags = json.loads(row["flags"]) + messages_by_id[row["id"]].flags = MessageFlags(**flags) + + def app_select_message(app: AppState, message: Optional[Message], show_pager: bool = False) -> None: app.selected_message = message @@ -235,6 +273,7 @@ def app_select_message(app: AppState, message: Optional[Message], show_pager: bo if app.pager_visible: message.flags.read = True + db_save_message(app.db, message) def app_load_messages( @@ -245,6 +284,8 @@ def app_load_messages( selected_message = None + db_load_message_flags(app.db, messages) + for i, message in enumerate(messages): message.index_position = i @@ -413,10 +454,12 @@ def app_init_logging() -> None: def app_init(screen: "curses._CursesWindow") -> AppState: + db = db_init() + curses.curs_set(0) curses.use_default_colors() - app = AppState(screen=screen, colors=Colors()) + app = AppState(screen=screen, colors=Colors(), db=db) app_load_messages(app, hn_search_stories()) return app