mirror of
https://github.com/matrix-org/sliding-sync.git
synced 2025-03-10 13:37:11 +00:00
Implement the room name calculation algorithm
This commit is contained in:
parent
f9ed9ddd8c
commit
5ee1e422a1
88
internal/roomname.go
Normal file
88
internal/roomname.go
Normal file
@ -0,0 +1,88 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Hero struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
|
||||
func CalculateRoomName(roomName, canonicalAlias string, maxNumNamesPerRoom int, heroes []Hero, joinedCount, invitedCount int) string {
|
||||
// If the room has an m.room.name state event with a non-empty name field, use the name given by that field.
|
||||
if roomName != "" {
|
||||
return roomName
|
||||
}
|
||||
// If the room has an m.room.canonical_alias state event with a valid alias field, use the alias given by that field as the name.
|
||||
if canonicalAlias != "" {
|
||||
return canonicalAlias
|
||||
}
|
||||
// If none of the above conditions are met, a name should be composed based on the members of the room.
|
||||
disambiguatedNames := disambiguate(heroes)
|
||||
totalNumOtherUsers := (joinedCount + invitedCount - 1)
|
||||
isAlone := totalNumOtherUsers <= 0
|
||||
|
||||
// If m.joined_member_count + m.invited_member_count is less than or equal to 1 (indicating the member is alone),
|
||||
// the client should use the rules BELOW to indicate that the room was empty. For example, "Empty Room (was Alice)",
|
||||
// "Empty Room (was Alice and 1234 others)", or "Empty Room" if there are no heroes.
|
||||
if len(heroes) == 0 && isAlone {
|
||||
return "Empty Room"
|
||||
}
|
||||
|
||||
// If the number of m.heroes for the room are greater or equal to m.joined_member_count + m.invited_member_count - 1,
|
||||
// then use the membership events for the heroes to calculate display names for the users (disambiguating them if required)
|
||||
// and concatenating them.
|
||||
if len(heroes) >= totalNumOtherUsers {
|
||||
if len(disambiguatedNames) == 1 {
|
||||
return disambiguatedNames[0]
|
||||
}
|
||||
calculatedRoomName := strings.Join(disambiguatedNames[:len(disambiguatedNames)-1], ", ") + " and " + disambiguatedNames[len(disambiguatedNames)-1]
|
||||
if isAlone {
|
||||
return fmt.Sprintf("Empty Room (was %s)", calculatedRoomName)
|
||||
}
|
||||
return calculatedRoomName
|
||||
}
|
||||
|
||||
// if we're here then len(heroes) < (joinedCount + invitedCount - 1)
|
||||
numEntries := len(disambiguatedNames)
|
||||
if numEntries > maxNumNamesPerRoom {
|
||||
numEntries = maxNumNamesPerRoom
|
||||
}
|
||||
calculatedRoomName := fmt.Sprintf(
|
||||
"%s and %d others", strings.Join(disambiguatedNames[:numEntries], ", "), totalNumOtherUsers-numEntries,
|
||||
)
|
||||
|
||||
// If there are fewer heroes than m.joined_member_count + m.invited_member_count - 1,
|
||||
// and m.joined_member_count + m.invited_member_count is greater than 1, the client should use the heroes to calculate
|
||||
// display names for the users (disambiguating them if required) and concatenating them alongside a count of the remaining users.
|
||||
if (joinedCount + invitedCount) > 1 {
|
||||
return calculatedRoomName
|
||||
}
|
||||
|
||||
// If m.joined_member_count + m.invited_member_count is less than or equal to 1 (indicating the member is alone),
|
||||
// the client should use the rules above to indicate that the room was empty. For example, "Empty Room (was Alice)",
|
||||
// "Empty Room (was Alice and 1234 others)", or "Empty Room" if there are no heroes.
|
||||
return fmt.Sprintf("Empty Room (was %s)", calculatedRoomName)
|
||||
}
|
||||
|
||||
func disambiguate(heroes []Hero) []string {
|
||||
displayNames := make(map[string][]int)
|
||||
for i, h := range heroes {
|
||||
displayNames[h.Name] = append(displayNames[h.Name], i)
|
||||
}
|
||||
disambiguatedNames := make([]string, len(heroes))
|
||||
for _, indexes := range displayNames {
|
||||
if len(indexes) == 1 {
|
||||
disambiguatedNames[indexes[0]] = heroes[indexes[0]].Name
|
||||
continue
|
||||
}
|
||||
// disambiguate all these heroes
|
||||
for _, i := range indexes {
|
||||
h := heroes[i]
|
||||
disambiguatedNames[i] = fmt.Sprintf("%s (%s)", h.Name, h.ID)
|
||||
}
|
||||
}
|
||||
return disambiguatedNames
|
||||
}
|
196
internal/roomname_test.go
Normal file
196
internal/roomname_test.go
Normal file
@ -0,0 +1,196 @@
|
||||
package internal
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCalculateRoomName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
roomName string
|
||||
canonicalAlias string
|
||||
heroes []Hero
|
||||
joinedCount int
|
||||
invitedCount int
|
||||
maxNumNamesPerRoom int
|
||||
|
||||
wantRoomName string
|
||||
}{
|
||||
// Room name takes precedence
|
||||
{
|
||||
roomName: "My Room Name",
|
||||
canonicalAlias: "#alias:localhost",
|
||||
joinedCount: 5,
|
||||
invitedCount: 1,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
},
|
||||
wantRoomName: "My Room Name",
|
||||
},
|
||||
// Alias takes precedence if room name is missing
|
||||
{
|
||||
canonicalAlias: "#alias:localhost",
|
||||
joinedCount: 5,
|
||||
invitedCount: 1,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
},
|
||||
wantRoomName: "#alias:localhost",
|
||||
},
|
||||
// ... and N others (large group chat)
|
||||
{
|
||||
joinedCount: 5,
|
||||
invitedCount: 1,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice, Bob and 3 others",
|
||||
},
|
||||
// Small group chat
|
||||
{
|
||||
joinedCount: 4,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
{
|
||||
ID: "@charlie:localhost",
|
||||
Name: "Charlie",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice, Bob and Charlie",
|
||||
},
|
||||
// DM room
|
||||
{
|
||||
joinedCount: 2,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice",
|
||||
},
|
||||
// 3-way room
|
||||
{
|
||||
joinedCount: 3,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice and Bob",
|
||||
},
|
||||
// disambiguation all
|
||||
{
|
||||
joinedCount: 10,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@charlie:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice (@alice:localhost), Alice (@bob:localhost), Alice (@charlie:localhost) and 6 others",
|
||||
},
|
||||
// disambiguation some
|
||||
{
|
||||
joinedCount: 10,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
{
|
||||
ID: "@charlie:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Alice (@alice:localhost), Bob, Alice (@charlie:localhost) and 6 others",
|
||||
},
|
||||
// left room
|
||||
{
|
||||
joinedCount: 1,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{
|
||||
{
|
||||
ID: "@alice:localhost",
|
||||
Name: "Alice",
|
||||
},
|
||||
{
|
||||
ID: "@bob:localhost",
|
||||
Name: "Bob",
|
||||
},
|
||||
},
|
||||
wantRoomName: "Empty Room (was Alice and Bob)",
|
||||
},
|
||||
// empty room
|
||||
{
|
||||
joinedCount: 1,
|
||||
invitedCount: 0,
|
||||
maxNumNamesPerRoom: 3,
|
||||
heroes: []Hero{},
|
||||
wantRoomName: "Empty Room",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
gotName := CalculateRoomName(tc.roomName, tc.canonicalAlias, tc.maxNumNamesPerRoom, tc.heroes, tc.joinedCount, tc.invitedCount)
|
||||
if gotName != tc.wantRoomName {
|
||||
t.Errorf("got %s want %s for test case: %+v", gotName, tc.wantRoomName, tc)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user