~rjarry/aerc-devel

aerc: Allow not marking viewed messages as seen. v1 NEEDS REVISION

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
> Looking at that IMAP RFC link, I think the right thing to do is to fetch
> BODY.PEEK instead of BODY. Maybe aerc could always fetch BODY.PEEK
> instead of BOD, so setting \Seen is always deliberate.
I'd argue that BODY is the correct way to handle it and rather remove
the \Seen flag when requested. aerc is a mail client for which this
behaviour is the expected in most cases.
> I've never written code that speaks IMAP so take this all with a grain
> of salt. (I see "BODY.PEEK" appears in isync's source [0] though I
> haven't looked carefully.)
I think this is due to isync having a completely different purpose from
aerc. isync indiscriminately downloads all mail and would thereby mess
up the \Seen flags for all messages.
Next
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
Please also send your new revisions as a v2, v3, … vn :)
-- 
Moritz Poldrack
https://moritz.sh
(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
Thanks for the updates, James! Comments below:

On Sat Aug 20, 2022 at 9:15 AM CDT, James Cook wrote:
Next
(Sorry for butting in... :))

On Sat Aug 20, 2022 at 3:15 PM BST, James Cook wrote:
> Where should I write v2, v3, etc?
Git's `send-email` provides a `-v` flag:

---
# Send a v2 of the last 10 commits to blah@foobar.dev, with
# a cover letter preceding the patchset.
git send-email -10 --to=blah@foobar.dev -v2 --cover-letter
---

    -- (
> > 1. Move store.Flag to worker because it's currently redundant for IMAP,
> > but needed for other workers
> >
> > 2. Add PEEK option to the worker message and all relevant function
> > calls, with the fix to the ones that should be true
> >
> > 3. Add the option to set peek on opening message
> >
> > This approach, at least to me, makes the imap worker more efficient than
> > it currently is, fixes some bugs, and gets this feature in.
> >
> > Tim
> 
> I like this plan, except I wonder if #1 and #2 should be done in the
> same patch.
I don't think that merging them makes a lot of sense… I think their
topics are different enough to warrant multiple commits.
> If I understand you right, after #1, every message fetch would cause
> it to be marked as read, including e.g. running :pipe, so non-IMAP
> backends would get a little bit broken until step #2 is implemented.
Not necessarily. While extracting you can already add a way to pass
whether to mark as read or not. Otherwise I don't think it matters if
one patch in between changes the default behaviour slightly when the
next commit already restores said standard.
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
Please also send your new revisions as a v2, v3, … vn :)
-- 
Moritz Poldrack
https://moritz.sh