From 7f2a2fec560770b978b1ac87b7450e5295f47131 Mon Sep 17 00:00:00 2001
From: Kevin Lyda <kevin@lyda.ie>
Date: Tue, 20 May 2025 08:48:24 +0100
Subject: [PATCH] An attempt at REPLY

---
 editor/tview.go              |  5 +++-
 folders/messages.go          |  2 +-
 repl/command.go              |  1 +
 repl/messages.go             | 45 ++++++++++++++++++++++++++++++++++--
 storage/messages.sql.go      | 10 ++++----
 storage/queries/messages.sql |  6 ++---
 6 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/editor/tview.go b/editor/tview.go
index 543215b..c4d5a58 100644
--- a/editor/tview.go
+++ b/editor/tview.go
@@ -9,7 +9,7 @@ import (
 )
 
 // Editor is the editor for text files.
-func Editor(placeholder, title string) (string, error) {
+func Editor(placeholder, title, message string) (string, error) {
 	theme := tview.Theme{
 		PrimitiveBackgroundColor:    tcell.ColorDefault,
 		ContrastBackgroundColor:     tcell.ColorDefault,
@@ -138,6 +138,9 @@ Press Enter for more help, press Escape to return.`)
 		return event
 	})
 
+	if message != "" {
+		textArea.SetText(message, true)
+	}
 	err := app.SetRoot(pages, true).Run()
 	if err != nil {
 		return "", err
diff --git a/folders/messages.go b/folders/messages.go
index 92f859b..8450cf4 100644
--- a/folders/messages.go
+++ b/folders/messages.go
@@ -41,7 +41,7 @@ func CreateMessage(author, subject, message, folder string, permanent, shutdown
 // ReadMessage reads a message for a user.
 func ReadMessage(login, folder string, msgid int64) (*storage.Message, error) {
 	ctx := storage.Context()
-	fmt.Printf("TODO: Make sure %s can read this message.\n", login)
+	fmt.Printf("TODO(ReadMessage): Make sure %s can read this message.\n", login)
 	msg, err := this.Q.ReadMessage(ctx, storage.ReadMessageParams{
 		Folder: folder,
 		ID:     msgid,
diff --git a/repl/command.go b/repl/command.go
index b2b37b9..43ee934 100644
--- a/repl/command.go
+++ b/repl/command.go
@@ -834,6 +834,7 @@ the same as the ADD command except for /NOINDENT and /EXTRACT.
   message. This is  the default - suppress with /NOEXTRACT.  The text of
   the message is indented with > at the beginning of each line. This can
   be suppressed with /NOINDENT.`,
+				Default: "true",
 			},
 			"/INDENT": {
 				Description: `  See /EXTRACT for information on this qualifier.
diff --git a/repl/messages.go b/repl/messages.go
index 6c9ff65..bf805ee 100644
--- a/repl/messages.go
+++ b/repl/messages.go
@@ -138,7 +138,10 @@ func ActionAdd(cmd *dclish.Command) error {
 	}
 	// TODO: check we have permission for shutdown and permanent
 
-	message, err := editor.Editor(fmt.Sprintf("Enter message for '%s'...", optSubject), "Edit message")
+	message, err := editor.Editor(
+		fmt.Sprintf("Enter message for '%s'...", optSubject),
+		"Edit message",
+		"")
 	if err != nil {
 		return err
 	}
@@ -293,7 +296,45 @@ func ActionRead(cmd *dclish.Command) error {
 
 // ActionReply handles the `REPLY` command.
 func ActionReply(cmd *dclish.Command) error {
-	fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description)
+	extract := true
+	if cmd.Flags["/EXTRACT"].Value == "false" {
+		extract = false
+	}
+	indent := true
+	if cmd.Flags["/INDENT"].Value == "false" {
+		indent = false
+	}
+	fmt.Printf("TODO: getting message %d.\n", this.MsgID)
+	original, err := folders.ReadMessage(this.User.Login, this.Folder.Name, this.MsgID)
+	origMsg := ""
+	if extract {
+		if indent {
+			origMsg = "> " + strings.Join(strings.Split(original.Message, "\n"), "\n> ")
+			fmt.Println("TODO: extract and indent are true.")
+		} else {
+			origMsg = original.Message
+			fmt.Println("TODO: extract is true.")
+		}
+	} else {
+		fmt.Println("TODO: neither is true.")
+	}
+
+	// TODO; this isn't working - the message isn't getting loaded.
+	subject := "Re: " + original.Subject
+	message, err := editor.Editor(
+		fmt.Sprintf("Enter message for '%s'...", subject),
+		"Edit message",
+		origMsg)
+	if err != nil {
+		fmt.Printf("ERROR: Editor failure (%s).\n", err)
+		return nil
+	}
+	err = folders.CreateMessage(this.User.Login, subject,
+		message, this.Folder.Name, 0, 0, nil)
+	if err != nil {
+		fmt.Printf("ERROR: CreateMessage failure (%s).\n", err)
+		return nil
+	}
 	return nil
 }
 
diff --git a/storage/messages.sql.go b/storage/messages.sql.go
index a916720..d804c87 100644
--- a/storage/messages.sql.go
+++ b/storage/messages.sql.go
@@ -15,12 +15,12 @@ INSERT INTO messages (
   id, folder, author, subject, message, permanent, shutdown, expiration
 ) VALUES (
   (SELECT COALESCE(MAX(id), 0) + 1 FROM messages AS m WHERE m.folder = ?1),
-  ?2, ?1, ?3, ?4, ?5, ?6, ?7)
+  ?1, ?2, ?3, ?4, ?5, ?6, ?7)
 `
 
 type CreateMessageParams struct {
-	Author     string
 	Folder     string
+	Author     string
 	Subject    string
 	Message    string
 	Permanent  int64
@@ -30,8 +30,8 @@ type CreateMessageParams struct {
 
 func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) error {
 	_, err := q.db.ExecContext(ctx, createMessage,
-		arg.Author,
 		arg.Folder,
+		arg.Author,
 		arg.Subject,
 		arg.Message,
 		arg.Permanent,
@@ -162,7 +162,7 @@ func (q *Queries) LastMsgidIgnoringSeen(ctx context.Context, folder string) (int
 const nextMsgid = `-- name: NextMsgid :one
 SELECT CAST(COALESCE(MIN(id), 0) AS INT) FROM messages AS m
   WHERE m.folder = ?1 AND m.id > ?2
-  AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3)
+  AND id NOT IN (SELECT msgid FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3)
 `
 
 type NextMsgidParams struct {
@@ -198,7 +198,7 @@ func (q *Queries) NextMsgidIgnoringSeen(ctx context.Context, arg NextMsgidIgnori
 const prevMsgid = `-- name: PrevMsgid :one
 SELECT CAST(COALESCE(MAX(id), 0) AS INT) FROM messages AS m
   WHERE m.folder = ?1 AND m.id < ?2
-  AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3)
+  AND id NOT IN (SELECT msgid FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3)
 `
 
 type PrevMsgidParams struct {
diff --git a/storage/queries/messages.sql b/storage/queries/messages.sql
index aaf975c..d6f48c6 100644
--- a/storage/queries/messages.sql
+++ b/storage/queries/messages.sql
@@ -3,7 +3,7 @@ INSERT INTO messages (
   id, folder, author, subject, message, permanent, shutdown, expiration
 ) VALUES (
   (SELECT COALESCE(MAX(id), 0) + 1 FROM messages AS m WHERE m.folder = ?1),
-  ?2, ?1, ?3, ?4, ?5, ?6, ?7);
+  ?1, ?2, ?3, ?4, ?5, ?6, ?7);
 
 -- name: SetMessageSeen :exec
 INSERT INTO seen (login, folder, msgid) VALUES (?, ?, ?);
@@ -17,7 +17,7 @@ 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 = ?1 AND m.id > ?2
-  AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3);
+  AND id NOT IN (SELECT msgid FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3);
 
 -- name: NextMsgidIgnoringSeen :one
 SELECT CAST(MIN(id) AS INT) FROM messages AS m
@@ -26,7 +26,7 @@ SELECT CAST(MIN(id) AS INT) FROM messages AS m
 -- name: PrevMsgid :one
 SELECT CAST(COALESCE(MAX(id), 0) AS INT) FROM messages AS m
   WHERE m.folder = ?1 AND m.id < ?2
-  AND id NOT IN (SELECT id FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3);
+  AND id NOT IN (SELECT msgid FROM seen AS s WHERE s.folder = ?1 AND s.login = ?3);
 
 -- name: PrevMsgidIgnoringSeen :one
 SELECT CAST(MAX(id) AS INT) FROM messages AS m
-- 
GitLab