Move TagRoomList stuff into new file and split RoomList.Draw()
This commit is contained in:
		
							
								
								
									
										452
									
								
								ui/room-list.go
									
									
									
									
									
								
							
							
						
						
									
										452
									
								
								ui/room-list.go
									
									
									
									
									
								
							@@ -17,210 +17,23 @@
 | 
				
			|||||||
package ui
 | 
					package ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"maunium.net/go/gomuks/debug"
 | 
						"maunium.net/go/gomuks/debug"
 | 
				
			||||||
	"maunium.net/go/gomuks/matrix/rooms"
 | 
						"maunium.net/go/gomuks/matrix/rooms"
 | 
				
			||||||
	"maunium.net/go/gomuks/ui/widget"
 | 
					 | 
				
			||||||
	"maunium.net/go/tcell"
 | 
						"maunium.net/go/tcell"
 | 
				
			||||||
	"maunium.net/go/tview"
 | 
						"maunium.net/go/tview"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type orderedRoom struct {
 | 
					 | 
				
			||||||
	*rooms.Room
 | 
					 | 
				
			||||||
	order string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newOrderedRoom(order string, room *rooms.Room) *orderedRoom {
 | 
					 | 
				
			||||||
	return &orderedRoom{
 | 
					 | 
				
			||||||
		Room:  room,
 | 
					 | 
				
			||||||
		order: order,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func convertRoom(room *rooms.Room) *orderedRoom {
 | 
					 | 
				
			||||||
	return newOrderedRoom("0.5", room)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type tagRoomList struct {
 | 
					 | 
				
			||||||
	rooms    []*orderedRoom
 | 
					 | 
				
			||||||
	maxShown int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func newTagRoomList(rooms ...*orderedRoom) *tagRoomList {
 | 
					 | 
				
			||||||
	return &tagRoomList{
 | 
					 | 
				
			||||||
		maxShown: 10,
 | 
					 | 
				
			||||||
		rooms:    rooms,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Visible() []*orderedRoom {
 | 
					 | 
				
			||||||
	return trl.rooms[len(trl.rooms)-trl.Length():]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) FirstVisible() *rooms.Room {
 | 
					 | 
				
			||||||
	visible := trl.Visible()
 | 
					 | 
				
			||||||
	if len(visible) > 0 {
 | 
					 | 
				
			||||||
		return visible[len(visible)-1].Room
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) LastVisible() *rooms.Room {
 | 
					 | 
				
			||||||
	visible := trl.Visible()
 | 
					 | 
				
			||||||
	if len(visible) > 0 {
 | 
					 | 
				
			||||||
		return visible[0].Room
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) All() []*orderedRoom {
 | 
					 | 
				
			||||||
	return trl.rooms
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Length() int {
 | 
					 | 
				
			||||||
	if len(trl.rooms) < trl.maxShown {
 | 
					 | 
				
			||||||
		return len(trl.rooms)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return trl.maxShown
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) TotalLength() int {
 | 
					 | 
				
			||||||
	return len(trl.rooms)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) IsEmpty() bool {
 | 
					 | 
				
			||||||
	return len(trl.rooms) == 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) IsCollapsed() bool {
 | 
					 | 
				
			||||||
	return trl.maxShown == 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) ToggleCollapse() {
 | 
					 | 
				
			||||||
	if trl.IsCollapsed() {
 | 
					 | 
				
			||||||
		trl.maxShown = 10
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		trl.maxShown = 0
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) HasInvisibleRooms() bool {
 | 
					 | 
				
			||||||
	return trl.maxShown < trl.TotalLength()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) HasVisibleRooms() bool {
 | 
					 | 
				
			||||||
	return !trl.IsEmpty() && trl.maxShown > 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ShouldBeBefore returns if the first room should be after the second room in the room list.
 | 
					 | 
				
			||||||
// The manual order and last received message timestamp are considered.
 | 
					 | 
				
			||||||
func (trl *tagRoomList) ShouldBeAfter(room1 *orderedRoom, room2 *orderedRoom) bool {
 | 
					 | 
				
			||||||
	orderComp := strings.Compare(room1.order, room2.order)
 | 
					 | 
				
			||||||
	return orderComp == 1 || (orderComp == 0 && room2.LastReceivedMessage.After(room1.LastReceivedMessage))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Insert(order string, mxRoom *rooms.Room) {
 | 
					 | 
				
			||||||
	room := newOrderedRoom(order, mxRoom)
 | 
					 | 
				
			||||||
	trl.rooms = append(trl.rooms, nil)
 | 
					 | 
				
			||||||
	// The default insert index is the newly added slot.
 | 
					 | 
				
			||||||
	// That index will be used if all other rooms in the list have the same LastReceivedMessage timestamp.
 | 
					 | 
				
			||||||
	insertAt := len(trl.rooms) - 1
 | 
					 | 
				
			||||||
	// Find the spot where the new room should be put according to the last received message timestamps.
 | 
					 | 
				
			||||||
	for i := 0; i < len(trl.rooms)-1; i++ {
 | 
					 | 
				
			||||||
		if trl.ShouldBeAfter(room, trl.rooms[i]) {
 | 
					 | 
				
			||||||
			insertAt = i
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Move newer rooms forward in the array.
 | 
					 | 
				
			||||||
	for i := len(trl.rooms) - 1; i > insertAt; i-- {
 | 
					 | 
				
			||||||
		trl.rooms[i] = trl.rooms[i-1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Insert room.
 | 
					 | 
				
			||||||
	trl.rooms[insertAt] = room
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) String() string {
 | 
					 | 
				
			||||||
	var str strings.Builder
 | 
					 | 
				
			||||||
	fmt.Fprintln(&str, "&tagRoomList{")
 | 
					 | 
				
			||||||
	fmt.Fprintf(&str, "    maxShown: %d,\n", trl.maxShown)
 | 
					 | 
				
			||||||
	fmt.Fprint(&str, "    rooms: {")
 | 
					 | 
				
			||||||
	for i, room := range trl.rooms {
 | 
					 | 
				
			||||||
		if room == nil {
 | 
					 | 
				
			||||||
			fmt.Fprintf(&str, "<<NIL>>")
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			fmt.Fprint(&str, room.ID)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if i != len(trl.rooms)-1 {
 | 
					 | 
				
			||||||
			fmt.Fprint(&str, ", ")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fmt.Fprintln(&str, "},")
 | 
					 | 
				
			||||||
	fmt.Fprintln(&str, "}")
 | 
					 | 
				
			||||||
	return str.String()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Bump(mxRoom *rooms.Room) {
 | 
					 | 
				
			||||||
	var found *orderedRoom
 | 
					 | 
				
			||||||
	for i := 0; i < len(trl.rooms); i++ {
 | 
					 | 
				
			||||||
		currentRoom := trl.rooms[i]
 | 
					 | 
				
			||||||
		if found != nil {
 | 
					 | 
				
			||||||
			if trl.ShouldBeAfter(found, trl.rooms[i]) {
 | 
					 | 
				
			||||||
				// This room should be after the room being bumped, so insert the
 | 
					 | 
				
			||||||
				// room being bumped here and return
 | 
					 | 
				
			||||||
				trl.rooms[i-1] = found
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// Move older rooms back in the array
 | 
					 | 
				
			||||||
			trl.rooms[i-1] = currentRoom
 | 
					 | 
				
			||||||
		} else if currentRoom.Room == mxRoom {
 | 
					 | 
				
			||||||
			found = currentRoom
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// If the room being bumped should be first in the list, it won't be inserted during the loop.
 | 
					 | 
				
			||||||
	trl.rooms[len(trl.rooms)-1] = found
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Remove(room *rooms.Room) {
 | 
					 | 
				
			||||||
	trl.RemoveIndex(trl.Index(room))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) RemoveIndex(index int) {
 | 
					 | 
				
			||||||
	if index < 0 || index > len(trl.rooms) {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	trl.rooms = append(trl.rooms[0:index], trl.rooms[index+1:]...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) Index(room *rooms.Room) int {
 | 
					 | 
				
			||||||
	return trl.indexInList(trl.All(), room)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) IndexVisible(room *rooms.Room) int {
 | 
					 | 
				
			||||||
	return trl.indexInList(trl.Visible(), room)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (trl *tagRoomList) indexInList(list []*orderedRoom, room *rooms.Room) int {
 | 
					 | 
				
			||||||
	for index, entry := range list {
 | 
					 | 
				
			||||||
		if entry.Room == room {
 | 
					 | 
				
			||||||
			return index
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return -1
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type RoomList struct {
 | 
					type RoomList struct {
 | 
				
			||||||
	*tview.Box
 | 
						*tview.Box
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The list of tags in display order.
 | 
						// The list of tags in display order.
 | 
				
			||||||
	tags []string
 | 
						tags []string
 | 
				
			||||||
	// The list of rooms, in reverse order.
 | 
						// The list of rooms, in reverse order.
 | 
				
			||||||
	items map[string]*tagRoomList
 | 
						items map[string]*TagRoomList
 | 
				
			||||||
	// The selected room.
 | 
						// The selected room.
 | 
				
			||||||
	selected    *rooms.Room
 | 
						selected    *rooms.Room
 | 
				
			||||||
	selectedTag string
 | 
						selectedTag string
 | 
				
			||||||
@@ -238,7 +51,7 @@ type RoomList struct {
 | 
				
			|||||||
func NewRoomList() *RoomList {
 | 
					func NewRoomList() *RoomList {
 | 
				
			||||||
	list := &RoomList{
 | 
						list := &RoomList{
 | 
				
			||||||
		Box:   tview.NewBox(),
 | 
							Box:   tview.NewBox(),
 | 
				
			||||||
		items: make(map[string]*tagRoomList),
 | 
							items: make(map[string]*TagRoomList),
 | 
				
			||||||
		tags:  []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"},
 | 
							tags:  []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		scrollOffset: 0,
 | 
							scrollOffset: 0,
 | 
				
			||||||
@@ -248,14 +61,14 @@ func NewRoomList() *RoomList {
 | 
				
			|||||||
		selectedBackgroundColor: tcell.ColorDarkGreen,
 | 
							selectedBackgroundColor: tcell.ColorDarkGreen,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		list.items[tag] = newTagRoomList()
 | 
							list.items[tag] = NewTagRoomList(list, tag)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return list
 | 
						return list
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) Contains(roomID string) bool {
 | 
					func (list *RoomList) Contains(roomID string) bool {
 | 
				
			||||||
	for _, tagRoomList := range list.items {
 | 
						for _, trl := range list.items {
 | 
				
			||||||
		for _, room := range tagRoomList.All() {
 | 
							for _, room := range trl.All() {
 | 
				
			||||||
			if room.ID == roomID {
 | 
								if room.ID == roomID {
 | 
				
			||||||
				return true
 | 
									return true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -274,9 +87,9 @@ func (list *RoomList) Add(room *rooms.Room) {
 | 
				
			|||||||
func (list *RoomList) CheckTag(tag string) {
 | 
					func (list *RoomList) CheckTag(tag string) {
 | 
				
			||||||
	index := list.IndexTag(tag)
 | 
						index := list.IndexTag(tag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList, ok := list.items[tag]
 | 
						trl, ok := list.items[tag]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ok && tagRoomList.IsEmpty() {
 | 
						if ok && trl.IsEmpty() {
 | 
				
			||||||
		//delete(list.items, tag)
 | 
							//delete(list.items, tag)
 | 
				
			||||||
		ok = false
 | 
							ok = false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -290,13 +103,13 @@ func (list *RoomList) CheckTag(tag string) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
 | 
					func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
 | 
				
			||||||
	tagRoomList, ok := list.items[tag.Tag]
 | 
						trl, ok := list.items[tag.Tag]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		list.items[tag.Tag] = newTagRoomList(convertRoom(room))
 | 
							list.items[tag.Tag] = NewTagRoomList(list, tag.Tag, convertRoom(room))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList.Insert(tag.Order, room)
 | 
						trl.Insert(tag.Order, room)
 | 
				
			||||||
	list.CheckTag(tag.Tag)
 | 
						list.CheckTag(tag.Tag)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -307,27 +120,27 @@ func (list *RoomList) Remove(room *rooms.Room) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
 | 
					func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
 | 
				
			||||||
	tagRoomList, ok := list.items[tag]
 | 
						trl, ok := list.items[tag]
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	index := tagRoomList.Index(room)
 | 
						index := trl.Index(room)
 | 
				
			||||||
	if index == -1 {
 | 
						if index == -1 {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList.RemoveIndex(index)
 | 
						trl.RemoveIndex(index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if tagRoomList.IsEmpty() {
 | 
						if trl.IsEmpty() {
 | 
				
			||||||
		// delete(list.items, tag)
 | 
							// delete(list.items, tag)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if room == list.selected {
 | 
						if room == list.selected {
 | 
				
			||||||
		if index > 0 {
 | 
							if index > 0 {
 | 
				
			||||||
			list.selected = tagRoomList.All()[index-1].Room
 | 
								list.selected = trl.All()[index-1].Room
 | 
				
			||||||
		} else if tagRoomList.Length() > 0 {
 | 
							} else if trl.Length() > 0 {
 | 
				
			||||||
			list.selected = tagRoomList.Visible()[0].Room
 | 
								list.selected = trl.Visible()[0].Room
 | 
				
			||||||
		} else if len(list.items) > 0 {
 | 
							} else if len(list.items) > 0 {
 | 
				
			||||||
			for _, tag := range list.tags {
 | 
								for _, tag := range list.tags {
 | 
				
			||||||
				moreItems := list.items[tag]
 | 
									moreItems := list.items[tag]
 | 
				
			||||||
@@ -346,19 +159,19 @@ func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) Bump(room *rooms.Room) {
 | 
					func (list *RoomList) Bump(room *rooms.Room) {
 | 
				
			||||||
	for _, tag := range room.Tags() {
 | 
						for _, tag := range room.Tags() {
 | 
				
			||||||
		tagRoomList, ok := list.items[tag.Tag]
 | 
							trl, ok := list.items[tag.Tag]
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		tagRoomList.Bump(room)
 | 
							trl.Bump(room)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) Clear() {
 | 
					func (list *RoomList) Clear() {
 | 
				
			||||||
	list.items = make(map[string]*tagRoomList)
 | 
						list.items = make(map[string]*TagRoomList)
 | 
				
			||||||
	list.tags = []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"}
 | 
						list.tags = []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"}
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		list.items[tag] = newTagRoomList()
 | 
							list.items[tag] = NewTagRoomList(list, tag)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	list.selected = nil
 | 
						list.selected = nil
 | 
				
			||||||
	list.selectedTag = ""
 | 
						list.selectedTag = ""
 | 
				
			||||||
@@ -406,9 +219,9 @@ func (list *RoomList) AddScrollOffset(offset int) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) First() (string, *rooms.Room) {
 | 
					func (list *RoomList) First() (string, *rooms.Room) {
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		tagRoomList := list.items[tag]
 | 
							trl := list.items[tag]
 | 
				
			||||||
		if tagRoomList.HasVisibleRooms() {
 | 
							if trl.HasVisibleRooms() {
 | 
				
			||||||
			return tag, tagRoomList.FirstVisible()
 | 
								return tag, trl.FirstVisible()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
@@ -417,9 +230,9 @@ func (list *RoomList) First() (string, *rooms.Room) {
 | 
				
			|||||||
func (list *RoomList) Last() (string, *rooms.Room) {
 | 
					func (list *RoomList) Last() (string, *rooms.Room) {
 | 
				
			||||||
	for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
 | 
						for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
 | 
				
			||||||
		tag := list.tags[tagIndex]
 | 
							tag := list.tags[tagIndex]
 | 
				
			||||||
		tagRoomList := list.items[tag]
 | 
							trl := list.items[tag]
 | 
				
			||||||
		if tagRoomList.HasVisibleRooms() {
 | 
							if trl.HasVisibleRooms() {
 | 
				
			||||||
			return tag, tagRoomList.LastVisible()
 | 
								return tag, trl.LastVisible()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return "", nil
 | 
						return "", nil
 | 
				
			||||||
@@ -441,28 +254,28 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
 | 
				
			|||||||
		return list.First()
 | 
							return list.First()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList := list.items[list.selectedTag]
 | 
						trl := list.items[list.selectedTag]
 | 
				
			||||||
	index := tagRoomList.IndexVisible(list.selected)
 | 
						index := trl.IndexVisible(list.selected)
 | 
				
			||||||
	indexInvisible := tagRoomList.Index(list.selected)
 | 
						indexInvisible := trl.Index(list.selected)
 | 
				
			||||||
	if index == -1 && indexInvisible >= 0 {
 | 
						if index == -1 && indexInvisible >= 0 {
 | 
				
			||||||
		num := tagRoomList.TotalLength() - indexInvisible
 | 
							num := trl.TotalLength() - indexInvisible
 | 
				
			||||||
		tagRoomList.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
 | 
							trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
 | 
				
			||||||
		index = tagRoomList.IndexVisible(list.selected)
 | 
							index = trl.IndexVisible(list.selected)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if index == tagRoomList.Length()-1 {
 | 
						if index == trl.Length()-1 {
 | 
				
			||||||
		tagIndex := list.IndexTag(list.selectedTag)
 | 
							tagIndex := list.IndexTag(list.selectedTag)
 | 
				
			||||||
		tagIndex--
 | 
							tagIndex--
 | 
				
			||||||
		for ; tagIndex >= 0; tagIndex-- {
 | 
							for ; tagIndex >= 0; tagIndex-- {
 | 
				
			||||||
			prevTag := list.tags[tagIndex]
 | 
								prevTag := list.tags[tagIndex]
 | 
				
			||||||
			prevTagRoomList := list.items[prevTag]
 | 
								prevTRL := list.items[prevTag]
 | 
				
			||||||
			if prevTagRoomList.HasVisibleRooms() {
 | 
								if prevTRL.HasVisibleRooms() {
 | 
				
			||||||
				return prevTag, prevTagRoomList.LastVisible()
 | 
									return prevTag, prevTRL.LastVisible()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return list.Last()
 | 
							return list.Last()
 | 
				
			||||||
	} else if index >= 0 {
 | 
						} else if index >= 0 {
 | 
				
			||||||
		return list.selectedTag, tagRoomList.Visible()[index+1].Room
 | 
							return list.selectedTag, trl.Visible()[index+1].Room
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return list.First()
 | 
						return list.First()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -474,13 +287,13 @@ func (list *RoomList) Next() (string, *rooms.Room) {
 | 
				
			|||||||
		return list.First()
 | 
							return list.First()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList := list.items[list.selectedTag]
 | 
						trl := list.items[list.selectedTag]
 | 
				
			||||||
	index := tagRoomList.IndexVisible(list.selected)
 | 
						index := trl.IndexVisible(list.selected)
 | 
				
			||||||
	indexInvisible := tagRoomList.Index(list.selected)
 | 
						indexInvisible := trl.Index(list.selected)
 | 
				
			||||||
	if index == -1 && indexInvisible >= 0 {
 | 
						if index == -1 && indexInvisible >= 0 {
 | 
				
			||||||
		num := tagRoomList.TotalLength() - indexInvisible + 1
 | 
							num := trl.TotalLength() - indexInvisible + 1
 | 
				
			||||||
		tagRoomList.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
 | 
							trl.maxShown = int(math.Ceil(float64(num)/10.0) * 10.0)
 | 
				
			||||||
		index = tagRoomList.IndexVisible(list.selected)
 | 
							index = trl.IndexVisible(list.selected)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if index == 0 {
 | 
						if index == 0 {
 | 
				
			||||||
@@ -488,14 +301,14 @@ func (list *RoomList) Next() (string, *rooms.Room) {
 | 
				
			|||||||
		tagIndex++
 | 
							tagIndex++
 | 
				
			||||||
		for ; tagIndex < len(list.tags); tagIndex++ {
 | 
							for ; tagIndex < len(list.tags); tagIndex++ {
 | 
				
			||||||
			nextTag := list.tags[tagIndex]
 | 
								nextTag := list.tags[tagIndex]
 | 
				
			||||||
			nextTagRoomList := list.items[nextTag]
 | 
								nextTRL := list.items[nextTag]
 | 
				
			||||||
			if nextTagRoomList.HasVisibleRooms() {
 | 
								if nextTRL.HasVisibleRooms() {
 | 
				
			||||||
				return nextTag, nextTagRoomList.FirstVisible()
 | 
									return nextTag, nextTRL.FirstVisible()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return list.First()
 | 
							return list.First()
 | 
				
			||||||
	} else if index > 0 {
 | 
						} else if index > 0 {
 | 
				
			||||||
		return list.selectedTag, tagRoomList.Visible()[index-1].Room
 | 
							return list.selectedTag, trl.Visible()[index-1].Room
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return list.Last()
 | 
						return list.Last()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -506,39 +319,24 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
 | 
				
			|||||||
		return -1
 | 
							return -1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tagRoomList, ok := list.items[tag]
 | 
						trl, ok := list.items[tag]
 | 
				
			||||||
	localIndex := -1
 | 
						localIndex := -1
 | 
				
			||||||
	if ok {
 | 
						if ok {
 | 
				
			||||||
		localIndex = tagRoomList.IndexVisible(room)
 | 
							localIndex = trl.IndexVisible(room)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if localIndex == -1 {
 | 
						if localIndex == -1 {
 | 
				
			||||||
		return -1
 | 
							return -1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	localIndex = tagRoomList.Length() - 1 - localIndex
 | 
						localIndex = trl.Length() - 1 - localIndex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Tag header
 | 
						// Tag header
 | 
				
			||||||
	localIndex += 1
 | 
						localIndex += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if tagIndex > 0 {
 | 
						if tagIndex > 0 {
 | 
				
			||||||
		for i := 0; i < tagIndex; i++ {
 | 
							for i := 0; i < tagIndex; i++ {
 | 
				
			||||||
			previousTag := list.tags[i]
 | 
								prevTag := list.tags[i]
 | 
				
			||||||
			previousTagRoomList := list.items[previousTag]
 | 
								prevTRL := list.items[prevTag]
 | 
				
			||||||
 | 
								localIndex += prevTRL.RenderHeight()
 | 
				
			||||||
			tagDisplayName := list.GetTagDisplayName(previousTag)
 | 
					 | 
				
			||||||
			if len(tagDisplayName) > 0 {
 | 
					 | 
				
			||||||
				if previousTagRoomList.IsCollapsed() {
 | 
					 | 
				
			||||||
					localIndex++
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Previous tag header + space
 | 
					 | 
				
			||||||
				localIndex += 2
 | 
					 | 
				
			||||||
				if previousTagRoomList.HasInvisibleRooms() {
 | 
					 | 
				
			||||||
					// Previous tag "Show more" button
 | 
					 | 
				
			||||||
					localIndex++
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				// Previous tag items
 | 
					 | 
				
			||||||
				localIndex += previousTagRoomList.Length()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -547,19 +345,7 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (list *RoomList) ContentHeight() (height int) {
 | 
					func (list *RoomList) ContentHeight() (height int) {
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		tagRoomList := list.items[tag]
 | 
							height += list.items[tag].RenderHeight()
 | 
				
			||||||
		tagDisplayName := list.GetTagDisplayName(tag)
 | 
					 | 
				
			||||||
		if len(tagDisplayName) == 0 {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if tagRoomList.IsCollapsed() {
 | 
					 | 
				
			||||||
			height++
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		height += 2 + tagRoomList.Length()
 | 
					 | 
				
			||||||
		if tagRoomList.HasInvisibleRooms() {
 | 
					 | 
				
			||||||
			height++
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -570,27 +356,27 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro
 | 
				
			|||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		tagRoomList := list.items[tag]
 | 
							trl := list.items[tag]
 | 
				
			||||||
		if line--; line == -1 {
 | 
							if line--; line == -1 {
 | 
				
			||||||
			tagRoomList.ToggleCollapse()
 | 
								trl.ToggleCollapse()
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if tagRoomList.IsCollapsed() {
 | 
							if trl.IsCollapsed() {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if line < 0 {
 | 
							if line < 0 {
 | 
				
			||||||
			return "", nil
 | 
								return "", nil
 | 
				
			||||||
		} else if line < tagRoomList.Length() {
 | 
							} else if line < trl.Length() {
 | 
				
			||||||
			return tag, tagRoomList.Visible()[tagRoomList.Length()-1-line].Room
 | 
								return tag, trl.Visible()[trl.Length()-1-line].Room
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Tag items
 | 
							// Tag items
 | 
				
			||||||
		line -= tagRoomList.Length()
 | 
							line -= trl.Length()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hasMore := tagRoomList.HasInvisibleRooms()
 | 
							hasMore := trl.HasInvisibleRooms()
 | 
				
			||||||
		hasLess := tagRoomList.maxShown > 10
 | 
							hasLess := trl.maxShown > 10
 | 
				
			||||||
		if hasMore || hasLess {
 | 
							if hasMore || hasLess {
 | 
				
			||||||
			if line--; line == -1 {
 | 
								if line--; line == -1 {
 | 
				
			||||||
				diff := 10
 | 
									diff := 10
 | 
				
			||||||
@@ -599,12 +385,12 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				_, _, width, _ := list.GetRect()
 | 
									_, _, width, _ := list.GetRect()
 | 
				
			||||||
				if column <= 6 && hasLess {
 | 
									if column <= 6 && hasLess {
 | 
				
			||||||
					tagRoomList.maxShown -= diff
 | 
										trl.maxShown -= diff
 | 
				
			||||||
				} else if column >= width-6 && hasMore {
 | 
									} else if column >= width-6 && hasMore {
 | 
				
			||||||
					tagRoomList.maxShown += diff
 | 
										trl.maxShown += diff
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if tagRoomList.maxShown < 10 {
 | 
									if trl.maxShown < 10 {
 | 
				
			||||||
					tagRoomList.maxShown = 10
 | 
										trl.maxShown = 10
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				return "", nil
 | 
									return "", nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -641,108 +427,26 @@ func (list *RoomList) Draw(screen tcell.Screen) {
 | 
				
			|||||||
	list.Box.Draw(screen)
 | 
						list.Box.Draw(screen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	x, y, width, height := list.GetInnerRect()
 | 
						x, y, width, height := list.GetInnerRect()
 | 
				
			||||||
	bottomLimit := y + height
 | 
						yLimit := y + height
 | 
				
			||||||
 | 
						y -= list.scrollOffset
 | 
				
			||||||
	handledOffset := 0
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Draw the list items.
 | 
						// Draw the list items.
 | 
				
			||||||
	for _, tag := range list.tags {
 | 
						for _, tag := range list.tags {
 | 
				
			||||||
		tagRoomList := list.items[tag]
 | 
							trl := list.items[tag]
 | 
				
			||||||
		tagDisplayName := list.GetTagDisplayName(tag)
 | 
							tagDisplayName := list.GetTagDisplayName(tag)
 | 
				
			||||||
		if len(tagDisplayName) == 0 {
 | 
							if len(tagDisplayName) == 0 {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		localOffset := 0
 | 
							renderHeight := trl.RenderHeight()
 | 
				
			||||||
 | 
							if y + renderHeight >= yLimit {
 | 
				
			||||||
		if handledOffset < list.scrollOffset {
 | 
								renderHeight = yLimit - y
 | 
				
			||||||
			if handledOffset+tagRoomList.Length() < list.scrollOffset {
 | 
					 | 
				
			||||||
				if tagRoomList.IsCollapsed() {
 | 
					 | 
				
			||||||
					handledOffset++
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					handledOffset += tagRoomList.Length() + 2
 | 
					 | 
				
			||||||
					if tagRoomList.HasInvisibleRooms() || tagRoomList.maxShown > 10 {
 | 
					 | 
				
			||||||
						handledOffset++
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				localOffset = list.scrollOffset - handledOffset
 | 
					 | 
				
			||||||
				handledOffset += localOffset
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							trl.SetRect(x, y, width, renderHeight)
 | 
				
			||||||
		roomCount := strconv.Itoa(tagRoomList.TotalLength())
 | 
							trl.Draw(screen)
 | 
				
			||||||
		widget.WriteLine(screen, tview.AlignLeft, tagDisplayName, x, y, width-1-len(roomCount), tcell.StyleDefault.Underline(true).Bold(true))
 | 
							y += renderHeight
 | 
				
			||||||
		widget.WriteLine(screen, tview.AlignLeft, roomCount, x+len(tagDisplayName)+1, y, width-2-len(tagDisplayName), tcell.StyleDefault.Italic(true))
 | 
							if y >= yLimit {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
		items := tagRoomList.Visible()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if tagRoomList.IsCollapsed() {
 | 
					 | 
				
			||||||
			screen.SetCell(x+width-1, y, tcell.StyleDefault, '▶')
 | 
					 | 
				
			||||||
			y++
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		screen.SetCell(x+width-1, y, tcell.StyleDefault, '▼')
 | 
					 | 
				
			||||||
		y++
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for i := tagRoomList.Length() - 1; i >= 0; i-- {
 | 
					 | 
				
			||||||
			item := items[i]
 | 
					 | 
				
			||||||
			index := len(items) - 1 - i
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if y >= bottomLimit {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if index < localOffset {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			text := item.GetTitle()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			lineWidth := width
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			style := tcell.StyleDefault.Foreground(list.mainTextColor)
 | 
					 | 
				
			||||||
			if tag == list.selectedTag && item.Room == list.selected {
 | 
					 | 
				
			||||||
				style = style.Foreground(list.selectedTextColor).Background(list.selectedBackgroundColor)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if item.HasNewMessages() {
 | 
					 | 
				
			||||||
				style = style.Bold(true)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			unreadCount := item.UnreadCount()
 | 
					 | 
				
			||||||
			if unreadCount > 0 {
 | 
					 | 
				
			||||||
				unreadMessageCount := "99+"
 | 
					 | 
				
			||||||
				if unreadCount < 100 {
 | 
					 | 
				
			||||||
					unreadMessageCount = strconv.Itoa(unreadCount)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if item.Highlighted() {
 | 
					 | 
				
			||||||
					unreadMessageCount += "!"
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				unreadMessageCount = fmt.Sprintf("(%s)", unreadMessageCount)
 | 
					 | 
				
			||||||
				widget.WriteLine(screen, tview.AlignRight, unreadMessageCount, x+lineWidth-7, y, 7, style)
 | 
					 | 
				
			||||||
				lineWidth -= len(unreadMessageCount)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			widget.WriteLinePadded(screen, tview.AlignLeft, text, x, y, lineWidth, style)
 | 
					 | 
				
			||||||
			y++
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if y >= bottomLimit {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		hasLess := tagRoomList.maxShown > 10
 | 
					 | 
				
			||||||
		hasMore := tagRoomList.HasInvisibleRooms()
 | 
					 | 
				
			||||||
		if hasLess || hasMore {
 | 
					 | 
				
			||||||
			if hasMore {
 | 
					 | 
				
			||||||
				widget.WriteLine(screen, tview.AlignRight, "More ↓", x, y, width, tcell.StyleDefault)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if hasLess {
 | 
					 | 
				
			||||||
				widget.WriteLine(screen, tview.AlignLeft, "↑ Less", x, y, width, tcell.StyleDefault)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			y++
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		y++
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										314
									
								
								ui/tag-room-list.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								ui/tag-room-list.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,314 @@
 | 
				
			|||||||
 | 
					// gomuks - A terminal Matrix client written in Go.
 | 
				
			||||||
 | 
					// Copyright (C) 2018 Tulir Asokan
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package ui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"maunium.net/go/gomuks/matrix/rooms"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"maunium.net/go/tcell"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"maunium.net/go/gomuks/ui/widget"
 | 
				
			||||||
 | 
						"maunium.net/go/tview"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type orderedRoom struct {
 | 
				
			||||||
 | 
						*rooms.Room
 | 
				
			||||||
 | 
						order string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newOrderedRoom(order string, room *rooms.Room) *orderedRoom {
 | 
				
			||||||
 | 
						return &orderedRoom{
 | 
				
			||||||
 | 
							Room:  room,
 | 
				
			||||||
 | 
							order: order,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func convertRoom(room *rooms.Room) *orderedRoom {
 | 
				
			||||||
 | 
						return newOrderedRoom("0.5", room)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TagRoomList struct {
 | 
				
			||||||
 | 
						*tview.Box
 | 
				
			||||||
 | 
						rooms       []*orderedRoom
 | 
				
			||||||
 | 
						maxShown    int
 | 
				
			||||||
 | 
						name        string
 | 
				
			||||||
 | 
						displayname string
 | 
				
			||||||
 | 
						parent      *RoomList
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewTagRoomList(parent *RoomList, name string, rooms ...*orderedRoom) *TagRoomList {
 | 
				
			||||||
 | 
						return &TagRoomList{
 | 
				
			||||||
 | 
							Box:         tview.NewBox(),
 | 
				
			||||||
 | 
							maxShown:    10,
 | 
				
			||||||
 | 
							rooms:       rooms,
 | 
				
			||||||
 | 
							name:        name,
 | 
				
			||||||
 | 
							displayname: parent.GetTagDisplayName(name),
 | 
				
			||||||
 | 
							parent:      parent,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Visible() []*orderedRoom {
 | 
				
			||||||
 | 
						return trl.rooms[len(trl.rooms)-trl.Length():]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) FirstVisible() *rooms.Room {
 | 
				
			||||||
 | 
						visible := trl.Visible()
 | 
				
			||||||
 | 
						if len(visible) > 0 {
 | 
				
			||||||
 | 
							return visible[len(visible)-1].Room
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) LastVisible() *rooms.Room {
 | 
				
			||||||
 | 
						visible := trl.Visible()
 | 
				
			||||||
 | 
						if len(visible) > 0 {
 | 
				
			||||||
 | 
							return visible[0].Room
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) All() []*orderedRoom {
 | 
				
			||||||
 | 
						return trl.rooms
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Length() int {
 | 
				
			||||||
 | 
						if len(trl.rooms) < trl.maxShown {
 | 
				
			||||||
 | 
							return len(trl.rooms)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return trl.maxShown
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) TotalLength() int {
 | 
				
			||||||
 | 
						return len(trl.rooms)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) IsEmpty() bool {
 | 
				
			||||||
 | 
						return len(trl.rooms) == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) IsCollapsed() bool {
 | 
				
			||||||
 | 
						return trl.maxShown == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) ToggleCollapse() {
 | 
				
			||||||
 | 
						if trl.IsCollapsed() {
 | 
				
			||||||
 | 
							trl.maxShown = 10
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							trl.maxShown = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) HasInvisibleRooms() bool {
 | 
				
			||||||
 | 
						return trl.maxShown < trl.TotalLength()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) HasVisibleRooms() bool {
 | 
				
			||||||
 | 
						return !trl.IsEmpty() && trl.maxShown > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ShouldBeBefore returns if the first room should be after the second room in the room list.
 | 
				
			||||||
 | 
					// The manual order and last received message timestamp are considered.
 | 
				
			||||||
 | 
					func (trl *TagRoomList) ShouldBeAfter(room1 *orderedRoom, room2 *orderedRoom) bool {
 | 
				
			||||||
 | 
						orderComp := strings.Compare(room1.order, room2.order)
 | 
				
			||||||
 | 
						return orderComp == 1 || (orderComp == 0 && room2.LastReceivedMessage.After(room1.LastReceivedMessage))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Insert(order string, mxRoom *rooms.Room) {
 | 
				
			||||||
 | 
						room := newOrderedRoom(order, mxRoom)
 | 
				
			||||||
 | 
						trl.rooms = append(trl.rooms, nil)
 | 
				
			||||||
 | 
						// The default insert index is the newly added slot.
 | 
				
			||||||
 | 
						// That index will be used if all other rooms in the list have the same LastReceivedMessage timestamp.
 | 
				
			||||||
 | 
						insertAt := len(trl.rooms) - 1
 | 
				
			||||||
 | 
						// Find the spot where the new room should be put according to the last received message timestamps.
 | 
				
			||||||
 | 
						for i := 0; i < len(trl.rooms)-1; i++ {
 | 
				
			||||||
 | 
							if trl.ShouldBeAfter(room, trl.rooms[i]) {
 | 
				
			||||||
 | 
								insertAt = i
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Move newer rooms forward in the array.
 | 
				
			||||||
 | 
						for i := len(trl.rooms) - 1; i > insertAt; i-- {
 | 
				
			||||||
 | 
							trl.rooms[i] = trl.rooms[i-1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Insert room.
 | 
				
			||||||
 | 
						trl.rooms[insertAt] = room
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) String() string {
 | 
				
			||||||
 | 
						var str strings.Builder
 | 
				
			||||||
 | 
						fmt.Fprintln(&str, "&TagRoomList{")
 | 
				
			||||||
 | 
						fmt.Fprintf(&str, "    maxShown: %d,\n", trl.maxShown)
 | 
				
			||||||
 | 
						fmt.Fprint(&str, "    rooms: {")
 | 
				
			||||||
 | 
						for i, room := range trl.rooms {
 | 
				
			||||||
 | 
							if room == nil {
 | 
				
			||||||
 | 
								fmt.Fprintf(&str, "<<NIL>>")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Fprint(&str, room.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if i != len(trl.rooms)-1 {
 | 
				
			||||||
 | 
								fmt.Fprint(&str, ", ")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Fprintln(&str, "},")
 | 
				
			||||||
 | 
						fmt.Fprintln(&str, "}")
 | 
				
			||||||
 | 
						return str.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Bump(mxRoom *rooms.Room) {
 | 
				
			||||||
 | 
						var found *orderedRoom
 | 
				
			||||||
 | 
						for i := 0; i < len(trl.rooms); i++ {
 | 
				
			||||||
 | 
							currentRoom := trl.rooms[i]
 | 
				
			||||||
 | 
							if found != nil {
 | 
				
			||||||
 | 
								if trl.ShouldBeAfter(found, trl.rooms[i]) {
 | 
				
			||||||
 | 
									// This room should be after the room being bumped, so insert the
 | 
				
			||||||
 | 
									// room being bumped here and return
 | 
				
			||||||
 | 
									trl.rooms[i-1] = found
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// Move older rooms back in the array
 | 
				
			||||||
 | 
								trl.rooms[i-1] = currentRoom
 | 
				
			||||||
 | 
							} else if currentRoom.Room == mxRoom {
 | 
				
			||||||
 | 
								found = currentRoom
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// If the room being bumped should be first in the list, it won't be inserted during the loop.
 | 
				
			||||||
 | 
						trl.rooms[len(trl.rooms)-1] = found
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Remove(room *rooms.Room) {
 | 
				
			||||||
 | 
						trl.RemoveIndex(trl.Index(room))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) RemoveIndex(index int) {
 | 
				
			||||||
 | 
						if index < 0 || index > len(trl.rooms) {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						trl.rooms = append(trl.rooms[0:index], trl.rooms[index+1:]...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Index(room *rooms.Room) int {
 | 
				
			||||||
 | 
						return trl.indexInList(trl.All(), room)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) IndexVisible(room *rooms.Room) int {
 | 
				
			||||||
 | 
						return trl.indexInList(trl.Visible(), room)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) indexInList(list []*orderedRoom, room *rooms.Room) int {
 | 
				
			||||||
 | 
						for index, entry := range list {
 | 
				
			||||||
 | 
							if entry.Room == room {
 | 
				
			||||||
 | 
								return index
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return -1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var TagDisplayNameStyle = tcell.StyleDefault.Underline(true).Bold(true)
 | 
				
			||||||
 | 
					var TagRoomCountStyle = tcell.StyleDefault.Italic(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) RenderHeight() int {
 | 
				
			||||||
 | 
						if len(trl.displayname) == 0 {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if trl.IsCollapsed() {
 | 
				
			||||||
 | 
							return 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						height := 2 + trl.Length()
 | 
				
			||||||
 | 
						if trl.HasInvisibleRooms() || trl.maxShown > 10 {
 | 
				
			||||||
 | 
							height++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return height
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (trl *TagRoomList) Draw(screen tcell.Screen) {
 | 
				
			||||||
 | 
						if len(trl.displayname) == 0 {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x, y, width, height := trl.GetRect()
 | 
				
			||||||
 | 
						yLimit := y + height
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						roomCount := strconv.Itoa(trl.TotalLength())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Draw tag name
 | 
				
			||||||
 | 
						displayNameWidth := width - 1 - len(roomCount)
 | 
				
			||||||
 | 
						widget.WriteLine(screen, tview.AlignLeft, trl.displayname, x, y, displayNameWidth, TagDisplayNameStyle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Draw tag room count
 | 
				
			||||||
 | 
						roomCountX := x + len(trl.displayname) + 1
 | 
				
			||||||
 | 
						roomCountWidth := width - 2 - len(trl.displayname)
 | 
				
			||||||
 | 
						widget.WriteLine(screen, tview.AlignLeft, roomCount, roomCountX, y, roomCountWidth, TagRoomCountStyle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						items := trl.Visible()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if trl.IsCollapsed() {
 | 
				
			||||||
 | 
							screen.SetCell(x+width-1, y, tcell.StyleDefault, '▶')
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						screen.SetCell(x+width-1, y, tcell.StyleDefault, '▼')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offsetY := 1
 | 
				
			||||||
 | 
						for i := trl.Length() - 1; i >= 0; i-- {
 | 
				
			||||||
 | 
							if y+offsetY >= yLimit {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							item := items[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							text := item.GetTitle()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lineWidth := width
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							style := tcell.StyleDefault.Foreground(trl.parent.mainTextColor)
 | 
				
			||||||
 | 
							if trl.name == trl.parent.selectedTag && item.Room == trl.parent.selected {
 | 
				
			||||||
 | 
								style = style.Foreground(trl.parent.selectedTextColor).Background(trl.parent.selectedBackgroundColor)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if item.HasNewMessages() {
 | 
				
			||||||
 | 
								style = style.Bold(true)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							unreadCount := item.UnreadCount()
 | 
				
			||||||
 | 
							if unreadCount > 0 {
 | 
				
			||||||
 | 
								unreadMessageCount := "99+"
 | 
				
			||||||
 | 
								if unreadCount < 100 {
 | 
				
			||||||
 | 
									unreadMessageCount = strconv.Itoa(unreadCount)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if item.Highlighted() {
 | 
				
			||||||
 | 
									unreadMessageCount += "!"
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								unreadMessageCount = fmt.Sprintf("(%s)", unreadMessageCount)
 | 
				
			||||||
 | 
								widget.WriteLine(screen, tview.AlignRight, unreadMessageCount, x+lineWidth-7, y+offsetY, 7, style)
 | 
				
			||||||
 | 
								lineWidth -= len(unreadMessageCount)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							widget.WriteLinePadded(screen, tview.AlignLeft, text, x, y+offsetY, lineWidth, style)
 | 
				
			||||||
 | 
							offsetY++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						hasLess := trl.maxShown > 10
 | 
				
			||||||
 | 
						hasMore := trl.HasInvisibleRooms()
 | 
				
			||||||
 | 
						if (hasLess || hasMore) && y+offsetY < yLimit {
 | 
				
			||||||
 | 
							if hasMore {
 | 
				
			||||||
 | 
								widget.WriteLine(screen, tview.AlignRight, "More ↓", x, y+offsetY, width, tcell.StyleDefault)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if hasLess {
 | 
				
			||||||
 | 
								widget.WriteLine(screen, tview.AlignLeft, "↑ Less", x, y+offsetY, width, tcell.StyleDefault)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							offsetY++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user