Merge branch 'develop' into kegan/lists-as-keys

pull/28788/head^2
kegsay 2023-01-20 16:24:18 +00:00 committed by GitHub
commit 6ec25234d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 127 additions and 77 deletions

View File

@ -1,3 +1,9 @@
Changes in [3.64.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.64.2) (2023-01-20)
=====================================================================================================
## 🐛 Bug Fixes
* Fix second occurence of a crash in older browsers
Changes in [3.64.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.64.1) (2023-01-18)
=====================================================================================================

View File

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "3.64.1",
"version": "3.64.2",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@ -57,7 +57,7 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/analytics-events": "^0.4.0",
"@matrix-org/matrix-wysiwyg": "^0.19.0",
"@matrix-org/matrix-wysiwyg": "^0.20.0",
"@matrix-org/react-sdk-module-api": "^0.0.3",
"@sentry/browser": "^7.0.0",
"@sentry/tracing": "^7.0.0",

View File

@ -84,7 +84,7 @@ limitations under the License.
align-items: center;
&:hover,
&:focus {
&:focus-visible {
background-color: $menu-selected-color;
}

View File

@ -108,7 +108,7 @@ limitations under the License.
display: flex;
user-select: none;
&:not(.mx_RoomHeader_name--textonly):hover {
&:hover {
background-color: $quinary-content;
}

View File

@ -44,9 +44,21 @@ limitations under the License.
padding-inline-start: $spacing-28;
}
blockquote {
color: #777;
border-left: 2px solid $blockquote-bar-color;
border-radius: 2px;
padding: 0 10px;
margin-block-start: 0;
margin-block-end: 0;
margin-inline-start: 0;
margin-inline-end: 0;
}
// model output always includes a linebreak but we do not want the user
// to see it when writing input in lists
:is(ol, ul, pre) + br:last-of-type {
:is(ol, ul, pre, blockquote) + br:last-of-type {
display: none;
}

View File

@ -17,4 +17,5 @@ limitations under the License.
.mx_CallDuration {
color: $secondary-content;
font-size: $font-12px;
white-space: nowrap;
}

View File

@ -160,7 +160,7 @@ limitations under the License.
content: "";
display: inline-block;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-size: $size;
mask-size: 20px;
mask-position: center;
background-color: $call-primary-content;
height: 100%;
@ -181,7 +181,7 @@ limitations under the License.
.mx_CallView_deviceButton {
&.mx_CallView_deviceButton_audio::before {
mask-image: url("$(res)/img/element-icons/Mic-off.svg");
mask-size: 14px;
mask-size: 18px;
}
&.mx_CallView_deviceButton_video::before {

View File

@ -0,0 +1,6 @@
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.1458 0.893371C2.20888 0.465499 1.90205 0.0690897 1.46046 0.00796516C1.01887 -0.0531594 0.609758 0.244148 0.546674 0.67202L0.00822047 4.32413C-0.0548633 4.752 0.251974 5.14841 0.69356 5.20954C1.13515 5.27066 1.54426 4.97336 1.60735 4.54548L2.1458 0.893371Z" fill="currentColor"/>
<path d="M10.2226 7.67587C10.2857 7.248 9.97885 6.85159 9.53726 6.79046C9.09568 6.72934 8.68656 7.02664 8.62348 7.45452L8.08502 11.1066C8.02194 11.5345 8.32878 11.9309 8.77036 11.992C9.21195 12.0532 9.62107 11.7559 9.68415 11.328L10.2226 7.67587Z" fill="currentColor"/>
<path d="M5.21224 0.00574343C5.65509 0.0575575 5.97074 0.447414 5.91727 0.876513L5.90255 0.993287C5.89309 1.06788 5.87936 1.17541 5.86224 1.30757C5.828 1.57178 5.78013 1.93492 5.72561 2.33035C5.6179 3.11153 5.48009 4.04989 5.36895 4.58829C5.28147 5.01211 4.85597 5.28697 4.41856 5.20221C3.98115 5.11744 3.69748 4.70515 3.78496 4.28133C3.88411 3.80099 4.01552 2.91329 4.12447 2.12309C4.17828 1.73284 4.22559 1.37397 4.25946 1.11259C4.27639 0.981947 4.28994 0.875787 4.29925 0.802389L4.31351 0.689266C4.36698 0.260167 4.76938 -0.0460706 5.21224 0.00574343Z" fill="currentColor"/>
<path d="M13.9918 7.67587C14.0549 7.248 13.748 6.85159 13.3064 6.79046C12.8649 6.72934 12.4557 7.02664 12.3927 7.45452L11.8542 11.1066C11.7911 11.5345 12.098 11.9309 12.5395 11.992C12.9811 12.0532 13.3902 11.7559 13.4533 11.328L13.9918 7.67587Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -336,6 +336,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
const { permalinkCreator, relation, replyToEvent } = this.props;
const composerContent = this.state.composerContent;
this.setState({ composerContent: "", initialComposerContent: "" });
dis.dispatch({ action: Action.ClearAndFocusSendMessageComposer });
await sendMessage(composerContent, this.state.isRichTextEnabled, {
mxClient: this.props.mxClient,
roomContext: this.context,
@ -343,7 +344,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
relation,
replyToEvent,
});
dis.dispatch({ action: Action.ClearAndFocusSendMessageComposer });
}
};

View File

@ -743,6 +743,13 @@ export default class RoomHeader extends React.Component<IProps, IState> {
const buttons = this.props.showButtons ? this.renderButtons(isVideoRoom) : null;
let oobName = _t("Join Room");
if (this.props.oobData && this.props.oobData.name) {
oobName = this.props.oobData.name;
}
const name = this.renderName(oobName);
if (this.props.viewingCall && !isVideoRoom) {
return (
<header className="mx_RoomHeader light-panel">
@ -752,9 +759,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
>
<div className="mx_RoomHeader_avatar">{roomAvatar}</div>
{icon}
<div className="mx_RoomHeader_name mx_RoomHeader_name--textonly mx_RoomHeader_name--small">
{_t("Video call")}
</div>
{name}
{this.props.activeCall instanceof ElementCall && (
<GroupCallDuration groupCall={this.props.activeCall.groupCall} />
)}
@ -779,13 +784,6 @@ export default class RoomHeader extends React.Component<IProps, IState> {
);
}
let oobName = _t("Join Room");
if (this.props.oobData && this.props.oobData.name) {
oobName = this.props.oobData.name;
}
const name = this.renderName(oobName);
const topicElement = <RoomTopic room={this.props.room} className="mx_RoomHeader_topic" />;
const viewLabs = (): void =>

View File

@ -22,6 +22,7 @@ import { Icon as BoldIcon } from "../../../../../../res/img/element-icons/room/c
import { Icon as ItalicIcon } from "../../../../../../res/img/element-icons/room/composer/italic.svg";
import { Icon as UnderlineIcon } from "../../../../../../res/img/element-icons/room/composer/underline.svg";
import { Icon as StrikeThroughIcon } from "../../../../../../res/img/element-icons/room/composer/strikethrough.svg";
import { Icon as QuoteIcon } from "../../../../../../res/img/element-icons/room/composer/quote.svg";
import { Icon as InlineCodeIcon } from "../../../../../../res/img/element-icons/room/composer/inline_code.svg";
import { Icon as LinkIcon } from "../../../../../../res/img/element-icons/room/composer/link.svg";
import { Icon as BulletedListIcon } from "../../../../../../res/img/element-icons/room/composer/bulleted_list.svg";
@ -126,6 +127,12 @@ export function FormattingButtons({ composer, actionStates }: FormattingButtonsP
onClick={() => composer.orderedList()}
icon={<NumberedListIcon className="mx_FormattingButtons_Icon" />}
/>
<Button
actionState={actionStates.quote}
label={_td("Quote")}
onClick={() => composer.quote()}
icon={<QuoteIcon className="mx_FormattingButtons_Icon" />}
/>
<Button
actionState={actionStates.inlineCode}
label={_td("Code")}

View File

@ -38,7 +38,7 @@ import IconizedContextMenu, {
IconizedContextMenuOption,
IconizedContextMenuOptionList,
} from "../context_menus/IconizedContextMenu";
import { aboveLeftOf, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
import { aboveRightOf, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
import { Alignment } from "../elements/Tooltip";
import { ButtonEvent } from "../elements/AccessibleButton";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
@ -81,7 +81,7 @@ const DeviceButton: FC<DeviceButtonProps> = ({
if (showMenu) {
const buttonRect = buttonRef.current!.getBoundingClientRect();
contextMenu = (
<IconizedContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}>
<IconizedContextMenu {...aboveRightOf(buttonRect, undefined, 10)} onFinished={closeMenu}>
<IconizedContextMenuOptionList>
{devices.map((d) => (
<IconizedContextMenuOption key={d.deviceId} label={d.label} onClick={() => selectDevice(d)} />
@ -101,6 +101,7 @@ const DeviceButton: FC<DeviceButtonProps> = ({
>
<AccessibleTooltipButton
className={`mx_CallView_deviceButton mx_CallView_deviceButton_${kind}`}
inputRef={buttonRef}
title={muted ? mutedTitle : unmutedTitle}
alignment={Alignment.Top}
onClick={toggle}
@ -109,7 +110,6 @@ const DeviceButton: FC<DeviceButtonProps> = ({
{devices.length > 1 ? (
<ContextMenuButton
className="mx_CallView_deviceListButton"
inputRef={buttonRef}
onClick={openMenu}
isExpanded={showMenu}
label={deviceListLabel}

View File

@ -63,6 +63,9 @@ export function useEventEmitter(emitter: EventEmitter | undefined, eventName: st
type Mapper<T> = (...args: any[]) => T;
/**
* {@link useEventEmitterState}
*/
export function useTypedEventEmitterState<T, Events extends string, Arguments extends ListenerMap<Events>>(
emitter: TypedEventEmitter<Events, Arguments>,
eventName: Events,
@ -71,6 +74,16 @@ export function useTypedEventEmitterState<T, Events extends string, Arguments ex
return useEventEmitterState<T>(emitter, eventName, fn);
}
/**
* Creates a state, that can be updated by events.
*
* @param emitter The emitter sending the event
* @param eventName Event name to listen for
* @param fn The callback function, that should return the state value.
* It should have the signature of the event callback, except that all parameters are optional.
* If the params are not set, a default value for the state should be returned.
* @returns State
*/
export function useEventEmitterState<T>(
emitter: EventEmitter | undefined,
eventName: string | symbol,

View File

@ -1997,9 +1997,9 @@
"Close call": "Close call",
"View chat timeline": "View chat timeline",
"Room options": "Room options",
"Join Room": "Join Room",
"(~%(count)s results)|other": "(~%(count)s results)",
"(~%(count)s results)|one": "(~%(count)s result)",
"Join Room": "Join Room",
"Video rooms are a beta feature": "Video rooms are a beta feature",
"Video room": "Video room",
"Public space": "Public space",

View File

@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState } from "react";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { useTypedEventEmitterState } from "../../hooks/useEventEmitter";
import { VoiceBroadcastPlayback } from "../models/VoiceBroadcastPlayback";
import {
VoiceBroadcastPlaybacksStore,
@ -28,15 +26,11 @@ export const useCurrentVoiceBroadcastPlayback = (
): {
currentVoiceBroadcastPlayback: VoiceBroadcastPlayback | null;
} => {
const [currentVoiceBroadcastPlayback, setVoiceBroadcastPlayback] = useState(
voiceBroadcastPlaybackStore.getCurrent(),
);
useTypedEventEmitter(
const currentVoiceBroadcastPlayback = useTypedEventEmitterState(
voiceBroadcastPlaybackStore,
VoiceBroadcastPlaybacksStoreEvent.CurrentChanged,
(playback: VoiceBroadcastPlayback) => {
setVoiceBroadcastPlayback(playback);
(playback?: VoiceBroadcastPlayback) => {
return playback ?? voiceBroadcastPlaybackStore.getCurrent();
},
);

View File

@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState } from "react";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { useTypedEventEmitterState } from "../../hooks/useEventEmitter";
import { VoiceBroadcastPreRecordingStore } from "../stores/VoiceBroadcastPreRecordingStore";
import { VoiceBroadcastPreRecording } from "../models/VoiceBroadcastPreRecording";
@ -25,12 +23,14 @@ export const useCurrentVoiceBroadcastPreRecording = (
): {
currentVoiceBroadcastPreRecording: VoiceBroadcastPreRecording | null;
} => {
const [currentVoiceBroadcastPreRecording, setCurrentVoiceBroadcastPreRecording] = useState(
voiceBroadcastPreRecordingStore.getCurrent(),
const currentVoiceBroadcastPreRecording = useTypedEventEmitterState(
voiceBroadcastPreRecordingStore,
"changed",
(preRecording?: VoiceBroadcastPreRecording) => {
return preRecording ?? voiceBroadcastPreRecordingStore.getCurrent();
},
);
useTypedEventEmitter(voiceBroadcastPreRecordingStore, "changed", setCurrentVoiceBroadcastPreRecording);
return {
currentVoiceBroadcastPreRecording,
};

View File

@ -14,24 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState } from "react";
import { VoiceBroadcastRecording, VoiceBroadcastRecordingsStore, VoiceBroadcastRecordingsStoreEvent } from "..";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { useTypedEventEmitterState } from "../../hooks/useEventEmitter";
export const useCurrentVoiceBroadcastRecording = (
voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore,
): {
currentVoiceBroadcastRecording: VoiceBroadcastRecording;
} => {
const [currentVoiceBroadcastRecording, setCurrentVoiceBroadcastRecording] = useState(
voiceBroadcastRecordingsStore.getCurrent(),
);
useTypedEventEmitter(
const currentVoiceBroadcastRecording = useTypedEventEmitterState(
voiceBroadcastRecordingsStore,
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
setCurrentVoiceBroadcastRecording,
(recording?: VoiceBroadcastRecording) => {
return recording ?? voiceBroadcastRecordingsStore.getCurrent();
},
);
return {

View File

@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { useTypedEventEmitterState } from "../../hooks/useEventEmitter";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import {
VoiceBroadcastLiveness,
VoiceBroadcastPlayback,
VoiceBroadcastPlaybackEvent,
VoiceBroadcastPlaybackState,
VoiceBroadcastPlaybackTimes,
} from "..";
export const useVoiceBroadcastPlayback = (
@ -52,24 +52,35 @@ export const useVoiceBroadcastPlayback = (
playback.toggle();
};
const [playbackState, setPlaybackState] = useState(playback.getState());
useTypedEventEmitter(
const playbackState = useTypedEventEmitterState(
playback,
VoiceBroadcastPlaybackEvent.StateChanged,
(state: VoiceBroadcastPlaybackState, _playback: VoiceBroadcastPlayback) => {
setPlaybackState(state);
(state?: VoiceBroadcastPlaybackState) => {
return state ?? playback.getState();
},
);
const [times, setTimes] = useState({
duration: playback.durationSeconds,
position: playback.timeSeconds,
timeLeft: playback.timeLeftSeconds,
});
useTypedEventEmitter(playback, VoiceBroadcastPlaybackEvent.TimesChanged, (t) => setTimes(t));
const times = useTypedEventEmitterState(
playback,
VoiceBroadcastPlaybackEvent.TimesChanged,
(t?: VoiceBroadcastPlaybackTimes) => {
return (
t ?? {
duration: playback.durationSeconds,
position: playback.timeSeconds,
timeLeft: playback.timeLeftSeconds,
}
);
},
);
const [liveness, setLiveness] = useState(playback.getLiveness());
useTypedEventEmitter(playback, VoiceBroadcastPlaybackEvent.LivenessChanged, (l) => setLiveness(l));
const liveness = useTypedEventEmitterState(
playback,
VoiceBroadcastPlaybackEvent.LivenessChanged,
(l?: VoiceBroadcastLiveness) => {
return l ?? playback.getLiveness();
},
);
return {
times,

View File

@ -16,7 +16,7 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import React, { useState } from "react";
import React from "react";
import {
VoiceBroadcastInfoState,
@ -25,7 +25,7 @@ import {
VoiceBroadcastRecordingState,
} from "..";
import QuestionDialog from "../../components/views/dialogs/QuestionDialog";
import { useTypedEventEmitter } from "../../hooks/useEventEmitter";
import { useTypedEventEmitterState } from "../../hooks/useEventEmitter";
import { _t } from "../../languageHandler";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import Modal from "../../Modal";
@ -74,17 +74,21 @@ export const useVoiceBroadcastRecording = (
}
};
const [recordingState, setRecordingState] = useState(recording.getState());
useTypedEventEmitter(
const recordingState = useTypedEventEmitterState(
recording,
VoiceBroadcastRecordingEvent.StateChanged,
(state: VoiceBroadcastInfoState, _recording: VoiceBroadcastRecording) => {
setRecordingState(state);
(state?: VoiceBroadcastRecordingState) => {
return state ?? recording.getState();
},
);
const [timeLeft, setTimeLeft] = useState(recording.getTimeLeft());
useTypedEventEmitter(recording, VoiceBroadcastRecordingEvent.TimeLeftChanged, setTimeLeft);
const timeLeft = useTypedEventEmitterState(
recording,
VoiceBroadcastRecordingEvent.TimeLeftChanged,
(t?: number) => {
return t ?? recording.getTimeLeft();
},
);
const live = (
[VoiceBroadcastInfoState.Started, VoiceBroadcastInfoState.Resumed] as VoiceBroadcastRecordingState[]

View File

@ -57,7 +57,7 @@ export enum VoiceBroadcastPlaybackEvent {
InfoStateChanged = "info_state_changed",
}
type VoiceBroadcastPlaybackTimes = {
export type VoiceBroadcastPlaybackTimes = {
duration: number;
position: number;
timeLeft: number;

View File

@ -32,6 +32,7 @@ const mockWysiwyg = {
link: jest.fn(),
orderedList: jest.fn(),
unorderedList: jest.fn(),
quote: jest.fn(),
} as unknown as FormattingFunctions;
const openLinkModalSpy = jest.spyOn(LinkModal, "openLinkModal");
@ -49,6 +50,7 @@ const testCases: Record<
link: { label: "Link", mockFormatFn: openLinkModalSpy },
orderedList: { label: "Numbered list", mockFormatFn: mockWysiwyg.orderedList },
unorderedList: { label: "Bulleted list", mockFormatFn: mockWysiwyg.unorderedList },
quote: { label: "Quote", mockFormatFn: mockWysiwyg.quote },
};
const createActionStates = (state: ActionState): AllActionStates => {

View File

@ -1589,10 +1589,10 @@
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.2.tgz#a09d0fea858e817da971a3c9f904632ef7b49eb6"
integrity sha512-oVkBCh9YP7H9i4gAoQbZzswniczfo/aIptNa4dxRi4Ff9lSvUCFv6Hvzi7C+90c0/PWZLXjIDTIAWZYmwyd2fA==
"@matrix-org/matrix-wysiwyg@^0.19.0":
version "0.19.0"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-wysiwyg/-/matrix-wysiwyg-0.19.0.tgz#5ffbabf8a59317ecdb45ba5fa1d06fff150ede40"
integrity sha512-1iL/+kjwWAlpWAq64DbkDkE7KGxvR5lNojZgCKMIyuvuKWv8Ikqxa9VOOYFtovKvSqgGRJaYN7/OkKWxZjiDcw==
"@matrix-org/matrix-wysiwyg@^0.20.0":
version "0.20.0"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-wysiwyg/-/matrix-wysiwyg-0.20.0.tgz#6193f790b031eaa96e944b647fe2b27018639d57"
integrity sha512-9VqzyccwizglssShi/M+tCxdZDgzsecH4WjlS0HC5KkLmljIxxlHFstf/D3C/G4ZfRodFUh6wUvd+oQI/ScPpw==
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
version "3.2.14"
@ -6492,8 +6492,8 @@ matrix-events-sdk@0.0.1:
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
version "23.1.0"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/83563c7a01bbeaf7f83f4b7feccc03647b536e7c"
version "23.1.1"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c309fe69426d701893ebee315105f8fa8fef03f8"
dependencies:
"@babel/runtime" "^7.12.5"
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.2"