aboutsummaryrefslogtreecommitdiff
path: root/matrix
diff options
context:
space:
mode:
authorTulir Asokan <tulir@maunium.net>2018-05-16 20:09:09 +0300
committerTulir Asokan <tulir@maunium.net>2018-05-16 20:51:43 +0300
commit8a3fbc24ab430443b89dfa45e726ab96ad3ea1ec (patch)
tree7aab3b58b1d4119c752f8fd549c14e587da15279 /matrix
parentc88801a65782d28184aa73a8d25ed3e8a8641f82 (diff)
Handle m.direct and m.receipt events
Fixes #12 Fixes #45
Diffstat (limited to 'matrix')
-rw-r--r--matrix/matrix.go92
-rw-r--r--matrix/rooms/room.go86
-rw-r--r--matrix/rooms/room_test.go22
-rw-r--r--matrix/sync.go4
4 files changed, 180 insertions, 24 deletions
diff --git a/matrix/matrix.go b/matrix/matrix.go
index 7fbef5f..92fcabe 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -177,7 +177,9 @@ func (c *Container) OnLogin() {
c.syncer = NewGomuksSyncer(c.config.Session)
c.syncer.OnEventType("m.room.message", c.HandleMessage)
c.syncer.OnEventType("m.room.member", c.HandleMembership)
+ c.syncer.OnEventType("m.receipt", c.HandleReadReceipt)
c.syncer.OnEventType("m.typing", c.HandleTyping)
+ c.syncer.OnEventType("m.direct", c.HandleDirectChatInfo)
c.syncer.OnEventType("m.push_rules", c.HandlePushRules)
c.syncer.OnEventType("m.tag", c.HandleTag)
c.syncer.InitDoneCallback = func() {
@@ -228,7 +230,7 @@ func (c *Container) Start() {
// HandleMessage is the event handler for the m.room.message timeline event.
func (c *Container) HandleMessage(source EventSource, evt *gomatrix.Event) {
- if source & EventSourceLeave != 0 {
+ if source&EventSourceLeave != 0 {
return
}
mainView := c.ui.MainView()
@@ -253,6 +255,82 @@ func (c *Container) HandleMessage(source EventSource, evt *gomatrix.Event) {
}
}
+func (c *Container) parseReadReceipt(evt *gomatrix.Event) (largestTimestampEvent string) {
+ var largestTimestamp int64
+ for eventID, rawContent := range evt.Content {
+ content, ok := rawContent.(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ mRead, ok := content["m.read"].(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ myInfo, ok := mRead[c.config.Session.UserID].(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ ts, ok := myInfo["ts"].(float64)
+ if int64(ts) > largestTimestamp {
+ largestTimestamp = int64(ts)
+ largestTimestampEvent = eventID
+ }
+ }
+ return
+}
+
+func (c *Container) HandleReadReceipt(source EventSource, evt *gomatrix.Event) {
+ if source&EventSourceLeave != 0 {
+ return
+ }
+
+ lastReadEvent := c.parseReadReceipt(evt)
+ if len(lastReadEvent) == 0 {
+ return
+ }
+
+ room := c.GetRoom(evt.RoomID)
+ room.MarkRead(lastReadEvent)
+ c.ui.Render()
+}
+
+func (c *Container) parseDirectChatInfo(evt *gomatrix.Event) (map[*rooms.Room]bool){
+ directChats := make(map[*rooms.Room]bool)
+ for _, rawRoomIDList := range evt.Content {
+ roomIDList, ok := rawRoomIDList.([]interface{})
+ if !ok {
+ continue
+ }
+
+ for _, rawRoomID := range roomIDList {
+ roomID, ok := rawRoomID.(string)
+ if !ok {
+ continue
+ }
+
+ room := c.GetRoom(roomID)
+ if room != nil && !room.HasLeft {
+ directChats[room] = true
+ }
+ }
+ }
+ return directChats
+}
+
+func (c *Container) HandleDirectChatInfo(source EventSource, evt *gomatrix.Event) {
+ directChats := c.parseDirectChatInfo(evt)
+ for _, room := range c.config.Session.Rooms {
+ shouldBeDirect := directChats[room]
+ if shouldBeDirect != room.IsDirect {
+ room.IsDirect = shouldBeDirect
+ c.ui.MainView().UpdateTags(room)
+ }
+ }
+}
+
// HandlePushRules is the event handler for the m.push_rules account data event.
func (c *Container) HandlePushRules(source EventSource, evt *gomatrix.Event) {
debug.Print("Received updated push rules")
@@ -285,7 +363,8 @@ func (c *Container) HandleTag(source EventSource, evt *gomatrix.Event) {
}
mainView := c.ui.MainView()
- mainView.UpdateTags(room, newTags)
+ room.RawTags = newTags
+ mainView.UpdateTags(room)
}
func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) {
@@ -314,8 +393,8 @@ func (c *Container) processOwnMembershipChange(evt *gomatrix.Event) {
// HandleMembership is the event handler for the m.room.member state event.
func (c *Container) HandleMembership(source EventSource, evt *gomatrix.Event) {
- isLeave := source & EventSourceLeave != 0
- isTimeline := source & EventSourceTimeline != 0
+ isLeave := source&EventSourceLeave != 0
+ isTimeline := source&EventSourceTimeline != 0
isNonTimelineLeave := isLeave && !isTimeline
if !c.config.Session.InitialSyncDone && isNonTimelineLeave {
return
@@ -356,6 +435,11 @@ func (c *Container) HandleTyping(source EventSource, evt *gomatrix.Event) {
c.ui.MainView().SetTyping(evt.RoomID, strUsers)
}
+func (c *Container) MarkRead(roomID, eventID string) {
+ urlPath := c.client.BuildURL("rooms", roomID, "receipt", "m.read", eventID)
+ c.client.MakeRequest("POST", urlPath, struct{}{}, nil)
+}
+
// SendMessage sends a message with the given text to the given room.
func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) {
defer debug.Recover()
diff --git a/matrix/rooms/room.go b/matrix/rooms/room.go
index 40303be..17bf21b 100644
--- a/matrix/rooms/room.go
+++ b/matrix/rooms/room.go
@@ -43,6 +43,12 @@ type RoomTag struct {
Order string
}
+type UnreadMessage struct {
+ EventID string
+ Counted bool
+ Highlight bool
+}
+
// Room represents a single Matrix room.
type Room struct {
*gomatrix.Room
@@ -57,13 +63,11 @@ type Room struct {
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
+ UnreadMessages []UnreadMessage
+ unreadCountCache *int
+ highlightCache *bool
+ // Whether or not this room is marked as a direct chat.
+ IsDirect bool
// List of tags given to this room
RawTags []RoomTag
@@ -110,14 +114,74 @@ 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
+func (room *Room) MarkRead(eventID string) {
+ readToIndex := -1
+ for index, unreadMessage := range room.UnreadMessages {
+ if unreadMessage.EventID == eventID {
+ readToIndex = index
+ }
+ }
+ if readToIndex >= 0 {
+ room.UnreadMessages = room.UnreadMessages[readToIndex+1:]
+ room.highlightCache = nil
+ room.unreadCountCache = nil
+ }
+}
+
+func (room *Room) UnreadCount() int {
+ if room.unreadCountCache == nil {
+ room.unreadCountCache = new(int)
+ for _, unreadMessage := range room.UnreadMessages {
+ if unreadMessage.Counted {
+ *room.unreadCountCache++
+ }
+ }
+ }
+ return *room.unreadCountCache
+}
+
+func (room *Room) Highlighted() bool {
+ if room.highlightCache == nil {
+ room.highlightCache = new(bool)
+ for _, unreadMessage := range room.UnreadMessages {
+ if unreadMessage.Highlight {
+ *room.highlightCache = true
+ break
+ }
+ }
+ }
+ return *room.highlightCache
+}
+
+func (room *Room) HasNewMessages() bool {
+ return len(room.UnreadMessages) > 0
+}
+
+func (room *Room) AddUnread(eventID string, counted, highlight bool) {
+ room.UnreadMessages = append(room.UnreadMessages, UnreadMessage{
+ EventID: eventID,
+ Counted: counted,
+ Highlight: highlight,
+ })
+ if counted {
+ if room.unreadCountCache == nil {
+ room.unreadCountCache = new(int)
+ }
+ *room.unreadCountCache++
+ }
+ if highlight {
+ if room.highlightCache == nil {
+ room.highlightCache = new(bool)
+ }
+ *room.highlightCache = true
+ }
}
func (room *Room) Tags() []RoomTag {
if len(room.RawTags) == 0 {
+ if room.IsDirect {
+ return []RoomTag{{"net.maunium.gomuks.fake.direct", "0.5"}}
+ }
return []RoomTag{{"", "0.5"}}
}
return room.RawTags
diff --git a/matrix/rooms/room_test.go b/matrix/rooms/room_test.go
index 258e57b..1bd47ff 100644
--- a/matrix/rooms/room_test.go
+++ b/matrix/rooms/room_test.go
@@ -215,11 +215,19 @@ func TestRoom_GetTitle_Members_GroupChat(t *testing.T) {
func TestRoom_MarkRead(t *testing.T) {
room := rooms.NewRoom("!test:maunium.net", "@tulir:maunium.net")
- room.UnreadMessages = 123
- room.Highlighted = true
- room.HasNewMessages = true
- room.MarkRead()
- assert.Zero(t, room.UnreadMessages)
- assert.False(t, room.Highlighted)
- assert.False(t, room.HasNewMessages)
+
+ room.AddUnread("foo", true, false)
+ assert.Equal(t, 1, room.UnreadCount())
+ assert.False(t, room.Highlighted())
+
+ room.AddUnread("bar", true, false)
+ assert.Equal(t, 2, room.UnreadCount())
+ assert.False(t, room.Highlighted())
+
+ room.AddUnread("asd", false, true)
+ assert.Equal(t, 2, room.UnreadCount())
+ assert.True(t, room.Highlighted())
+
+ room.MarkRead("")
+ assert.Empty(t, room.UnreadMessages)
}
diff --git a/matrix/sync.go b/matrix/sync.go
index 93868c7..330a8c5 100644
--- a/matrix/sync.go
+++ b/matrix/sync.go
@@ -177,14 +177,14 @@ func (s *GomuksSyncer) GetFilterJSON(userID string) json.RawMessage {
Limit: 50,
},
Ephemeral: gomatrix.FilterPart{
- Types: []string{"m.typing"},
+ Types: []string{"m.typing", "m.receipt"},
},
AccountData: gomatrix.FilterPart{
Types: []string{"m.tag"},
},
},
AccountData: gomatrix.FilterPart{
- Types: []string{"m.push_rules"},
+ Types: []string{"m.push_rules", "m.direct"},
},
Presence: gomatrix.FilterPart{
Types: []string{},