Merge pull request #6153 from matrix-org/t3chguy/ts/123

pull/21833/head
Michael Telatynski 2021-06-07 12:40:41 +01:00 committed by GitHub
commit 7abf680e51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 274 additions and 237 deletions

View File

@ -1,6 +1,5 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015-2021 The Matrix.org Foundation C.I.C.
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,52 +14,61 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from "react"; import React, { ComponentType } from "react";
import * as sdk from './index'; import * as sdk from './index';
import PropTypes from 'prop-types';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import { IDialogProps } from "./components/views/dialogs/IDialogProps";
type AsyncImport<T> = { default: T };
interface IProps extends IDialogProps {
// A promise which resolves with the real component
prom: Promise<ComponentType | AsyncImport<ComponentType>>;
}
interface IState {
component?: ComponentType;
error?: Error;
}
/** /**
* Wrap an asynchronous loader function with a react component which shows a * Wrap an asynchronous loader function with a react component which shows a
* spinner until the real component loads. * spinner until the real component loads.
*/ */
export default class AsyncWrapper extends React.Component { export default class AsyncWrapper extends React.Component<IProps, IState> {
static propTypes = { private unmounted = false;
/** A promise which resolves with the real component
*/
prom: PropTypes.object.isRequired,
};
state = { public state = {
component: null, component: null,
error: null, error: null,
}; };
componentDidMount() { componentDidMount() {
this._unmounted = false;
// XXX: temporary logging to try to diagnose // XXX: temporary logging to try to diagnose
// https://github.com/vector-im/element-web/issues/3148 // https://github.com/vector-im/element-web/issues/3148
console.log('Starting load of AsyncWrapper for modal'); console.log('Starting load of AsyncWrapper for modal');
this.props.prom.then((result) => { this.props.prom.then((result) => {
if (this._unmounted) { if (this.unmounted) return;
return;
}
// Take the 'default' member if it's there, then we support // Take the 'default' member if it's there, then we support
// passing in just an import()ed module, since ES6 async import // passing in just an import()ed module, since ES6 async import
// always returns a module *namespace*. // always returns a module *namespace*.
const component = result.default ? result.default : result; const component = (result as AsyncImport<ComponentType>).default
this.setState({component}); ? (result as AsyncImport<ComponentType>).default
: result as ComponentType;
this.setState({ component });
}).catch((e) => { }).catch((e) => {
console.warn('AsyncWrapper promise failed', e); console.warn('AsyncWrapper promise failed', e);
this.setState({error: e}); this.setState({ error: e });
}); });
} }
componentWillUnmount() { componentWillUnmount() {
this._unmounted = true; this.unmounted = true;
} }
_onWrapperCancelClick = () => { private onWrapperCancelClick = () => {
this.props.onFinished(false); this.props.onFinished(false);
}; };
@ -71,12 +79,10 @@ export default class AsyncWrapper extends React.Component {
} else if (this.state.error) { } else if (this.state.error) {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished} return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}>
title={_t("Error")} { _t("Unable to load! Check your network connectivity and try again.") }
>
{_t("Unable to load! Check your network connectivity and try again.")}
<DialogButtons primaryButton={_t("Dismiss")} <DialogButtons primaryButton={_t("Dismiss")}
onPrimaryButtonClick={this._onWrapperCancelClick} onPrimaryButtonClick={this.onWrapperCancelClick}
hasCancel={false} hasCancel={false}
/> />
</BaseDialog>; </BaseDialog>;

View File

@ -1,6 +1,5 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015-2021 The Matrix.org Foundation C.I.C.
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,35 +14,37 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import { EventStatus } from 'matrix-js-sdk/src/models/event';
export default class Resend { export default class Resend {
static resendUnsentEvents(room) { static resendUnsentEvents(room: Room): Promise<void[]> {
return Promise.all(room.getPendingEvents().filter(function(ev) { return Promise.all(room.getPendingEvents().filter(function(ev: MatrixEvent) {
return ev.status === EventStatus.NOT_SENT; return ev.status === EventStatus.NOT_SENT;
}).map(function(event) { }).map(function(event: MatrixEvent) {
return Resend.resend(event); return Resend.resend(event);
})); }));
} }
static cancelUnsentEvents(room) { static cancelUnsentEvents(room: Room): void {
room.getPendingEvents().filter(function(ev) { room.getPendingEvents().filter(function(ev: MatrixEvent) {
return ev.status === EventStatus.NOT_SENT; return ev.status === EventStatus.NOT_SENT;
}).forEach(function(event) { }).forEach(function(event: MatrixEvent) {
Resend.removeFromQueue(event); Resend.removeFromQueue(event);
}); });
} }
static resend(event) { static resend(event: MatrixEvent): Promise<void> {
const room = MatrixClientPeg.get().getRoom(event.getRoomId()); const room = MatrixClientPeg.get().getRoom(event.getRoomId());
return MatrixClientPeg.get().resendEvent(event, room).then(function(res) { return MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
dis.dispatch({ dis.dispatch({
action: 'message_sent', action: 'message_sent',
event: event, event: event,
}); });
}, function(err) { }, function(err: Error) {
// XXX: temporary logging to try to diagnose // XXX: temporary logging to try to diagnose
// https://github.com/vector-im/element-web/issues/3148 // https://github.com/vector-im/element-web/issues/3148
console.log('Resend got send failure: ' + err.name + '(' + err + ')'); console.log('Resend got send failure: ' + err.name + '(' + err + ')');
@ -55,7 +56,7 @@ export default class Resend {
}); });
} }
static removeFromQueue(event) { static removeFromQueue(event: MatrixEvent): void {
MatrixClientPeg.get().cancelPendingEvent(event); MatrixClientPeg.get().cancelPendingEvent(event);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -24,12 +24,12 @@ limitations under the License.
* A similar thing could also be achieved via `pushState` with a state object, * A similar thing could also be achieved via `pushState` with a state object,
* but keeping it separate like this seems easier in case we do want to extend. * but keeping it separate like this seems easier in case we do want to extend.
*/ */
const aliasToIDMap = new Map(); const aliasToIDMap = new Map<string, string>();
export function storeRoomAliasInCache(alias, id) { export function storeRoomAliasInCache(alias: string, id: string): void {
aliasToIDMap.set(alias, id); aliasToIDMap.set(alias, id);
} }
export function getCachedRoomIDForAlias(alias) { export function getCachedRoomIDForAlias(alias: string): string {
return aliasToIDMap.get(alias); return aliasToIDMap.get(alias);
} }

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2017 New Vector Ltd Copyright 2017, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import dis from '../dispatcher/dispatcher'; import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import dis from "../dispatcher/dispatcher";
import {ActionPayload} from "../dispatcher/payloads";
// TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events // TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events
// become dispatches in the same place. // become dispatches in the same place.
@ -27,7 +33,7 @@ import dis from '../dispatcher/dispatcher';
* @param {string} prevState the previous sync state. * @param {string} prevState the previous sync state.
* @returns {Object} an action of type MatrixActions.sync. * @returns {Object} an action of type MatrixActions.sync.
*/ */
function createSyncAction(matrixClient, state, prevState) { function createSyncAction(matrixClient: MatrixClient, state: string, prevState: string): ActionPayload {
return { return {
action: 'MatrixActions.sync', action: 'MatrixActions.sync',
state, state,
@ -53,7 +59,7 @@ function createSyncAction(matrixClient, state, prevState) {
* @param {MatrixEvent} accountDataEvent the account data event. * @param {MatrixEvent} accountDataEvent the account data event.
* @returns {AccountDataAction} an action of type MatrixActions.accountData. * @returns {AccountDataAction} an action of type MatrixActions.accountData.
*/ */
function createAccountDataAction(matrixClient, accountDataEvent) { function createAccountDataAction(matrixClient: MatrixClient, accountDataEvent: MatrixEvent): ActionPayload {
return { return {
action: 'MatrixActions.accountData', action: 'MatrixActions.accountData',
event: accountDataEvent, event: accountDataEvent,
@ -81,7 +87,11 @@ function createAccountDataAction(matrixClient, accountDataEvent) {
* @param {Room} room the room where account data was changed * @param {Room} room the room where account data was changed
* @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData. * @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData.
*/ */
function createRoomAccountDataAction(matrixClient, accountDataEvent, room) { function createRoomAccountDataAction(
matrixClient: MatrixClient,
accountDataEvent: MatrixEvent,
room: Room,
): ActionPayload {
return { return {
action: 'MatrixActions.Room.accountData', action: 'MatrixActions.Room.accountData',
event: accountDataEvent, event: accountDataEvent,
@ -106,7 +116,7 @@ function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
* @param {Room} room the Room that was stored. * @param {Room} room the Room that was stored.
* @returns {RoomAction} an action of type `MatrixActions.Room`. * @returns {RoomAction} an action of type `MatrixActions.Room`.
*/ */
function createRoomAction(matrixClient, room) { function createRoomAction(matrixClient: MatrixClient, room: Room): ActionPayload {
return { action: 'MatrixActions.Room', room }; return { action: 'MatrixActions.Room', room };
} }
@ -127,7 +137,7 @@ function createRoomAction(matrixClient, room) {
* @param {Room} room the Room whose tags were changed. * @param {Room} room the Room whose tags were changed.
* @returns {RoomTagsAction} an action of type `MatrixActions.Room.tags`. * @returns {RoomTagsAction} an action of type `MatrixActions.Room.tags`.
*/ */
function createRoomTagsAction(matrixClient, roomTagsEvent, room) { function createRoomTagsAction(matrixClient: MatrixClient, roomTagsEvent: MatrixEvent, room: Room): ActionPayload {
return { action: 'MatrixActions.Room.tags', room }; return { action: 'MatrixActions.Room.tags', room };
} }
@ -140,7 +150,7 @@ function createRoomTagsAction(matrixClient, roomTagsEvent, room) {
* @param {Room} room the room the receipt happened in. * @param {Room} room the room the receipt happened in.
* @returns {Object} an action of type MatrixActions.Room.receipt. * @returns {Object} an action of type MatrixActions.Room.receipt.
*/ */
function createRoomReceiptAction(matrixClient, event, room) { function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent, room: Room): ActionPayload {
return { return {
action: 'MatrixActions.Room.receipt', action: 'MatrixActions.Room.receipt',
event, event,
@ -178,7 +188,17 @@ function createRoomReceiptAction(matrixClient, event, room) {
* @param {EventTimeline} data.timeline the timeline being altered. * @param {EventTimeline} data.timeline the timeline being altered.
* @returns {RoomTimelineAction} an action of type `MatrixActions.Room.timeline`. * @returns {RoomTimelineAction} an action of type `MatrixActions.Room.timeline`.
*/ */
function createRoomTimelineAction(matrixClient, timelineEvent, room, toStartOfTimeline, removed, data) { function createRoomTimelineAction(
matrixClient: MatrixClient,
timelineEvent: MatrixEvent,
room: Room,
toStartOfTimeline: boolean,
removed: boolean,
data: {
liveEvent: boolean;
timeline: EventTimeline;
},
): ActionPayload {
return { return {
action: 'MatrixActions.Room.timeline', action: 'MatrixActions.Room.timeline',
event: timelineEvent, event: timelineEvent,
@ -208,8 +228,13 @@ function createRoomTimelineAction(matrixClient, timelineEvent, room, toStartOfTi
* @param {string} oldMembership the previous membership, can be null. * @param {string} oldMembership the previous membership, can be null.
* @returns {RoomMembershipAction} an action of type `MatrixActions.Room.myMembership`. * @returns {RoomMembershipAction} an action of type `MatrixActions.Room.myMembership`.
*/ */
function createSelfMembershipAction(matrixClient, room, membership, oldMembership) { function createSelfMembershipAction(
return { action: 'MatrixActions.Room.myMembership', room, membership, oldMembership}; matrixClient: MatrixClient,
room: Room,
membership: string,
oldMembership: string,
): ActionPayload {
return { action: 'MatrixActions.Room.myMembership', room, membership, oldMembership };
} }
/** /**
@ -228,61 +253,65 @@ function createSelfMembershipAction(matrixClient, room, membership, oldMembershi
* @param {MatrixEvent} event the matrix event that was decrypted. * @param {MatrixEvent} event the matrix event that was decrypted.
* @returns {EventDecryptedAction} an action of type `MatrixActions.Event.decrypted`. * @returns {EventDecryptedAction} an action of type `MatrixActions.Event.decrypted`.
*/ */
function createEventDecryptedAction(matrixClient, event) { function createEventDecryptedAction(matrixClient: MatrixClient, event: MatrixEvent): ActionPayload {
return { action: 'MatrixActions.Event.decrypted', event }; return { action: 'MatrixActions.Event.decrypted', event };
} }
type Listener = () => void;
type ActionCreator = (matrixClient: MatrixClient, ...args: any) => ActionPayload;
// A list of callbacks to call to unregister all listeners added
let matrixClientListenersStop: Listener[] = [];
/**
* Start listening to events of type eventName on matrixClient and when they are emitted,
* dispatch an action created by the actionCreator function.
* @param {MatrixClient} matrixClient a MatrixClient to register a listener with.
* @param {string} eventName the event to listen to on MatrixClient.
* @param {function} actionCreator a function that should return an action to dispatch
* when given the MatrixClient as an argument as well as
* arguments emitted in the MatrixClient event.
*/
function addMatrixClientListener(matrixClient: MatrixClient, eventName: string, actionCreator: ActionCreator): void {
const listener: Listener = (...args) => {
const payload = actionCreator(matrixClient, ...args);
if (payload) {
dis.dispatch(payload, true);
}
};
matrixClient.on(eventName, listener);
matrixClientListenersStop.push(() => {
matrixClient.removeListener(eventName, listener);
});
}
/** /**
* This object is responsible for dispatching actions when certain events are emitted by * This object is responsible for dispatching actions when certain events are emitted by
* the given MatrixClient. * the given MatrixClient.
*/ */
export default { export default {
// A list of callbacks to call to unregister all listeners added
_matrixClientListenersStop: [],
/** /**
* Start listening to certain events from the MatrixClient and dispatch actions when * Start listening to certain events from the MatrixClient and dispatch actions when
* they are emitted. * they are emitted.
* @param {MatrixClient} matrixClient the MatrixClient to listen to events from * @param {MatrixClient} matrixClient the MatrixClient to listen to events from
*/ */
start(matrixClient) { start(matrixClient: MatrixClient) {
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction); addMatrixClientListener(matrixClient, 'sync', createSyncAction);
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction); addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
this._addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction); addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
this._addMatrixClientListener(matrixClient, 'Room', createRoomAction); addMatrixClientListener(matrixClient, 'Room', createRoomAction);
this._addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction); addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
this._addMatrixClientListener(matrixClient, 'Room.receipt', createRoomReceiptAction); addMatrixClientListener(matrixClient, 'Room.receipt', createRoomReceiptAction);
this._addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction); addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
this._addMatrixClientListener(matrixClient, 'Room.myMembership', createSelfMembershipAction); addMatrixClientListener(matrixClient, 'Room.myMembership', createSelfMembershipAction);
this._addMatrixClientListener(matrixClient, 'Event.decrypted', createEventDecryptedAction); addMatrixClientListener(matrixClient, 'Event.decrypted', createEventDecryptedAction);
},
/**
* Start listening to events of type eventName on matrixClient and when they are emitted,
* dispatch an action created by the actionCreator function.
* @param {MatrixClient} matrixClient a MatrixClient to register a listener with.
* @param {string} eventName the event to listen to on MatrixClient.
* @param {function} actionCreator a function that should return an action to dispatch
* when given the MatrixClient as an argument as well as
* arguments emitted in the MatrixClient event.
*/
_addMatrixClientListener(matrixClient, eventName, actionCreator) {
const listener = (...args) => {
const payload = actionCreator(matrixClient, ...args);
if (payload) {
dis.dispatch(payload, true);
}
};
matrixClient.on(eventName, listener);
this._matrixClientListenersStop.push(() => {
matrixClient.removeListener(eventName, listener);
});
}, },
/** /**
* Stop listening to events. * Stop listening to events.
*/ */
stop() { stop() {
this._matrixClientListenersStop.forEach((stopListener) => stopListener()); matrixClientListenersStop.forEach((stopListener) => stopListener());
matrixClientListenersStop = [];
}, },
}; };

View File

@ -15,5 +15,5 @@ limitations under the License.
*/ */
export interface IDialogProps { export interface IDialogProps {
onFinished: (bool) => void; onFinished(...args: any): void;
} }

View File

@ -35,7 +35,7 @@ export interface MatrixProfile {
export interface CrawlerCheckpoint { export interface CrawlerCheckpoint {
roomId: string; roomId: string;
token: string; token: string;
fullCrawl: boolean; fullCrawl?: boolean;
direction: string; direction: string;
} }
@ -73,14 +73,14 @@ export interface EventAndProfile {
export interface LoadArgs { export interface LoadArgs {
roomId: string; roomId: string;
limit: number; limit: number;
fromEvent: string; fromEvent?: string;
direction: string; direction?: string;
} }
export interface IndexStats { export interface IndexStats {
size: number; size: number;
event_count: number; eventCount: number;
room_count: number; roomCount: number;
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,33 +14,42 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { EventEmitter } from "events";
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
import { RoomState } from 'matrix-js-sdk/src/models/room-state';
import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
import PlatformPeg from "../PlatformPeg"; import PlatformPeg from "../PlatformPeg";
import {MatrixClientPeg} from "../MatrixClientPeg"; import { MatrixClientPeg } from "../MatrixClientPeg";
import {RoomMember} from 'matrix-js-sdk/src/models/room-member'; import { sleep } from "../utils/promise";
import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline';
import {sleep} from "../utils/promise";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import {EventEmitter} from "events"; import { SettingLevel } from "../settings/SettingLevel";
import {SettingLevel} from "../settings/SettingLevel"; import {CrawlerCheckpoint, LoadArgs, SearchArgs} from "./BaseEventIndexManager";
// The time in ms that the crawler will wait loop iterations if there
// have not been any checkpoints to consume in the last iteration.
const CRAWLER_IDLE_TIME = 5000;
// The maximum number of events our crawler should fetch in a single crawl.
const EVENTS_PER_CRAWL = 100;
interface ICrawler {
cancel(): void;
}
/* /*
* Event indexing class that wraps the platform specific event indexing. * Event indexing class that wraps the platform specific event indexing.
*/ */
export default class EventIndex extends EventEmitter { export default class EventIndex extends EventEmitter {
constructor() { private crawlerCheckpoints: CrawlerCheckpoint[] = [];
super(); private crawler: ICrawler = null;
this.crawlerCheckpoints = []; private currentCheckpoint: CrawlerCheckpoint = null;
// The time in ms that the crawler will wait loop iterations if there
// have not been any checkpoints to consume in the last iteration.
this._crawlerIdleTime = 5000;
// The maximum number of events our crawler should fetch in a single
// crawl.
this._eventsPerCrawl = 100;
this._crawler = null;
this._currentCheckpoint = null;
}
async init() { public async init() {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
this.crawlerCheckpoints = await indexManager.loadCheckpoints(); this.crawlerCheckpoints = await indexManager.loadCheckpoints();
@ -52,7 +61,7 @@ export default class EventIndex extends EventEmitter {
/** /**
* Register event listeners that are necessary for the event index to work. * Register event listeners that are necessary for the event index to work.
*/ */
registerListeners() { public registerListeners() {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
client.on('sync', this.onSync); client.on('sync', this.onSync);
@ -66,7 +75,7 @@ export default class EventIndex extends EventEmitter {
/** /**
* Remove the event index specific event listeners. * Remove the event index specific event listeners.
*/ */
removeListeners() { public removeListeners() {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (client === null) return; if (client === null) return;
@ -81,7 +90,7 @@ export default class EventIndex extends EventEmitter {
/** /**
* Get crawler checkpoints for the encrypted rooms and store them in the index. * Get crawler checkpoints for the encrypted rooms and store them in the index.
*/ */
async addInitialCheckpoints() { public async addInitialCheckpoints() {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const rooms = client.getRooms(); const rooms = client.getRooms();
@ -102,14 +111,14 @@ export default class EventIndex extends EventEmitter {
const timeline = room.getLiveTimeline(); const timeline = room.getLiveTimeline();
const token = timeline.getPaginationToken("b"); const token = timeline.getPaginationToken("b");
const backCheckpoint = { const backCheckpoint: CrawlerCheckpoint = {
roomId: room.roomId, roomId: room.roomId,
token: token, token: token,
direction: "b", direction: "b",
fullCrawl: true, fullCrawl: true,
}; };
const forwardCheckpoint = { const forwardCheckpoint: CrawlerCheckpoint = {
roomId: room.roomId, roomId: room.roomId,
token: token, token: token,
direction: "f", direction: "f",
@ -146,7 +155,7 @@ export default class EventIndex extends EventEmitter {
* - Every other sync, tell the event index to commit all the queued up * - Every other sync, tell the event index to commit all the queued up
* live events * live events
*/ */
onSync = async (state, prevState, data) => { private onSync = async (state: string, prevState: string, data: object) => {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
if (prevState === "PREPARED" && state === "SYNCING") { if (prevState === "PREPARED" && state === "SYNCING") {
@ -176,7 +185,15 @@ export default class EventIndex extends EventEmitter {
* otherwise we save their event id and wait for them in the Event.decrypted * otherwise we save their event id and wait for them in the Event.decrypted
* listener. * listener.
*/ */
onRoomTimeline = async (ev, room, toStartOfTimeline, removed, data) => { private onRoomTimeline = async (
ev: MatrixEvent,
room: Room,
toStartOfTimeline: boolean,
removed: boolean,
data: {
liveEvent: boolean;
},
) => {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
// We only index encrypted rooms locally. // We only index encrypted rooms locally.
@ -194,7 +211,7 @@ export default class EventIndex extends EventEmitter {
await this.addLiveEventToIndex(ev); await this.addLiveEventToIndex(ev);
} }
onRoomStateEvent = async (ev, state) => { private onRoomStateEvent = async (ev: MatrixEvent, state: RoomState) => {
if (!MatrixClientPeg.get().isRoomEncrypted(state.roomId)) return; if (!MatrixClientPeg.get().isRoomEncrypted(state.roomId)) return;
if (ev.getType() === "m.room.encryption" && !await this.isRoomIndexed(state.roomId)) { if (ev.getType() === "m.room.encryption" && !await this.isRoomIndexed(state.roomId)) {
@ -209,7 +226,7 @@ export default class EventIndex extends EventEmitter {
* Checks if the event was marked for addition in the Room.timeline * Checks if the event was marked for addition in the Room.timeline
* listener, if so queues it up to be added to the index. * listener, if so queues it up to be added to the index.
*/ */
onEventDecrypted = async (ev, err) => { private onEventDecrypted = async (ev: MatrixEvent, err: Error) => {
// If the event isn't in our live event set, ignore it. // If the event isn't in our live event set, ignore it.
if (err) return; if (err) return;
await this.addLiveEventToIndex(ev); await this.addLiveEventToIndex(ev);
@ -220,7 +237,7 @@ export default class EventIndex extends EventEmitter {
* *
* Removes a redacted event from our event index. * Removes a redacted event from our event index.
*/ */
onRedaction = async (ev, room) => { private onRedaction = async (ev: MatrixEvent, room: Room) => {
// We only index encrypted rooms locally. // We only index encrypted rooms locally.
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return;
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
@ -238,7 +255,7 @@ export default class EventIndex extends EventEmitter {
* Listens for timeline resets that are caused by a limited timeline to * Listens for timeline resets that are caused by a limited timeline to
* re-add checkpoints for rooms that need to be crawled again. * re-add checkpoints for rooms that need to be crawled again.
*/ */
onTimelineReset = async (room, timelineSet, resetAllTimelines) => { private onTimelineReset = async (room: Room, timelineSet: EventTimelineSet, resetAllTimelines: boolean) => {
if (room === null) return; if (room === null) return;
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return; if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) return;
@ -258,7 +275,7 @@ export default class EventIndex extends EventEmitter {
* @returns {bool} Returns true if the event can be indexed, false * @returns {bool} Returns true if the event can be indexed, false
* otherwise. * otherwise.
*/ */
isValidEvent(ev) { private isValidEvent(ev: MatrixEvent) {
const isUsefulType = ["m.room.message", "m.room.name", "m.room.topic"].includes(ev.getType()); const isUsefulType = ["m.room.message", "m.room.name", "m.room.topic"].includes(ev.getType());
const validEventType = isUsefulType && !ev.isRedacted() && !ev.isDecryptionFailure(); const validEventType = isUsefulType && !ev.isRedacted() && !ev.isDecryptionFailure();
@ -282,7 +299,7 @@ export default class EventIndex extends EventEmitter {
return validEventType && validMsgType && hasContentValue; return validEventType && validMsgType && hasContentValue;
} }
eventToJson(ev) { private eventToJson(ev: MatrixEvent) {
const jsonEvent = ev.toJSON(); const jsonEvent = ev.toJSON();
const e = ev.isEncrypted() ? jsonEvent.decrypted : jsonEvent; const e = ev.isEncrypted() ? jsonEvent.decrypted : jsonEvent;
@ -314,7 +331,7 @@ export default class EventIndex extends EventEmitter {
* *
* @param {MatrixEvent} ev The event that should be added to the index. * @param {MatrixEvent} ev The event that should be added to the index.
*/ */
async addLiveEventToIndex(ev) { private async addLiveEventToIndex(ev: MatrixEvent) {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
if (!this.isValidEvent(ev)) return; if (!this.isValidEvent(ev)) return;
@ -333,11 +350,11 @@ export default class EventIndex extends EventEmitter {
* Emmit that the crawler has changed the checkpoint that it's currently * Emmit that the crawler has changed the checkpoint that it's currently
* handling. * handling.
*/ */
emitNewCheckpoint() { private emitNewCheckpoint() {
this.emit("changedCheckpoint", this.currentRoom()); this.emit("changedCheckpoint", this.currentRoom());
} }
async addEventsFromLiveTimeline(timeline) { private async addEventsFromLiveTimeline(timeline: EventTimeline) {
const events = timeline.getEvents(); const events = timeline.getEvents();
for (let i = 0; i < events.length; i++) { for (let i = 0; i < events.length; i++) {
@ -346,7 +363,7 @@ export default class EventIndex extends EventEmitter {
} }
} }
async addRoomCheckpoint(roomId, fullCrawl = false) { private async addRoomCheckpoint(roomId: string, fullCrawl = false) {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const room = client.getRoom(roomId); const room = client.getRoom(roomId);
@ -396,16 +413,16 @@ export default class EventIndex extends EventEmitter {
* crawl, otherwise create a new checkpoint and push it to the * crawl, otherwise create a new checkpoint and push it to the
* crawlerCheckpoints queue so we go through them in a round-robin way. * crawlerCheckpoints queue so we go through them in a round-robin way.
*/ */
async crawlerFunc() { private async crawlerFunc() {
let cancelled = false; let cancelled = false;
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
this._crawler = {}; this.crawler = {
cancel: () => {
this._crawler.cancel = () => { cancelled = true;
cancelled = true; },
}; };
let idle = false; let idle = false;
@ -417,11 +434,11 @@ export default class EventIndex extends EventEmitter {
sleepTime = Math.max(sleepTime, 100); sleepTime = Math.max(sleepTime, 100);
if (idle) { if (idle) {
sleepTime = this._crawlerIdleTime; sleepTime = CRAWLER_IDLE_TIME;
} }
if (this._currentCheckpoint !== null) { if (this.currentCheckpoint !== null) {
this._currentCheckpoint = null; this.currentCheckpoint = null;
this.emitNewCheckpoint(); this.emitNewCheckpoint();
} }
@ -440,7 +457,7 @@ export default class EventIndex extends EventEmitter {
continue; continue;
} }
this._currentCheckpoint = checkpoint; this.currentCheckpoint = checkpoint;
this.emitNewCheckpoint(); this.emitNewCheckpoint();
idle = false; idle = false;
@ -454,8 +471,11 @@ export default class EventIndex extends EventEmitter {
try { try {
res = await client.createMessagesRequest( res = await client.createMessagesRequest(
checkpoint.roomId, checkpoint.token, this._eventsPerCrawl, checkpoint.roomId,
checkpoint.direction); checkpoint.token,
EVENTS_PER_CRAWL,
checkpoint.direction,
);
} catch (e) { } catch (e) {
if (e.httpStatus === 403) { if (e.httpStatus === 403) {
console.log("EventIndex: Removing checkpoint as we don't have ", console.log("EventIndex: Removing checkpoint as we don't have ",
@ -612,23 +632,23 @@ export default class EventIndex extends EventEmitter {
} }
} }
this._crawler = null; this.crawler = null;
} }
/** /**
* Start the crawler background task. * Start the crawler background task.
*/ */
startCrawler() { public startCrawler() {
if (this._crawler !== null) return; if (this.crawler !== null) return;
this.crawlerFunc(); this.crawlerFunc();
} }
/** /**
* Stop the crawler background task. * Stop the crawler background task.
*/ */
stopCrawler() { public stopCrawler() {
if (this._crawler === null) return; if (this.crawler === null) return;
this._crawler.cancel(); this.crawler.cancel();
} }
/** /**
@ -637,7 +657,7 @@ export default class EventIndex extends EventEmitter {
* This removes all the MatrixClient event listeners, stops the crawler * This removes all the MatrixClient event listeners, stops the crawler
* task, and closes the index. * task, and closes the index.
*/ */
async close() { public async close() {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
this.removeListeners(); this.removeListeners();
this.stopCrawler(); this.stopCrawler();
@ -654,7 +674,7 @@ export default class EventIndex extends EventEmitter {
* @return {Promise<[SearchResult]>} A promise that will resolve to an array * @return {Promise<[SearchResult]>} A promise that will resolve to an array
* of search results once the search is done. * of search results once the search is done.
*/ */
async search(searchArgs) { public async search(searchArgs: SearchArgs) {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
return indexManager.searchEventIndex(searchArgs); return indexManager.searchEventIndex(searchArgs);
} }
@ -680,11 +700,16 @@ export default class EventIndex extends EventEmitter {
* @returns {Promise<MatrixEvent[]>} Resolves to an array of events that * @returns {Promise<MatrixEvent[]>} Resolves to an array of events that
* contain URLs. * contain URLs.
*/ */
async loadFileEvents(room, limit = 10, fromEvent = null, direction = EventTimeline.BACKWARDS) { public async loadFileEvents(
room: Room,
limit = 10,
fromEvent: string = null,
direction: string = EventTimeline.BACKWARDS,
) {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
const loadArgs = { const loadArgs: LoadArgs = {
roomId: room.roomId, roomId: room.roomId,
limit: limit, limit: limit,
}; };
@ -772,13 +797,13 @@ export default class EventIndex extends EventEmitter {
* @returns {Promise<boolean>} Resolves to true if events were added to the * @returns {Promise<boolean>} Resolves to true if events were added to the
* timeline, false otherwise. * timeline, false otherwise.
*/ */
async populateFileTimeline( public async populateFileTimeline(
timelineSet, timelineSet: EventTimelineSet,
timeline, timeline: EventTimeline,
room, room: Room,
limit = 10, limit = 10,
fromEvent = null, fromEvent: string = null,
direction = EventTimeline.BACKWARDS, direction: string = EventTimeline.BACKWARDS,
) { ) {
const matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction); const matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction);
@ -837,7 +862,7 @@ export default class EventIndex extends EventEmitter {
* @returns {Promise<boolean>} Resolves to a boolean which is true if more * @returns {Promise<boolean>} Resolves to a boolean which is true if more
* events were successfully retrieved. * events were successfully retrieved.
*/ */
paginateTimelineWindow(room, timelineWindow, direction, limit) { public paginateTimelineWindow(room: Room, timelineWindow: TimelineWindow, direction: string, limit: number) {
const tl = timelineWindow.getTimelineIndex(direction); const tl = timelineWindow.getTimelineIndex(direction);
if (!tl) return Promise.resolve(false); if (!tl) return Promise.resolve(false);
@ -871,7 +896,7 @@ export default class EventIndex extends EventEmitter {
* @return {Promise<IndexStats>} A promise that will resolve to the index * @return {Promise<IndexStats>} A promise that will resolve to the index
* statistics. * statistics.
*/ */
async getStats() { public async getStats() {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
return indexManager.getStats(); return indexManager.getStats();
} }
@ -885,7 +910,7 @@ export default class EventIndex extends EventEmitter {
* @return {Promise<boolean>} Returns true if the index contains events for * @return {Promise<boolean>} Returns true if the index contains events for
* the given room, false otherwise. * the given room, false otherwise.
*/ */
async isRoomIndexed(roomId) { public async isRoomIndexed(roomId) {
const indexManager = PlatformPeg.get().getEventIndexingManager(); const indexManager = PlatformPeg.get().getEventIndexingManager();
return indexManager.isRoomIndexed(roomId); return indexManager.isRoomIndexed(roomId);
} }
@ -896,21 +921,21 @@ export default class EventIndex extends EventEmitter {
* @returns {Room} A MatrixRoom that is being currently crawled, null * @returns {Room} A MatrixRoom that is being currently crawled, null
* if no room is currently being crawled. * if no room is currently being crawled.
*/ */
currentRoom() { public currentRoom() {
if (this._currentCheckpoint === null && this.crawlerCheckpoints.length === 0) { if (this.currentCheckpoint === null && this.crawlerCheckpoints.length === 0) {
return null; return null;
} }
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (this._currentCheckpoint !== null) { if (this.currentCheckpoint !== null) {
return client.getRoom(this._currentCheckpoint.roomId); return client.getRoom(this.currentCheckpoint.roomId);
} else { } else {
return client.getRoom(this.crawlerCheckpoints[0].roomId); return client.getRoom(this.crawlerCheckpoints[0].roomId);
} }
} }
crawlingRooms() { public crawlingRooms() {
const totalRooms = new Set(); const totalRooms = new Set();
const crawlingRooms = new Set(); const crawlingRooms = new Set();
@ -918,14 +943,14 @@ export default class EventIndex extends EventEmitter {
crawlingRooms.add(checkpoint.roomId); crawlingRooms.add(checkpoint.roomId);
}); });
if (this._currentCheckpoint !== null) { if (this.currentCheckpoint !== null) {
crawlingRooms.add(this._currentCheckpoint.roomId); crawlingRooms.add(this.currentCheckpoint.roomId);
} }
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const rooms = client.getRooms(); const rooms = client.getRooms();
const isRoomEncrypted = (room) => { const isRoomEncrypted = (room: Room) => {
return client.isRoomEncrypted(room.roomId); return client.isRoomEncrypted(room.roomId);
}; };
@ -934,6 +959,6 @@ export default class EventIndex extends EventEmitter {
totalRooms.add(room.roomId); totalRooms.add(room.roomId);
}); });
return {crawlingRooms, totalRooms}; return { crawlingRooms, totalRooms };
} }
} }

View File

@ -1,7 +1,5 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017-2021 The Matrix.org Foundation C.I.C.
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -15,11 +13,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { Store } from 'flux/utils';
import dis from '../dispatcher/dispatcher'; import dis from '../dispatcher/dispatcher';
import {Store} from 'flux/utils'; import { ActionPayload } from "../dispatcher/payloads";
interface IState {
deferredAction: any;
}
const INITIAL_STATE = { const INITIAL_STATE = {
deferred_action: null, deferredAction: null,
}; };
/** /**
@ -27,39 +32,38 @@ const INITIAL_STATE = {
* store that listens for actions and updates its state accordingly, informing any * store that listens for actions and updates its state accordingly, informing any
* listeners (views) of state changes. * listeners (views) of state changes.
*/ */
class LifecycleStore extends Store { class LifecycleStore extends Store<ActionPayload> {
private state: IState = INITIAL_STATE;
constructor() { constructor() {
super(dis); super(dis);
// Initialise state
this._state = INITIAL_STATE;
} }
_setState(newState) { private setState(newState: Partial<IState>) {
this._state = Object.assign(this._state, newState); this.state = Object.assign(this.state, newState);
this.__emitChange(); this.__emitChange();
} }
__onDispatch(payload) { protected __onDispatch(payload: ActionPayload) {
switch (payload.action) { switch (payload.action) {
case 'do_after_sync_prepared': case 'do_after_sync_prepared':
this._setState({ this.setState({
deferred_action: payload.deferred_action, deferredAction: payload.deferred_action,
}); });
break; break;
case 'cancel_after_sync_prepared': case 'cancel_after_sync_prepared':
this._setState({ this.setState({
deferred_action: null, deferredAction: null,
}); });
break; break;
case 'sync_state': { case 'syncstate': {
if (payload.state !== 'PREPARED') { if (payload.state !== 'PREPARED') {
break; break;
} }
if (!this._state.deferred_action) break; if (!this.state.deferredAction) break;
const deferredAction = Object.assign({}, this._state.deferred_action); const deferredAction = Object.assign({}, this.state.deferredAction);
this._setState({ this.setState({
deferred_action: null, deferredAction: null,
}); });
dis.dispatch(deferredAction); dis.dispatch(deferredAction);
break; break;
@ -71,8 +75,8 @@ class LifecycleStore extends Store {
} }
} }
reset() { private reset() {
this._state = Object.assign({}, INITIAL_STATE); this.state = Object.assign({}, INITIAL_STATE);
} }
} }

View File

@ -1,28 +0,0 @@
/*
Copyright 2019 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.
*/
/**
* Automatically focuses the captured reference when receiving a non-null
* object. Useful in scenarios where componentDidMount does not have a
* useful reference to an element, but one needs to focus the element on
* first render. Example usage: ref={focusCapturedRef}
* @param {function} ref The React reference to focus on, if not null
*/
export function focusCapturedRef(ref) {
if (ref) {
ref.focus();
}
}

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import url from "url";
* @param {string} u The url to be abbreviated * @param {string} u The url to be abbreviated
* @returns {string} The abbreviated url * @returns {string} The abbreviated url
*/ */
export function abbreviateUrl(u) { export function abbreviateUrl(u: string): string {
if (!u) return ''; if (!u) return '';
const parsedUrl = url.parse(u); const parsedUrl = url.parse(u);
@ -37,7 +37,7 @@ export function abbreviateUrl(u) {
return u; return u;
} }
export function unabbreviateUrl(u) { export function unabbreviateUrl(u: string): string {
if (!u) return ''; if (!u) return '';
let longUrl = u; let longUrl = u;

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2020 The Matrix.org Foundation C.I.C. Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,22 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {_t} from "../languageHandler"; import { _t } from "../languageHandler";
// These are the constants we use for when to break the text
const MILLISECONDS_RECENT = 15000;
const MILLISECONDS_1_MIN = 75000;
const MINUTES_UNDER_1_HOUR = 45;
const MINUTES_1_HOUR = 75;
const HOURS_UNDER_1_DAY = 23;
const HOURS_1_DAY = 26;
/** /**
* Converts a timestamp into human-readable, translated, text. * Converts a timestamp into human-readable, translated, text.
* @param {number} timeMillis The time in millis to compare against. * @param {number} timeMillis The time in millis to compare against.
* @returns {string} The humanized time. * @returns {string} The humanized time.
*/ */
export function humanizeTime(timeMillis) { export function humanizeTime(timeMillis: number): string {
// These are the constants we use for when to break the text
const MILLISECONDS_RECENT = 15000;
const MILLISECONDS_1_MIN = 75000;
const MINUTES_UNDER_1_HOUR = 45;
const MINUTES_1_HOUR = 75;
const HOURS_UNDER_1_DAY = 23;
const HOURS_1_DAY = 26;
const now = (new Date()).getTime(); const now = (new Date()).getTime();
let msAgo = now - timeMillis; let msAgo = now - timeMillis;
const minutes = Math.abs(Math.ceil(msAgo / 60000)); const minutes = Math.abs(Math.ceil(msAgo / 60000));