@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{},
|
||||
|
Reference in New Issue
Block a user