mirror of https://github.com/vector-im/riot-web
Merge pull request #6153 from matrix-org/t3chguy/ts/123
commit
7abf680e51
|
@ -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>;
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -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 = [];
|
||||||
},
|
},
|
||||||
};
|
};
|
|
@ -15,5 +15,5 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface IDialogProps {
|
export interface IDialogProps {
|
||||||
onFinished: (bool) => void;
|
onFinished(...args: any): void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
@ -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));
|
Loading…
Reference in New Issue