Merge branch 'develop' into travis/room-list/enable
This commit is contained in:
commit
75dbd5f1d4
67 changed files with 2347 additions and 823 deletions
|
@ -89,11 +89,12 @@ export class ListLayout {
|
|||
return 5 + RESIZER_BOX_FACTOR;
|
||||
}
|
||||
|
||||
public setVisibleTilesWithin(diff: number, maxPossible: number) {
|
||||
if (this.visibleTiles > maxPossible) {
|
||||
this.visibleTiles = maxPossible + diff;
|
||||
public setVisibleTilesWithin(newVal: number, maxPossible: number) {
|
||||
maxPossible = maxPossible + RESIZER_BOX_FACTOR;
|
||||
if (newVal > maxPossible) {
|
||||
this.visibleTiles = maxPossible;
|
||||
} else {
|
||||
this.visibleTiles += diff;
|
||||
this.visibleTiles = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,10 +109,6 @@ export class ListLayout {
|
|||
return this.tilesToPixels(Math.min(maxTiles, n)) + padding;
|
||||
}
|
||||
|
||||
public tilesWithResizerBoxFactor(n: number): number {
|
||||
return n + RESIZER_BOX_FACTOR;
|
||||
}
|
||||
|
||||
public tilesWithPadding(n: number, paddingPx: number): number {
|
||||
return this.pixelsToTiles(this.tilesToPixelsWithPadding(n, paddingPx));
|
||||
}
|
||||
|
|
73
src/stores/room-list/RoomListLayoutStore.ts
Normal file
73
src/stores/room-list/RoomListLayoutStore.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { TagID } from "./models";
|
||||
import { ListLayout } from "./ListLayout";
|
||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
|
||||
interface IState {}
|
||||
|
||||
export default class RoomListLayoutStore extends AsyncStoreWithClient<IState> {
|
||||
private static internalInstance: RoomListLayoutStore;
|
||||
|
||||
private readonly layoutMap = new Map<TagID, ListLayout>();
|
||||
|
||||
constructor() {
|
||||
super(defaultDispatcher);
|
||||
}
|
||||
|
||||
public static get instance(): RoomListLayoutStore {
|
||||
if (!RoomListLayoutStore.internalInstance) {
|
||||
RoomListLayoutStore.internalInstance = new RoomListLayoutStore();
|
||||
}
|
||||
return RoomListLayoutStore.internalInstance;
|
||||
}
|
||||
|
||||
public ensureLayoutExists(tagId: TagID) {
|
||||
if (!this.layoutMap.has(tagId)) {
|
||||
this.layoutMap.set(tagId, new ListLayout(tagId));
|
||||
}
|
||||
}
|
||||
|
||||
public getLayoutFor(tagId: TagID): ListLayout {
|
||||
if (!this.layoutMap.has(tagId)) {
|
||||
this.layoutMap.set(tagId, new ListLayout(tagId));
|
||||
}
|
||||
return this.layoutMap.get(tagId);
|
||||
}
|
||||
|
||||
// Note: this primarily exists for debugging, and isn't really intended to be used by anything.
|
||||
public async resetLayouts() {
|
||||
console.warn("Resetting layouts for room list");
|
||||
for (const layout of this.layoutMap.values()) {
|
||||
layout.reset();
|
||||
}
|
||||
}
|
||||
|
||||
protected async onNotReady(): Promise<any> {
|
||||
// On logout, clear the map.
|
||||
this.layoutMap.clear();
|
||||
}
|
||||
|
||||
// We don't need this function, but our contract says we do
|
||||
protected async onAction(payload: ActionPayload): Promise<any> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
window.mx_RoomListLayoutStore = RoomListLayoutStore.instance;
|
|
@ -32,6 +32,7 @@ import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
|||
import { EffectiveMembership, getEffectiveMembership } from "./membership";
|
||||
import { ListLayout } from "./ListLayout";
|
||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||
import RoomListLayoutStore from "./RoomListLayoutStore";
|
||||
|
||||
interface IState {
|
||||
tagsEnabled?: boolean;
|
||||
|
@ -50,6 +51,7 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
|||
private algorithm = new Algorithm();
|
||||
private filterConditions: IFilterCondition[] = [];
|
||||
private tagWatcher = new TagWatcher(this);
|
||||
private layoutMap: Map<TagID, ListLayout> = new Map<TagID, ListLayout>();
|
||||
|
||||
private readonly watchedSettings = [
|
||||
'feature_custom_tags',
|
||||
|
@ -435,6 +437,8 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
|||
for (const tagId of OrderedDefaultTagIDs) {
|
||||
sorts[tagId] = this.calculateTagSorting(tagId);
|
||||
orders[tagId] = this.calculateListOrder(tagId);
|
||||
|
||||
RoomListLayoutStore.instance.ensureLayoutExists(tagId);
|
||||
}
|
||||
|
||||
if (this.state.tagsEnabled) {
|
||||
|
@ -453,15 +457,6 @@ export class RoomListStore2 extends AsyncStore<ActionPayload> {
|
|||
this.emit(LISTS_UPDATE_EVENT, this);
|
||||
}
|
||||
|
||||
// Note: this primarily exists for debugging, and isn't really intended to be used by anything.
|
||||
public async resetLayouts() {
|
||||
console.warn("Resetting layouts for room list");
|
||||
for (const tagId of Object.keys(this.orderedLists)) {
|
||||
new ListLayout(tagId).reset();
|
||||
}
|
||||
await this.regenerateAllLists();
|
||||
}
|
||||
|
||||
public addFilter(filter: IFilterCondition): void {
|
||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||
console.log("Adding filter condition:", filter);
|
||||
|
|
|
@ -41,6 +41,17 @@ import { getListAlgorithmInstance } from "./list-ordering";
|
|||
*/
|
||||
export const LIST_UPDATED_EVENT = "list_updated_event";
|
||||
|
||||
// These are the causes which require a room to be known in order for us to handle them. If
|
||||
// a cause in this list is raised and we don't know about the room, we don't handle the update.
|
||||
//
|
||||
// Note: these typically happen when a new room is coming in, such as the user creating or
|
||||
// joining the room. For these cases, we need to know about the room prior to handling it otherwise
|
||||
// we'll make bad assumptions.
|
||||
const CAUSES_REQUIRING_ROOM = [
|
||||
RoomUpdateCause.Timeline,
|
||||
RoomUpdateCause.ReadReceipt,
|
||||
];
|
||||
|
||||
interface IStickyRoom {
|
||||
room: Room;
|
||||
position: number;
|
||||
|
@ -655,24 +666,36 @@ export class Algorithm extends EventEmitter {
|
|||
cause = RoomUpdateCause.PossibleTagChange;
|
||||
}
|
||||
|
||||
// If we have tags for a room and don't have the room referenced, the room reference
|
||||
// probably changed. We need to swap out the problematic reference.
|
||||
if (hasTags && !this.rooms.includes(room) && !isSticky) {
|
||||
console.warn(`${room.roomId} is missing from room array but is known - trying to find duplicate`);
|
||||
// Check to see if the room is known first
|
||||
let knownRoomRef = this.rooms.includes(room);
|
||||
if (hasTags && !knownRoomRef) {
|
||||
console.warn(`${room.roomId} might be a reference change - attempting to update reference`);
|
||||
this.rooms = this.rooms.map(r => r.roomId === room.roomId ? room : r);
|
||||
|
||||
// Sanity check
|
||||
if (!this.rooms.includes(room)) {
|
||||
throw new Error(`Failed to replace ${room.roomId} with an updated reference`);
|
||||
knownRoomRef = this.rooms.includes(room);
|
||||
if (!knownRoomRef) {
|
||||
console.warn(`${room.roomId} is still not referenced. It may be sticky.`);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have tags for a room and don't have the room referenced, something went horribly
|
||||
// wrong - the reference should have been updated above.
|
||||
if (hasTags && !knownRoomRef && !isSticky) {
|
||||
throw new Error(`${room.roomId} is missing from room array but is known - trying to find duplicate`);
|
||||
}
|
||||
|
||||
// Like above, update the reference to the sticky room if we need to
|
||||
if (hasTags && isSticky) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// If after all that we're still a NewRoom update, add the room if applicable.
|
||||
// We don't do this for the sticky room (because it causes duplication issues)
|
||||
// or if we know about the reference (as it should be replaced).
|
||||
if (cause === RoomUpdateCause.NewRoom && !isSticky && !knownRoomRef) {
|
||||
this.rooms.push(room);
|
||||
}
|
||||
}
|
||||
|
||||
if (cause === RoomUpdateCause.PossibleTagChange) {
|
||||
|
@ -687,6 +710,7 @@ export class Algorithm extends EventEmitter {
|
|||
const algorithm: OrderingAlgorithm = this.algorithms[rmTag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${rmTag}`);
|
||||
await algorithm.handleRoomUpdate(room, RoomUpdateCause.RoomRemoved);
|
||||
this.cachedRooms[rmTag] = algorithm.orderedRooms;
|
||||
}
|
||||
for (const addTag of diff.added) {
|
||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||
|
@ -694,6 +718,7 @@ export class Algorithm extends EventEmitter {
|
|||
const algorithm: OrderingAlgorithm = this.algorithms[addTag];
|
||||
if (!algorithm) throw new Error(`No algorithm for ${addTag}`);
|
||||
await algorithm.handleRoomUpdate(room, RoomUpdateCause.NewRoom);
|
||||
this.cachedRooms[addTag] = algorithm.orderedRooms;
|
||||
}
|
||||
|
||||
// Update the tag map so we don't regen it in a moment
|
||||
|
@ -738,6 +763,11 @@ export class Algorithm extends EventEmitter {
|
|||
}
|
||||
|
||||
if (!this.roomIdsToTags[room.roomId]) {
|
||||
if (CAUSES_REQUIRING_ROOM.includes(cause)) {
|
||||
console.warn(`Skipping tag update for ${room.roomId} because we don't know about the room`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14035
|
||||
console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`);
|
||||
|
||||
|
|
|
@ -160,7 +160,10 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
|||
this.cachedOrderedRooms.splice(this.indices[category], 0, room); // splice in the new room (pre-adjusted)
|
||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||
const roomIdx = this.getRoomIndex(room);
|
||||
if (roomIdx === -1) return false; // no change
|
||||
if (roomIdx === -1) {
|
||||
console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
|
||||
return false; // no change
|
||||
}
|
||||
const oldCategory = this.getCategoryFromIndices(roomIdx, this.indices);
|
||||
this.alterCategoryPositionBy(oldCategory, -1, this.indices);
|
||||
this.cachedOrderedRooms.splice(roomIdx, 1); // remove the room
|
||||
|
@ -169,15 +172,6 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
|
|||
}
|
||||
}
|
||||
|
||||
private getRoomIndex(room: Room): number {
|
||||
let roomIdx = this.cachedOrderedRooms.indexOf(room);
|
||||
if (roomIdx === -1) { // can only happen if the js-sdk's store goes sideways.
|
||||
console.warn(`Degrading performance to find missing room in "${this.tagId}": ${room.roomId}`);
|
||||
roomIdx = this.cachedOrderedRooms.findIndex(r => r.roomId === room.roomId);
|
||||
}
|
||||
return roomIdx;
|
||||
}
|
||||
|
||||
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
|
||||
try {
|
||||
await this.updateLock.acquireAsync();
|
||||
|
|
|
@ -50,8 +50,12 @@ export class NaturalAlgorithm extends OrderingAlgorithm {
|
|||
if (cause === RoomUpdateCause.NewRoom) {
|
||||
this.cachedOrderedRooms.push(room);
|
||||
} else if (cause === RoomUpdateCause.RoomRemoved) {
|
||||
const idx = this.cachedOrderedRooms.indexOf(room);
|
||||
if (idx >= 0) this.cachedOrderedRooms.splice(idx, 1);
|
||||
const idx = this.getRoomIndex(room);
|
||||
if (idx >= 0) {
|
||||
this.cachedOrderedRooms.splice(idx, 1);
|
||||
} else {
|
||||
console.warn(`Tried to remove unknown room from ${this.tagId}: ${room.roomId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimize this to avoid useless operations: https://github.com/vector-im/riot-web/issues/14035
|
||||
|
|
|
@ -70,4 +70,13 @@ export abstract class OrderingAlgorithm {
|
|||
* @returns True if the update requires the Algorithm to update the presentation layers.
|
||||
*/
|
||||
public abstract handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean>;
|
||||
|
||||
protected getRoomIndex(room: Room): number {
|
||||
let roomIdx = this.cachedOrderedRooms.indexOf(room);
|
||||
if (roomIdx === -1) { // can only happen if the js-sdk's store goes sideways.
|
||||
console.warn(`Degrading performance to find missing room in "${this.tagId}": ${room.roomId}`);
|
||||
roomIdx = this.cachedOrderedRooms.findIndex(r => r.roomId === room.roomId);
|
||||
}
|
||||
return roomIdx;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|||
import { _t } from "../../../languageHandler";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import ReplyThread from "../../../components/views/elements/ReplyThread";
|
||||
import { sanitizedHtmlNodeInnerText } from "../../../HtmlUtils";
|
||||
|
||||
export class MessageEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||
|
@ -36,14 +37,27 @@ export class MessageEventPreview implements IPreview {
|
|||
const msgtype = eventContent['msgtype'];
|
||||
if (!body || !msgtype) return null; // invalid event, no preview
|
||||
|
||||
const hasHtml = eventContent.format === "org.matrix.custom.html" && eventContent.formatted_body;
|
||||
if (hasHtml) {
|
||||
body = eventContent.formatted_body;
|
||||
}
|
||||
|
||||
// XXX: Newer relations have a getRelation() function which is not compatible with replies.
|
||||
const mRelatesTo = event.getWireContent()['m.relates_to'];
|
||||
if (mRelatesTo && mRelatesTo['m.in_reply_to']) {
|
||||
// If this is a reply, get the real reply and use that
|
||||
body = (ReplyThread.stripPlainReply(body) || '').trim();
|
||||
if (hasHtml) {
|
||||
body = (ReplyThread.stripHTMLReply(body) || '').trim();
|
||||
} else {
|
||||
body = (ReplyThread.stripPlainReply(body) || '').trim();
|
||||
}
|
||||
if (!body) return null; // invalid event, no preview
|
||||
}
|
||||
|
||||
if (hasHtml) {
|
||||
body = sanitizedHtmlNodeInnerText(body);
|
||||
}
|
||||
|
||||
if (msgtype === 'm.emote') {
|
||||
return _t("%(senderName)s %(emote)s", {senderName: getSenderName(event), emote: body});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue