aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/account.go4
-rw-r--r--app/dirtree.go213
-rw-r--r--app/msglist.go18
-rw-r--r--app/msgviewer.go2
-rw-r--r--app/providesmessage.go2
5 files changed, 108 insertions, 131 deletions
diff --git a/app/account.go b/app/account.go
index 5577e461..6d690142 100644
--- a/app/account.go
+++ b/app/account.go
@@ -240,7 +240,7 @@ func (acct *AccountView) SelectedMessage() (*models.MessageInfo, error) {
return msg, nil
}
-func (acct *AccountView) MarkedMessages() ([]uint32, error) {
+func (acct *AccountView) MarkedMessages() ([]models.UID, error) {
if store := acct.Store(); store != nil {
return store.Marker().Marked(), nil
}
@@ -495,7 +495,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
acct.setTitle()
}
-func (acct *AccountView) updateDirCounts(destination string, uids []uint32) {
+func (acct *AccountView) updateDirCounts(destination string, uids []models.UID) {
// Only update the destination destDir if it is initialized
if destDir := acct.dirlist.Directory(destination); destDir != nil {
var recent, unseen int
diff --git a/app/dirtree.go b/app/dirtree.go
index a7d0a5e7..dc80fa7f 100644
--- a/app/dirtree.go
+++ b/app/dirtree.go
@@ -3,7 +3,6 @@ package app
import (
"fmt"
"sort"
- "strconv"
"strings"
"time"
@@ -23,8 +22,6 @@ type DirectoryTree struct {
listIdx int
list []*types.Thread
- treeDirs []string
-
virtual bool
virtualCb func()
}
@@ -33,7 +30,6 @@ func NewDirectoryTree(dirlist *DirectoryList) DirectoryLister {
dt := &DirectoryTree{
DirectoryList: dirlist,
listIdx: -1,
- list: make([]*types.Thread, 0),
virtualCb: func() {},
}
return dt
@@ -48,13 +44,12 @@ func (dt *DirectoryTree) Selected() string {
return dt.DirectoryList.Selected()
}
node := dt.list[dt.listIdx]
- sep := dt.DirectoryList.worker.PathSeparator()
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], sep)
+ elems := dt.nodeElems(node)
n := countLevels(node)
if n < 0 || n >= len(elems) {
return ""
}
- return strings.Join(elems[:(n+1)], sep)
+ return strings.Join(elems[:(n+1)], dt.DirectoryList.worker.PathSeparator())
}
func (dt *DirectoryTree) SelectedDirectory() *models.Directory {
@@ -211,27 +206,24 @@ func (dt *DirectoryTree) SelectedMsgStore() (*lib.MessageStore, bool) {
if dt.virtual {
return nil, false
}
- if findString(dt.treeDirs, dt.selected) < 0 {
+
+ selected := models.UID(dt.selected)
+ if _, node := dt.getTreeNode(selected); node == nil {
dt.buildTree()
- if idx := findString(dt.treeDirs, dt.selected); idx >= 0 {
- selIdx, node := dt.getTreeNode(uint32(idx))
- if node != nil {
- makeVisible(node)
- dt.listIdx = selIdx
- }
+ selIdx, node := dt.getTreeNode(selected)
+ if node != nil {
+ makeVisible(node)
+ dt.listIdx = selIdx
}
}
return dt.DirectoryList.SelectedMsgStore()
}
func (dt *DirectoryTree) reindex(name string) {
- idx := findString(dt.treeDirs, name)
- if idx >= 0 {
- selIdx, node := dt.getTreeNode(uint32(idx))
- if node != nil {
- makeVisible(node)
- dt.listIdx = selIdx
- }
+ selIdx, node := dt.getTreeNode(models.UID(name))
+ if node != nil {
+ makeVisible(node)
+ dt.listIdx = selIdx
}
}
@@ -247,7 +239,8 @@ func (dt *DirectoryTree) Open(name string, query string, delay time.Duration, cb
return
}
again := false
- if findString(dt.dirs, name) < 0 {
+ uid := models.UID(name)
+ if _, node := dt.getTreeNode(uid); node == nil {
again = true
} else {
dt.reindex(name)
@@ -300,13 +293,14 @@ func (dt *DirectoryTree) NextPrev(delta int) {
func (dt *DirectoryTree) selectIndex(i int) {
dt.listIdx = i
- if path := dt.getDirectory(dt.list[dt.listIdx]); path != "" {
- dt.virtual = false
- dt.Select(path)
- } else {
+ node := dt.list[dt.listIdx]
+ if node.Dummy {
dt.virtual = true
dt.NewContext()
dt.virtualCb()
+ } else {
+ dt.virtual = false
+ dt.Select(dt.getDirectory(node))
}
}
@@ -345,37 +339,43 @@ func (dt *DirectoryTree) countVisible(list []*types.Thread) (n int) {
return
}
+func (dt *DirectoryTree) nodeElems(node *types.Thread) []string {
+ dir := string(node.Uid)
+ sep := dt.DirectoryList.worker.PathSeparator()
+ return strings.Split(dir, sep)
+}
+
+func (dt *DirectoryTree) nodeName(node *types.Thread) string {
+ if elems := dt.nodeElems(node); len(elems) > 0 {
+ return elems[len(elems)-1]
+ }
+ return ""
+}
+
func (dt *DirectoryTree) displayText(node *types.Thread) string {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
return fmt.Sprintf("%s%s%s",
threadPrefix(node, false, false),
- getFlag(node), elems[countLevels(node)])
+ getFlag(node), dt.nodeName(node))
}
func (dt *DirectoryTree) getDirectory(node *types.Thread) string {
- if uid := node.Uid; int(uid) < len(dt.treeDirs) {
- return dt.treeDirs[uid]
- }
- return ""
+ return string(node.Uid)
}
-func (dt *DirectoryTree) getTreeNode(uid uint32) (int, *types.Thread) {
- var found *types.Thread
- var idx int
+func (dt *DirectoryTree) getTreeNode(uid models.UID) (int, *types.Thread) {
for i, node := range dt.list {
if node.Uid == uid {
- found = node
- idx = i
+ return i, node
}
}
- return idx, found
+ return -1, nil
}
func (dt *DirectoryTree) hiddenDirectories() map[string]bool {
hidden := make(map[string]bool, 0)
for _, node := range dt.list {
if node.Hidden != 0 && node.FirstChild != nil {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
+ elems := dt.nodeElems(node)
if levels := countLevels(node); levels < len(elems) {
if node.FirstChild != nil && (levels+1) < len(elems) {
levels += 1
@@ -390,8 +390,9 @@ func (dt *DirectoryTree) hiddenDirectories() map[string]bool {
}
func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
+ log.Tracef("setHiddenDirectories: %#v", hiddenDirs)
for _, node := range dt.list {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
+ elems := dt.nodeElems(node)
if levels := countLevels(node); levels < len(elems) {
if node.FirstChild != nil && (levels+1) < len(elems) {
levels += 1
@@ -399,6 +400,7 @@ func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
strDir := strings.Join(elems[:levels], dt.DirectoryList.worker.PathSeparator())
if hidden, ok := hiddenDirs[strDir]; hidden && ok {
node.Hidden = 1
+ log.Tracef("setHiddenDirectories: %q -> %#v", strDir, node)
}
}
}
@@ -407,29 +409,15 @@ func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
func (dt *DirectoryTree) buildTree() {
if len(dt.list) != 0 {
hiddenDirs := dt.hiddenDirectories()
- defer func() {
- dt.setHiddenDirectories(hiddenDirs)
- }()
- }
-
- sTree := make([][]string, 0)
- for i, dir := range dt.dirs {
- elems := strings.Split(dir, dt.DirectoryList.worker.PathSeparator())
- if len(elems) == 0 {
- continue
- }
- elems = append(elems, fmt.Sprintf("%d", i))
- sTree = append(sTree, elems)
+ defer dt.setHiddenDirectories(hiddenDirs)
}
- dt.treeDirs = make([]string, len(dt.dirs))
- copy(dt.treeDirs, dt.dirs)
-
- root := &types.Thread{Uid: 0}
- dt.buildTreeNode(root, sTree, 0xFFFFFF, 1)
-
- threads := make([]*types.Thread, 0)
+ dirs := make([]string, len(dt.dirs))
+ copy(dirs, dt.dirs)
+ root := &types.Thread{}
+ dt.buildTreeNode(root, dirs, 1)
+ var threads []*types.Thread
for iter := root.FirstChild; iter != nil; iter = iter.NextSibling {
iter.Parent = nil
threads = append(threads, iter)
@@ -437,16 +425,10 @@ func (dt *DirectoryTree) buildTree() {
// folders-sort
if dt.DirectoryList.acctConf.EnableFoldersSort {
- toStr := func(t *types.Thread) string {
- if elems := strings.Split(dt.treeDirs[getAnyUid(t)], dt.DirectoryList.worker.PathSeparator()); len(elems) > 0 {
- return elems[0]
- }
- return ""
- }
sort.Slice(threads, func(i, j int) bool {
foldersSort := dt.DirectoryList.acctConf.FoldersSort
- iInFoldersSort := findString(foldersSort, toStr(threads[i]))
- jInFoldersSort := findString(foldersSort, toStr(threads[j]))
+ iInFoldersSort := findString(foldersSort, dt.getDirectory(threads[i]))
+ jInFoldersSort := findString(foldersSort, dt.getDirectory(threads[j]))
if iInFoldersSort >= 0 && jInFoldersSort >= 0 {
return iInFoldersSort < jInFoldersSort
}
@@ -456,7 +438,7 @@ func (dt *DirectoryTree) buildTree() {
if jInFoldersSort >= 0 {
return false
}
- return toStr(threads[i]) < toStr(threads[j])
+ return dt.getDirectory(threads[i]) < dt.getDirectory(threads[j])
})
}
@@ -472,41 +454,51 @@ func (dt *DirectoryTree) buildTree() {
}
}
-func (dt *DirectoryTree) buildTreeNode(node *types.Thread, stree [][]string, defaultUid uint32, depth int) {
- m := make(map[string][][]string)
- for _, branch := range stree {
- if len(branch) > 1 {
- next := append(m[branch[0]], branch[1:]) //nolint:gocritic // intentional append to different slice
- m[branch[0]] = next
- }
- }
- keys := make([]string, 0)
- for key := range m {
- keys = append(keys, key)
- }
- sort.Strings(keys)
- path := dt.getDirectory(node)
- for _, key := range keys {
- next := m[key]
- var uid uint32 = defaultUid
- for _, testStr := range next {
- if len(testStr) == 1 {
- if uidI, err := strconv.Atoi(next[0][0]); err == nil {
- uid = uint32(uidI)
- }
+func (dt *DirectoryTree) buildTreeNode(node *types.Thread, dirs []string, depth int) {
+ dirmap := make(map[string][]string)
+ for _, dir := range dirs {
+ base, dir, cut := strings.Cut(
+ dir, dt.DirectoryList.worker.PathSeparator())
+ if _, found := dirmap[base]; found {
+ if cut {
+ dirmap[base] = append(dirmap[base], dir)
}
+ } else if cut {
+ dirmap[base] = append(dirmap[base], dir)
+ } else {
+ dirmap[base] = []string{}
}
- nextNode := &types.Thread{Uid: uid}
+ }
+ bases := make([]string, 0, len(dirmap))
+ for base, dirs := range dirmap {
+ bases = append(bases, base)
+ sort.Strings(dirs)
+ }
+ sort.Strings(bases)
+
+ basePath := dt.getDirectory(node)
+ if depth > dt.UiConfig(basePath).DirListCollapse {
+ node.Hidden = 1
+ } else {
+ node.Hidden = 0
+ }
+
+ for _, base := range bases {
+ path := dt.childPath(basePath, base)
+ nextNode := &types.Thread{Uid: models.UID(path)}
+
+ nextNode.Dummy = findString(dt.dirs, path) == -1
+
node.AddChild(nextNode)
- if dt.UiConfig(path).DirListCollapse != 0 && dt.listIdx < 0 {
- if depth > dt.UiConfig(path).DirListCollapse {
- node.Hidden = 1
- } else {
- node.Hidden = 0
- }
- }
- dt.buildTreeNode(nextNode, next, defaultUid, depth+1)
+ dt.buildTreeNode(nextNode, dirmap[base], depth+1)
+ }
+}
+
+func (dt *DirectoryTree) childPath(base, relpath string) string {
+ if base == "" {
+ return relpath
}
+ return base + dt.DirectoryList.worker.PathSeparator() + relpath
}
func makeVisible(node *types.Thread) {
@@ -519,27 +511,12 @@ func makeVisible(node *types.Thread) {
}
func isVisible(node *types.Thread) bool {
- isVisible := true
for iter := node.Parent; iter != nil; iter = iter.Parent {
if iter.Hidden != 0 {
- isVisible = false
- break
+ return false
}
}
- return isVisible
-}
-
-func getAnyUid(node *types.Thread) (uid uint32) {
- err := node.Walk(func(t *types.Thread, l int, err error) error {
- if t.FirstChild == nil {
- uid = t.Uid
- }
- return nil
- })
- if err != nil {
- log.Warnf("failed to get uid: %v", err)
- }
- return
+ return true
}
func countLevels(node *types.Thread) (level int) {
@@ -550,7 +527,7 @@ func countLevels(node *types.Thread) (level int) {
}
func getFlag(node *types.Thread) string {
- if node == nil && node.FirstChild == nil {
+ if node == nil || node.FirstChild == nil {
return ""
}
if node.Hidden != 0 {
diff --git a/app/msglist.go b/app/msglist.go
index 9e6f3289..4db54f4d 100644
--- a/app/msglist.go
+++ b/app/msglist.go
@@ -44,7 +44,7 @@ func (ml *MessageList) Invalidate() {
}
type messageRowParams struct {
- uid uint32
+ uid models.UID
needsHeaders bool
err error
uiConfig *config.UIConfig
@@ -61,7 +61,7 @@ func (ml *MessageList) AlignMessage(pos AlignPosition) {
idx := 0
iter := store.UidsIterator()
for i := 0; iter.Next(); i++ {
- if store.SelectedUid() == iter.Value().(uint32) {
+ if store.SelectedUid() == iter.Value().(models.UID) {
idx = i
break
}
@@ -92,7 +92,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
ml.UpdateScroller(ml.height, len(store.Uids()))
iter := store.UidsIterator()
for i := 0; iter.Next(); i++ {
- if store.SelectedUid() == iter.Value().(uint32) {
+ if store.SelectedUid() == iter.Value().(models.UID) {
ml.EnsureScroll(i)
break
}
@@ -108,7 +108,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
return
}
- var needsHeaders []uint32
+ var needsHeaders []models.UID
data := state.NewDataSetter()
data.SetAccount(acct.acct)
@@ -166,7 +166,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
if i < ml.Scroll() {
continue
}
- uid := iter.Value().(uint32)
+ uid := iter.Value().(models.UID)
if showThreads {
threadView.Update(data, uid)
}
@@ -201,7 +201,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
}
func addMessage(
- store *lib.MessageStore, uid uint32,
+ store *lib.MessageStore, uid models.UID,
table *ui.Table, data state.DataSetter,
uiConfig *config.UIConfig,
) bool {
@@ -406,14 +406,14 @@ func (ml *MessageList) Select(index int) {
iter := store.UidsIterator()
- var uid uint32
+ var uid models.UID
if index < 0 {
uid = uids[iter.EndIndex()]
} else {
uid = uids[iter.StartIndex()]
for i := 0; iter.Next(); i++ {
if i >= index {
- uid = iter.Value().(uint32)
+ uid = iter.Value().(models.UID)
break
}
}
@@ -579,7 +579,7 @@ func newThreadView(store *lib.MessageStore) *threadView {
}
}
-func (t *threadView) Update(data state.DataSetter, uid uint32) {
+func (t *threadView) Update(data state.DataSetter, uid models.UID) {
thread, err := t.store.Thread(uid)
info := state.ThreadInfo{}
if thread != nil && err == nil {
diff --git a/app/msgviewer.go b/app/msgviewer.go
index 8760bf74..d4dbd73e 100644
--- a/app/msgviewer.go
+++ b/app/msgviewer.go
@@ -333,7 +333,7 @@ func (mv *MessageViewer) SelectedMessage() (*models.MessageInfo, error) {
return mv.msg.MessageInfo(), nil
}
-func (mv *MessageViewer) MarkedMessages() ([]uint32, error) {
+func (mv *MessageViewer) MarkedMessages() ([]models.UID, error) {
return mv.acct.MarkedMessages()
}
diff --git a/app/providesmessage.go b/app/providesmessage.go
index c89c811f..4572f65b 100644
--- a/app/providesmessage.go
+++ b/app/providesmessage.go
@@ -26,5 +26,5 @@ type ProvidesMessages interface {
Store() *lib.MessageStore
SelectedAccount() *AccountView
SelectedMessage() (*models.MessageInfo, error)
- MarkedMessages() ([]uint32, error)
+ MarkedMessages() ([]models.UID, error)
}