Show notifications and highlights in room list. Fixes #8
This commit is contained in:
parent
6095638fbb
commit
b31d968814
@ -18,6 +18,8 @@ package ifc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
"maunium.net/go/gomuks/ui/types"
|
"maunium.net/go/gomuks/ui/types"
|
||||||
"maunium.net/go/gomuks/ui/widget"
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
@ -51,6 +53,7 @@ type MainView interface {
|
|||||||
AddServiceMessage(roomID *widget.RoomView, message string)
|
AddServiceMessage(roomID *widget.RoomView, message string)
|
||||||
ProcessMessageEvent(roomView *widget.RoomView, evt *gomatrix.Event) *types.Message
|
ProcessMessageEvent(roomView *widget.RoomView, evt *gomatrix.Event) *types.Message
|
||||||
ProcessMembershipEvent(roomView *widget.RoomView, evt *gomatrix.Event) *types.Message
|
ProcessMembershipEvent(roomView *widget.RoomView, evt *gomatrix.Event) *types.Message
|
||||||
|
NotifyMessage(room *rooms.Room, message *types.Message, should pushrules.PushActionArrayShould)
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginView interface {
|
type LoginView interface {
|
||||||
|
@ -22,13 +22,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
"maunium.net/go/gomuks/config"
|
"maunium.net/go/gomuks/config"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
"maunium.net/go/gomuks/matrix/pushrules"
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
"maunium.net/go/gomuks/matrix/rooms"
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
"maunium.net/go/gomuks/notification"
|
|
||||||
"maunium.net/go/gomuks/ui/debug"
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
"maunium.net/go/gomuks/ui/widget"
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
)
|
)
|
||||||
@ -214,33 +212,18 @@ func (c *Container) Start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyMessage sends a desktop notification of the message with the given details.
|
|
||||||
func (c *Container) NotifyMessage(room *rooms.Room, sender, text string, critical bool) {
|
|
||||||
if room.GetTitle() != sender {
|
|
||||||
sender = fmt.Sprintf("%s (%s)", sender, room.GetTitle())
|
|
||||||
}
|
|
||||||
notification.Send(sender, text, critical)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleMessage is the event handler for the m.room.message timeline event.
|
// HandleMessage is the event handler for the m.room.message timeline event.
|
||||||
func (c *Container) HandleMessage(evt *gomatrix.Event) {
|
func (c *Container) HandleMessage(evt *gomatrix.Event) {
|
||||||
roomView := c.ui.MainView().GetRoom(evt.RoomID)
|
mainView := c.ui.MainView()
|
||||||
|
roomView := mainView.GetRoom(evt.RoomID)
|
||||||
if roomView == nil {
|
if roomView == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := c.ui.MainView().ProcessMessageEvent(roomView, evt)
|
message := mainView.ProcessMessageEvent(roomView, evt)
|
||||||
if message != nil {
|
if message != nil {
|
||||||
pushRules := c.PushRules().GetActions(roomView.Room, evt).Should()
|
pushRules := c.PushRules().GetActions(roomView.Room, evt).Should()
|
||||||
if (pushRules.Notify || !pushRules.NotifySpecified) && evt.Sender != c.config.Session.UserID {
|
mainView.NotifyMessage(roomView.Room, message, pushRules)
|
||||||
c.NotifyMessage(roomView.Room, message.Sender, message.Text, pushRules.Highlight)
|
|
||||||
}
|
|
||||||
if pushRules.Highlight {
|
|
||||||
message.TextColor = tcell.ColorYellow
|
|
||||||
}
|
|
||||||
if pushRules.PlaySound {
|
|
||||||
// TODO play sound
|
|
||||||
}
|
|
||||||
roomView.AddMessage(message, widget.AppendMessage)
|
roomView.AddMessage(message, widget.AppendMessage)
|
||||||
c.ui.Render()
|
c.ui.Render()
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,16 @@ type Room struct {
|
|||||||
PrevBatch string
|
PrevBatch string
|
||||||
// The MXID of the user whose session this room was created for.
|
// The MXID of the user whose session this room was created for.
|
||||||
SessionUserID string
|
SessionUserID string
|
||||||
|
|
||||||
|
// The number of unread messages that were notified about.
|
||||||
|
UnreadMessages int
|
||||||
|
// Whether or not any of the unread messages were highlights.
|
||||||
|
Highlighted bool
|
||||||
|
// Whether or not the room contains any new messages.
|
||||||
|
// This can be true even when UnreadMessages is zero if there's
|
||||||
|
// a notificationless message like bot notices.
|
||||||
|
HasNewMessages bool
|
||||||
|
|
||||||
// MXID -> Member cache calculated from membership events.
|
// MXID -> Member cache calculated from membership events.
|
||||||
memberCache map[string]*Member
|
memberCache map[string]*Member
|
||||||
// The first non-SessionUserID member in the room. Calculated at
|
// The first non-SessionUserID member in the room. Calculated at
|
||||||
@ -65,6 +75,13 @@ func (room *Room) UnlockHistory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkRead clears the new message statuses on this room.
|
||||||
|
func (room *Room) MarkRead() {
|
||||||
|
room.UnreadMessages = 0
|
||||||
|
room.Highlighted = false
|
||||||
|
room.HasNewMessages = false
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateState updates the room's current state with the given Event. This will clobber events based
|
// UpdateState updates the room's current state with the given Event. This will clobber events based
|
||||||
// on the type/state_key combination.
|
// on the type/state_key combination.
|
||||||
func (room *Room) UpdateState(event *gomatrix.Event) {
|
func (room *Room) UpdateState(event *gomatrix.Event) {
|
||||||
|
@ -28,6 +28,9 @@ import (
|
|||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
"maunium.net/go/gomuks/config"
|
"maunium.net/go/gomuks/config"
|
||||||
"maunium.net/go/gomuks/interface"
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
|
"maunium.net/go/gomuks/notification"
|
||||||
"maunium.net/go/gomuks/ui/debug"
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
"maunium.net/go/gomuks/ui/types"
|
"maunium.net/go/gomuks/ui/types"
|
||||||
"maunium.net/go/gomuks/ui/widget"
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
@ -239,6 +242,10 @@ func (view *MainView) MouseEventHandler(roomView *widget.RoomView, event *tcell.
|
|||||||
msgView.AddScrollOffset(-WheelScrollOffsetDiff)
|
msgView.AddScrollOffset(-WheelScrollOffsetDiff)
|
||||||
|
|
||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
|
|
||||||
|
if msgView.ScrollOffset == 0 {
|
||||||
|
roomView.Room.MarkRead()
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
debug.Print("Mouse event received:", event.Buttons(), event.Modifiers(), x, y)
|
debug.Print("Mouse event received:", event.Buttons(), event.Modifiers(), x, y)
|
||||||
return event
|
return event
|
||||||
@ -263,7 +270,11 @@ func (view *MainView) SwitchRoom(roomIndex int) {
|
|||||||
}
|
}
|
||||||
view.currentRoomIndex = roomIndex % len(view.roomIDs)
|
view.currentRoomIndex = roomIndex % len(view.roomIDs)
|
||||||
view.roomView.SwitchToPage(view.CurrentRoomID())
|
view.roomView.SwitchToPage(view.CurrentRoomID())
|
||||||
view.roomList.SetSelected(view.rooms[view.CurrentRoomID()].Room)
|
roomView := view.rooms[view.CurrentRoomID()]
|
||||||
|
if roomView.MessageView().ScrollOffset == 0 {
|
||||||
|
roomView.Room.MarkRead()
|
||||||
|
}
|
||||||
|
view.roomList.SetSelected(roomView.Room)
|
||||||
view.gmx.App().SetFocus(view)
|
view.gmx.App().SetFocus(view)
|
||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
}
|
}
|
||||||
@ -367,6 +378,36 @@ func (view *MainView) SetTyping(room string, users []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendNotification(room *rooms.Room, sender, text string, critical bool) {
|
||||||
|
if room.GetTitle() != sender {
|
||||||
|
sender = fmt.Sprintf("%s (%s)", sender, room.GetTitle())
|
||||||
|
}
|
||||||
|
notification.Send(sender, text, critical)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *MainView) NotifyMessage(room *rooms.Room, message *types.Message, should pushrules.PushActionArrayShould) {
|
||||||
|
isCurrent := room.ID == view.CurrentRoomID()
|
||||||
|
if !isCurrent {
|
||||||
|
room.HasNewMessages = true
|
||||||
|
}
|
||||||
|
shouldNotify := (should.Notify || !should.NotifySpecified) && message.Sender != view.config.Session.UserID
|
||||||
|
if shouldNotify {
|
||||||
|
sendNotification(room, message.Sender, message.Text, should.Highlight)
|
||||||
|
if !isCurrent {
|
||||||
|
room.UnreadMessages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should.Highlight {
|
||||||
|
message.TextColor = tcell.ColorYellow
|
||||||
|
if !isCurrent {
|
||||||
|
room.Highlighted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should.PlaySound {
|
||||||
|
// TODO play sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (view *MainView) AddServiceMessage(roomView *widget.RoomView, text string) {
|
func (view *MainView) AddServiceMessage(roomView *widget.RoomView, text string) {
|
||||||
message := roomView.NewMessage("", "*", "gomuks.service", text, time.Now())
|
message := roomView.NewMessage("", "*", "gomuks.service", text, time.Now())
|
||||||
message.TextColor = tcell.ColorGray
|
message.TextColor = tcell.ColorGray
|
||||||
|
@ -276,11 +276,11 @@ func getScrollbarStyle(scrollbarHere, isTop, isBottom bool) (char rune, style tc
|
|||||||
func (view *MessageView) Draw(screen tcell.Screen) {
|
func (view *MessageView) Draw(screen tcell.Screen) {
|
||||||
view.Box.Draw(screen)
|
view.Box.Draw(screen)
|
||||||
|
|
||||||
x, y, width, height := view.GetInnerRect()
|
x, y, _, height := view.GetInnerRect()
|
||||||
view.recalculateBuffers()
|
view.recalculateBuffers()
|
||||||
|
|
||||||
if len(view.textBuffer) == 0 {
|
if len(view.textBuffer) == 0 {
|
||||||
writeLine(screen, tview.AlignLeft, "It's quite empty in here.", x, y+height, width, tcell.ColorDefault)
|
writeLineSimple(screen, "It's quite empty in here.", x, y+height)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +294,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
if view.LoadingMessages {
|
if view.LoadingMessages {
|
||||||
message = "Loading more messages..."
|
message = "Loading more messages..."
|
||||||
}
|
}
|
||||||
writeLine(screen, tview.AlignLeft, message, messageX, y, width, tcell.ColorGreen)
|
writeLineSimpleColor(screen, message, messageX, y, tcell.ColorGreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(view.textBuffer) != len(view.metaBuffer) {
|
if len(view.textBuffer) != len(view.metaBuffer) {
|
||||||
@ -339,16 +339,16 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
text, meta := view.textBuffer[index], view.metaBuffer[index]
|
text, meta := view.textBuffer[index], view.metaBuffer[index]
|
||||||
if meta != prevMeta {
|
if meta != prevMeta {
|
||||||
if len(meta.GetTimestamp()) > 0 {
|
if len(meta.GetTimestamp()) > 0 {
|
||||||
writeLine(screen, tview.AlignLeft, meta.GetTimestamp(), x, y+line, width, meta.GetTimestampColor())
|
writeLineSimpleColor(screen, meta.GetTimestamp(), x, y+line, meta.GetTimestampColor())
|
||||||
}
|
}
|
||||||
if prevMeta == nil || meta.GetSender() != prevMeta.GetSender() {
|
if prevMeta == nil || meta.GetSender() != prevMeta.GetSender() {
|
||||||
writeLine(
|
writeLineColor(
|
||||||
screen, tview.AlignRight, meta.GetSender(),
|
screen, tview.AlignRight, meta.GetSender(),
|
||||||
usernameX, y+line, view.widestSender,
|
usernameX, y+line, view.widestSender,
|
||||||
meta.GetSenderColor())
|
meta.GetSenderColor())
|
||||||
}
|
}
|
||||||
prevMeta = meta
|
prevMeta = meta
|
||||||
}
|
}
|
||||||
writeLine(screen, tview.AlignLeft, text, messageX, y+line, width, meta.GetTextColor())
|
writeLineSimpleColor(screen, text, messageX, y+line, meta.GetTextColor())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"maunium.net/go/gomuks/matrix/rooms"
|
"maunium.net/go/gomuks/matrix/rooms"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
@ -104,21 +107,30 @@ func (list *RoomList) Draw(screen tcell.Screen) {
|
|||||||
|
|
||||||
text := item.GetTitle()
|
text := item.GetTitle()
|
||||||
|
|
||||||
writeLine(screen, tview.AlignLeft, text, x, y, width, list.mainTextColor)
|
lineWidth := width
|
||||||
|
|
||||||
// Background color of selected text.
|
style := tcell.StyleDefault.Foreground(list.mainTextColor)
|
||||||
if item == list.selected {
|
if item == list.selected {
|
||||||
textWidth := tview.StringWidth(text)
|
style = style.Foreground(list.selectedTextColor).Background(list.selectedBackgroundColor)
|
||||||
for bx := 0; bx < textWidth && bx < width; bx++ {
|
|
||||||
m, c, style, _ := screen.GetContent(x+bx, y)
|
|
||||||
fg, _, _ := style.Decompose()
|
|
||||||
if fg == list.mainTextColor {
|
|
||||||
fg = list.selectedTextColor
|
|
||||||
}
|
}
|
||||||
style = style.Background(list.selectedBackgroundColor).Foreground(fg)
|
if item.HasNewMessages {
|
||||||
screen.SetContent(x+bx, y, m, c, style)
|
style = style.Bold(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.UnreadMessages > 0 {
|
||||||
|
unreadMessageCount := "99+"
|
||||||
|
if item.UnreadMessages < 100 {
|
||||||
|
unreadMessageCount = strconv.Itoa(item.UnreadMessages)
|
||||||
}
|
}
|
||||||
|
if item.Highlighted {
|
||||||
|
unreadMessageCount += "!"
|
||||||
|
}
|
||||||
|
unreadMessageCount = fmt.Sprintf("(%s)", unreadMessageCount)
|
||||||
|
writeLine(screen, tview.AlignRight, unreadMessageCount, x+lineWidth-6, y, 6, style)
|
||||||
|
lineWidth -= len(unreadMessageCount) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
writeLine(screen, tview.AlignLeft, text, x, y, lineWidth, style)
|
||||||
|
|
||||||
y++
|
y++
|
||||||
if y >= bottomLimit {
|
if y >= bottomLimit {
|
||||||
|
@ -22,7 +22,19 @@ import (
|
|||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeLine(screen tcell.Screen, align int, line string, x, y, maxWidth int, color tcell.Color) {
|
func writeLineSimple(screen tcell.Screen, line string, x, y int) {
|
||||||
|
writeLine(screen, tview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLineSimpleColor(screen tcell.Screen, line string, x, y int, color tcell.Color) {
|
||||||
|
writeLine(screen, tview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault.Foreground(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLineColor(screen tcell.Screen, align int, line string, x, y, maxWidth int, color tcell.Color) {
|
||||||
|
writeLine(screen, align, line, x, y, maxWidth, tcell.StyleDefault.Foreground(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLine(screen tcell.Screen, align int, line string, x, y, maxWidth int, style tcell.Style) {
|
||||||
offsetX := 0
|
offsetX := 0
|
||||||
if align == tview.AlignRight {
|
if align == tview.AlignRight {
|
||||||
offsetX = maxWidth - runewidth.StringWidth(line)
|
offsetX = maxWidth - runewidth.StringWidth(line)
|
||||||
@ -37,7 +49,7 @@ func writeLine(screen tcell.Screen, align int, line string, x, y, maxWidth int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for localOffset := 0; localOffset < chWidth; localOffset++ {
|
for localOffset := 0; localOffset < chWidth; localOffset++ {
|
||||||
screen.SetContent(x+offsetX+localOffset, y, ch, nil, tcell.StyleDefault.Foreground(color))
|
screen.SetContent(x+offsetX+localOffset, y, ch, nil, style)
|
||||||
}
|
}
|
||||||
offsetX += chWidth
|
offsetX += chWidth
|
||||||
if offsetX > maxWidth {
|
if offsetX > maxWidth {
|
||||||
|
Loading…
Reference in New Issue
Block a user