Merge remote-tracking branch 'origin/develop' into travis/voicemessages/timeline
This commit is contained in:
commit
54931cb43e
41 changed files with 1133 additions and 257 deletions
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {useContext, useState} from "react";
|
||||
import React, {useContext, useMemo, useState} from "react";
|
||||
import classNames from "classnames";
|
||||
import {Room} from "matrix-js-sdk/src/models/room";
|
||||
import {MatrixClient} from "matrix-js-sdk/src/client";
|
||||
|
@ -34,6 +34,7 @@ import DMRoomMap from "../../../utils/DMRoomMap";
|
|||
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
|
||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
matrixClient: MatrixClient;
|
||||
|
@ -57,6 +58,8 @@ interface IAddExistingToSpaceProps {
|
|||
|
||||
export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space, selected, onChange }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const visibleRooms = useMemo(() => sortRooms(cli.getVisibleRooms()), [cli]);
|
||||
|
||||
const [query, setQuery] = useState("");
|
||||
const lcQuery = query.toLowerCase();
|
||||
|
||||
|
@ -65,7 +68,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
|
|||
const existingRoomsSet = new Set(SpaceStore.instance.getChildRooms(space.roomId));
|
||||
|
||||
const joinRule = space.getJoinRule();
|
||||
const [spaces, rooms, dms] = cli.getVisibleRooms().reduce((arr, room) => {
|
||||
const [spaces, rooms, dms] = visibleRooms.reduce((arr, room) => {
|
||||
if (room.getMyMembership() !== "join") return arr;
|
||||
if (!room.name.toLowerCase().includes(lcQuery)) return arr;
|
||||
|
||||
|
|
|
@ -539,6 +539,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
onResize={this.props.onResize}
|
||||
showSkeleton={showSkeleton}
|
||||
extraTiles={extraTiles}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
alwaysVisible={ALWAYS_VISIBLE_TAGS.includes(orderedTagId)}
|
||||
/>
|
||||
});
|
||||
|
|
|
@ -44,6 +44,7 @@ import { ActionPayload } from "../../../dispatcher/payloads";
|
|||
import { Enable, Resizable } from "re-resizable";
|
||||
import { Direction } from "re-resizable/lib/resizer";
|
||||
import { polyfillTouchEvent } from "../../../@types/polyfill";
|
||||
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
|
||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||
import RoomListLayoutStore from "../../../stores/room-list/RoomListLayoutStore";
|
||||
import { arrayFastClone, arrayHasOrderChange } from "../../../utils/arrays";
|
||||
|
@ -75,7 +76,7 @@ interface IProps {
|
|||
onResize: () => void;
|
||||
showSkeleton?: boolean;
|
||||
alwaysVisible?: boolean;
|
||||
|
||||
resizeNotifier: ResizeNotifier;
|
||||
extraTiles?: ReactComponentElement<typeof ExtraTile>[];
|
||||
|
||||
// TODO: Account for https://github.com/vector-im/element-web/issues/14179
|
||||
|
@ -528,6 +529,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
tiles.push(<RoomTile
|
||||
room={room}
|
||||
key={`room-${room.roomId}`}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
showMessagePreview={this.layout.showPreviews}
|
||||
isMinimized={this.props.isMinimized}
|
||||
tag={this.props.tagId}
|
||||
|
|
|
@ -53,12 +53,14 @@ import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/Community
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { getUnsentMessages } from "../../structures/RoomStatusBar";
|
||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
|
||||
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
showMessagePreview: boolean;
|
||||
isMinimized: boolean;
|
||||
tag: TagID;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
}
|
||||
|
||||
type PartialDOMRect = Pick<DOMRect, "left" | "bottom">;
|
||||
|
@ -102,6 +104,9 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
};
|
||||
this.notificationState = RoomNotificationStateStore.instance.getRoomState(this.props.room);
|
||||
this.roomProps = EchoChamber.forRoom(this.props.room);
|
||||
if (this.props.resizeNotifier) {
|
||||
this.props.resizeNotifier.on("middlePanelResized", this.onResize);
|
||||
}
|
||||
}
|
||||
|
||||
private countUnsentEvents(): number {
|
||||
|
@ -116,6 +121,12 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
this.forceUpdate(); // notification state changed - update
|
||||
};
|
||||
|
||||
private onResize = () => {
|
||||
if (this.showMessagePreview && !this.state.messagePreview) {
|
||||
this.setState({messagePreview: this.generatePreview()});
|
||||
}
|
||||
};
|
||||
|
||||
private onLocalEchoUpdated = (ev: MatrixEvent, room: Room) => {
|
||||
if (!room?.roomId === this.props.room.roomId) return;
|
||||
this.setState({hasUnsentEvents: this.countUnsentEvents() > 0});
|
||||
|
@ -195,6 +206,9 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
);
|
||||
this.props.room.off("Room.name", this.onRoomNameUpdate);
|
||||
}
|
||||
if (this.props.resizeNotifier) {
|
||||
this.props.resizeNotifier.off("middlePanelResized", this.onResize);
|
||||
}
|
||||
ActiveRoomObserver.removeListener(this.props.room.roomId, this.onActiveRoomUpdate);
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
this.notificationState.off(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
|
||||
|
|
|
@ -28,6 +28,9 @@ import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
|
|||
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
|
||||
import RecordingPlayback from "../voice_messages/RecordingPlayback";
|
||||
import {MsgType} from "matrix-js-sdk/src/@types/event";
|
||||
import Modal from "../../../Modal";
|
||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||
import CallMediaHandler from "../../../CallMediaHandler";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -113,16 +116,59 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
|||
await this.state.recorder.stop();
|
||||
return;
|
||||
}
|
||||
const recorder = VoiceRecordingStore.instance.startRecording();
|
||||
await recorder.start();
|
||||
|
||||
// We don't need to remove the listener: the recorder will clean that up for us.
|
||||
recorder.on(UPDATE_EVENT, (ev: RecordingState) => {
|
||||
if (ev === RecordingState.EndingSoon) return; // ignore this state: it has no UI purpose here
|
||||
this.setState({recordingPhase: ev});
|
||||
});
|
||||
// The "microphone access error" dialogs are used a lot, so let's functionify them
|
||||
const accessError = () => {
|
||||
Modal.createTrackedDialog('Microphone Access Error', '', ErrorDialog, {
|
||||
title: _t("Unable to access your microphone"),
|
||||
description: <>
|
||||
<p>{_t(
|
||||
"We were unable to access your microphone. Please check your browser settings and try again.",
|
||||
)}</p>
|
||||
</>,
|
||||
});
|
||||
};
|
||||
|
||||
this.setState({recorder, recordingPhase: RecordingState.Started});
|
||||
// Do a sanity test to ensure we're about to grab a valid microphone reference. Things might
|
||||
// change between this and recording, but at least we will have tried.
|
||||
try {
|
||||
const devices = await CallMediaHandler.getDevices();
|
||||
if (!devices?.['audioinput']?.length) {
|
||||
Modal.createTrackedDialog('No Microphone Error', '', ErrorDialog, {
|
||||
title: _t("No microphone found"),
|
||||
description: <>
|
||||
<p>{_t(
|
||||
"We didn't find a microphone on your device. Please check your settings and try again.",
|
||||
)}</p>
|
||||
</>,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// else we probably have a device that is good enough
|
||||
} catch (e) {
|
||||
console.error("Error getting devices: ", e);
|
||||
accessError();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const recorder = VoiceRecordingStore.instance.startRecording();
|
||||
await recorder.start();
|
||||
|
||||
// We don't need to remove the listener: the recorder will clean that up for us.
|
||||
recorder.on(UPDATE_EVENT, (ev: RecordingState) => {
|
||||
if (ev === RecordingState.EndingSoon) return; // ignore this state: it has no UI purpose here
|
||||
this.setState({recordingPhase: ev});
|
||||
});
|
||||
|
||||
this.setState({recorder, recordingPhase: RecordingState.Started});
|
||||
} catch (e) {
|
||||
console.error("Error starting recording: ", e);
|
||||
accessError();
|
||||
|
||||
// noinspection ES6MissingAwait - if this goes wrong we don't want it to affect the call stack
|
||||
VoiceRecordingStore.instance.disposeRecording();
|
||||
}
|
||||
};
|
||||
|
||||
private renderWaveformArea(): ReactNode {
|
||||
|
|
|
@ -297,15 +297,19 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
const isActive = activeSpaces.includes(space);
|
||||
const itemClasses = classNames({
|
||||
"mx_SpaceItem": true,
|
||||
"mx_SpaceItem_narrow": isNarrow,
|
||||
"collapsed": collapsed,
|
||||
"hasSubSpaces": childSpaces && childSpaces.length,
|
||||
});
|
||||
|
||||
const isInvite = space.getMyMembership() === "invite";
|
||||
const classes = classNames("mx_SpaceButton", {
|
||||
mx_SpaceButton_active: isActive,
|
||||
mx_SpaceButton_hasMenuOpen: !!this.state.contextMenuPosition,
|
||||
mx_SpaceButton_narrow: isNarrow,
|
||||
mx_SpaceButton_invite: isInvite,
|
||||
});
|
||||
const notificationState = space.getMyMembership() === "invite"
|
||||
const notificationState = isInvite
|
||||
? StaticNotificationState.forSymbol("!", NotificationColor.Red)
|
||||
: SpaceStore.instance.getNotificationState(space.roomId);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue