Merge branch 'develop' into gsouquet/ts-components-migration
This commit is contained in:
commit
8b567944ec
29 changed files with 719 additions and 726 deletions
|
@ -250,7 +250,15 @@ export default class CallHandler extends EventEmitter {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
private areAnyCallsUnsilenced(): boolean {
|
||||
return this.calls.size > this.silencedCalls.size;
|
||||
for (const call of this.calls.values()) {
|
||||
if (
|
||||
call.state === CallState.Ringing &&
|
||||
!this.isCallSilenced(call.callId)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async checkProtocols(maxTries) {
|
||||
|
@ -878,6 +886,8 @@ export default class CallHandler extends EventEmitter {
|
|||
break;
|
||||
case 'hangup':
|
||||
case 'reject':
|
||||
this.stopRingingIfPossible(this.calls.get(payload.room_id).callId);
|
||||
|
||||
if (!this.calls.get(payload.room_id)) {
|
||||
return; // no call to hangup
|
||||
}
|
||||
|
@ -890,11 +900,15 @@ export default class CallHandler extends EventEmitter {
|
|||
// the hangup event away)
|
||||
break;
|
||||
case 'hangup_all':
|
||||
this.stopRingingIfPossible(this.calls.get(payload.room_id).callId);
|
||||
|
||||
for (const call of this.calls.values()) {
|
||||
call.hangup(CallErrorCode.UserHangup, false);
|
||||
}
|
||||
break;
|
||||
case 'answer': {
|
||||
this.stopRingingIfPossible(this.calls.get(payload.room_id).callId);
|
||||
|
||||
if (!this.calls.has(payload.room_id)) {
|
||||
return; // no call to answer
|
||||
}
|
||||
|
@ -929,6 +943,12 @@ export default class CallHandler extends EventEmitter {
|
|||
}
|
||||
};
|
||||
|
||||
private stopRingingIfPossible(callId: string): void {
|
||||
this.silencedCalls.delete(callId);
|
||||
if (this.areAnyCallsUnsilenced()) return;
|
||||
this.pause(AudioID.Ring);
|
||||
}
|
||||
|
||||
private async dialNumber(number: string) {
|
||||
const results = await this.pstnLookup(number);
|
||||
if (!results || results.length === 0 || !results[0].userid) {
|
||||
|
|
|
@ -50,7 +50,6 @@ import CallHandler from "./CallHandler";
|
|||
import { guessAndSetDMRoom } from "./Rooms";
|
||||
import { upgradeRoom } from './utils/RoomUpgrade';
|
||||
import UploadConfirmDialog from './components/views/dialogs/UploadConfirmDialog';
|
||||
import ErrorDialog from './components/views/dialogs/ErrorDialog';
|
||||
import DevtoolsDialog from './components/views/dialogs/DevtoolsDialog';
|
||||
import RoomUpgradeWarningDialog from "./components/views/dialogs/RoomUpgradeWarningDialog";
|
||||
import InfoDialog from "./components/views/dialogs/InfoDialog";
|
||||
|
@ -245,21 +244,6 @@ export const Commands = [
|
|||
},
|
||||
category: CommandCategories.messages,
|
||||
}),
|
||||
new Command({
|
||||
command: 'ddg',
|
||||
args: '<query>',
|
||||
description: _td('Searches DuckDuckGo for results'),
|
||||
runFn: function() {
|
||||
// TODO Don't explain this away, actually show a search UI here.
|
||||
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
|
||||
title: _t('/ddg is not a command'),
|
||||
description: _t('To use it, just wait for autocomplete results to load and tab through them.'),
|
||||
});
|
||||
return success();
|
||||
},
|
||||
category: CommandCategories.actions,
|
||||
hideCompletionAfterSpace: true,
|
||||
}),
|
||||
new Command({
|
||||
command: 'upgraderoom',
|
||||
args: '<new_version>',
|
||||
|
|
|
@ -45,7 +45,13 @@ class MxVoiceWorklet extends AudioWorkletProcessor {
|
|||
|
||||
process(inputs, outputs, parameters) {
|
||||
const currentSecond = roundTimeToTargetFreq(currentTime);
|
||||
if (currentSecond === this.nextAmplitudeSecond) {
|
||||
// We special case the first ping because there's a fairly good chance that we'll miss the zeroth
|
||||
// update. Firefox for instance takes 0.06 seconds (roughly) to call this function for the first
|
||||
// time. Edge and Chrome occasionally lag behind too, but for the most part are on time.
|
||||
//
|
||||
// When this doesn't work properly we end up producing a waveform of nulls and no live preview
|
||||
// of the recorded message.
|
||||
if (currentSecond === this.nextAmplitudeSecond || this.nextAmplitudeSecond === 0) {
|
||||
// We're expecting exactly one mono input source, so just grab the very first frame of
|
||||
// samples for the analysis.
|
||||
const monoChan = inputs[0][0];
|
||||
|
|
|
@ -20,7 +20,6 @@ import { Room } from 'matrix-js-sdk/src/models/room';
|
|||
|
||||
import CommandProvider from './CommandProvider';
|
||||
import CommunityProvider from './CommunityProvider';
|
||||
import DuckDuckGoProvider from './DuckDuckGoProvider';
|
||||
import RoomProvider from './RoomProvider';
|
||||
import UserProvider from './UserProvider';
|
||||
import EmojiProvider from './EmojiProvider';
|
||||
|
@ -55,7 +54,6 @@ const PROVIDERS = [
|
|||
EmojiProvider,
|
||||
NotifProvider,
|
||||
CommandProvider,
|
||||
DuckDuckGoProvider,
|
||||
];
|
||||
|
||||
if (SpaceStore.spacesEnabled) {
|
||||
|
|
|
@ -53,7 +53,7 @@ export default class CommandProvider extends AutocompleteProvider {
|
|||
// The input looks like a command with arguments, perform exact match
|
||||
const name = command[1].substr(1); // strip leading `/`
|
||||
if (CommandMap.has(name) && CommandMap.get(name).isEnabled()) {
|
||||
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
|
||||
// some commands, namely `me` don't suit having the usage shown whilst typing their arguments
|
||||
if (CommandMap.get(name).hideCompletionAfterSpace) return [];
|
||||
matches = [CommandMap.get(name)];
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ export default class CommandProvider extends AutocompleteProvider {
|
|||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||
return (
|
||||
<div
|
||||
className="mx_Autocomplete_Completion_container_block"
|
||||
className="mx_Autocomplete_Completion_container_pill"
|
||||
role="presentation"
|
||||
aria-label={_t("Command Autocomplete")}
|
||||
>
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017, 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from '../languageHandler';
|
||||
import AutocompleteProvider from './AutocompleteProvider';
|
||||
|
||||
import { TextualCompletion } from './Components';
|
||||
import { ICompletion, ISelectionRange } from "./Autocompleter";
|
||||
|
||||
const DDG_REGEX = /\/ddg\s+(.+)$/g;
|
||||
const REFERRER = 'vector';
|
||||
|
||||
export default class DuckDuckGoProvider extends AutocompleteProvider {
|
||||
constructor() {
|
||||
super(DDG_REGEX);
|
||||
}
|
||||
|
||||
static getQueryUri(query: string) {
|
||||
return `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}`
|
||||
+ `&format=json&no_redirect=1&no_html=1&t=${encodeURIComponent(REFERRER)}`;
|
||||
}
|
||||
|
||||
async getCompletions(
|
||||
query: string,
|
||||
selection: ISelectionRange,
|
||||
force = false,
|
||||
limit = -1,
|
||||
): Promise<ICompletion[]> {
|
||||
const { command, range } = this.getCurrentCommand(query, selection);
|
||||
if (!query || !command) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const response = await fetch(DuckDuckGoProvider.getQueryUri(command[1]), {
|
||||
method: 'GET',
|
||||
});
|
||||
const json = await response.json();
|
||||
const maxLength = limit > -1 ? limit : json.Results.length;
|
||||
const results = json.Results.slice(0, maxLength).map((result) => {
|
||||
return {
|
||||
completion: result.Text,
|
||||
component: (
|
||||
<TextualCompletion
|
||||
title={result.Text}
|
||||
description={result.Result} />
|
||||
),
|
||||
range,
|
||||
};
|
||||
});
|
||||
if (json.Answer) {
|
||||
results.unshift({
|
||||
completion: json.Answer,
|
||||
component: (
|
||||
<TextualCompletion
|
||||
title={json.Answer}
|
||||
description={json.AnswerType} />
|
||||
),
|
||||
range,
|
||||
});
|
||||
}
|
||||
if (json.RelatedTopics && json.RelatedTopics.length > 0) {
|
||||
results.unshift({
|
||||
completion: json.RelatedTopics[0].Text,
|
||||
component: (
|
||||
<TextualCompletion
|
||||
title={json.RelatedTopics[0].Text} />
|
||||
),
|
||||
range,
|
||||
});
|
||||
}
|
||||
if (json.AbstractText) {
|
||||
results.unshift({
|
||||
completion: json.AbstractText,
|
||||
component: (
|
||||
<TextualCompletion
|
||||
title={json.AbstractText} />
|
||||
),
|
||||
range,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return '🔍 ' + _t('Results from DuckDuckGo');
|
||||
}
|
||||
|
||||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||
return (
|
||||
<div
|
||||
className="mx_Autocomplete_Completion_container_block"
|
||||
role="presentation"
|
||||
aria-label={_t("DuckDuckGo Results")}
|
||||
>
|
||||
{ completions }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -115,7 +115,7 @@ const LeftPanelWidget: React.FC = () => {
|
|||
aria-expanded={expanded}
|
||||
aria-level={1}
|
||||
onClick={() => {
|
||||
setExpanded(e => !e);
|
||||
setExpanded(!expanded);
|
||||
}}
|
||||
>
|
||||
<span className={classNames({
|
||||
|
|
|
@ -80,7 +80,7 @@ const SpaceChildPicker = ({ filterPlaceholder, rooms, selected, onChange }) => {
|
|||
|
||||
const LeaveRoomsPicker = ({ space, spaceChildren, roomsToLeave, setRoomsToLeave }) => {
|
||||
const selected = useMemo(() => new Set(roomsToLeave), [roomsToLeave]);
|
||||
const [state, setState] = useState<string>(RoomsToLeave.All);
|
||||
const [state, setState] = useState<string>(RoomsToLeave.None);
|
||||
|
||||
useEffect(() => {
|
||||
if (state === RoomsToLeave.All) {
|
||||
|
@ -97,11 +97,11 @@ const LeaveRoomsPicker = ({ space, spaceChildren, roomsToLeave, setRoomsToLeave
|
|||
onChange={setState}
|
||||
definitions={[
|
||||
{
|
||||
value: RoomsToLeave.All,
|
||||
label: _t("Leave all rooms and spaces"),
|
||||
}, {
|
||||
value: RoomsToLeave.None,
|
||||
label: _t("Don't leave any"),
|
||||
}, {
|
||||
value: RoomsToLeave.All,
|
||||
label: _t("Leave all rooms and spaces"),
|
||||
}, {
|
||||
value: RoomsToLeave.Specific,
|
||||
label: _t("Leave specific rooms and spaces"),
|
||||
|
|
|
@ -31,14 +31,6 @@ import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
|||
import { CapabilityText } from "../../../widgets/CapabilityText";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
export function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] {
|
||||
return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]");
|
||||
}
|
||||
|
||||
function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) {
|
||||
localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps));
|
||||
}
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
requestedCapabilities: Set<Capability>;
|
||||
widget: Widget;
|
||||
|
@ -95,10 +87,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
|
|||
};
|
||||
|
||||
private closeAndTryRemember(approved: Capability[]) {
|
||||
if (this.state.rememberSelection) {
|
||||
setRememberedCapabilitiesForWidget(this.props.widget, approved);
|
||||
}
|
||||
this.props.onFinished({ approved });
|
||||
this.props.onFinished({ approved, remember: this.state.rememberSelection });
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
@ -185,8 +185,8 @@ export default class MemberList extends React.Component<IProps, IState> {
|
|||
return {
|
||||
loading: false,
|
||||
members: members,
|
||||
filteredJoinedMembers: this.filterMembers(members, 'join'),
|
||||
filteredInvitedMembers: this.filterMembers(members, 'invite'),
|
||||
filteredJoinedMembers: this.filterMembers(members, 'join', searchQuery),
|
||||
filteredInvitedMembers: this.filterMembers(members, 'invite', searchQuery),
|
||||
canInvite: this.canInvite,
|
||||
|
||||
// ideally we'd size this to the page height, but
|
||||
|
|
|
@ -15,22 +15,25 @@ limitations under the License.
|
|||
*/
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import AppTile from '../elements/AppTile';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
import WidgetUtils, { IWidgetEvent } from '../../../utils/WidgetUtils';
|
||||
import PersistedElement from "../elements/PersistedElement";
|
||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { ContextMenu } from "../../structures/ContextMenu";
|
||||
import { ChevronFace, ContextMenu } from "../../structures/ContextMenu";
|
||||
import { WidgetType } from "../../../widgets/WidgetType";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { ActionPayload } from '../../../dispatcher/payloads';
|
||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import GenericElementContextMenu from "../context_menus/GenericElementContextMenu";
|
||||
|
||||
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
|
||||
// We sit in a context menu, so this should be given to the context menu.
|
||||
|
@ -39,27 +42,35 @@ const STICKERPICKER_Z_INDEX = 3500;
|
|||
// Key to store the widget's AppTile under in PersistedElement
|
||||
const PERSISTED_ELEMENT_KEY = "stickerPicker";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
showStickers: boolean;
|
||||
imError: string;
|
||||
stickerpickerX: number;
|
||||
stickerpickerY: number;
|
||||
stickerpickerChevronOffset?: number;
|
||||
stickerpickerWidget: IWidgetEvent;
|
||||
widgetId: string;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.rooms.Stickerpicker")
|
||||
export default class Stickerpicker extends React.PureComponent {
|
||||
export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||
static currentWidget;
|
||||
|
||||
constructor(props) {
|
||||
private dispatcherRef: string;
|
||||
|
||||
private prevSentVisibility: boolean;
|
||||
|
||||
private popoverWidth = 300;
|
||||
private popoverHeight = 300;
|
||||
// This is loaded by _acquireScalarClient on an as-needed basis.
|
||||
private scalarClient: ScalarAuthClient = null;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this._onShowStickersClick = this._onShowStickersClick.bind(this);
|
||||
this._onHideStickersClick = this._onHideStickersClick.bind(this);
|
||||
this._launchManageIntegrations = this._launchManageIntegrations.bind(this);
|
||||
this._removeStickerpickerWidgets = this._removeStickerpickerWidgets.bind(this);
|
||||
this._updateWidget = this._updateWidget.bind(this);
|
||||
this._onWidgetAction = this._onWidgetAction.bind(this);
|
||||
this._onResize = this._onResize.bind(this);
|
||||
this._onFinished = this._onFinished.bind(this);
|
||||
|
||||
this.popoverWidth = 300;
|
||||
this.popoverHeight = 300;
|
||||
|
||||
// This is loaded by _acquireScalarClient on an as-needed basis.
|
||||
this.scalarClient = null;
|
||||
|
||||
this.state = {
|
||||
showStickers: false,
|
||||
imError: null,
|
||||
|
@ -70,7 +81,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
_acquireScalarClient() {
|
||||
private acquireScalarClient(): Promise<void | ScalarAuthClient> {
|
||||
if (this.scalarClient) return Promise.resolve(this.scalarClient);
|
||||
// TODO: Pick the right manager for the widget
|
||||
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||
|
@ -79,15 +90,15 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
this.forceUpdate();
|
||||
return this.scalarClient;
|
||||
}).catch((e) => {
|
||||
this._imError(_td("Failed to connect to integration manager"), e);
|
||||
this.imError(_td("Failed to connect to integration manager"), e);
|
||||
});
|
||||
} else {
|
||||
IntegrationManagers.sharedInstance().openNoManagerDialog();
|
||||
}
|
||||
}
|
||||
|
||||
async _removeStickerpickerWidgets() {
|
||||
const scalarClient = await this._acquireScalarClient();
|
||||
private removeStickerpickerWidgets = async (): Promise<void> => {
|
||||
const scalarClient = await this.acquireScalarClient();
|
||||
console.log('Removing Stickerpicker widgets');
|
||||
if (this.state.widgetId) {
|
||||
if (scalarClient) {
|
||||
|
@ -109,36 +120,36 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
}).catch((e) => {
|
||||
console.error('Failed to remove sticker picker widget', e);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
// Close the sticker picker when the window resizes
|
||||
window.addEventListener('resize', this._onResize);
|
||||
window.addEventListener('resize', this.onResize);
|
||||
|
||||
this.dispatcherRef = dis.register(this._onWidgetAction);
|
||||
this.dispatcherRef = dis.register(this.onWidgetAction);
|
||||
|
||||
// Track updates to widget state in account data
|
||||
MatrixClientPeg.get().on('accountData', this._updateWidget);
|
||||
MatrixClientPeg.get().on('accountData', this.updateWidget);
|
||||
|
||||
// Initialise widget state from current account data
|
||||
this._updateWidget();
|
||||
this.updateWidget();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
const client = MatrixClientPeg.get();
|
||||
if (client) client.removeListener('accountData', this._updateWidget);
|
||||
if (client) client.removeListener('accountData', this.updateWidget);
|
||||
|
||||
window.removeEventListener('resize', this._onResize);
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
if (this.dispatcherRef) {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
this._sendVisibilityToWidget(this.state.showStickers);
|
||||
public componentDidUpdate(prevProps: IProps, prevState: IState): void {
|
||||
this.sendVisibilityToWidget(this.state.showStickers);
|
||||
}
|
||||
|
||||
_imError(errorMsg, e) {
|
||||
private imError(errorMsg: string, e: Error): void {
|
||||
console.error(errorMsg, e);
|
||||
this.setState({
|
||||
showStickers: false,
|
||||
|
@ -146,7 +157,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
_updateWidget() {
|
||||
private updateWidget = (): void => {
|
||||
const stickerpickerWidget = WidgetUtils.getStickerpickerWidgets()[0];
|
||||
if (!stickerpickerWidget) {
|
||||
Stickerpicker.currentWidget = null;
|
||||
|
@ -175,9 +186,9 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
stickerpickerWidget,
|
||||
widgetId: stickerpickerWidget ? stickerpickerWidget.id : null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onWidgetAction(payload) {
|
||||
private onWidgetAction = (payload: ActionPayload): void => {
|
||||
switch (payload.action) {
|
||||
case "user_widget_updated":
|
||||
this.forceUpdate();
|
||||
|
@ -191,11 +202,11 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
this.setState({ showStickers: false });
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_defaultStickerpickerContent() {
|
||||
private defaultStickerpickerContent(): JSX.Element {
|
||||
return (
|
||||
<AccessibleButton onClick={this._launchManageIntegrations}
|
||||
<AccessibleButton onClick={this.launchManageIntegrations}
|
||||
className='mx_Stickers_contentPlaceholder'>
|
||||
<p>{ _t("You don't currently have any stickerpacks enabled") }</p>
|
||||
<p className='mx_Stickers_addLink'>{ _t("Add some now") }</p>
|
||||
|
@ -204,29 +215,29 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
_errorStickerpickerContent() {
|
||||
private errorStickerpickerContent(): JSX.Element {
|
||||
return (
|
||||
<div style={{ "text-align": "center" }} className="error">
|
||||
<div style={{ textAlign: "center" }} className="error">
|
||||
<p> { this.state.imError } </p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_sendVisibilityToWidget(visible) {
|
||||
private sendVisibilityToWidget(visible: boolean): void {
|
||||
if (!this.state.stickerpickerWidget) return;
|
||||
const messaging = WidgetMessagingStore.instance.getMessagingForId(this.state.stickerpickerWidget.id);
|
||||
if (messaging && visible !== this._prevSentVisibility) {
|
||||
if (messaging && visible !== this.prevSentVisibility) {
|
||||
messaging.updateVisibility(visible).catch(err => {
|
||||
console.error("Error updating widget visibility: ", err);
|
||||
});
|
||||
this._prevSentVisibility = visible;
|
||||
this.prevSentVisibility = visible;
|
||||
}
|
||||
}
|
||||
|
||||
_getStickerpickerContent() {
|
||||
public getStickerpickerContent(): JSX.Element {
|
||||
// Handle integration manager errors
|
||||
if (this.state._imError) {
|
||||
return this._errorStickerpickerContent();
|
||||
if (this.state.imError) {
|
||||
return this.errorStickerpickerContent();
|
||||
}
|
||||
|
||||
// Stickers
|
||||
|
@ -239,12 +250,11 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
// Use a separate ReactDOM tree to render the AppTile separately so that it persists and does
|
||||
// not unmount when we (a) close the sticker picker (b) switch rooms. It's properties are still
|
||||
// updated.
|
||||
const PersistedElement = sdk.getComponent("elements.PersistedElement");
|
||||
|
||||
// Load stickerpack content
|
||||
if (stickerpickerWidget && stickerpickerWidget.content && stickerpickerWidget.content.url) {
|
||||
// Set default name
|
||||
stickerpickerWidget.content.name = stickerpickerWidget.name || _t("Stickerpack");
|
||||
stickerpickerWidget.content.name = stickerpickerWidget.content.name || _t("Stickerpack");
|
||||
|
||||
// FIXME: could this use the same code as other apps?
|
||||
const stickerApp = {
|
||||
|
@ -275,12 +285,12 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
creatorUserId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId}
|
||||
waitForIframeLoad={true}
|
||||
showMenubar={true}
|
||||
onEditClick={this._launchManageIntegrations}
|
||||
onDeleteClick={this._removeStickerpickerWidgets}
|
||||
onEditClick={this.launchManageIntegrations}
|
||||
onDeleteClick={this.removeStickerpickerWidgets}
|
||||
showTitle={false}
|
||||
showCancel={false}
|
||||
showPopout={false}
|
||||
onMinimiseClick={this._onHideStickersClick}
|
||||
onMinimiseClick={this.onHideStickersClick}
|
||||
handleMinimisePointerEvents={true}
|
||||
userWidget={true}
|
||||
/>
|
||||
|
@ -290,7 +300,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
);
|
||||
} else {
|
||||
// Default content to show if stickerpicker widget not added
|
||||
stickersContent = this._defaultStickerpickerContent();
|
||||
stickersContent = this.defaultStickerpickerContent();
|
||||
}
|
||||
return stickersContent;
|
||||
}
|
||||
|
@ -300,7 +310,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
* Show the sticker picker overlay
|
||||
* If no stickerpacks have been added, show a link to the integration manager add sticker packs page.
|
||||
*/
|
||||
_onShowStickersClick(e) {
|
||||
private onShowStickersClick = (e: React.MouseEvent<HTMLElement>): void => {
|
||||
if (!SettingsStore.getValue("integrationProvisioning")) {
|
||||
// Intercept this case and spawn a warning.
|
||||
return IntegrationManagers.sharedInstance().showDisabledDialog();
|
||||
|
@ -308,7 +318,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
|
||||
// XXX: Simplify by using a context menu that is positioned relative to the sticker picker button
|
||||
|
||||
const buttonRect = e.target.getBoundingClientRect();
|
||||
const buttonRect = e.currentTarget.getBoundingClientRect();
|
||||
|
||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||
let x = buttonRect.right + window.pageXOffset - 41;
|
||||
|
@ -324,50 +334,50 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
// Offset the chevron location, which is relative to the left of the context menu
|
||||
// (10 = offset when context menu would not be displayed off viewport)
|
||||
// (2 = context menu borders)
|
||||
const stickerPickerChevronOffset = Math.max(10, 2 + window.pageXOffset + buttonRect.left - x);
|
||||
const stickerpickerChevronOffset = Math.max(10, 2 + window.pageXOffset + buttonRect.left - x);
|
||||
|
||||
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
|
||||
|
||||
this.setState({
|
||||
showStickers: true,
|
||||
stickerPickerX: x,
|
||||
stickerPickerY: y,
|
||||
stickerPickerChevronOffset,
|
||||
stickerpickerX: x,
|
||||
stickerpickerY: y,
|
||||
stickerpickerChevronOffset,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger hiding of the sticker picker overlay
|
||||
* @param {Event} ev Event that triggered the function call
|
||||
*/
|
||||
_onHideStickersClick(ev) {
|
||||
private onHideStickersClick = (ev: React.MouseEvent): void => {
|
||||
if (this.state.showStickers) {
|
||||
this.setState({ showStickers: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the window is resized
|
||||
*/
|
||||
_onResize() {
|
||||
private onResize = (): void => {
|
||||
if (this.state.showStickers) {
|
||||
this.setState({ showStickers: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The stickers picker was hidden
|
||||
*/
|
||||
_onFinished() {
|
||||
private onFinished = (): void => {
|
||||
if (this.state.showStickers) {
|
||||
this.setState({ showStickers: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch the integration manager on the stickers integration page
|
||||
*/
|
||||
_launchManageIntegrations = () => {
|
||||
private launchManageIntegrations = (): void => {
|
||||
// TODO: Open the right integration manager for the widget
|
||||
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||
IntegrationManagers.sharedInstance().openAll(
|
||||
|
@ -384,7 +394,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
let stickerPicker;
|
||||
let stickersButton;
|
||||
const className = classNames(
|
||||
|
@ -400,26 +410,24 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
id='stickersButton'
|
||||
key="controls_hide_stickers"
|
||||
className={className}
|
||||
onClick={this._onHideStickersClick}
|
||||
active={this.state.showStickers.toString()}
|
||||
onClick={this.onHideStickersClick}
|
||||
title={_t("Hide Stickers")}
|
||||
/>;
|
||||
|
||||
const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu');
|
||||
stickerPicker = <ContextMenu
|
||||
chevronOffset={this.state.stickerPickerChevronOffset}
|
||||
chevronFace="bottom"
|
||||
left={this.state.stickerPickerX}
|
||||
top={this.state.stickerPickerY}
|
||||
chevronOffset={this.state.stickerpickerChevronOffset}
|
||||
chevronFace={ChevronFace.Bottom}
|
||||
left={this.state.stickerpickerX}
|
||||
top={this.state.stickerpickerY}
|
||||
menuWidth={this.popoverWidth}
|
||||
menuHeight={this.popoverHeight}
|
||||
onFinished={this._onFinished}
|
||||
onFinished={this.onFinished}
|
||||
menuPaddingTop={0}
|
||||
menuPaddingLeft={0}
|
||||
menuPaddingRight={0}
|
||||
zIndex={STICKERPICKER_Z_INDEX}
|
||||
>
|
||||
<GenericElementContextMenu element={this._getStickerpickerContent()} onResize={this._onFinished} />
|
||||
<GenericElementContextMenu element={this.getStickerpickerContent()} onResize={this.onFinished} />
|
||||
</ContextMenu>;
|
||||
} else {
|
||||
// Show show-stickers button
|
||||
|
@ -428,7 +436,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
id='stickersButton'
|
||||
key="controls_show_stickers"
|
||||
className="mx_MessageComposer_button mx_MessageComposer_stickers"
|
||||
onClick={this._onShowStickersClick}
|
||||
onClick={this.onShowStickersClick}
|
||||
title={_t("Show Stickers")}
|
||||
/>;
|
||||
}
|
|
@ -149,10 +149,12 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
|||
"To avoid these issues, create a <a>new encrypted room</a> for " +
|
||||
"the conversation you plan to have.",
|
||||
null,
|
||||
{ "a": (sub) => <a onClick={() => {
|
||||
dialog.close();
|
||||
this.createNewRoom(false, true);
|
||||
}}> { sub } </a> },
|
||||
{ "a": (sub) => <a
|
||||
className="mx_linkButton"
|
||||
onClick={() => {
|
||||
dialog.close();
|
||||
this.createNewRoom(false, true);
|
||||
}}> { sub } </a> },
|
||||
) } </p>
|
||||
</div>,
|
||||
|
||||
|
@ -248,10 +250,12 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
|
|||
"you plan to have.",
|
||||
null,
|
||||
{
|
||||
"a": (sub) => <a onClick={() => {
|
||||
dialog.close();
|
||||
this.createNewRoom(true, false);
|
||||
}}> { sub } </a>,
|
||||
"a": (sub) => <a
|
||||
className="mx_linkButton"
|
||||
onClick={() => {
|
||||
dialog.close();
|
||||
this.createNewRoom(true, false);
|
||||
}}> { sub } </a>,
|
||||
},
|
||||
) } </p>
|
||||
</div>,
|
||||
|
|
|
@ -54,8 +54,8 @@ const SpacePublicShare = ({ space, onFinished }: IProps) => {
|
|||
{ space.canInvite(MatrixClientPeg.get()?.getUserId()) ? <AccessibleButton
|
||||
className="mx_SpacePublicShare_inviteButton"
|
||||
onClick={() => {
|
||||
showRoomInviteDialog(space.roomId);
|
||||
if (onFinished) onFinished();
|
||||
showRoomInviteDialog(space.roomId);
|
||||
}}
|
||||
>
|
||||
<h3>{ _t("Invite people") }</h3>
|
||||
|
|
|
@ -45,6 +45,7 @@ interface IProps {
|
|||
interface IState {
|
||||
audioMuted: boolean;
|
||||
videoMuted: boolean;
|
||||
speaking: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.voip.VideoFeed")
|
||||
|
@ -57,6 +58,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
this.state = {
|
||||
audioMuted: this.props.feed.isAudioMuted(),
|
||||
videoMuted: this.props.feed.isVideoMuted(),
|
||||
speaking: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -103,11 +105,19 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
if (oldFeed) {
|
||||
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||
this.props.feed.removeListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||
this.props.feed.removeListener(CallFeedEvent.Speaking, this.onSpeaking);
|
||||
this.props.feed.measureVolumeActivity(false);
|
||||
}
|
||||
this.stopMedia();
|
||||
}
|
||||
if (newFeed) {
|
||||
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||
this.props.feed.addListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||
this.props.feed.addListener(CallFeedEvent.Speaking, this.onSpeaking);
|
||||
this.props.feed.measureVolumeActivity(true);
|
||||
}
|
||||
this.playMedia();
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +172,10 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onSpeaking = (speaking: boolean): void => {
|
||||
this.setState({ speaking });
|
||||
};
|
||||
|
||||
private onResize = (e) => {
|
||||
if (this.props.onResize && !this.props.feed.isLocal()) {
|
||||
this.props.onResize(e);
|
||||
|
@ -173,6 +187,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
|
||||
const wrapperClasses = classnames("mx_VideoFeed", {
|
||||
mx_VideoFeed_voice: this.state.videoMuted,
|
||||
mx_VideoFeed_speaking: this.state.speaking,
|
||||
});
|
||||
const micIconClasses = classnames("mx_VideoFeed_mic", {
|
||||
mx_VideoFeed_mic_muted: this.state.audioMuted,
|
||||
|
|
|
@ -426,9 +426,6 @@
|
|||
"Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message",
|
||||
"Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown",
|
||||
"Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown",
|
||||
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
||||
"/ddg is not a command": "/ddg is not a command",
|
||||
"To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.",
|
||||
"Upgrades a room to a new version": "Upgrades a room to a new version",
|
||||
"You do not have the required permissions to use this command.": "You do not have the required permissions to use this command.",
|
||||
"Changes your display nickname": "Changes your display nickname",
|
||||
|
@ -2411,8 +2408,8 @@
|
|||
"Clear cache and resync": "Clear cache and resync",
|
||||
"%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!",
|
||||
"Updating %(brand)s": "Updating %(brand)s",
|
||||
"Leave all rooms and spaces": "Leave all rooms and spaces",
|
||||
"Don't leave any": "Don't leave any",
|
||||
"Leave all rooms and spaces": "Leave all rooms and spaces",
|
||||
"Leave specific rooms and spaces": "Leave specific rooms and spaces",
|
||||
"Search %(spaceName)s": "Search %(spaceName)s",
|
||||
"You won't be able to rejoin unless you are re-invited.": "You won't be able to rejoin unless you are re-invited.",
|
||||
|
@ -3002,8 +2999,6 @@
|
|||
"Commands": "Commands",
|
||||
"Command Autocomplete": "Command Autocomplete",
|
||||
"Community Autocomplete": "Community Autocomplete",
|
||||
"Results from DuckDuckGo": "Results from DuckDuckGo",
|
||||
"DuckDuckGo Results": "DuckDuckGo Results",
|
||||
"Emoji": "Emoji",
|
||||
"Emoji Autocomplete": "Emoji Autocomplete",
|
||||
"Notify the whole room": "Notify the whole room",
|
||||
|
|
|
@ -71,7 +71,13 @@ export default class DeviceSettingsHandler extends SettingsHandler {
|
|||
// Special case for old useIRCLayout setting
|
||||
if (settingName === "layout") {
|
||||
const settings = this.getSettings() || {};
|
||||
if (settings["useIRCLayout"]) return Layout.IRC;
|
||||
if (settings["useIRCLayout"]) {
|
||||
// Set the new layout setting and delete the old one so that we
|
||||
// can delete this block of code after some time
|
||||
settings["layout"] = Layout.IRC;
|
||||
delete settings["useIRCLayout"];
|
||||
localStorage.setItem("mx_local_settings", JSON.stringify(settings));
|
||||
}
|
||||
return settings[settingName];
|
||||
}
|
||||
|
||||
|
|
|
@ -678,12 +678,14 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
this.emit(room.roomId);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
case EventType.RoomMember:
|
||||
if (room.isSpaceRoom()) {
|
||||
this.onSpaceMembersChange(ev);
|
||||
}
|
||||
break;
|
||||
// listening for m.room.member events in onRoomState above doesn't work as the Member object isn't updated by then
|
||||
private onRoomStateMembers = (ev: MatrixEvent) => {
|
||||
const room = this.matrixClient.getRoom(ev.getRoomId());
|
||||
if (room?.isSpaceRoom()) {
|
||||
this.onSpaceMembersChange(ev);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -743,6 +745,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
|
||||
this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
|
||||
this.matrixClient.removeListener("RoomState.events", this.onRoomState);
|
||||
this.matrixClient.removeListener("RoomState.members", this.onRoomStateMembers);
|
||||
this.matrixClient.removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
await this.reset();
|
||||
|
@ -754,6 +757,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.matrixClient.on("Room.myMembership", this.onRoom);
|
||||
this.matrixClient.on("Room.accountData", this.onRoomAccountData);
|
||||
this.matrixClient.on("RoomState.events", this.onRoomState);
|
||||
this.matrixClient.on("RoomState.members", this.onRoomStateMembers);
|
||||
this.matrixClient.on("accountData", this.onAccountData);
|
||||
|
||||
this.matrixClient.getCapabilities().then(capabilities => {
|
||||
|
|
|
@ -20,6 +20,27 @@ import { IAlgorithm } from "./IAlgorithm";
|
|||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import * as Unread from "../../../../Unread";
|
||||
import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/membership";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
export function shouldCauseReorder(event: MatrixEvent): boolean {
|
||||
const type = event.getType();
|
||||
const content = event.getContent();
|
||||
const prevContent = event.getPrevContent();
|
||||
|
||||
// Never ignore membership changes
|
||||
if (type === EventType.RoomMember && prevContent.membership !== content.membership) return true;
|
||||
|
||||
// Ignore status changes
|
||||
// XXX: This should be an enum
|
||||
if (type === "im.vector.user_status") return false;
|
||||
// Ignore display name changes
|
||||
if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return false;
|
||||
// Ignore avatar changes
|
||||
if (type === EventType.RoomMember && prevContent.avatar_url !== content.avatar_url) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export const sortRooms = (rooms: Room[]): Room[] => {
|
||||
// We cache the timestamp lookup to avoid iterating forever on the timeline
|
||||
|
@ -68,7 +89,10 @@ export const sortRooms = (rooms: Room[]): Room[] => {
|
|||
const ev = r.timeline[i];
|
||||
if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?)
|
||||
|
||||
if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) {
|
||||
if (
|
||||
(ev.getSender() === myUserId && shouldCauseReorder(ev)) ||
|
||||
Unread.eventTriggersUnreadCount(ev)
|
||||
) {
|
||||
return ev.getTs();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,9 +33,7 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
|
|||
import ActiveRoomObserver from "../../ActiveRoomObserver";
|
||||
import Modal from "../../Modal";
|
||||
import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
|
||||
import WidgetCapabilitiesPromptDialog, {
|
||||
getRememberedCapabilitiesForWidget,
|
||||
} from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog";
|
||||
import WidgetCapabilitiesPromptDialog from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog";
|
||||
import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions";
|
||||
import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore";
|
||||
import { WidgetType } from "../../widgets/WidgetType";
|
||||
|
@ -48,6 +46,14 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|||
|
||||
// TODO: Purge this from the universe
|
||||
|
||||
function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] {
|
||||
return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]");
|
||||
}
|
||||
|
||||
function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) {
|
||||
localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps));
|
||||
}
|
||||
|
||||
export class StopGapWidgetDriver extends WidgetDriver {
|
||||
private allowedCapabilities: Set<Capability>;
|
||||
|
||||
|
@ -100,6 +106,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
}
|
||||
}
|
||||
// TODO: Do something when the widget requests new capabilities not yet asked for
|
||||
let rememberApproved = false;
|
||||
if (missing.size > 0) {
|
||||
try {
|
||||
const [result] = await Modal.createTrackedDialog(
|
||||
|
@ -111,12 +118,19 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
widgetKind: this.forWidgetKind,
|
||||
}).finished;
|
||||
(result.approved || []).forEach(cap => allowedSoFar.add(cap));
|
||||
rememberApproved = result.remember;
|
||||
} catch (e) {
|
||||
console.error("Non-fatal error getting capabilities: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new Set(iterableUnion(allowedSoFar, requested));
|
||||
const allAllowed = new Set(iterableUnion(allowedSoFar, requested));
|
||||
|
||||
if (rememberApproved) {
|
||||
setRememberedCapabilitiesForWidget(this.forWidget, Array.from(allAllowed));
|
||||
}
|
||||
|
||||
return allAllowed;
|
||||
}
|
||||
|
||||
public async sendEvent(eventType: string, content: any, stateKey: string = null): Promise<ISendEventDetails> {
|
||||
|
@ -129,6 +143,9 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
if (stateKey !== null) {
|
||||
// state event
|
||||
r = await client.sendStateEvent(roomId, eventType, content, stateKey);
|
||||
} else if (eventType === EventType.RoomRedaction) {
|
||||
// special case: extract the `redacts` property and call redact
|
||||
r = await client.redactEvent(roomId, content['redacts']);
|
||||
} else {
|
||||
// message event
|
||||
r = await client.sendEvent(roomId, eventType, content);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue