about summary refs log tree commit diff stats
path: root/commands/msg/read.go
blob: ef075236fb7ca31af51cdd036998c5d0a13a3c35 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package msg

import (
	"errors"
	"sync"
	"time"

	"git.sr.ht/~sircmpwn/getopt"

	"github.com/gdamore/tcell"

	"git.sr.ht/~sircmpwn/aerc/lib"
	"git.sr.ht/~sircmpwn/aerc/models"
	"git.sr.ht/~sircmpwn/aerc/widgets"
	"git.sr.ht/~sircmpwn/aerc/worker/types"
)

type Read struct{}

func init() {
	register(Read{})
}

func (Read) Aliases() []string {
	return []string{"read", "unread"}
}

func (Read) Complete(aerc *widgets.Aerc, args []string) []string {
	return nil
}

func (Read) Execute(aerc *widgets.Aerc, args []string) error {
	opts, optind, err := getopt.Getopts(args, "t")
	if err != nil {
		return err
	}
	if optind != len(args) {
		return errors.New("Usage: " + args[0] + " [-t]")
	}
	var toggle bool

	for _, opt := range opts {
		switch opt.Option {
		case 't':
			toggle = true
		}
	}

	h := newHelper(aerc)
	store, err := h.store()
	if err != nil {
		return err
	}

	if toggle {
		// ignore commmand given, simply toggle all the read states
		return submitToggle(aerc, store, h)
	}
	msgUids, err := h.messageUids()
	if err != nil {
		return err
	}
	switch args[0] {
	case "read":
		submitReadChange(aerc, store, msgUids, true)
	case "unread":
		submitReadChange(aerc, store, msgUids, false)

	}
	return nil
}

func splitMessages(msgs []*models.MessageInfo) (read []uint32, unread []uint32) {
	for _, m := range msgs {
		var seen bool
		for _, flag := range m.Flags {
			if flag == models.SeenFlag {
				seen = true
				break
			}
		}
		if seen {
			read = append(read, m.Uid)
		} else {
			unread = append(unread, m.Uid)
		}
	}
	return read, unread
}

func submitReadChange(aerc *widgets.Aerc, store *lib.MessageStore,
	uids []uint32, newState bool) {
	store.Read(uids, newState, func(msg types.WorkerMessage) {
		switch msg := msg.(type) {
		case *types.Done:
			aerc.PushStatus(msg_success, 10*time.Second)
		case *types.Error:
			aerc.PushStatus(" "+msg.Error.Error(), 10*time.Second).
				Color(tcell.ColorDefault, tcell.ColorRed)
		}
	})
}

func submitReadChangeWg(aerc *widgets.Aerc, store *lib.MessageStore,
	uids []uint32, newState bool, wg sync.WaitGroup, success *bool) {
	store.Read(uids, newState, func(msg types.WorkerMessage) {
		wg.Add(1)
		switch msg := msg.(type) {
		case *types.Done:
			wg.Done()
		case *types.Error:
			aerc.PushStatus(" "+msg.Error.Error(), 10*time.Second).
				Color(tcell.ColorDefault, tcell.ColorRed)
			*success = false
			wg.Done()
		}
	})
}

func submitToggle(aerc *widgets.Aerc, store *lib.MessageStore, h *helper) error {
	msgs, err := h.messages()
	if err != nil {
		return err
	}
	read, unread := splitMessages(msgs)

	var wg sync.WaitGroup
	success := true

	if len(read) != 0 {
		newState := false
		submitReadChangeWg(aerc, store, read, newState, wg, &success)
	}

	if len(unread) != 0 {
		newState := true
		submitReadChangeWg(aerc, store, unread, newState, wg, &success)
	}
	// we need to do that in the background, else we block the main thread
	go func() {
		wg.Wait()
		if success {
			aerc.PushStatus(msg_success, 10*time.Second)
		}
	}()
	return nil

}

const msg_success = "read state set successfully"