From bfb5f0dd457be326b1ae7638a64d2e79cbace371 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 22 Apr 2018 21:05:42 +0300 Subject: Add room alias autocompletion --- matrix/rooms/room.go | 83 ++++++++++++++++++++++++++++++++++++++++++++-------- ui/room-view.go | 21 ++++++++++--- ui/view-main.go | 2 +- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/matrix/rooms/room.go b/matrix/rooms/room.go index 2e12a28..2b41ebc 100644 --- a/matrix/rooms/room.go +++ b/matrix/rooms/room.go @@ -23,6 +23,15 @@ import ( "maunium.net/go/gomatrix" ) +type RoomNameSource int + +const ( + ExplicitRoomName RoomNameSource = iota + CanonicalAliasRoomName + AliasRoomName + MemberRoomName +) + // Room represents a single Matrix room. type Room struct { *gomatrix.Room @@ -50,8 +59,14 @@ type Room struct { // The name of the room. Calculated from the state event name, // canonical_alias or alias or the member cache. nameCache string + // The event type from which the name cache was calculated from. + nameCacheSource RoomNameSource // The topic of the room. Directly fetched from the m.room.topic state event. topicCache string + // The canonical alias of the room. Directly fetched from the m.room.canonical_alias state event. + canonicalAliasCache string + // The list of aliases. Directly fetched from the m.room.aliases state event. + aliasesCache []string // fetchHistoryLock is used to make sure multiple goroutines don't fetch // history for this room at the same time. @@ -90,12 +105,24 @@ func (room *Room) UpdateState(event *gomatrix.Event) { room.State[event.Type] = make(map[string]*gomatrix.Event) } switch event.Type { + case "m.room.name": + room.nameCache = "" + case "m.room.canonical_alias": + if room.nameCacheSource >= CanonicalAliasRoomName { + room.nameCache = "" + } + room.canonicalAliasCache = "" + case "m.room.aliases": + if room.nameCacheSource >= AliasRoomName { + room.nameCache = "" + } + room.aliasesCache = nil case "m.room.member": room.memberCache = nil room.firstMemberCache = "" - fallthrough - case "m.room.name", "m.room.canonical_alias", "m.room.alias": - room.nameCache = "" + if room.nameCacheSource >= MemberRoomName { + room.nameCache = "" + } case "m.room.topic": room.topicCache = "" } @@ -126,6 +153,40 @@ func (room *Room) GetTopic() string { return room.topicCache } +func (room *Room) GetCanonicalAlias() string { + if len(room.canonicalAliasCache) == 0 { + canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") + if canonicalAliasEvt != nil { + room.canonicalAliasCache, _ = canonicalAliasEvt.Content["alias"].(string) + } else { + room.canonicalAliasCache = "-" + } + } + if room.canonicalAliasCache == "-" { + return "" + } + return room.canonicalAliasCache +} + +// GetAliases returns the list of aliases that point to this room. +func (room *Room) GetAliases() []string { + if room.aliasesCache == nil { + aliasEvents := room.GetStateEvents("m.room.aliases") + room.aliasesCache = []string{} + for _, event := range aliasEvents { + aliases, _ := event.Content["aliases"].([]interface{}) + + newAliases := make([]string, len(room.aliasesCache)+len(aliases)) + copy(newAliases, room.aliasesCache) + for index, alias := range aliases { + newAliases[len(room.aliasesCache) + index], _ = alias.(string) + } + room.aliasesCache = newAliases + } + } + return room.aliasesCache +} + // updateNameFromNameEvent updates the room display name to be the name set in the name event. func (room *Room) updateNameFromNameEvent() { nameEvt := room.GetStateEvent("m.room.name", "") @@ -134,17 +195,9 @@ func (room *Room) updateNameFromNameEvent() { } } -// updateNameFromCanonicalAlias updates the room display name to be the canonical alias of the room. -func (room *Room) updateNameFromCanonicalAlias() { - canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") - if canonicalAliasEvt != nil { - room.nameCache, _ = canonicalAliasEvt.Content["alias"].(string) - } -} - // updateNameFromAliases updates the room display name to be the first room alias it finds. // -// Deprecated: the Client-Server API recommends against using aliases as display name. +// Deprecated: the Client-Server API recommends against using non-canonical aliases as display name. func (room *Room) updateNameFromAliases() { // TODO the spec says clients should not use m.room.aliases for room names. // However, Riot also uses m.room.aliases, so this is here now. @@ -183,15 +236,19 @@ func (room *Room) updateNameFromMembers() { func (room *Room) updateNameCache() { if len(room.nameCache) == 0 { room.updateNameFromNameEvent() + room.nameCacheSource = ExplicitRoomName } if len(room.nameCache) == 0 { - room.updateNameFromCanonicalAlias() + room.nameCache = room.GetCanonicalAlias() + room.nameCacheSource = CanonicalAliasRoomName } if len(room.nameCache) == 0 { room.updateNameFromAliases() + room.nameCacheSource = AliasRoomName } if len(room.nameCache) == 0 { room.updateNameFromMembers() + room.nameCacheSource = MemberRoomName } } diff --git a/ui/room-view.go b/ui/room-view.go index bfcb547..4cf7320 100644 --- a/ui/room-view.go +++ b/ui/room-view.go @@ -45,7 +45,10 @@ type RoomView struct { input *widget.AdvancedInputField Room *rooms.Room + parent *MainView + typing []string + completions struct { list []string textCache string @@ -53,7 +56,7 @@ type RoomView struct { } } -func NewRoomView(room *rooms.Room) *RoomView { +func NewRoomView(parent *MainView, room *rooms.Room) *RoomView { view := &RoomView{ Box: tview.NewBox(), topic: tview.NewTextView(), @@ -62,6 +65,7 @@ func NewRoomView(room *rooms.Room) *RoomView { ulBorder: widget.NewBorder(), input: widget.NewAdvancedInputField(), Room: room, + parent: parent, } view.content = NewMessageView(view) @@ -257,9 +261,18 @@ func (view *RoomView) autocompleteUser(existingText string) (completions []compl } func (view *RoomView) autocompleteRoom(existingText string) (completions []completion) { - // TODO - This was harder than I expected. - - return []completion{} + for _, room := range view.parent.rooms { + alias := room.Room.GetCanonicalAlias() + if alias == existingText { + // Exact match, return that. + return []completion{{alias, room.Room.ID}} + } + if strings.HasPrefix(alias, existingText) { + completions = append(completions, completion{alias, room.Room.ID}) + continue + } + } + return } func (view *RoomView) InputTabComplete(text string, cursorOffset int) { diff --git a/ui/view-main.go b/ui/view-main.go index 0b0708c..ba98719 100644 --- a/ui/view-main.go +++ b/ui/view-main.go @@ -299,7 +299,7 @@ func (view *MainView) addRoom(index int, room string) { view.roomList.Add(roomStore) if !view.roomView.HasPage(room) { - roomView := NewRoomView(roomStore). + roomView := NewRoomView(view, roomStore). SetInputSubmitFunc(view.InputSubmit). SetInputChangedFunc(view.InputChanged). SetInputCapture(view.KeyEventHandler). -- cgit v1.2.3