threads: reverse thread ordering
Add reverse-thread-order option to the ui config to enable reverse display of the mesage threads. Default order is the the intial message is on the top with all the replies being displayed below. The reverse options will put the initial message at the bottom with the replies on top. Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
parent
88afe7bb4a
commit
006e10357b
7 changed files with 51 additions and 13 deletions
|
@ -12,6 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
`:rmdir`, `:archive` and the `copy-to` option.
|
`:rmdir`, `:archive` and the `copy-to` option.
|
||||||
- Display messages from bottom to top with `reverse-msglist-order=true` in
|
- Display messages from bottom to top with `reverse-msglist-order=true` in
|
||||||
`aerc.conf`.
|
`aerc.conf`.
|
||||||
|
- Display threads from bottom to top with `reverse-thread-order=true` in
|
||||||
|
`aerc.conf`.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,14 @@ completion-popovers=true
|
||||||
# Default: false
|
# Default: false
|
||||||
#reverse-msglist-order = false
|
#reverse-msglist-order = false
|
||||||
|
|
||||||
|
# Reverse display of the mesage threads. Default order is the the intial
|
||||||
|
# message is on the top with all the replies being displayed below. The
|
||||||
|
# reverse option will put the initial message at the bottom with the
|
||||||
|
# replies on top.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
#reverse-thread-order=false
|
||||||
|
|
||||||
#[ui:account=foo]
|
#[ui:account=foo]
|
||||||
#
|
#
|
||||||
# Enable a threaded view of messages. If this is not supported by the backend
|
# Enable a threaded view of messages. If this is not supported by the backend
|
||||||
|
|
|
@ -79,7 +79,8 @@ type UIConfig struct {
|
||||||
BorderCharVertical rune `ini:"-"`
|
BorderCharVertical rune `ini:"-"`
|
||||||
BorderCharHorizontal rune `ini:"-"`
|
BorderCharHorizontal rune `ini:"-"`
|
||||||
|
|
||||||
ReverseOrder bool `ini:"reverse-msglist-order"`
|
ReverseOrder bool `ini:"reverse-msglist-order"`
|
||||||
|
ReverseThreadOrder bool `ini:"reverse-thread-order"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContextType int
|
type ContextType int
|
||||||
|
|
|
@ -335,6 +335,14 @@ These options are configured in the *[ui]* section of aerc.conf.
|
||||||
|
|
||||||
Default: false
|
Default: false
|
||||||
|
|
||||||
|
*reverse-thread-order*
|
||||||
|
Reverse display of the mesage threads. Default order is the the intial
|
||||||
|
message is on the top with all the replies being displayed below. The
|
||||||
|
reverse option will put the initial message at the bottom with the
|
||||||
|
replies on top.
|
||||||
|
|
||||||
|
Default: false
|
||||||
|
|
||||||
*threading-enabled*
|
*threading-enabled*
|
||||||
Enable a threaded view of messages. If this is not supported by the
|
Enable a threaded view of messages. If this is not supported by the
|
||||||
backend (IMAP server or notmuch), threads will be built by the client.
|
backend (IMAP server or notmuch), threads will be built by the client.
|
||||||
|
|
|
@ -39,9 +39,10 @@ type MessageStore struct {
|
||||||
|
|
||||||
sortCriteria []*types.SortCriterion
|
sortCriteria []*types.SortCriterion
|
||||||
|
|
||||||
threadedView bool
|
threadedView bool
|
||||||
buildThreads bool
|
reverseThreadOrder bool
|
||||||
builder *ThreadBuilder
|
buildThreads bool
|
||||||
|
builder *ThreadBuilder
|
||||||
|
|
||||||
// Map of uids we've asked the worker to fetch
|
// Map of uids we've asked the worker to fetch
|
||||||
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
|
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
|
||||||
|
@ -74,7 +75,7 @@ func NewMessageStore(worker *types.Worker,
|
||||||
dirInfo *models.DirectoryInfo,
|
dirInfo *models.DirectoryInfo,
|
||||||
defaultSortCriteria []*types.SortCriterion,
|
defaultSortCriteria []*types.SortCriterion,
|
||||||
thread bool, clientThreads bool, clientThreadsDelay time.Duration,
|
thread bool, clientThreads bool, clientThreadsDelay time.Duration,
|
||||||
reverseOrder bool,
|
reverseOrder bool, reverseThreadOrder bool,
|
||||||
triggerNewEmail func(*models.MessageInfo),
|
triggerNewEmail func(*models.MessageInfo),
|
||||||
triggerDirectoryChange func(),
|
triggerDirectoryChange func(),
|
||||||
) *MessageStore {
|
) *MessageStore {
|
||||||
|
@ -91,8 +92,9 @@ func NewMessageStore(worker *types.Worker,
|
||||||
|
|
||||||
bodyCallbacks: make(map[uint32][]func(*types.FullMessage)),
|
bodyCallbacks: make(map[uint32][]func(*types.FullMessage)),
|
||||||
|
|
||||||
threadedView: thread,
|
threadedView: thread,
|
||||||
buildThreads: clientThreads,
|
buildThreads: clientThreads,
|
||||||
|
reverseThreadOrder: reverseThreadOrder,
|
||||||
|
|
||||||
filter: []string{"filter"},
|
filter: []string{"filter"},
|
||||||
sortCriteria: defaultSortCriteria,
|
sortCriteria: defaultSortCriteria,
|
||||||
|
@ -231,7 +233,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
|
||||||
newMap := make(map[uint32]*models.MessageInfo)
|
newMap := make(map[uint32]*models.MessageInfo)
|
||||||
|
|
||||||
builder := NewThreadBuilder(store.iterFactory)
|
builder := NewThreadBuilder(store.iterFactory)
|
||||||
builder.RebuildUids(msg.Threads)
|
builder.RebuildUids(msg.Threads, store.reverseThreadOrder)
|
||||||
store.uids = builder.Uids()
|
store.uids = builder.Uids()
|
||||||
store.threads = msg.Threads
|
store.threads = msg.Threads
|
||||||
|
|
||||||
|
@ -364,6 +366,14 @@ func (store *MessageStore) update(threads bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *MessageStore) SetReverseThreadOrder(reverse bool) {
|
||||||
|
store.reverseThreadOrder = reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *MessageStore) ReverseThreadOrder() bool {
|
||||||
|
return store.reverseThreadOrder
|
||||||
|
}
|
||||||
|
|
||||||
func (store *MessageStore) SetThreadedView(thread bool) {
|
func (store *MessageStore) SetThreadedView(thread bool) {
|
||||||
store.threadedView = thread
|
store.threadedView = thread
|
||||||
if store.buildThreads {
|
if store.buildThreads {
|
||||||
|
@ -424,7 +434,7 @@ func (store *MessageStore) runThreadBuilderNow() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// build new threads
|
// build new threads
|
||||||
th := store.builder.Threads(store.uids)
|
th := store.builder.Threads(store.uids, store.reverseThreadOrder)
|
||||||
|
|
||||||
// save local threads to the message store variable and
|
// save local threads to the message store variable and
|
||||||
// run callback if defined (callback should reposition cursor)
|
// run callback if defined (callback should reposition cursor)
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (builder *ThreadBuilder) Update(msg *models.MessageInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Threads returns a slice of threads for the given list of uids
|
// Threads returns a slice of threads for the given list of uids
|
||||||
func (builder *ThreadBuilder) Threads(uids []uint32) []*types.Thread {
|
func (builder *ThreadBuilder) Threads(uids []uint32, inverse bool) []*types.Thread {
|
||||||
builder.Lock()
|
builder.Lock()
|
||||||
defer builder.Unlock()
|
defer builder.Unlock()
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ func (builder *ThreadBuilder) Threads(uids []uint32) []*types.Thread {
|
||||||
builder.sortThreads(threads, uids)
|
builder.sortThreads(threads, uids)
|
||||||
|
|
||||||
// rebuild uids from threads
|
// rebuild uids from threads
|
||||||
builder.RebuildUids(threads)
|
builder.RebuildUids(threads, inverse)
|
||||||
|
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
logging.Infof("%d threads created in %s", len(threads), elapsed)
|
logging.Infof("%d threads created in %s", len(threads), elapsed)
|
||||||
|
@ -155,15 +155,23 @@ func (builder *ThreadBuilder) sortThreads(threads []*types.Thread, orderedUids [
|
||||||
}
|
}
|
||||||
|
|
||||||
// RebuildUids rebuilds the uids from the given slice of threads
|
// RebuildUids rebuilds the uids from the given slice of threads
|
||||||
func (builder *ThreadBuilder) RebuildUids(threads []*types.Thread) {
|
func (builder *ThreadBuilder) RebuildUids(threads []*types.Thread, inverse bool) {
|
||||||
uids := make([]uint32, 0, len(threads))
|
uids := make([]uint32, 0, len(threads))
|
||||||
iterT := builder.iterFactory.NewIterator(threads)
|
iterT := builder.iterFactory.NewIterator(threads)
|
||||||
for iterT.Next() {
|
for iterT.Next() {
|
||||||
|
var threaduids []uint32
|
||||||
_ = iterT.Value().(*types.Thread).Walk(
|
_ = iterT.Value().(*types.Thread).Walk(
|
||||||
func(t *types.Thread, level int, currentErr error) error {
|
func(t *types.Thread, level int, currentErr error) error {
|
||||||
uids = append(uids, t.Uid)
|
threaduids = append(threaduids, t.Uid)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if inverse {
|
||||||
|
for j := len(threaduids) - 1; j >= 0; j-- {
|
||||||
|
uids = append(uids, threaduids[j])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uids = append(uids, threaduids...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result := make([]uint32, 0, len(uids))
|
result := make([]uint32, 0, len(uids))
|
||||||
iterU := builder.iterFactory.NewIterator(uids)
|
iterU := builder.iterFactory.NewIterator(uids)
|
||||||
|
|
|
@ -286,6 +286,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
|
||||||
acct.dirlist.UiConfig(name).ForceClientThreads,
|
acct.dirlist.UiConfig(name).ForceClientThreads,
|
||||||
acct.dirlist.UiConfig(name).ClientThreadsDelay,
|
acct.dirlist.UiConfig(name).ClientThreadsDelay,
|
||||||
acct.dirlist.UiConfig(name).ReverseOrder,
|
acct.dirlist.UiConfig(name).ReverseOrder,
|
||||||
|
acct.dirlist.UiConfig(name).ReverseThreadOrder,
|
||||||
func(msg *models.MessageInfo) {
|
func(msg *models.MessageInfo) {
|
||||||
acct.conf.Triggers.ExecNewEmail(acct.acct,
|
acct.conf.Triggers.ExecNewEmail(acct.acct,
|
||||||
acct.conf, msg)
|
acct.conf, msg)
|
||||||
|
|
Loading…
Reference in a new issue