This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 833
Support routing matrix.to links to joinable rooms #2250
Merged
Merged
Changes from 12 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
7383133
Support parsing matrix.to links in the timeline with ?via= in them
turt2live d7367a7
Merge branch 'develop' into travis/permalink-routing
turt2live 6ee495b
Merge remote-tracking branch 'origin/develop' into travis/permalink-r…
turt2live a878212
Install memfs because webpack is made of fail
turt2live e8cb636
Pick servers for ?via on matrix.to links based on some heuristics
turt2live 54ff5d8
Fix Karma/Webpack so it can build the tests
turt2live b9bfbdc
Fix the tests so they actually test something
turt2live 43980ad
Add hostname sanity tests
turt2live 5209496
Fix the server candidate picker to actually work
turt2live d58ff90
Merge branch 'develop' into travis/permalink-routing
turt2live ea8a37f
Merge branch 'develop' into travis/permalink-routing
turt2live ef8c924
Maybe fix UserSettings?
turt2live c389540
Appease the linter
turt2live d802ee0
Move the max candidates constant out of the function
turt2live 3734c8a
Don't mention that matrix.org is likely to be popular
turt2live 0857e2c
Clarify why we pick popular servers over the one that created the room
turt2live File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,21 +14,104 @@ See the License for the specific language governing permissions and | |
limitations under the License. | ||
*/ | ||
|
||
import MatrixClientPeg from "./MatrixClientPeg"; | ||
|
||
export const host = "matrix.to"; | ||
export const baseUrl = `https://${host}`; | ||
|
||
export function makeEventPermalink(roomId, eventId) { | ||
return `${baseUrl}/#/${roomId}/${eventId}`; | ||
const serverCandidates = pickServerCandidates(roomId); | ||
return `${baseUrl}/#/${roomId}/${eventId}?${encodeServerCandidates(serverCandidates)}`; | ||
} | ||
|
||
export function makeUserPermalink(userId) { | ||
return `${baseUrl}/#/${userId}`; | ||
} | ||
|
||
export function makeRoomPermalink(roomId) { | ||
return `${baseUrl}/#/${roomId}`; | ||
const serverCandidates = pickServerCandidates(roomId); | ||
return `${baseUrl}/#/${roomId}?${encodeServerCandidates(serverCandidates)}`; | ||
} | ||
|
||
export function makeGroupPermalink(groupId) { | ||
return `${baseUrl}/#/${groupId}`; | ||
} | ||
|
||
export function encodeServerCandidates(candidates) { | ||
if (!candidates) return ''; | ||
return `via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}` | ||
} | ||
|
||
export function pickServerCandidates(roomId) { | ||
const client = MatrixClientPeg.get(); | ||
const room = client.getRoom(roomId); | ||
if (!room) return []; | ||
|
||
// Permalinks can have servers appended to them so that the user | ||
// receiving them can have a fighting chance at joining the room. | ||
// These servers are called "candidates" at this point because | ||
// it is unclear whether they are going to be useful to actually | ||
// join in the future. | ||
// | ||
// We pick 3 servers based on the following criteria: | ||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// Server 1: The highest power level user in the room, provided | ||
// they are at least PL 50. We don't calculate "what is a moderator" | ||
// here because it is less relevant for the vast majority of rooms. | ||
// We also want to ensure that we get an admin or high-ranking mod | ||
// as they are less likely to leave the room. If no user happens | ||
// to meet this criteria, we'll pick the most popular server in the | ||
// room. | ||
// | ||
// Server 2: The next most popular server in the room (in user | ||
// distribution). This will probably be matrix.org in most cases | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd avoid mentioning matrix.org here; it's very much a misfeature that public rooms are dominated by matrix.org users today, plus this ignores usages in private federations. |
||
// although it is certainly possible to be some other server. This | ||
// cannot be the same as Server 1. If no other servers are available | ||
// then we'll only return Server 1. | ||
// | ||
// Server 3: The next most popular server by user distribution. This | ||
// has the same rules as Server 2, with the added exception that it | ||
// must be unique from Server 1 and 2. | ||
|
||
// Rationale for popular servers: It's hard to get rid of people when | ||
// they keep flocking in from a particular server. Sure, the server could | ||
// be ACL'd in the future or for some reason be evicted from the room | ||
// however an event like that is unlikely the larger the room gets. | ||
|
||
// Note: Users receiving permalinks that happen to have all 3 potential | ||
// servers fail them (in terms of joining) are somewhat expected to hunt | ||
// down the person who gave them the link to ask for a participating server. | ||
// The receiving user can then manually append the known-good server to | ||
// the list and magically have the link work. | ||
|
||
const populationMap: {[server:string]:number} = {}; | ||
const highestPlUser = {userId:null, powerLevel: 0, serverName: null}; | ||
|
||
for (const member of room.getJoinedMembers()) { | ||
const serverName = member.userId.split(":").splice(1).join(":"); | ||
if (member.powerLevel > highestPlUser.powerLevel) { | ||
highestPlUser.userId = member.userId; | ||
highestPlUser.powerLevel = member.powerLevel; | ||
highestPlUser.serverName = serverName; | ||
} | ||
|
||
if (!populationMap[serverName]) populationMap[serverName] = 0; | ||
populationMap[serverName]++; | ||
} | ||
|
||
const candidates = []; | ||
if (highestPlUser.powerLevel >= 50) candidates.push(highestPlUser.serverName); | ||
|
||
const beforePopulation = candidates.length; | ||
const maxCandidates = 3; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this const should be outside of the method to make its purpose even clearer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! |
||
const serversByPopulation = Object.keys(populationMap) | ||
.sort((a, b) => populationMap[b] - populationMap[a]) | ||
.filter(a => !candidates.includes(a)); | ||
for (let i = beforePopulation; i <= maxCandidates; i++) { | ||
const idx = i - beforePopulation; | ||
if (idx >= serversByPopulation.length) break; | ||
candidates.push(serversByPopulation[idx]); | ||
} | ||
|
||
return candidates; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: this is unrelated to the changeset here, but has a 50% chance of being the solution to the build failing in earlier commits. It seems worth keeping the change regardless of build status imo.