diff --git a/NOTES.md b/NOTES.md index 2eff90e73364471a3a945aac3e6f0d0ed710b4f0..4278c037b36430016f19f69a9e533c8699524e4b 100644 --- a/NOTES.md +++ b/NOTES.md @@ -28,12 +28,16 @@ repl.commands? ## Things to do * Run [godoc](http://localhost:6060/) and then review where the help text is lacking. - * Move to a storage layer. Next: this.Folder should be a storage.Folder. - * Implement batch jobs - and have install populate the database with some - test data. + * ~~Move to a storage layer.~~ + * this.Folder should be a storage.Folder. + * Implement batch jobs + * ~~Have install populate the database with some test data.~~ + * reboot + * expire * Implement each command. * Next: folder commands - ~~CREATE~~, ~~REMOVE~~, MODIFY, ~~INDEX~~, ~~SELECT~~ - * Messages: ~~ADD~~, CURRENT, ~~DIRECTORY~~, BACK, CHANGE, FIRST, REMOVE, NEXT, ~~READ~~ + * Messages: ~~ADD~~, CURRENT, ~~DIRECTORY~~, BACK, CHANGE, + FIRST, REMOVE, ~~NEXT~~, ~~READ~~ * Messages edit: CHANGE, REPLY, FORWARD * Moving messages: COPY, MOVE * Compound commands: SET and SHOW - make HELP work for them. diff --git a/editor/tview.go b/editor/tview.go index 50f3ab9b747f5130dc8b41fb654580053a345935..f062e75040c79e01d1b798e9aeda28f454692693 100644 --- a/editor/tview.go +++ b/editor/tview.go @@ -39,10 +39,8 @@ func Editor(placeholder, title string) (string, error) { updateInfos := func() { fromRow, fromColumn, toRow, toColumn := textArea.GetCursor() if fromRow == toRow && fromColumn == toColumn { - // position.SetText(fmt.Sprintf("Row: [yellow]%d[white], Column: [yellow]%d ", fromRow, fromColumn)) position.SetText(fmt.Sprintf("Row: %d, Column: %d ", fromRow, fromColumn)) } else { - // position.SetText(fmt.Sprintf("[red]From[white] Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d ", fromRow, fromColumn, toRow, toColumn)) position.SetText(fmt.Sprintf("From Row: %d, Column: %d - To Row: %d, To Column: %d ", fromRow, fromColumn, toRow, toColumn)) } } diff --git a/folders/messages.go b/folders/messages.go index 3be061c4dc24d512ba742ff7f12fe4bb7436f42a..96499917d55096380fc20f4ab75d55d8c57593eb 100644 --- a/folders/messages.go +++ b/folders/messages.go @@ -62,6 +62,21 @@ func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) { return &msg, nil } +// NextMsgid gets the next message id. +func NextMsgid(login, folder string, msgid int64) int64 { + ctx := context.TODO() + newid, err := this.Q.NextMsgid(ctx, storage.NextMsgidParams{ + Folder: folder, + Folder_2: folder, + Login: login, + ID: msgid, + }) + if err != nil { + return 0 + } + return newid +} + // ListMessages lists messages. func ListMessages(folder string) ([]storage.Message, error) { ctx := context.TODO() diff --git a/key/key.go b/key/key.go index 92f1373fb80bed002d5be8e2be129b1a31084653..2e716d95c8c290274ab9221fad92a5b011e4bb7c 100644 --- a/key/key.go +++ b/key/key.go @@ -9,7 +9,7 @@ import ( "github.com/adrg/xdg" ) -var keytemplate = `command="%s -u %s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s\n` +var keytemplate = `command="%s -u %s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %s\n` // Add adds an ssh key to the `authorized_keys` file. func Add(login, public string) error { diff --git a/pager/tview.go b/pager/tview.go new file mode 100644 index 0000000000000000000000000000000000000000..fd2634624acd32961d448f9b2d492bfc60355629 --- /dev/null +++ b/pager/tview.go @@ -0,0 +1,59 @@ +package pager + +import ( + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +// Pager is the pager for text data. +func Pager(text string) error { + theme := tview.Theme{ + PrimitiveBackgroundColor: tcell.ColorDefault, + ContrastBackgroundColor: tcell.ColorDefault, + MoreContrastBackgroundColor: tcell.ColorDefault, + BorderColor: tcell.ColorDefault, + TitleColor: tcell.ColorDefault, + GraphicsColor: tcell.ColorDefault, + PrimaryTextColor: tcell.ColorDefault, + SecondaryTextColor: tcell.ColorDefault, + TertiaryTextColor: tcell.ColorDefault, + InverseTextColor: tcell.ColorDefault, + ContrastSecondaryTextColor: tcell.ColorDefault, + } + tview.Styles = theme + app := tview.NewApplication() + + page := tview.NewTextView(). + SetWrap(true). + SetScrollable(true). + SetChangedFunc(func() { + app.Draw() + }). + SetText(text) + // TODO: this doesn't seem to be working. + page.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyRune: + switch event.Rune() { + case 'B', 'b': + _, y := page.GetScrollOffset() + _, _, _, height := page.GetInnerRect() + page.ScrollTo(0, max(y+height, 0)) + return nil + case ' ': + _, y := page.GetScrollOffset() + _, _, _, height := page.GetInnerRect() + page.ScrollTo(0, y+height) + return nil + case 'Q', 'q': + app.Stop() + return nil + } + case tcell.KeyEsc, tcell.KeyCtrlC: + app.Stop() + return nil + } + return event + }) + return app.SetRoot(page, true).Run() +} diff --git a/repl/messages.go b/repl/messages.go index 019f176d98eb5a4da7514bd4381a92b26444e24e..4778a6d1fa1d8f9d7eed42d6148360d8ad587484 100644 --- a/repl/messages.go +++ b/repl/messages.go @@ -12,6 +12,7 @@ import ( "git.lyda.ie/kevin/bulletin/dclish" "git.lyda.ie/kevin/bulletin/editor" "git.lyda.ie/kevin/bulletin/folders" + "git.lyda.ie/kevin/bulletin/pager" "git.lyda.ie/kevin/bulletin/this" ) @@ -172,13 +173,23 @@ func ActionFirst(cmd *dclish.Command) error { // ActionNext handles the `NEXT` command. func ActionNext(cmd *dclish.Command) error { - fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description) + // TODO: handle flags. + msgid := max(folders.NextMsgid(this.User.Login, this.Folder, this.MsgID), this.MsgID) + msg, err := folders.ReadMessage(this.User.Login, this.Folder, msgid) + if err != nil { + return err + } + // TODO: pager needs to report if the whole message was read + // and only increment if not. + pager.Pager(msg.String()) + msgid = folders.NextMsgid(this.User.Login, this.Folder, msgid) + this.MsgID = max(this.MsgID, msgid) return nil } // ActionRead handles the `READ` command. func ActionRead(cmd *dclish.Command) error { - // TODO: We need to set this.MsgID when we change folder. + // TODO: handle flags. msgid := this.MsgID if len(cmd.Args) == 1 { id, err := strconv.Atoi(cmd.Args[0]) @@ -187,13 +198,15 @@ func ActionRead(cmd *dclish.Command) error { } msgid = int64(id) } - msg, err := folders.ReadMessage( - this.User.Login, this.Folder, msgid) + msg, err := folders.ReadMessage(this.User.Login, this.Folder, msgid) if err != nil { return err } - // TODO: update this.MsgID - fmt.Printf("%s\n", msg) + // TODO: pager needs to report if the whole message was read + // and only increment if not. + pager.Pager(msg.String()) + msgid = folders.NextMsgid(this.User.Login, this.Folder, msgid) + this.MsgID = max(this.MsgID, msgid) return nil } diff --git a/storage/messages.sql.go b/storage/messages.sql.go index cf675fa48f064e5da4419f484ea7885862124eac..5fd8d92b91983bd5465aeb8c327d9d5a5f7e2952 100644 --- a/storage/messages.sql.go +++ b/storage/messages.sql.go @@ -54,6 +54,31 @@ func (q *Queries) GetFirstMessageID(ctx context.Context, folder string) (int64, return id, err } +const nextMsgid = `-- name: NextMsgid :one +SELECT CAST(COALESCE(MIN(id), 0) AS INT) FROM messages AS m + WHERE m.folder = ? AND m.id > ? + AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ? AND s.login = ?) +` + +type NextMsgidParams struct { + Folder string + ID int64 + Folder_2 string + Login string +} + +func (q *Queries) NextMsgid(ctx context.Context, arg NextMsgidParams) (int64, error) { + row := q.db.QueryRowContext(ctx, nextMsgid, + arg.Folder, + arg.ID, + arg.Folder_2, + arg.Login, + ) + var column_1 int64 + err := row.Scan(&column_1) + return column_1, err +} + const readMessage = `-- name: ReadMessage :one SELECT id, folder, author, subject, message, permanent, shutdown, expiration, create_at, update_at FROM messages WHERE folder = ? AND id = ? ` diff --git a/storage/queries/messages.sql b/storage/queries/messages.sql index 345619b3088c4c761ae6f83f6a0b905b8bc28101..62e04896ef8f2932930b782f1abc0f8d345d2022 100644 --- a/storage/queries/messages.sql +++ b/storage/queries/messages.sql @@ -13,3 +13,8 @@ SELECT id FROM messages WHERE folder = ? and id = MIN(id) GROUP BY folder; -- name: ReadMessage :one SELECT * FROM messages WHERE folder = ? AND id = ?; + +-- name: NextMsgid :one +SELECT CAST(COALESCE(MIN(id), 0) AS INT) FROM messages AS m + WHERE m.folder = ? AND m.id > ? + AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ? AND s.login = ?);