Add wysisyg composer (can only send message, enable behind a labs flag)
This commit is contained in:
parent
16c3efead5
commit
bfb1638ff3
11 changed files with 401 additions and 15 deletions
|
@ -94,6 +94,7 @@
|
||||||
"matrix-events-sdk": "^0.0.1-beta.7",
|
"matrix-events-sdk": "^0.0.1-beta.7",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||||
"matrix-widget-api": "^1.1.1",
|
"matrix-widget-api": "^1.1.1",
|
||||||
|
"matrix-wysiwyg": "link:../matrix-wysiwyg/platforms/web",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"opus-recorder": "^8.0.3",
|
"opus-recorder": "^8.0.3",
|
||||||
"pako": "^2.0.3",
|
"pako": "^2.0.3",
|
||||||
|
|
|
@ -58,6 +58,7 @@ import {
|
||||||
startNewVoiceBroadcastRecording,
|
startNewVoiceBroadcastRecording,
|
||||||
VoiceBroadcastRecordingsStore,
|
VoiceBroadcastRecordingsStore,
|
||||||
} from '../../../voice-broadcast';
|
} from '../../../voice-broadcast';
|
||||||
|
import { WysiwygComposer } from './wysiwyg_composer/WysiwygComposer';
|
||||||
|
|
||||||
let instanceCount = 0;
|
let instanceCount = 0;
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
private voiceRecordingButton = createRef<VoiceRecordComposerTile>();
|
private voiceRecordingButton = createRef<VoiceRecordComposerTile>();
|
||||||
private ref: React.RefObject<HTMLDivElement> = createRef();
|
private ref: React.RefObject<HTMLDivElement> = createRef();
|
||||||
private instanceId: number;
|
private instanceId: number;
|
||||||
|
private composerSendMessage?: () => void;
|
||||||
|
|
||||||
private _voiceRecording: Optional<VoiceMessageRecording>;
|
private _voiceRecording: Optional<VoiceMessageRecording>;
|
||||||
|
|
||||||
|
@ -313,6 +315,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messageComposerInput.current?.sendMessage();
|
this.messageComposerInput.current?.sendMessage();
|
||||||
|
this.composerSendMessage?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onChange = (model: EditorModel) => {
|
private onChange = (model: EditorModel) => {
|
||||||
|
@ -321,6 +324,13 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onWysiwygChange = (content: string) => {
|
||||||
|
console.log('content', content);
|
||||||
|
this.setState({
|
||||||
|
isComposerEmpty: content?.length === 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
private onVoiceStoreUpdate = () => {
|
private onVoiceStoreUpdate = () => {
|
||||||
this.updateRecordingState();
|
this.updateRecordingState();
|
||||||
};
|
};
|
||||||
|
@ -394,6 +404,22 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
const canSendMessages = this.context.canSendMessages && !this.context.tombstone;
|
const canSendMessages = this.context.canSendMessages && !this.context.tombstone;
|
||||||
if (canSendMessages) {
|
if (canSendMessages) {
|
||||||
|
const isWysiwygComposerEnabled = SettingsStore.getValue("feature_wysiwyg_composer");
|
||||||
|
|
||||||
|
if (isWysiwygComposerEnabled) {
|
||||||
|
controls.push(
|
||||||
|
<WysiwygComposer key="controls_input"
|
||||||
|
disabled={this.state.haveRecording}
|
||||||
|
onChange={this.onWysiwygChange}
|
||||||
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
|
relation={this.props.relation}
|
||||||
|
replyToEvent={this.props.replyToEvent}>
|
||||||
|
{ (sendMessage) => {
|
||||||
|
this.composerSendMessage = sendMessage;
|
||||||
|
} }
|
||||||
|
</WysiwygComposer>,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
controls.push(
|
controls.push(
|
||||||
<SendMessageComposer
|
<SendMessageComposer
|
||||||
ref={this.messageComposerInput}
|
ref={this.messageComposerInput}
|
||||||
|
@ -408,6 +434,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
toggleStickerPickerOpen={this.toggleStickerPickerOpen}
|
toggleStickerPickerOpen={this.toggleStickerPickerOpen}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
controls.push(<VoiceRecordComposerTile
|
controls.push(<VoiceRecordComposerTile
|
||||||
key="controls_voice_record"
|
key="controls_voice_record"
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useWysiwyg } from "matrix-wysiwyg";
|
||||||
|
import { IEventRelation, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
|
|
||||||
|
import { useRoomContext } from '../../../../contexts/RoomContext';
|
||||||
|
import { sendMessage } from './message';
|
||||||
|
import { RoomPermalinkCreator } from '../../../../utils/permalinks/Permalinks';
|
||||||
|
import { useMatrixClientContext } from '../../../../contexts/MatrixClientContext';
|
||||||
|
|
||||||
|
interface WysiwygProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
onChange: (content: string) => void;
|
||||||
|
relation: IEventRelation;
|
||||||
|
replyToEvent?: MatrixEvent;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
|
includeReplyLegacyFallback?: boolean;
|
||||||
|
children?: (sendMessage: () => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WysiwygComposer(
|
||||||
|
{ disabled = false, onChange, children, ...props }: WysiwygProps, forwardRef,
|
||||||
|
) {
|
||||||
|
const roomContext = useRoomContext();
|
||||||
|
const mxClient = useMatrixClientContext();
|
||||||
|
|
||||||
|
const [content, setContent] = useState<string>();
|
||||||
|
const { ref, isWysiwygReady } = useWysiwyg({ onChange: (_content) => {
|
||||||
|
setContent(_content);
|
||||||
|
onChange(_content);
|
||||||
|
} });
|
||||||
|
|
||||||
|
const memoizedSendMessage = useCallback(() => sendMessage(content, mxClient, { roomContext, ...props }),
|
||||||
|
[content, mxClient, roomContext, props],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_SendMessageComposer" style={{ minHeight: '30px' }} ref={ref} contentEditable={!disabled && isWysiwygReady} suppressContentEditableWarning={true}>
|
||||||
|
<br />
|
||||||
|
{ children?.(memoizedSendMessage) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
190
src/components/views/rooms/wysiwyg_composer/message.ts
Normal file
190
src/components/views/rooms/wysiwyg_composer/message.ts
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
||||||
|
import { IContent, IEventRelation, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
|
|
||||||
|
import { PosthogAnalytics } from "../../../../PosthogAnalytics";
|
||||||
|
import SettingsStore from "../../../../settings/SettingsStore";
|
||||||
|
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../sendTimePerformanceMetrics";
|
||||||
|
import { attachRelation } from "../SendMessageComposer";
|
||||||
|
import { addReplyToMessageContent } from "../../../../utils/Reply";
|
||||||
|
import { RoomPermalinkCreator } from "../../../../utils/permalinks/Permalinks";
|
||||||
|
import { doMaybeLocalRoomAction } from "../../../../utils/local-room";
|
||||||
|
import { CHAT_EFFECTS } from "../../../../effects";
|
||||||
|
import { containsEmoji } from "../../../../effects/utils";
|
||||||
|
import { IRoomState } from "../../../structures/RoomView";
|
||||||
|
import dis from '../../../../dispatcher/dispatcher';
|
||||||
|
|
||||||
|
interface SendMessageParams {
|
||||||
|
relation: IEventRelation;
|
||||||
|
replyToEvent?: MatrixEvent;
|
||||||
|
roomContext: IRoomState;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
|
includeReplyLegacyFallback?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exported for tests
|
||||||
|
export function createMessageContent(
|
||||||
|
message: string,
|
||||||
|
{ relation, replyToEvent, permalinkCreator, includeReplyLegacyFallback = true }:
|
||||||
|
Omit<SendMessageParams, 'roomContext'>,
|
||||||
|
): IContent {
|
||||||
|
const isEmote = false;
|
||||||
|
|
||||||
|
// TODO do somethings about emote ?
|
||||||
|
|
||||||
|
/*const isEmote = containsEmote(model);
|
||||||
|
if (isEmote) {
|
||||||
|
model = stripEmoteCommand(model);
|
||||||
|
}
|
||||||
|
if (startsWith(model, "//")) {
|
||||||
|
model = stripPrefix(model, "/");
|
||||||
|
}
|
||||||
|
model = unescapeMessage(model);*/
|
||||||
|
|
||||||
|
// const body = textSerialize(model);
|
||||||
|
const body = message;
|
||||||
|
|
||||||
|
const content: IContent = {
|
||||||
|
msgtype: isEmote ? "m.emote" : "m.text",
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO markdown support
|
||||||
|
|
||||||
|
/*const formattedBody = htmlSerializeIfNeeded(model, {
|
||||||
|
forceHTML: !!replyToEvent,
|
||||||
|
useMarkdown: SettingsStore.getValue("MessageComposerInput.useMarkdown"),
|
||||||
|
});*/
|
||||||
|
const formattedBody = message;
|
||||||
|
|
||||||
|
if (formattedBody) {
|
||||||
|
content.format = "org.matrix.custom.html";
|
||||||
|
content.formatted_body = formattedBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
attachRelation(content, relation);
|
||||||
|
if (replyToEvent) {
|
||||||
|
addReplyToMessageContent(content, replyToEvent, {
|
||||||
|
permalinkCreator,
|
||||||
|
includeLegacyFallback: includeReplyLegacyFallback,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendMessage(
|
||||||
|
message: string,
|
||||||
|
mxClient: MatrixClient,
|
||||||
|
{ roomContext, ...params }: SendMessageParams,
|
||||||
|
) {
|
||||||
|
console.log('message', message);
|
||||||
|
const { relation, replyToEvent } = params;
|
||||||
|
const { room } = roomContext;
|
||||||
|
const { roomId } = room;
|
||||||
|
|
||||||
|
const posthogEvent: ComposerEvent = {
|
||||||
|
eventName: "Composer",
|
||||||
|
isEditing: false,
|
||||||
|
isReply: Boolean(replyToEvent),
|
||||||
|
inThread: relation?.rel_type === THREAD_RELATION_TYPE.name,
|
||||||
|
};
|
||||||
|
if (posthogEvent.inThread) {
|
||||||
|
const threadRoot = room.findEventById(relation.event_id);
|
||||||
|
posthogEvent.startsThread = threadRoot?.getThread()?.events.length === 1;
|
||||||
|
}
|
||||||
|
PosthogAnalytics.instance.trackEvent<ComposerEvent>(posthogEvent);
|
||||||
|
|
||||||
|
let content: IContent;
|
||||||
|
|
||||||
|
// TODO slash comment
|
||||||
|
|
||||||
|
// TODO replace emotion end of message ?
|
||||||
|
|
||||||
|
// TODO quick reaction
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
content = createMessageContent(
|
||||||
|
message,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't bother sending an empty message
|
||||||
|
if (!content.body.trim()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||||
|
decorateStartSendingTime(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name
|
||||||
|
? relation.event_id
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const prom = doMaybeLocalRoomAction(
|
||||||
|
roomId,
|
||||||
|
(actualRoomId: string) => mxClient.sendMessage(actualRoomId, threadId, content),
|
||||||
|
mxClient,
|
||||||
|
);
|
||||||
|
if (replyToEvent) {
|
||||||
|
// Clear reply_to_event as we put the message into the queue
|
||||||
|
// if the send fails, retry will handle resending.
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'reply_to_event',
|
||||||
|
event: null,
|
||||||
|
context: roomContext.timelineRenderingType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dis.dispatch({ action: "message_sent" });
|
||||||
|
CHAT_EFFECTS.forEach((effect) => {
|
||||||
|
if (containsEmoji(content, effect.emojis)) {
|
||||||
|
// For initial threads launch, chat effects are disabled
|
||||||
|
// see #19731
|
||||||
|
const isNotThread = relation?.rel_type !== THREAD_RELATION_TYPE.name;
|
||||||
|
if (!SettingsStore.getValue("feature_thread") || isNotThread) {
|
||||||
|
dis.dispatch({ action: `effects.${effect.command}` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||||
|
prom.then(resp => {
|
||||||
|
sendRoundTripMetric(mxClient, roomId, resp.event_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO save history
|
||||||
|
// TODO save local state
|
||||||
|
|
||||||
|
// this.sendHistoryManager.save(model, replyToEvent);
|
||||||
|
// clear composer
|
||||||
|
// model.reset([]);
|
||||||
|
// this.editorRef.current?.clearUndoHistory();
|
||||||
|
// this.editorRef.current?.focus();
|
||||||
|
// this.clearStoredEditorState();
|
||||||
|
//if (shouldSend && SettingsStore.getValue("scrollToBottomOnMessageSent")) {
|
||||||
|
if (SettingsStore.getValue("scrollToBottomOnMessageSent")) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: "scroll_to_bottom",
|
||||||
|
timelineRenderingType: roomContext.timelineRenderingType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IEventRelation, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
|
||||||
|
export function useWysiwygStoredState(room: Room, relation: IEventRelation) {
|
||||||
|
const editorStateKey = useMemo(() => {
|
||||||
|
let key = `mx_cider_state_${room.roomId}`;
|
||||||
|
if (relation?.rel_type === THREAD_RELATION_TYPE.name) {
|
||||||
|
key += `_${relation.event_id}`;
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}, [room, relation]);
|
||||||
|
|
||||||
|
const clearStoredEditorState = useCallback(() => localStorage.removeItem(editorStateKey), [editorStateKey]);
|
||||||
|
|
||||||
|
return { clearStoredEditorState };
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DebouncedFunc, throttle } from "lodash";
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
|
export function useMatrixClient(room: Room) {
|
||||||
|
const mxClient = useMatrixClientContext();
|
||||||
|
|
||||||
|
const [prepareToEncrypt, setPrepareToEncrypt] = useState<DebouncedFunc<() => void>>();
|
||||||
|
useEffect(() => {
|
||||||
|
if (mxClient.isCryptoEnabled() && mxClient.isRoomEncrypted(room.roomId)) {
|
||||||
|
setPrepareToEncrypt(throttle(() => {
|
||||||
|
mxClient.prepareToEncrypt(room);
|
||||||
|
}, 60000, { leading: true, trailing: false }));
|
||||||
|
}
|
||||||
|
}, [mxClient, room]);
|
||||||
|
|
||||||
|
return { mxClient, prepareToEncrypt };
|
||||||
|
}
|
|
@ -25,6 +25,10 @@ export interface MatrixClientProps {
|
||||||
mxClient: MatrixClient;
|
mxClient: MatrixClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useMatrixClientContext() {
|
||||||
|
return useContext(MatrixClientContext);
|
||||||
|
}
|
||||||
|
|
||||||
const matrixHOC = <ComposedComponentProps extends {}>(
|
const matrixHOC = <ComposedComponentProps extends {}>(
|
||||||
ComposedComponent: ComponentClass<ComposedComponentProps>,
|
ComposedComponent: ComponentClass<ComposedComponentProps>,
|
||||||
) => {
|
) => {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
import { IRoomState } from "../components/structures/RoomView";
|
import { IRoomState } from "../components/structures/RoomView";
|
||||||
import { Layout } from "../settings/enums/Layout";
|
import { Layout } from "../settings/enums/Layout";
|
||||||
|
@ -68,3 +68,6 @@ const RoomContext = createContext<IRoomState>({
|
||||||
});
|
});
|
||||||
RoomContext.displayName = "RoomContext";
|
RoomContext.displayName = "RoomContext";
|
||||||
export default RoomContext;
|
export default RoomContext;
|
||||||
|
export function useRoomContext() {
|
||||||
|
return useContext(RoomContext);
|
||||||
|
}
|
||||||
|
|
|
@ -896,6 +896,7 @@
|
||||||
"How can I leave the beta?": "How can I leave the beta?",
|
"How can I leave the beta?": "How can I leave the beta?",
|
||||||
"To leave, return to this page and use the “%(leaveTheBeta)s” button.": "To leave, return to this page and use the “%(leaveTheBeta)s” button.",
|
"To leave, return to this page and use the “%(leaveTheBeta)s” button.": "To leave, return to this page and use the “%(leaveTheBeta)s” button.",
|
||||||
"Leave the beta": "Leave the beta",
|
"Leave the beta": "Leave the beta",
|
||||||
|
"Wysiwyg composer (under active development)": "Wysiwyg composer (under active development)",
|
||||||
"Render simple counters in room header": "Render simple counters in room header",
|
"Render simple counters in room header": "Render simple counters in room header",
|
||||||
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
|
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
|
||||||
"Support adding custom themes": "Support adding custom themes",
|
"Support adding custom themes": "Support adding custom themes",
|
||||||
|
|
|
@ -303,6 +303,13 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
"feature_wysiwyg_composer": {
|
||||||
|
isFeature: true,
|
||||||
|
labsGroup: LabGroup.Messaging,
|
||||||
|
displayName: _td("Wysiwyg composer (under active development)"),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"feature_state_counters": {
|
"feature_state_counters": {
|
||||||
isFeature: true,
|
isFeature: true,
|
||||||
labsGroup: LabGroup.Rooms,
|
labsGroup: LabGroup.Rooms,
|
||||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -6970,6 +6970,10 @@ matrix-widget-api@^1.1.1:
|
||||||
"@types/events" "^3.0.0"
|
"@types/events" "^3.0.0"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
|
|
||||||
|
"matrix-wysiwyg@link:../matrix-wysiwyg/platforms/web":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
mdurl@~1.0.1:
|
mdurl@~1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||||
|
@ -7943,6 +7947,14 @@ react-dom@17.0.2:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
scheduler "^0.20.2"
|
scheduler "^0.20.2"
|
||||||
|
|
||||||
|
react-dom@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||||
|
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
scheduler "^0.23.0"
|
||||||
|
|
||||||
react-focus-lock@^2.5.1:
|
react-focus-lock@^2.5.1:
|
||||||
version "2.9.1"
|
version "2.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16"
|
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16"
|
||||||
|
@ -8018,6 +8030,13 @@ react@17.0.2:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
react@^18.2.0:
|
||||||
|
version "18.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
|
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
read-pkg-up@^7.0.1:
|
read-pkg-up@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
|
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
|
||||||
|
@ -8403,6 +8422,13 @@ scheduler@^0.20.2:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
scheduler@^0.23.0:
|
||||||
|
version "0.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||||
|
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
|
||||||
schema-utils@^3.0.0:
|
schema-utils@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue