diff options
-rw-r--r-- | go.mod | 4 | ||||
-rw-r--r-- | go.sum | 22 | ||||
-rw-r--r-- | matrix/matrix.go | 51 | ||||
-rw-r--r-- | matrix/muksevt/event.go | 4 | ||||
-rw-r--r-- | matrix/rooms/room.go | 56 | ||||
-rw-r--r-- | matrix/sync.go | 37 | ||||
-rw-r--r-- | ui/fuzzy-search-modal.go | 1 | ||||
-rw-r--r-- | ui/member-list.go | 2 | ||||
-rw-r--r-- | ui/messages/base.go | 5 | ||||
-rw-r--r-- | ui/messages/filemessage.go | 9 | ||||
-rw-r--r-- | ui/messages/html/parser.go | 13 | ||||
-rw-r--r-- | ui/messages/parser.go | 86 | ||||
-rw-r--r-- | ui/room-view.go | 30 |
13 files changed, 165 insertions, 155 deletions
@@ -18,10 +18,10 @@ require ( github.com/stretchr/testify v1.5.1 go.etcd.io/bbolt v1.3.3 golang.org/x/image v0.0.0-20200119044424-58c23975cae1 - golang.org/x/net v0.0.0-20200301022130-244492dfa37a + golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2 gopkg.in/yaml.v2 v2.2.8 - maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b + maunium.net/go/mautrix v0.2.0-beta.1 maunium.net/go/mauview v0.1.0-beta.1 maunium.net/go/tcell v0.1.0 ) @@ -12,6 +12,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/kyokomi/emoji v2.1.0+incompatible h1:+DYU2RgpI6OHG4oQkM5KlqD3Wd3UPEsX8jamTo1Mp6o= @@ -54,12 +56,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg= golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -69,18 +73,8 @@ gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2/go.mod h1:s1Sn2yZos05Qfs7NK gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -maunium.net/go/mautrix v0.1.0-beta.1 h1:o7EzSO3sMf7tpNvxanITcpMw3nL3SaJ/LDpBRPaZIt8= -maunium.net/go/mautrix v0.1.0-beta.1/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320113420-8101f052c782 h1:gLsc8FRp9mIQHXjxFpdpISUCoJO8jt4SQyaFYUCLJ5U= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320113420-8101f052c782/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320123139-8ba1d97ed86b h1:sQ7w1dOTvTg76wLCUyb30+jEsVIVvI4Zw4IOPdwogIE= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320123139-8ba1d97ed86b/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320135656-32dd5f172592 h1:WybA4BBUyod/qN6/yChfh2Q0CAqoZjtWnpUBwmfbfFU= -maunium.net/go/mautrix v0.1.0-beta.1.0.20200320135656-32dd5f172592/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= -maunium.net/go/mautrix v0.1.0-beta.2.0.20200403220206-c81fdd9fe68f h1:Fg7t2i+AtUX4NDN5Ue9HsHO/Vog0JN9CZsOH3u+hDOc= -maunium.net/go/mautrix v0.1.0-beta.2.0.20200403220206-c81fdd9fe68f/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= -maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b h1:ifTLB2qPUcZmeCeh2/CvlBZz/ddrdXcszdH7H9qFHzE= -maunium.net/go/mautrix v0.1.0-beta.2.0.20200408115320-100bcaabf80b/go.mod h1:YFMU9DBeXH7cqx7sJLg0DkVxwNPbih8QbpUTYf/IjMM= +maunium.net/go/mautrix v0.2.0-beta.1 h1:gIFsA8HquYie36FVdoaEcARb2xca/H0FB7hrV84ZBb4= +maunium.net/go/mautrix v0.2.0-beta.1/go.mod h1:WeTUYKrM3/4LZK2bXQ9NRIXnRWKsa+6+OA1gw0nf5G8= maunium.net/go/mauview v0.1.0-beta.1 h1:hRprD6NTi5Mw7i97DKmgs/TzFQeNpGPytPoswNlU/Ww= maunium.net/go/mauview v0.1.0-beta.1/go.mod h1:og9WbzmWe9SNYNyOFlCv8qa9zMcOvG2nzRJ5vYyud9U= maunium.net/go/tcell v0.1.0 h1:XzsEoGCvOw5nac+tlkSLzQcliLYTN4PrtA7ar2ptjSM= diff --git a/matrix/matrix.go b/matrix/matrix.go index dcf9214..0a4c2e0 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "crypto/tls" + "encoding/gob" "encoding/json" "fmt" "io" @@ -29,6 +30,7 @@ import ( "os" "path" "path/filepath" + "reflect" "runtime" dbg "runtime/debug" "time" @@ -293,6 +295,11 @@ var AccountDataGomuksPreferences = event.Type{ Class: event.AccountDataEventType, } +func init() { + event.TypeMap[AccountDataGomuksPreferences] = reflect.TypeOf(config.UserPreferences{}) + gob.Register(&config.UserPreferences{}) +} + // OnLogin initializes the syncer and updates the room list. func (c *Container) OnLogin() { c.ui.OnLogin() @@ -462,7 +469,7 @@ func (c *Container) HandleEdit(room *rooms.Room, editsID id.EventID, editEvent * } func (c *Container) HandleReaction(room *rooms.Room, reactsTo id.EventID, reactEvent *muksevt.Event) { - rel := reactEvent.Content.GetRelatesTo() + rel := reactEvent.Content.AsReaction().RelatesTo var origEvt *muksevt.Event err := c.history.Update(room, reactsTo, func(evt *muksevt.Event) error { if evt.Unsigned.Relations.Annotations.Map == nil { @@ -502,10 +509,11 @@ func (c *Container) HandleMessage(source EventSource, mxEvent *event.Event) { return } - if editID := mxEvent.Content.GetRelatesTo().GetReplaceID(); len(editID) > 0 { + rel := mxEvent.Content.AsMessage().GetRelatesTo() + if editID := rel.GetReplaceID(); len(editID) > 0 { c.HandleEdit(room, editID, muksevt.Wrap(mxEvent)) return - } else if reactionID := mxEvent.Content.GetRelatesTo().GetAnnotationID(); mxEvent.Type == event.EventReaction && len(reactionID) > 0 { + } else if reactionID := rel.GetAnnotationID(); mxEvent.Type == event.EventReaction && len(reactionID) > 0 { c.HandleReaction(room, reactionID, muksevt.Wrap(mxEvent)) return } @@ -549,7 +557,7 @@ func (c *Container) HandleMessage(source EventSource, mxEvent *event.Event) { c.ui.Render() } } else { - debug.Printf("Parsing event %s type %s %v from %s in %s failed (ParseEvent() returned nil).", evt.ID, evt.Type.String(), evt.Content.Raw, evt.Sender, evt.RoomID) + debug.Printf("Parsing event %s type %s %v from %s in %s failed (ParseEvent() returned nil).", evt.ID, evt.Type.Repr(), evt.Content.Raw, evt.Sender, evt.RoomID) } } @@ -574,10 +582,10 @@ func (c *Container) HandleMembership(source EventSource, evt *event.Event) { } func (c *Container) processOwnMembershipChange(evt *event.Event) { - membership := evt.Content.Membership + membership := evt.Content.AsMember().Membership prevMembership := event.MembershipLeave if evt.Unsigned.PrevContent != nil { - prevMembership = evt.Unsigned.PrevContent.Membership + prevMembership = evt.Unsigned.PrevContent.AsMember().Membership } debug.Printf("Processing own membership change: %s->%s in %s", prevMembership, membership, evt.RoomID) if membership == prevMembership { @@ -704,12 +712,13 @@ func (c *Container) HandlePushRules(_ EventSource, evt *event.Event) { // HandleTag is the event handler for the m.tag account data event. func (c *Container) HandleTag(_ EventSource, evt *event.Event) { - debug.Printf("Received tags for %s: %s -- %s", evt.RoomID, evt.Content.RoomTags, string(evt.Content.VeryRaw)) room := c.GetOrCreateRoom(evt.RoomID) - newTags := make([]rooms.RoomTag, len(evt.Content.RoomTags)) + tags := evt.Content.AsTag().Tags + + newTags := make([]rooms.RoomTag, len(tags)) index := 0 - for tag, info := range evt.Content.RoomTags { + for tag, info := range tags { order := json.Number("0.5") if len(info.Order) > 0 { order = info.Order @@ -733,7 +742,7 @@ func (c *Container) HandleTyping(_ EventSource, evt *event.Event) { if !c.config.AuthCache.InitialSyncDone { return } - c.ui.MainView().SetTyping(evt.RoomID, evt.Content.TypingUserIDs) + c.ui.MainView().SetTyping(evt.RoomID, evt.Content.AsTyping().UserIDs) } func (c *Container) MarkRead(roomID id.RoomID, eventID id.EventID) { @@ -742,9 +751,9 @@ func (c *Container) MarkRead(roomID id.RoomID, eventID id.EventID) { } func (c *Container) PrepareMarkdownMessage(roomID id.RoomID, msgtype event.MessageType, text, html string, rel *ifc.Relation) *muksevt.Event { - var content event.Content + var content event.MessageEventContent if html != "" { - content = event.Content{ + content = event.MessageEventContent{ FormattedBody: html, Format: event.FormatHTML, Body: text, @@ -777,10 +786,8 @@ func (c *Container) PrepareMarkdownMessage(roomID id.RoomID, msgtype event.Messa Type: event.EventMessage, Timestamp: time.Now().UnixNano() / 1e6, RoomID: roomID, - Content: content, - Unsigned: event.Unsigned{ - TransactionID: txnID, - }, + Content: event.Content{Parsed: &content}, + Unsigned: event.Unsigned{TransactionID: txnID}, }) localEcho.Gomuks.OutgoingState = muksevt.StateLocalEcho if rel != nil && rel.Type == event.RelReplace { @@ -797,12 +804,12 @@ func (c *Container) Redact(roomID id.RoomID, eventID id.EventID, reason string) } // SendMessage sends the given event. -func (c *Container) SendEvent(event *muksevt.Event) (id.EventID, error) { +func (c *Container) SendEvent(evt *muksevt.Event) (id.EventID, error) { defer debug.Recover() - c.client.UserTyping(event.RoomID, false, 0) + c.client.UserTyping(evt.RoomID, false, 0) c.typing = 0 - resp, err := c.client.SendMessageEvent(event.RoomID, event.Type, event.Content, mautrix.ReqSendEvent{TransactionID: event.Unsigned.TransactionID}) + resp, err := c.client.SendMessageEvent(evt.RoomID, evt.Type, &evt.Content, mautrix.ReqSendEvent{TransactionID: evt.Unsigned.TransactionID}) if err != nil { return "", err } @@ -892,6 +899,12 @@ func (c *Container) GetHistory(room *rooms.Room, limit int) ([]*muksevt.Event, e return nil, err } debug.Printf("Loaded %d events for %s from server from %s to %s", len(resp.Chunk), room.ID, resp.Start, resp.End) + for _, evt := range resp.Chunk { + err := evt.Content.ParseRaw(evt.Type) + if err != nil { + debug.Printf("Failed to unmarshal content of event %s (type %s) by %s in %s: %v\n%s", evt.ID, evt.Type.Repr(), evt.Sender, evt.RoomID, err, string(evt.Content.VeryRaw)) + } + } for _, evt := range resp.State { room.UpdateState(evt) } diff --git a/matrix/muksevt/event.go b/matrix/muksevt/event.go index 9f7a3ce..d1bca13 100644 --- a/matrix/muksevt/event.go +++ b/matrix/muksevt/event.go @@ -27,8 +27,10 @@ type Event struct { func (evt *Event) SomewhatDangerousCopy() *Event { base := *evt.Event + content := *base.Content.Parsed.(*event.MessageEventContent) + evt.Content.Parsed = &content return &Event{ - Event: &base, + Event: &base, Gomuks: evt.Gomuks, } } diff --git a/matrix/rooms/room.go b/matrix/rooms/room.go index 50ec28f..87e63f0 100644 --- a/matrix/rooms/room.go +++ b/matrix/rooms/room.go @@ -62,7 +62,7 @@ type UnreadMessage struct { } type Member struct { - event.Member + event.MemberEventContent // The user who sent the membership event Sender id.UserID `json:"-"` @@ -397,25 +397,25 @@ func (room *Room) UpdateState(evt *event.Event) { if !exists { room.state[evt.Type] = make(map[string]*event.Event) } - switch evt.Type { - case event.StateRoomName: - room.NameCache = evt.Content.Name + switch content := evt.Content.Parsed.(type) { + case *event.RoomNameEventContent: + room.NameCache = content.Name room.nameCacheSource = ExplicitRoomName - case event.StateCanonicalAlias: + case *event.CanonicalAliasEventContent: if room.nameCacheSource <= CanonicalAliasRoomName { - room.NameCache = string(evt.Content.Alias) + room.NameCache = string(content.Alias) room.nameCacheSource = CanonicalAliasRoomName } - room.CanonicalAliasCache = evt.Content.Alias - case event.StateMember: + room.CanonicalAliasCache = content.Alias + case *event.MemberEventContent: if room.nameCacheSource <= MemberRoomName { room.NameCache = "" } - room.updateMemberState(evt) - case event.StateTopic: - room.topicCache = evt.Content.Topic - case event.StateEncryption: - if evt.Content.Algorithm == "m.megolm.v1.aes-sha2" { + room.updateMemberState(id.UserID(evt.GetStateKey()), evt.Sender, content) + case *event.TopicEventContent: + room.topicCache = content.Topic + case *event.EncryptionEventContent: + if content.Algorithm == "m.megolm.v1.aes-sha2" { room.Encrypted = true } } @@ -427,14 +427,13 @@ func (room *Room) UpdateState(evt *event.Event) { room.state[evt.Type][*evt.StateKey] = evt } -func (room *Room) updateMemberState(event *event.Event) { - userID := id.UserID(event.GetStateKey()) +func (room *Room) updateMemberState(userID, sender id.UserID, content *event.MemberEventContent) { if userID == room.SessionUserID { - debug.Print("Updating session user state:", string(event.Content.VeryRaw)) - room.SessionMember = room.eventToMember(userID, event.Sender, &event.Content) + debug.Print("Updating session user state:", content) + room.SessionMember = room.eventToMember(userID, sender, content) } if room.memberCache != nil { - member := room.eventToMember(userID, event.Sender, &event.Content) + member := room.eventToMember(userID, sender, content) if member.Membership.IsInviteOrJoin() { existingMember, ok := room.memberCache[userID] if ok { @@ -477,7 +476,7 @@ func (room *Room) GetTopic() string { if len(room.topicCache) == 0 { topicEvt := room.GetStateEvent(event.StateTopic, "") if topicEvt != nil { - room.topicCache = topicEvt.Content.Topic + room.topicCache = topicEvt.Content.AsTopic().Topic } } return room.topicCache @@ -487,7 +486,7 @@ func (room *Room) GetCanonicalAlias() id.RoomAlias { if len(room.CanonicalAliasCache) == 0 { canonicalAliasEvt := room.GetStateEvent(event.StateCanonicalAlias, "") if canonicalAliasEvt != nil { - room.CanonicalAliasCache = canonicalAliasEvt.Content.Alias + room.CanonicalAliasCache = canonicalAliasEvt.Content.AsCanonicalAlias().Alias } else { room.CanonicalAliasCache = "-" } @@ -502,7 +501,7 @@ func (room *Room) GetCanonicalAlias() id.RoomAlias { func (room *Room) updateNameFromNameEvent() { nameEvt := room.GetStateEvent(event.StateRoomName, "") if nameEvt != nil { - room.NameCache = nameEvt.Content.Name + room.NameCache = nameEvt.Content.AsRoomName().Name } } @@ -566,7 +565,10 @@ func (room *Room) IsReplaced() bool { evt := room.GetStateEvent(event.StateTombstone, "") var replacement id.RoomID if evt != nil { - replacement = evt.Content.ReplacementRoom + content, ok := evt.Content.Parsed.(*event.TombstoneEventContent) + if ok { + replacement = content.ReplacementRoom + } } room.replacedCache = evt != nil room.replacedByCache = &replacement @@ -581,15 +583,13 @@ func (room *Room) ReplacedBy() id.RoomID { return *room.replacedByCache } -func (room *Room) eventToMember(userID, sender id.UserID, content *event.Content) *Member { - member := content.Member - member.Membership = content.Membership +func (room *Room) eventToMember(userID, sender id.UserID, member *event.MemberEventContent) *Member { if len(member.Displayname) == 0 { member.Displayname = string(userID) } return &Member{ - Member: member, - Sender: sender, + MemberEventContent: *member, + Sender: sender, } } @@ -617,7 +617,7 @@ func (room *Room) createMemberCache() map[id.UserID]*Member { if memberEvents != nil { for userIDStr, evt := range memberEvents { userID := id.UserID(userIDStr) - member := room.eventToMember(userID, evt.Sender, &evt.Content) + member := room.eventToMember(userID, evt.Sender, evt.Content.AsMember()) if member.Membership.IsInviteOrJoin() { cache[userID] = member room.updateNthMemberCache(userID, member) diff --git a/matrix/sync.go b/matrix/sync.go index 53c1798..cd6334f 100644 --- a/matrix/sync.go +++ b/matrix/sync.go @@ -19,7 +19,6 @@ package matrix import ( - "encoding/json" "fmt" "time" @@ -154,38 +153,40 @@ func (s *GomuksSyncer) ProcessResponse(res *mautrix.RespSync, since string) (err return } -func (s *GomuksSyncer) processSyncEvents(room *rooms.Room, events []json.RawMessage, source EventSource) { +func (s *GomuksSyncer) processSyncEvents(room *rooms.Room, events []*event.Event, source EventSource) { for _, evt := range events { s.processSyncEvent(room, evt, source) } } -func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, eventJSON json.RawMessage, source EventSource) { - evt := &event.Event{} - err := json.Unmarshal(eventJSON, evt) - if err != nil { - debug.Print("Failed to unmarshal event: %v\n%s", err, string(eventJSON)) - return +func (s *GomuksSyncer) processSyncEvent(room *rooms.Room, evt *event.Event, source EventSource) { + if room != nil { + evt.RoomID = room.ID } // Ensure the type class is correct. It's safe to mutate since it's not a pointer. // Listeners are keyed by type structs, which means only the correct class will pass. switch { case evt.StateKey != nil: evt.Type.Class = event.StateEventType - case source == EventSourcePresence, source & EventSourceEphemeral != 0: + case source == EventSourcePresence, source&EventSourceEphemeral != 0: evt.Type.Class = event.EphemeralEventType - case source & EventSourceAccountData != 0: + case source&EventSourceAccountData != 0: evt.Type.Class = event.AccountDataEventType case source == EventSourceToDevice: evt.Type.Class = event.ToDeviceEventType default: evt.Type.Class = event.MessageEventType } - if room != nil { - evt.RoomID = room.ID - if evt.Type.IsState() { - room.UpdateState(evt) - } + + err := evt.Content.ParseRaw(evt.Type) + if err != nil { + debug.Printf("Failed to unmarshal content of event %s (type %s) by %s in %s: %v\n%s", evt.ID, evt.Type.Repr(), evt.Sender, evt.RoomID, err, string(evt.Content.VeryRaw)) + // TODO might be good to let these pass to allow handling invalid events too + return + } + + if room != nil && evt.Type.IsState() { + room.UpdateState(evt) } s.notifyListeners(source, evt) } @@ -217,8 +218,8 @@ func (s *GomuksSyncer) OnFailedSync(res *mautrix.RespSync, err error) (time.Dura } // GetFilterJSON returns a filter with a timeline limit of 50. -func (s *GomuksSyncer) GetFilterJSON(_ id.UserID) json.RawMessage { - filter := &mautrix.Filter{ +func (s *GomuksSyncer) GetFilterJSON(_ id.UserID) *mautrix.Filter { + return &mautrix.Filter{ Room: mautrix.RoomFilter{ IncludeLeave: false, State: mautrix.FilterPart{ @@ -264,6 +265,4 @@ func (s *GomuksSyncer) GetFilterJSON(_ id.UserID) json.RawMessage { NotTypes: []event.Type{event.NewEventType("*")}, }, } - rawFilter, _ := json.Marshal(&filter) - return rawFilter } diff --git a/ui/fuzzy-search-modal.go b/ui/fuzzy-search-modal.go index bf9004b..8aeb058 100644 --- a/ui/fuzzy-search-modal.go +++ b/ui/fuzzy-search-modal.go @@ -119,7 +119,6 @@ func (fs *FuzzySearchModal) changeHandler(str string) { func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool { highlights := fs.results.GetHighlights() - debug.Print("Search key event:", event) switch event.Key() { case tcell.KeyEsc: // Close room finder diff --git a/ui/member-list.go b/ui/member-list.go index b47840e..87e7b5d 100644 --- a/ui/member-list.go +++ b/ui/member-list.go @@ -65,7 +65,7 @@ func (rml roomMemberList) Swap(i, j int) { rml[i], rml[j] = rml[j], rml[i] } -func (ml *MemberList) Update(data map[id.UserID]*rooms.Member, levels *event.PowerLevels) *MemberList { +func (ml *MemberList) Update(data map[id.UserID]*rooms.Member, levels *event.PowerLevelsEventContent) *MemberList { ml.list = make(roomMemberList, len(data)) i := 0 highestLevel := math.MinInt32 diff --git a/ui/messages/base.go b/ui/messages/base.go index eaeb552..c4e10b0 100644 --- a/ui/messages/base.go +++ b/ui/messages/base.go @@ -95,7 +95,8 @@ const DateFormat = "January _2, 2006" const TimeFormat = "15:04:05" func newUIMessage(evt *muksevt.Event, displayname string, renderer MessageRenderer) *UIMessage { - msgtype := evt.Content.MsgType + msgContent := evt.Content.AsMessage() + msgtype := msgContent.MsgType if len(msgtype) == 0 { msgtype = event.MessageType(evt.Type.String()) } @@ -117,7 +118,7 @@ func newUIMessage(evt *muksevt.Event, displayname string, renderer MessageRender Type: msgtype, EventID: evt.ID, TxnID: evt.Unsigned.TransactionID, - Relation: *evt.Content.GetRelatesTo(), + Relation: *msgContent.GetRelatesTo(), State: evt.Gomuks.OutgoingState, IsHighlight: false, IsService: false, diff --git a/ui/messages/filemessage.go b/ui/messages/filemessage.go index baffd0f..5b7b21c 100644 --- a/ui/messages/filemessage.go +++ b/ui/messages/filemessage.go @@ -48,11 +48,12 @@ type FileMessage struct { // NewFileMessage creates a new FileMessage object with the provided values and the default state. func NewFileMessage(matrix ifc.MatrixContainer, evt *muksevt.Event, displayname string) *UIMessage { - url, _ := evt.Content.URL.Parse() - thumbnail, _ := evt.Content.GetInfo().ThumbnailURL.Parse() + content := evt.Content.AsMessage() + url, _ := content.URL.Parse() + thumbnail, _ := content.GetInfo().ThumbnailURL.Parse() return newUIMessage(evt, displayname, &FileMessage{ - Type: evt.Content.MsgType, - Body: evt.Content.Body, + Type: content.MsgType, + Body: content.Body, URL: url, Thumbnail: thumbnail, matrix: matrix, diff --git a/ui/messages/html/parser.go b/ui/messages/html/parser.go index 86b501d..7bb4a84 100644 --- a/ui/messages/html/parser.go +++ b/ui/messages/html/parser.go @@ -27,7 +27,6 @@ import ( "github.com/lucasb-eyer/go-colorful" "golang.org/x/net/html" - "maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" "maunium.net/go/tcell" @@ -384,10 +383,10 @@ func (parser *htmlParser) Parse(htmlData string) Entity { const TabLength = 4 // Parse parses a HTML-formatted Matrix event into a UIMessage. -func Parse(room *rooms.Room, evt *muksevt.Event, senderDisplayname string) Entity { - htmlData := evt.Content.FormattedBody - if evt.Content.Format != event.FormatHTML { - htmlData = strings.Replace(html.EscapeString(evt.Content.Body), "\n", "<br/>", -1) +func Parse(room *rooms.Room, content *event.MessageEventContent, sender id.UserID, senderDisplayname string) Entity { + htmlData := content.FormattedBody + if content.Format != event.FormatHTML { + htmlData = strings.Replace(html.EscapeString(content.Body), "\n", "<br/>", -1) } htmlData = strings.Replace(htmlData, "\t", strings.Repeat(" ", TabLength), -1) @@ -403,14 +402,14 @@ func Parse(room *rooms.Room, evt *muksevt.Event, senderDisplayname string) Entit } } - if evt.Content.MsgType == event.MsgEmote { + if content.MsgType == event.MsgEmote { root = &ContainerEntity{ BaseEntity: &BaseEntity{ Tag: "emote", }, Children: []Entity{ NewTextEntity("* "), - NewTextEntity(senderDisplayname).AdjustStyle(AdjustStyleTextColor(widget.GetHashColor(evt.Sender))), + NewTextEntity(senderDisplayname).AdjustStyle(AdjustStyleTextColor(widget.GetHashColor(sender))), NewTextEntity(" "), root, }, diff --git a/ui/messages/parser.go b/ui/messages/parser.go index d89bcb9..616e7cb 100644 --- a/ui/messages/parser.go +++ b/ui/messages/parser.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + "maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" @@ -48,10 +49,10 @@ func ParseEvent(matrix ifc.MatrixContainer, mainView ifc.MainView, room *rooms.R if msg == nil { return nil } - if len(evt.Content.GetReplyTo()) > 0 { - if replyToMsg := getCachedEvent(mainView, room.ID, evt.Content.GetReplyTo()); replyToMsg != nil { + if content, ok := evt.Content.Parsed.(*event.MessageEventContent); ok && len(content.GetReplyTo()) > 0 { + if replyToMsg := getCachedEvent(mainView, room.ID, content.GetReplyTo()); replyToMsg != nil { msg.ReplyTo = replyToMsg.Clone() - } else if replyToEvt, _ := matrix.GetEvent(room, evt.Content.GetReplyTo()); replyToEvt != nil { + } else if replyToEvt, _ := matrix.GetEvent(room, content.GetReplyTo()); replyToEvt != nil { if replyToMsg := directParseEvent(matrix, room, replyToEvt); replyToMsg != nil { msg.ReplyTo = replyToMsg msg.ReplyTo.Reactions = nil @@ -74,48 +75,49 @@ func directParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *muksevt if evt.Unsigned.RedactedBecause != nil || evt.Type == event.EventRedaction { return NewRedactedMessage(evt, displayname) } - switch evt.Type { - case event.EventSticker: - evt.Content.MsgType = event.MsgImage - fallthrough - case event.EventMessage: + switch content := evt.Content.Parsed.(type) { + case *event.MessageEventContent: + if evt.Type == event.EventSticker { + content.MsgType = event.MsgImage + } return ParseMessage(matrix, room, evt, displayname) - case event.EventEncrypted: + case *event.EncryptedEventContent: return NewExpandedTextMessage(evt, displayname, tstring.NewStyleTString("Encrypted messages are not yet supported", tcell.StyleDefault.Italic(true))) - case event.StateTopic, event.StateRoomName, event.StateAliases, event.StateCanonicalAlias: + case *event.TopicEventContent, *event.RoomNameEventContent, *event.CanonicalAliasEventContent: return ParseStateEvent(evt, displayname) - case event.StateMember: + case *event.MemberEventContent: return ParseMembershipEvent(room, evt) + default: + debug.Printf("Unknown event content type %T in directParseEvent", content) + return nil } - - return nil } func ParseStateEvent(evt *muksevt.Event, displayname string) *UIMessage { text := tstring.NewColorTString(displayname, widget.GetHashColor(evt.Sender)) - switch evt.Type { - case event.StateTopic: - if len(evt.Content.Topic) == 0 { + switch content := evt.Content.Parsed.(type) { + case *event.TopicEventContent: + if len(content.Topic) == 0 { text = text.AppendColor(" removed the topic.", tcell.ColorGreen) } else { text = text.AppendColor(" changed the topic to ", tcell.ColorGreen). - AppendStyle(evt.Content.Topic, tcell.StyleDefault.Underline(true)). + AppendStyle(content.Topic, tcell.StyleDefault.Underline(true)). AppendColor(".", tcell.ColorGreen) } - case event.StateRoomName: - if len(evt.Content.Name) == 0 { + case *event.RoomNameEventContent: + if len(content.Name) == 0 { text = text.AppendColor(" removed the room name.", tcell.ColorGreen) } else { text = text.AppendColor(" changed the room name to ", tcell.ColorGreen). - AppendStyle(evt.Content.Name, tcell.StyleDefault.Underline(true)). + AppendStyle(content.Name, tcell.StyleDefault.Underline(true)). AppendColor(".", tcell.ColorGreen) } - case event.StateCanonicalAlias: - if len(evt.Content.Alias) == 0 { + case *event.CanonicalAliasEventContent: + if len(content.Alias) == 0 { text = text.AppendColor(" removed the main address of the room.", tcell.ColorGreen) } else { text = text.AppendColor(" changed the main address of the room to ", tcell.ColorGreen). - AppendStyle(string(evt.Content.Alias), tcell.StyleDefault.Underline(true)). + AppendStyle(string(content.Alias), tcell.StyleDefault.Underline(true)). AppendColor(".", tcell.ColorGreen) } //case event.StateAliases: @@ -125,19 +127,20 @@ func ParseStateEvent(evt *muksevt.Event, displayname string) *UIMessage { } func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *muksevt.Event, displayname string) *UIMessage { - if len(evt.Content.GetReplyTo()) > 0 { - evt.Content.RemoveReplyFallback() + content := evt.Content.AsMessage() + if len(content.GetReplyTo()) > 0 { + content.RemoveReplyFallback() } if len(evt.Gomuks.Edits) > 0 { - evt.Content = *evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.NewContent + content = evt.Gomuks.Edits[len(evt.Gomuks.Edits)-1].Content.AsMessage().NewContent } - switch evt.Content.MsgType { + switch content.MsgType { case event.MsgText, event.MsgNotice, event.MsgEmote: - if evt.Content.Format == event.FormatHTML { - return NewHTMLMessage(evt, displayname, html.Parse(room, evt, displayname)) + if content.Format == event.FormatHTML { + return NewHTMLMessage(evt, displayname, html.Parse(room, content, evt.Sender, displayname)) } - evt.Content.Body = strings.Replace(evt.Content.Body, "\t", " ", -1) - return NewTextMessage(evt, displayname, evt.Content.Body) + content.Body = strings.Replace(content.Body, "\t", " ", -1) + return NewTextMessage(evt, displayname, content.Body) case event.MsgImage, event.MsgVideo, event.MsgAudio, event.MsgFile: msg := NewFileMessage(matrix, evt, displayname) if !matrix.Preferences().DisableDownloads { @@ -149,8 +152,8 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *muksevt.Eve return nil } -func getMembershipChangeMessage(evt *muksevt.Event, membership, prevMembership event.Membership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) { - switch membership { +func getMembershipChangeMessage(evt *muksevt.Event, content *event.MemberEventContent, prevMembership event.Membership, senderDisplayname, displayname, prevDisplayname string) (sender string, text tstring.TString) { + switch content.Membership { case "invite": sender = "---" text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", senderDisplayname, displayname), tcell.ColorGreen) @@ -171,7 +174,7 @@ func getMembershipChangeMessage(evt *muksevt.Event, membership, prevMembership e text = tstring.NewColorTString(fmt.Sprintf("%s unbanned %s", senderDisplayname, displayname), tcell.ColorGreen) text.Colorize(len(senderDisplayname)+len(" unbanned "), len(displayname), widget.GetHashColor(evt.StateKey)) } else { - text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed) + text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", senderDisplayname, displayname, content.Reason), tcell.ColorRed) text.Colorize(len(senderDisplayname)+len(" kicked "), len(displayname), widget.GetHashColor(evt.StateKey)) } text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) @@ -187,7 +190,7 @@ func getMembershipChangeMessage(evt *muksevt.Event, membership, prevMembership e text.Colorize(0, len(displayname), widget.GetHashColor(evt.StateKey)) } case "ban": - text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, evt.Content.Reason), tcell.ColorRed) + text = tstring.NewColorTString(fmt.Sprintf("%s banned %s: %s", senderDisplayname, displayname, content.Reason), tcell.ColorRed) text.Colorize(len(senderDisplayname)+len(" banned "), len(displayname), widget.GetHashColor(evt.StateKey)) text.Colorize(0, len(senderDisplayname), widget.GetHashColor(evt.Sender)) } @@ -201,8 +204,8 @@ func getMembershipEventContent(room *rooms.Room, evt *muksevt.Event) (sender str senderDisplayname = member.Displayname } - membership := evt.Content.Membership - displayname := evt.Content.Displayname + content := evt.Content.AsMember() + displayname := content.Displayname if len(displayname) == 0 { displayname = *evt.StateKey } @@ -210,15 +213,16 @@ func getMembershipEventContent(room *rooms.Room, evt *muksevt.Event) (sender str prevMembership := event.MembershipLeave prevDisplayname := *evt.StateKey if evt.Unsigned.PrevContent != nil { - prevMembership = evt.Unsigned.PrevContent.Membership - prevDisplayname = evt.Unsigned.PrevContent.Displayname + prevContent := evt.Unsigned.PrevContent.AsMember() + prevMembership = prevContent.Membership + prevDisplayname = prevContent.Displayname if len(prevDisplayname) == 0 { prevDisplayname = *evt.StateKey } } - if membership != prevMembership { - sender, text = getMembershipChangeMessage(evt, membership, prevMembership, senderDisplayname, displayname, prevDisplayname) + if content.Membership != prevMembership { + sender, text = getMembershipChangeMessage(evt, content, prevMembership, senderDisplayname, displayname, prevDisplayname) } else if displayname != prevDisplayname { sender = "---" color := widget.GetHashColor(evt.StateKey) diff --git a/ui/room-view.go b/ui/room-view.go index 8408e25..ba6259b 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -480,8 +480,8 @@ func (view *RoomView) SetEditing(evt *muksevt.Event) { view.editing = evt // replying should never be non-nil when SetEditing, but do this just to be safe view.replying = nil - text := view.editing.Content.Body - if view.editing.Content.MsgType == event.MsgEmote { + text := view.editing.Content.AsMessage().Body + if view.editing.Content.AsMessage().MsgType == event.MsgEmote { text = "/me " + text } view.input.SetText(text) @@ -497,11 +497,11 @@ func (view *RoomView) filterOwnOnly(evt *muksevt.Event) bool { } func (view *RoomView) filterMediaOnly(evt *muksevt.Event) bool { - return evt.Type == event.EventMessage && ( - evt.Content.MsgType == event.MsgFile || - evt.Content.MsgType == event.MsgImage || - evt.Content.MsgType == event.MsgAudio || - evt.Content.MsgType == event.MsgVideo) + content, ok := evt.Content.Parsed.(*event.MessageEventContent) + return ok && content.MsgType == event.MsgFile || + content.MsgType == event.MsgImage || + content.MsgType == event.MsgAudio || + content.MsgType == event.MsgVideo } func (view *RoomView) findMessage(current *muksevt.Event, forward bool, allow findFilter) *messages.UIMessage { @@ -660,13 +660,11 @@ func (view *RoomView) SendReaction(eventID id.EventID, reaction string) { Event: &event.Event{ Type: event.EventReaction, RoomID: view.Room.ID, - Content: event.Content{ - RelatesTo: &event.RelatesTo{ - Type: event.RelAnnotation, - EventID: eventID, - Key: reaction, - }, - }, + Content: event.Content{Parsed: event.ReactionEventContent{RelatesTo: event.RelatesTo{ + Type: event.RelAnnotation, + EventID: eventID, + Key: reaction, + }}}, }, }) if err != nil { @@ -745,9 +743,9 @@ func (view *RoomView) Update() { } func (view *RoomView) UpdateUserList() { - pls := &event.PowerLevels{} + pls := &event.PowerLevelsEventContent{} if plEvent := view.Room.GetStateEvent(event.StatePowerLevels, ""); plEvent != nil { - pls = plEvent.Content.GetPowerLevels() + pls = plEvent.Content.AsPowerLevels() } view.userList.Update(view.Room.GetMembers(), pls) view.userListLoaded = true |