Merge branch 'develop' into johannes/find-myself
This commit is contained in:
commit
6ee6accfc6
16 changed files with 392 additions and 774 deletions
|
@ -146,7 +146,7 @@ export default class PasswordReset {
|
|||
err.message = _t("Failed to verify email address: make sure you clicked the link in the email");
|
||||
} else if (err.httpStatus === 404) {
|
||||
err.message = _t(
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this homeserver.",
|
||||
);
|
||||
} else if (err.httpStatus) {
|
||||
err.message += ` (Status ${err.httpStatus})`;
|
||||
|
|
|
@ -63,7 +63,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
|
||||
inviteOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="invite-option"
|
||||
data-testid="invite-option"
|
||||
className="mx_SpacePanel_contextMenu_inviteButton"
|
||||
iconClassName="mx_SpacePanel_iconInvite"
|
||||
label={_t("Invite")}
|
||||
|
@ -85,7 +85,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
|
||||
settingsOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="settings-option"
|
||||
data-testid="settings-option"
|
||||
iconClassName="mx_SpacePanel_iconSettings"
|
||||
label={_t("Settings")}
|
||||
onClick={onSettingsClick}
|
||||
|
@ -102,7 +102,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
|
||||
leaveOption = (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="leave-option"
|
||||
data-testid="leave-option"
|
||||
iconClassName="mx_SpacePanel_iconLeave"
|
||||
className="mx_IconizedContextMenu_option_red"
|
||||
label={_t("Leave space")}
|
||||
|
@ -172,12 +172,12 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
|
||||
newRoomSection = (
|
||||
<>
|
||||
<div data-test-id="add-to-space-header" className="mx_SpacePanel_contextMenu_separatorLabel">
|
||||
<div data-testid="add-to-space-header" className="mx_SpacePanel_contextMenu_separatorLabel">
|
||||
{_t("Add")}
|
||||
</div>
|
||||
{canAddRooms && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-room-option"
|
||||
data-testid="new-room-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Room")}
|
||||
onClick={onNewRoomClick}
|
||||
|
@ -185,7 +185,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
)}
|
||||
{canAddVideoRooms && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-video-room-option"
|
||||
data-testid="new-video-room-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Video room")}
|
||||
onClick={onNewVideoRoomClick}
|
||||
|
@ -195,7 +195,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
|
|||
)}
|
||||
{canAddSubSpaces && (
|
||||
<IconizedContextMenuOption
|
||||
data-test-id="new-subspace-option"
|
||||
data-testid="new-subspace-option"
|
||||
iconClassName="mx_SpacePanel_iconPlus"
|
||||
label={_t("Space")}
|
||||
onClick={onNewSubspaceClick}
|
||||
|
|
|
@ -191,16 +191,19 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
public replaceEmoticon(caretPosition: DocumentPosition, regex: RegExp): number {
|
||||
const { model } = this.props;
|
||||
const range = model.startRange(caretPosition);
|
||||
// expand range max 8 characters backwards from caretPosition,
|
||||
// expand range max 9 characters backwards from caretPosition,
|
||||
// as a space to look for an emoticon
|
||||
let n = 8;
|
||||
let n = 9;
|
||||
range.expandBackwardsWhile((index, offset) => {
|
||||
const part = model.parts[index];
|
||||
n -= 1;
|
||||
return n >= 0 && [Type.Plain, Type.PillCandidate, Type.Newline].includes(part.type);
|
||||
});
|
||||
const emoticonMatch = regex.exec(range.text);
|
||||
if (emoticonMatch) {
|
||||
// ignore matches at start of proper substrings
|
||||
// so xd will not match if the string was "mixd 123456"
|
||||
// and we are lookinh at xd 123456 part of the string
|
||||
if (emoticonMatch && (n >= 0 || emoticonMatch.index !== 0)) {
|
||||
const query = emoticonMatch[1].replace("-", "");
|
||||
// try both exact match and lower-case, this means that xd won't match xD but :P will match :p
|
||||
const data = EMOTICON_TO_EMOJI.get(query) || EMOTICON_TO_EMOJI.get(query.toLowerCase());
|
||||
|
|
|
@ -73,7 +73,7 @@ const securityCardContent: Record<
|
|||
title: _t("Unverified session"),
|
||||
description: (
|
||||
<>
|
||||
<p>{_t(`This session doesn't support encryption, so it can't be verified.`)}</p>
|
||||
<p>{_t(`This session doesn't support encryption and thus can't be verified.`)}</p>
|
||||
<p>
|
||||
{_t(
|
||||
`You won't be able to participate in rooms where encryption is enabled when using this session.`,
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s was not given permission to send notifications - please try again",
|
||||
"Unable to enable Notifications": "Unable to enable Notifications",
|
||||
"This email address was not found": "This email address was not found",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.",
|
||||
"Your email address does not appear to be associated with a Matrix ID on this homeserver.": "Your email address does not appear to be associated with a Matrix ID on this homeserver.",
|
||||
"United Kingdom": "United Kingdom",
|
||||
"United States": "United States",
|
||||
"Afghanistan": "Afghanistan",
|
||||
|
@ -1826,7 +1826,7 @@
|
|||
"Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.": "Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.",
|
||||
"You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.": "You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.",
|
||||
"Unverified session": "Unverified session",
|
||||
"This session doesn't support encryption, so it can't be verified.": "This session doesn't support encryption, so it can't be verified.",
|
||||
"This session doesn't support encryption and thus can't be verified.": "This session doesn't support encryption and thus can't be verified.",
|
||||
"You won't be able to participate in rooms where encryption is enabled when using this session.": "You won't be able to participate in rooms where encryption is enabled when using this session.",
|
||||
"For best security and privacy, it is recommended to use Matrix clients that support encryption.": "For best security and privacy, it is recommended to use Matrix clients that support encryption.",
|
||||
"Inactive sessions": "Inactive sessions",
|
||||
|
@ -1842,7 +1842,6 @@
|
|||
"Your current session is ready for secure messaging.": "Your current session is ready for secure messaging.",
|
||||
"This session is ready for secure messaging.": "This session is ready for secure messaging.",
|
||||
"Verified session": "Verified session",
|
||||
"This session doesn't support encryption and thus can't be verified.": "This session doesn't support encryption and thus can't be verified.",
|
||||
"Verify your current session for enhanced secure messaging.": "Verify your current session for enhanced secure messaging.",
|
||||
"Verify or sign out from this session for best security and reliability.": "Verify or sign out from this session for best security and reliability.",
|
||||
"Verify session": "Verify session",
|
||||
|
|
|
@ -62,12 +62,12 @@ interface IStickyRoom {
|
|||
*/
|
||||
export class Algorithm extends EventEmitter {
|
||||
private _cachedRooms: ITagMap = {};
|
||||
private _cachedStickyRooms: ITagMap = {}; // a clone of the _cachedRooms, with the sticky room
|
||||
private _stickyRoom: IStickyRoom = null;
|
||||
private _lastStickyRoom: IStickyRoom = null; // only not-null when changing the sticky room
|
||||
private sortAlgorithms: ITagSortingMap;
|
||||
private listAlgorithms: IListOrderingMap;
|
||||
private algorithms: IOrderingAlgorithmMap;
|
||||
private _cachedStickyRooms: ITagMap | null = {}; // a clone of the _cachedRooms, with the sticky room
|
||||
private _stickyRoom: IStickyRoom | null = null;
|
||||
private _lastStickyRoom: IStickyRoom | null = null; // only not-null when changing the sticky room
|
||||
private sortAlgorithms: ITagSortingMap | null = null;
|
||||
private listAlgorithms: IListOrderingMap | null = null;
|
||||
private algorithms: IOrderingAlgorithmMap | null = null;
|
||||
private rooms: Room[] = [];
|
||||
private roomIdsToTags: {
|
||||
[roomId: string]: TagID[];
|
||||
|
@ -86,7 +86,7 @@ export class Algorithm extends EventEmitter {
|
|||
CallStore.instance.off(CallStoreEvent.ActiveCalls, this.onActiveCalls);
|
||||
}
|
||||
|
||||
public get stickyRoom(): Room {
|
||||
public get stickyRoom(): Room | null {
|
||||
return this._stickyRoom ? this._stickyRoom.room : null;
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ export class Algorithm extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
public getTagSorting(tagId: TagID): SortAlgorithm {
|
||||
public getTagSorting(tagId: TagID): SortAlgorithm | null {
|
||||
if (!this.sortAlgorithms) return null;
|
||||
return this.sortAlgorithms[tagId];
|
||||
}
|
||||
|
@ -132,6 +132,8 @@ export class Algorithm extends EventEmitter {
|
|||
public setTagSorting(tagId: TagID, sort: SortAlgorithm): void {
|
||||
if (!tagId) throw new Error("Tag ID must be defined");
|
||||
if (!sort) throw new Error("Algorithm must be defined");
|
||||
if (!this.sortAlgorithms) throw new Error("this.sortAlgorithms must be defined before calling setTagSorting");
|
||||
if (!this.algorithms) throw new Error("this.algorithms must be defined before calling setTagSorting");
|
||||
this.sortAlgorithms[tagId] = sort;
|
||||
|
||||
const algorithm: OrderingAlgorithm = this.algorithms[tagId];
|
||||
|
@ -141,7 +143,7 @@ export class Algorithm extends EventEmitter {
|
|||
this.recalculateActiveCallRooms(tagId);
|
||||
}
|
||||
|
||||
public getListOrdering(tagId: TagID): ListAlgorithm {
|
||||
public getListOrdering(tagId: TagID): ListAlgorithm | null {
|
||||
if (!this.listAlgorithms) return null;
|
||||
return this.listAlgorithms[tagId];
|
||||
}
|
||||
|
@ -149,6 +151,9 @@ export class Algorithm extends EventEmitter {
|
|||
public setListOrdering(tagId: TagID, order: ListAlgorithm): void {
|
||||
if (!tagId) throw new Error("Tag ID must be defined");
|
||||
if (!order) throw new Error("Algorithm must be defined");
|
||||
if (!this.sortAlgorithms) throw new Error("this.sortAlgorithms must be defined before calling setListOrdering");
|
||||
if (!this.listAlgorithms) throw new Error("this.listAlgorithms must be defined before calling setListOrdering");
|
||||
if (!this.algorithms) throw new Error("this.algorithms must be defined before calling setListOrdering");
|
||||
this.listAlgorithms[tagId] = order;
|
||||
|
||||
const algorithm = getListAlgorithmInstance(order, tagId, this.sortAlgorithms[tagId]);
|
||||
|
@ -160,12 +165,12 @@ export class Algorithm extends EventEmitter {
|
|||
this.recalculateActiveCallRooms(tagId);
|
||||
}
|
||||
|
||||
private updateStickyRoom(val: Room): void {
|
||||
private updateStickyRoom(val: Room | null): void {
|
||||
this.doUpdateStickyRoom(val);
|
||||
this._lastStickyRoom = null; // clear to indicate we're done changing
|
||||
}
|
||||
|
||||
private doUpdateStickyRoom(val: Room): void {
|
||||
private doUpdateStickyRoom(val: Room | null): void {
|
||||
if (val?.isSpaceRoom() && val.getMyMembership() !== "invite") {
|
||||
// no-op sticky rooms for spaces - they're effectively virtual rooms
|
||||
val = null;
|
||||
|
@ -237,6 +242,10 @@ export class Algorithm extends EventEmitter {
|
|||
// Lie to the algorithm and remove the room from it's field of view
|
||||
this.handleRoomUpdate(val, RoomUpdateCause.RoomRemoved);
|
||||
|
||||
// handleRoomUpdate may have modified this._stickyRoom. Convince the
|
||||
// compiler of this fact.
|
||||
this._stickyRoom = this.stickyRoomMightBeModified();
|
||||
|
||||
// Check for tag & position changes while we're here. We also check the room to ensure
|
||||
// it is still the same room.
|
||||
if (this._stickyRoom) {
|
||||
|
@ -284,6 +293,13 @@ export class Algorithm extends EventEmitter {
|
|||
this.emit(LIST_UPDATED_EVENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack to prevent Typescript claiming this._stickyRoom is always null.
|
||||
*/
|
||||
private stickyRoomMightBeModified(): IStickyRoom | null {
|
||||
return this._stickyRoom;
|
||||
}
|
||||
|
||||
private onActiveCalls = (): void => {
|
||||
// In case we're unsticking a room, sort it back into natural order
|
||||
this.recalculateStickyRoom();
|
||||
|
@ -310,7 +326,7 @@ export class Algorithm extends EventEmitter {
|
|||
* the call.
|
||||
* @param updatedTag The tag that was updated, if possible.
|
||||
*/
|
||||
protected recalculateStickyRoom(updatedTag: TagID = null): void {
|
||||
protected recalculateStickyRoom(updatedTag: TagID | null = null): void {
|
||||
// 🐉 Here be dragons.
|
||||
// This function does far too much for what it should, and is called by many places.
|
||||
// Not only is this responsible for ensuring the sticky room is held in place at all
|
||||
|
@ -336,14 +352,16 @@ export class Algorithm extends EventEmitter {
|
|||
if (updatedTag) {
|
||||
// Update the tag indicated by the caller, if possible. This is mostly to ensure
|
||||
// our cache is up to date.
|
||||
this._cachedStickyRooms[updatedTag] = [...this.cachedRooms[updatedTag]]; // shallow clone
|
||||
if (this._cachedStickyRooms) {
|
||||
this._cachedStickyRooms[updatedTag] = [...this.cachedRooms[updatedTag]]; // shallow clone
|
||||
}
|
||||
}
|
||||
|
||||
// Now try to insert the sticky room, if we need to.
|
||||
// We need to if there's no updated tag (we regenned the whole cache) or if the tag
|
||||
// we might have updated from the cache is also our sticky room.
|
||||
const sticky = this._stickyRoom;
|
||||
if (!updatedTag || updatedTag === sticky.tag) {
|
||||
if (sticky && (!updatedTag || updatedTag === sticky.tag) && this._cachedStickyRooms) {
|
||||
this._cachedStickyRooms[sticky.tag].splice(sticky.position, 0, sticky.room);
|
||||
}
|
||||
|
||||
|
@ -362,7 +380,7 @@ export class Algorithm extends EventEmitter {
|
|||
*
|
||||
* @param updatedTag The tag that was updated, if possible.
|
||||
*/
|
||||
protected recalculateActiveCallRooms(updatedTag: TagID = null): void {
|
||||
protected recalculateActiveCallRooms(updatedTag: TagID | null = null): void {
|
||||
if (!updatedTag) {
|
||||
// Assume all tags need updating
|
||||
// We're not modifying the map here, so can safely rely on the cached values
|
||||
|
@ -379,7 +397,7 @@ export class Algorithm extends EventEmitter {
|
|||
if (CallStore.instance.activeCalls.size) {
|
||||
// We operate on the sticky rooms map
|
||||
if (!this._cachedStickyRooms) this.initCachedStickyRooms();
|
||||
const rooms = this._cachedStickyRooms[updatedTag];
|
||||
const rooms = this._cachedStickyRooms![updatedTag];
|
||||
|
||||
const activeRoomIds = new Set([...CallStore.instance.activeCalls].map((call) => call.roomId));
|
||||
const activeRooms: Room[] = [];
|
||||
|
@ -390,7 +408,7 @@ export class Algorithm extends EventEmitter {
|
|||
}
|
||||
|
||||
// Stick rooms with active calls to the top
|
||||
this._cachedStickyRooms[updatedTag] = [...activeRooms, ...inactiveRooms];
|
||||
this._cachedStickyRooms![updatedTag] = [...activeRooms, ...inactiveRooms];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,7 +656,7 @@ export class Algorithm extends EventEmitter {
|
|||
}
|
||||
|
||||
// Like above, update the reference to the sticky room if we need to
|
||||
if (hasTags && isSticky) {
|
||||
if (hasTags && isSticky && this._stickyRoom) {
|
||||
// Go directly in and set the sticky room's new reference, being careful not
|
||||
// to trigger a sticky room update ourselves.
|
||||
this._stickyRoom.room = room;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue