~rjarry/aerc-devel

aerc: Allow not marking viewed messages as seen. v1 SUPERSEDED

James Cook: 2
 Allow not marking viewed messages as seen.
 Use IMAP PEEK and allow not marking messages read.
Tim Culverhouse: 1
 Allow not marking viewed messages as seen.

 20 files changed, 57 insertions(+), 13 deletions(-)
#794946 alpine-edge.yml success
#794947 openbsd.yml success
Thanks for your thoughts, Moritz. I'm happy to defer to you; in any case
I'm not using the IMAP backend.

I can try adding the change to unset \Seen myself, but probably won't
have time until next weekend, so if you or someone else wants to do the
work, please go ahead. (We could also just say the setting is not
supported with IMAP.)

Next
(List to bcc; my reply at bottom. If you're not interested in notmuch
workflows you can skip this.)
Next
Would it be useful to set this value (and the above
partBodySection.Peek) to only peek when needed? This way we'd use the
most appropriate method for getting the body.

I fail to find a satisfactory answer in your follow up email.

Next
Please also send your new revisions as a v2, v3, … vn :
(Note: I'm quoting from three different emails below, reordered; hope
you don't mind.)

On Sat Aug 20, 2022 at 7:29 AM UTC, Moritz Poldrack wrote:
(snip)
Next
Where should I write v2, v3, etc? As far as I can tell, if I want to add
that to the subject line, it will also end up in the commit message,
which doesn't make sense because in the end only one version will appear
in the commit history. (Sorry, I'm new to the git-send-email workflow.)
Next
No, it affects other backends; at least, I tested it with notmuch. See:

-	store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
+	if uiConfig.AutoMarkRead {
+		store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag,
true, nil)
+	}

In fact, I started with only that change (and related plumbing), and
only got into the IMAP stuff when someone pointed out it was not
sufficient for the IMAP backend.
Next
(Sorry for butting in... :))
Next
Thanks for the updates, James! Comments below:
Next
I agree it's annoying, but I do think it's the right way to do it. More
below.
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rjarry/aerc-devel/patches/33568/mbox | git am -3
Learn more about email & git

[PATCH aerc] Allow not marking viewed messages as seen. Export this patch

Fixes: https://todo.sr.ht/~rjarry/aerc/48
Signed-off-by: James Cook <falsifian@falsifian.org>
---
Sending this out for feedback. I should probably test it at least a
little more; e.g. I haven't tried deleting a message to check that the
change to delete.go makes sense.
 commands/account/view.go | 2 +-
 commands/msg/delete.go   | 2 +-
 commands/msgview/next.go | 2 +-
 config/aerc.conf         | 6 ++++++
 config/config.go         | 2 ++
 doc/aerc-config.5.scd    | 5 +++++
 lib/messageview.go       | 7 +++++--
 widgets/msglist.go       | 2 +-
 8 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/commands/account/view.go b/commands/account/view.go
index 8537d33..04a1687 100644
--- a/commands/account/view.go
+++ b/commands/account/view.go
@@ -46,7 +46,7 @@ func (ViewMessage) Execute(aerc *widgets.Aerc, args []string) error {
		return nil
	}
	lib.NewMessageStoreView(msg, store, aerc.Crypto, aerc.DecryptKeys,
		func(view lib.MessageView, err error) {
		acct.UiConfig(), func(view lib.MessageView, err error) {
			if err != nil {
				aerc.PushError(err.Error())
				return
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index d26169f..aa129fa 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -69,7 +69,7 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error {
				return nil
			}
			lib.NewMessageStoreView(next, store, aerc.Crypto, aerc.DecryptKeys,
				func(view lib.MessageView, err error) {
				acct.UiConfig(), func(view lib.MessageView, err error) {
					if err != nil {
						aerc.PushError(err.Error())
						return
diff --git a/commands/msgview/next.go b/commands/msgview/next.go
index 928b9fb..ca72715 100644
--- a/commands/msgview/next.go
+++ b/commands/msgview/next.go
@@ -43,7 +43,7 @@ func (NextPrevMsg) Execute(aerc *widgets.Aerc, args []string) error {
		return nil
	}
	lib.NewMessageStoreView(nextMsg, store, aerc.Crypto, aerc.DecryptKeys,
		func(view lib.MessageView, err error) {
		acct.UiConfig(), func(view lib.MessageView, err error) {
			if err != nil {
				aerc.PushError(err.Error())
				return
diff --git a/config/aerc.conf b/config/aerc.conf
index 2f7597c..2f7a3d9 100644
--- a/config/aerc.conf
+++ b/config/aerc.conf
@@ -18,6 +18,12 @@ pgp-provider=internal
unsafe-accounts-conf=false

[ui]
#
# Set the "seen" flag when a message is viewed.
#
# Default: true
auto-mark-read=true

#
# Describes the format for each row in a mailbox view. This field is compatible
# with mutt's printf-like syntax.
diff --git a/config/config.go b/config/config.go
index 8a243c3..b1ecc0d 100644
--- a/config/config.go
+++ b/config/config.go
@@ -32,6 +32,7 @@ type GeneralConfig struct {
}

type UIConfig struct {
	AutoMarkRead        bool          `ini:"auto-mark-read"`
	IndexFormat         string        `ini:"index-format"`
	TimestampFormat     string        `ini:"timestamp-format"`
	ThisDayTimeFormat   string        `ini:"this-day-time-format"`
@@ -689,6 +690,7 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) {
		},

		Ui: UIConfig{
			AutoMarkRead:       true,
			IndexFormat:        "%D %-17.17n %s",
			TimestampFormat:    "2006-01-02 03:04 PM",
			ThisDayTimeFormat:  "",
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 518fe51..02a1514 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -97,6 +97,11 @@ These options are configured in the *[ui]* section of aerc.conf.
|  %Z
:  flags (O=old, N=new, r=answered, D=deleted, !=flagged, \*=marked)

*auto-mark-read*
	Set the "seen" flag when a message is viewed.

	Default: true

*timestamp-format*
	See time.Time#Format at https://godoc.org/time#Time.Format

diff --git a/lib/messageview.go b/lib/messageview.go
index a1797d5..0b7b5b9 100644
--- a/lib/messageview.go
+++ b/lib/messageview.go
@@ -9,6 +9,7 @@ import (
	"github.com/emersion/go-message"
	_ "github.com/emersion/go-message/charset"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/crypto"
	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/lib"
@@ -62,7 +63,7 @@ type MessageStoreView struct {

func NewMessageStoreView(messageInfo *models.MessageInfo,
	store *MessageStore, pgp crypto.Provider, decryptKeys openpgp.PromptFunction,
	cb func(MessageView, error)) {
	uiConfig *config.UIConfig, cb func(MessageView, error)) {

	msv := &MessageStoreView{messageInfo, store,
		nil, nil, messageInfo.BodyStructure}
@@ -97,7 +98,9 @@ func NewMessageStoreView(messageInfo *models.MessageInfo,
	} else {
		cb(msv, nil)
	}
	store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
	if uiConfig.AutoMarkRead {
		store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
	}
}

func (msv *MessageStoreView) MessageInfo() *models.MessageInfo {
diff --git a/widgets/msglist.go b/widgets/msglist.go
index ec14e79..949662c 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -305,7 +305,7 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
					return
				}
				lib.NewMessageStoreView(msg, store, ml.aerc.Crypto,
					ml.aerc.DecryptKeys,
					ml.aerc.DecryptKeys, acct.UiConfig(),
					func(view lib.MessageView, err error) {
						if err != nil {
							ml.aerc.PushError(err.Error())
-- 
2.36.1
aerc/patches: SUCCESS in 2m58s

[Allow not marking viewed messages as seen.][0] from [James Cook][1]

[0]: https://lists.sr.ht/~rjarry/aerc-devel/patches/33568
[1]: mailto:falsifian@falsifian.org

✓ #794947 SUCCESS aerc/patches/openbsd.yml     https://builds.sr.ht/~rjarry/job/794947
✓ #794946 SUCCESS aerc/patches/alpine-edge.yml https://builds.sr.ht/~rjarry/job/794946
James Cook, Jul 05, 2022 at 08:06:

Re: [PATCH aerc] Allow not marking viewed messages as seen. Export this patch

On Sun Jul 10, 2022 at 11:34 AM CDT, James Cook wrote:
> I can try adding the change to unset \Seen myself, but probably won't
> have time until next weekend, so if you or someone else wants to do the
> work, please go ahead. (We could also just say the setting is not
> supported with IMAP.)

First, I think we should support the feature for all workers.

Second, I really think an approach like below is best. In both cases, it's only
one call to the imap server and is very few lines of code to implement.

diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index 7d7b72f..5beb886 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -96,6 +96,9 @@ func (imapw *IMAPWorker) handleFetchMessageBodyPart(
		partBodySection.Specifier = imap.TextSpecifier
	}
	partBodySection.Path = msg.Part
	if msg.Peek {
		partBodySection.Peek = true
	}

	items := []imap.FetchItem{
		imap.FetchEnvelope,
@@ -150,6 +153,9 @@ func (imapw *IMAPWorker) handleFetchFullMessages(

	imapw.worker.Logger.Printf("Fetching full messages")
	section := &imap.BodySectionName{}
	if msg.Peek {
		section.Peek = true
	}
	items := []imap.FetchItem{
		imap.FetchEnvelope,
		imap.FetchFlags,
diff --git a/worker/types/messages.go b/worker/types/messages.go
index e303ade..67658c5 100644
--- a/worker/types/messages.go
+++ b/worker/types/messages.go
@@ -125,12 +125,14 @@ type FetchMessageHeaders struct {
type FetchFullMessages struct {
	Message
	Uids []uint32
	Peek bool
}

type FetchMessageBodyPart struct {
	Message
	Uid  uint32
	Part []int
	Peek bool
}

type FetchMessageFlags struct {

[PATCH aerc] Use IMAP PEEK and allow not marking messages read. Export this patch

This commit adds a new configuration option, auto-mark-read, which
defaults to true. When set to false, emails are not marked as seen when
viewed.

We also fetch BODY.PEEK[...] instead of BODY when fetching messages with
IMAP. This means messages are only marked as read when we deliberately
set the flag (which we automatically anyway, as long as
auto-mark-read=true).

I imagine before this change the export-mbox command caused all messages
in an IMAP folder to be marked as seen.

Fixes: https://todo.sr.ht/~rjarry/aerc/48
Signed-off-by: James Cook <falsifian@falsifian.org>
Squashed commit of the following:

commit 9ca6eda82438c0ee0836afb91ec2be9cdce2b393
Author: James Cook <falsifian@falsifian.org>
Date:   Fri Aug 19 23:51:26 2022 +0000

    Use PEEK with IMAP.

commit a49578363c989825fd2e1af1e404adcc8cf60d5a
Author: James Cook <falsifian@falsifian.org>
Date:   Fri Aug 19 22:55:48 2022 +0000

    Post-merge fix: Pass UI config.

commit b13b7be79c85a408dcb6c509fb7b6284e29ff9ee
Merge: 8bfe752 1b91b68
Author: James Cook <falsifian@falsifian.org>
Date:   Fri Aug 19 22:50:05 2022 +0000

    Merge branch 'master' into unread

commit 8bfe752c2807efaf37507c87fe00a568239e23b7
Author: James Cook <falsifian@falsifian.org>
Date:   Tue Jul 5 05:38:38 2022 +0000
---
 commands/account/view.go | 2 +-
 commands/msg/delete.go   | 1 +
 commands/msg/recall.go   | 2 +-
 commands/msgview/next.go | 2 +-
 config/aerc.conf         | 6 ++++++
 config/config.go         | 2 ++
 doc/aerc-config.5.scd    | 5 +++++
 lib/messageview.go       | 7 +++++--
 widgets/msglist.go       | 2 +-
 worker/imap/fetch.go     | 5 ++++-
 10 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/commands/account/view.go b/commands/account/view.go
index 8537d33..04a1687 100644
--- a/commands/account/view.go
+++ b/commands/account/view.go
@@ -46,7 +46,7 @@ func (ViewMessage) Execute(aerc *widgets.Aerc, args []string) error {
		return nil
	}
	lib.NewMessageStoreView(msg, store, aerc.Crypto, aerc.DecryptKeys,
		func(view lib.MessageView, err error) {
		acct.UiConfig(), func(view lib.MessageView, err error) {
			if err != nil {
				aerc.PushError(err.Error())
				return
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index ceb570b..4c3b7f0 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -63,6 +63,7 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error {
						return
					}
					lib.NewMessageStoreView(next, store, aerc.Crypto, aerc.DecryptKeys,
						acct.UiConfig(),
						func(view lib.MessageView, err error) {
							if err != nil {
								aerc.PushError(err.Error())
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 5fc3a26..578f910 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -137,7 +137,7 @@ func (Recall) Execute(aerc *widgets.Aerc, args []string) error {
		})
	}

	lib.NewMessageStoreView(msgInfo, store, aerc.Crypto, aerc.DecryptKeys,
	lib.NewMessageStoreView(msgInfo, store, aerc.Crypto, aerc.DecryptKeys, acct.UiConfig(),
		func(msg lib.MessageView, err error) {
			if err != nil {
				aerc.PushError(err.Error())
diff --git a/commands/msgview/next.go b/commands/msgview/next.go
index c80e0ab..5114031 100644
--- a/commands/msgview/next.go
+++ b/commands/msgview/next.go
@@ -43,7 +43,7 @@ func (NextPrevMsg) Execute(aerc *widgets.Aerc, args []string) error {
		return nil
	}
	lib.NewMessageStoreView(nextMsg, store, aerc.Crypto, aerc.DecryptKeys,
		func(view lib.MessageView, err error) {
		acct.UiConfig(), func(view lib.MessageView, err error) {
			if err != nil {
				aerc.PushError(err.Error())
				return
diff --git a/config/aerc.conf b/config/aerc.conf
index fc6479a..0f03aa0 100644
--- a/config/aerc.conf
+++ b/config/aerc.conf
@@ -18,6 +18,12 @@ pgp-provider=internal
unsafe-accounts-conf=false

[ui]
#
# Set the "seen" flag when a message is viewed.
#
# Default: true
auto-mark-read=true

#
# Describes the format for each row in a mailbox view. This field is compatible
# with mutt's printf-like syntax.
diff --git a/config/config.go b/config/config.go
index 66c6dd1..cfabc44 100644
--- a/config/config.go
+++ b/config/config.go
@@ -33,6 +33,7 @@ type GeneralConfig struct {
}

type UIConfig struct {
	AutoMarkRead        bool          `ini:"auto-mark-read"`
	IndexFormat         string        `ini:"index-format"`
	TimestampFormat     string        `ini:"timestamp-format"`
	ThisDayTimeFormat   string        `ini:"this-day-time-format"`
@@ -710,6 +711,7 @@ func LoadConfigFromFile(root *string) (*AercConfig, error) {
		},

		Ui: UIConfig{
			AutoMarkRead:       true,
			IndexFormat:        "%D %-17.17n %s",
			TimestampFormat:    "2006-01-02 03:04 PM",
			ThisDayTimeFormat:  "",
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index aaf15b8..fe825de 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -97,6 +97,11 @@ These options are configured in the *[ui]* section of aerc.conf.
|  %Z
:  flags (O=old, N=new, r=answered, D=deleted, !=flagged, \*=marked)

*auto-mark-read*
	Set the "seen" flag when a message is viewed.

	Default: true

*timestamp-format*
	See time.Time#Format at https://godoc.org/time#Time.Format

diff --git a/lib/messageview.go b/lib/messageview.go
index e0e86ea..597e58d 100644
--- a/lib/messageview.go
+++ b/lib/messageview.go
@@ -9,6 +9,7 @@ import (
	"github.com/emersion/go-message"
	_ "github.com/emersion/go-message/charset"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/crypto"
	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/lib"
@@ -62,7 +63,7 @@ type MessageStoreView struct {

func NewMessageStoreView(messageInfo *models.MessageInfo,
	store *MessageStore, pgp crypto.Provider, decryptKeys openpgp.PromptFunction,
	cb func(MessageView, error),
	uiConfig *config.UIConfig, cb func(MessageView, error),
) {
	msv := &MessageStoreView{
		messageInfo, store,
@@ -99,7 +100,9 @@ func NewMessageStoreView(messageInfo *models.MessageInfo,
	} else {
		cb(msv, nil)
	}
	store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
	if uiConfig.AutoMarkRead {
		store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
	}
}

func (msv *MessageStoreView) MessageInfo() *models.MessageInfo {
diff --git a/widgets/msglist.go b/widgets/msglist.go
index e431c2e..dfd6f34 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -307,7 +307,7 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
					return
				}
				lib.NewMessageStoreView(msg, store, ml.aerc.Crypto,
					ml.aerc.DecryptKeys,
					ml.aerc.DecryptKeys, acct.UiConfig(),
					func(view lib.MessageView, err error) {
						if err != nil {
							ml.aerc.PushError(err.Error())
diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index f21c6e9..0d2bb16 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -91,6 +91,7 @@ func (imapw *IMAPWorker) handleFetchMessageBodyPart(
	partHeaderSection.Path = msg.Part

	var partBodySection imap.BodySectionName
	partBodySection.Peek = true
	if len(msg.Part) > 0 {
		partBodySection.Specifier = imap.EntireSpecifier
	} else {
@@ -150,7 +151,9 @@ func (imapw *IMAPWorker) handleFetchFullMessages(
	msg *types.FetchFullMessages,
) {
	logging.Infof("Fetching full messages: %v", msg.Uids)
	section := &imap.BodySectionName{}
	section := &imap.BodySectionName{
		Peek: true,
	}
	items := []imap.FetchItem{
		imap.FetchEnvelope,
		imap.FetchFlags,
-- 
2.37.2
Would it be useful to set this value (and the above
partBodySection.Peek) to only peek when needed? This way we'd use the
most appropriate method for getting the body.

I fail to find a satisfactory answer in your follow up email.
Please also send your new revisions as a v2, v3, … vn :