Merge branch 'develop' into sort-imports
Signed-off-by: Aaron Raimist <aaron@raim.ist>
This commit is contained in:
commit
7b94e13a84
642 changed files with 30052 additions and 8035 deletions
|
@ -31,6 +31,7 @@ interface IProps {
|
|||
className?: string;
|
||||
withoutScrollContainer?: boolean;
|
||||
previousPhase?: RightPanelPhases;
|
||||
previousPhaseLabel?: string;
|
||||
closeLabel?: string;
|
||||
onClose?(): void;
|
||||
refireParams?;
|
||||
|
@ -56,6 +57,7 @@ const BaseCard: React.FC<IProps> = ({
|
|||
footer,
|
||||
withoutScrollContainer,
|
||||
previousPhase,
|
||||
previousPhaseLabel,
|
||||
children,
|
||||
refireParams,
|
||||
}) => {
|
||||
|
@ -68,7 +70,8 @@ const BaseCard: React.FC<IProps> = ({
|
|||
refireParams: refireParams,
|
||||
});
|
||||
};
|
||||
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={_t("Back")} />;
|
||||
const label = previousPhaseLabel ?? _t("Back");
|
||||
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={label} />;
|
||||
}
|
||||
|
||||
let closeButton;
|
||||
|
|
|
@ -67,7 +67,11 @@ const EncryptionInfo: React.FC<IProps> = ({
|
|||
content = <PendingActionSpinner text={text} />;
|
||||
} else {
|
||||
content = (
|
||||
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={onStartVerification}>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
className="mx_UserInfo_wideButton mx_UserInfo_startVerification"
|
||||
onClick={onStartVerification}
|
||||
>
|
||||
{ _t("Start Verification") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
|
|
@ -93,9 +93,9 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
|||
{ _t("One of the following may be compromised:") }
|
||||
<ul>
|
||||
<li>{ _t("Your homeserver") }</li>
|
||||
<li>{ _t("The homeserver the user you’re verifying is connected to") }</li>
|
||||
<li>{ _t("Yours, or the other users’ internet connection") }</li>
|
||||
<li>{ _t("Yours, or the other users’ session") }</li>
|
||||
<li>{ _t("The homeserver the user you're verifying is connected to") }</li>
|
||||
<li>{ _t("Yours, or the other users' internet connection") }</li>
|
||||
<li>{ _t("Yours, or the other users' session") }</li>
|
||||
</ul>
|
||||
</div>,
|
||||
onFinished: onClose,
|
||||
|
|
|
@ -121,24 +121,26 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => {
|
|||
if (!pinnedEvents) {
|
||||
content = <Spinner />;
|
||||
} else if (pinnedEvents.length > 0) {
|
||||
let onUnpinClicked;
|
||||
if (canUnpin) {
|
||||
onUnpinClicked = async (event: MatrixEvent) => {
|
||||
const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, "");
|
||||
if (pinnedEvents?.getContent()?.pinned) {
|
||||
const pinned = pinnedEvents.getContent().pinned;
|
||||
const index = pinned.indexOf(event.getId());
|
||||
if (index !== -1) {
|
||||
pinned.splice(index, 1);
|
||||
await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, "");
|
||||
}
|
||||
const onUnpinClicked = async (event: MatrixEvent) => {
|
||||
const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, "");
|
||||
if (pinnedEvents?.getContent()?.pinned) {
|
||||
const pinned = pinnedEvents.getContent().pinned;
|
||||
const index = pinned.indexOf(event.getId());
|
||||
if (index !== -1) {
|
||||
pinned.splice(index, 1);
|
||||
await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, "");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// show them in reverse, with latest pinned at the top
|
||||
content = pinnedEvents.filter(Boolean).reverse().map(ev => (
|
||||
<PinnedEventTile key={ev.getId()} room={room} event={ev} onUnpinClicked={() => onUnpinClicked(ev)} />
|
||||
<PinnedEventTile
|
||||
key={ev.getId()}
|
||||
room={room}
|
||||
event={ev}
|
||||
onUnpinClicked={canUnpin ? () => onUnpinClicked(ev) : undefined}
|
||||
/>
|
||||
));
|
||||
} else {
|
||||
content = <div className="mx_PinnedMessagesCard_empty">
|
||||
|
|
|
@ -33,6 +33,9 @@ import { useSettingValue } from "../../../hooks/useSettings";
|
|||
import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
|
||||
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
||||
|
||||
const ROOM_INFO_PHASES = [
|
||||
RightPanelPhases.RoomSummary,
|
||||
|
@ -44,7 +47,24 @@ const ROOM_INFO_PHASES = [
|
|||
RightPanelPhases.Room3pidMemberInfo,
|
||||
];
|
||||
|
||||
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
|
||||
interface IUnreadIndicatorProps {
|
||||
className: string;
|
||||
}
|
||||
|
||||
const UnreadIndicator = ({ className }: IUnreadIndicatorProps) => {
|
||||
return <React.Fragment>
|
||||
<div className="mx_RightPanel_headerButton_unreadIndicator_bg" />
|
||||
<div className={className} />
|
||||
</React.Fragment>;
|
||||
};
|
||||
|
||||
interface IHeaderButtonProps {
|
||||
room: Room;
|
||||
isHighlighted: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
|
||||
const pinningEnabled = useSettingValue("feature_pinning");
|
||||
const pinnedEvents = usePinnedEvents(pinningEnabled && room);
|
||||
const readPinnedEvents = useReadPinnedEvents(pinningEnabled && room);
|
||||
|
@ -52,7 +72,7 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
|
|||
|
||||
let unreadIndicator;
|
||||
if (pinnedEvents.some(id => !readPinnedEvents.has(id))) {
|
||||
unreadIndicator = <div className="mx_RightPanel_pinnedMessagesButton_unreadIndicator" />;
|
||||
unreadIndicator = <UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
|
||||
}
|
||||
|
||||
return <HeaderButton
|
||||
|
@ -66,12 +86,44 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
|
|||
</HeaderButton>;
|
||||
};
|
||||
|
||||
const TimelineCardHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
|
||||
if (!SettingsStore.getValue("feature_maximised_widgets")) return null;
|
||||
let unreadIndicator;
|
||||
switch (RoomNotificationStateStore.instance.getRoomState(room).color) {
|
||||
case NotificationColor.Grey:
|
||||
unreadIndicator =
|
||||
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator mx_Indicator_gray" />;
|
||||
break;
|
||||
case NotificationColor.Red:
|
||||
unreadIndicator =
|
||||
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return <HeaderButton
|
||||
name="timelineCardButton"
|
||||
title={_t("Chat")}
|
||||
isHighlighted={isHighlighted}
|
||||
onClick={onClick}
|
||||
analytics={["Right Panel", "Timeline Panel Button", "click"]}
|
||||
>
|
||||
{ unreadIndicator }
|
||||
</HeaderButton>;
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
room?: Room;
|
||||
excludedRightPanelPhaseButtons?: Array<RightPanelPhases>;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.right_panel.RoomHeaderButtons")
|
||||
export default class RoomHeaderButtons extends HeaderButtons<IProps> {
|
||||
private static readonly THREAD_PHASES = [
|
||||
RightPanelPhases.ThreadPanel,
|
||||
RightPanelPhases.ThreadView,
|
||||
];
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props, HeaderKind.Room);
|
||||
}
|
||||
|
@ -116,35 +168,70 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
|
|||
// This toggles for us, if needed
|
||||
this.setPhase(RightPanelPhases.PinnedMessages);
|
||||
};
|
||||
private onTimelineCardClicked = () => {
|
||||
this.setPhase(RightPanelPhases.Timeline);
|
||||
};
|
||||
|
||||
private onThreadsPanelClicked = () => {
|
||||
if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
|
||||
dis.dispatch({
|
||||
action: Action.ToggleRightPanel,
|
||||
type: "room",
|
||||
});
|
||||
} else {
|
||||
dispatchShowThreadsPanelEvent();
|
||||
}
|
||||
};
|
||||
|
||||
public renderButtons() {
|
||||
return <>
|
||||
const rightPanelPhaseButtons: Map<RightPanelPhases, any> = new Map();
|
||||
|
||||
rightPanelPhaseButtons.set(RightPanelPhases.PinnedMessages,
|
||||
<PinnedMessagesHeaderButton
|
||||
room={this.props.room}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.PinnedMessages)}
|
||||
onClick={this.onPinnedMessagesClicked}
|
||||
/>
|
||||
{ SettingsStore.getValue("feature_thread") && <HeaderButton
|
||||
name="threadsButton"
|
||||
title={_t("Threads")}
|
||||
onClick={dispatchShowThreadsPanelEvent}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.ThreadPanel)}
|
||||
analytics={['Right Panel', 'Threads List Button', 'click']}
|
||||
/> }
|
||||
onClick={this.onPinnedMessagesClicked} />,
|
||||
);
|
||||
rightPanelPhaseButtons.set(RightPanelPhases.Timeline,
|
||||
<TimelineCardHeaderButton
|
||||
room={this.props.room}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.Timeline)}
|
||||
onClick={this.onTimelineCardClicked} />,
|
||||
);
|
||||
rightPanelPhaseButtons.set(RightPanelPhases.ThreadPanel,
|
||||
SettingsStore.getValue("feature_thread")
|
||||
? <HeaderButton
|
||||
name="threadsButton"
|
||||
title={_t("Threads")}
|
||||
onClick={this.onThreadsPanelClicked}
|
||||
isHighlighted={this.isPhase(RoomHeaderButtons.THREAD_PHASES)}
|
||||
analytics={['Right Panel', 'Threads List Button', 'click']} />
|
||||
: null,
|
||||
);
|
||||
rightPanelPhaseButtons.set(RightPanelPhases.NotificationPanel,
|
||||
<HeaderButton
|
||||
name="notifsButton"
|
||||
title={_t('Notifications')}
|
||||
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
|
||||
onClick={this.onNotificationsClicked}
|
||||
analytics={['Right Panel', 'Notification List Button', 'click']}
|
||||
/>
|
||||
analytics={['Right Panel', 'Notification List Button', 'click']} />,
|
||||
);
|
||||
rightPanelPhaseButtons.set(RightPanelPhases.RoomSummary,
|
||||
<HeaderButton
|
||||
name="roomSummaryButton"
|
||||
title={_t('Room Info')}
|
||||
isHighlighted={this.isPhase(ROOM_INFO_PHASES)}
|
||||
onClick={this.onRoomSummaryClicked}
|
||||
analytics={['Right Panel', 'Room Summary Button', 'click']}
|
||||
/>
|
||||
analytics={['Right Panel', 'Room Summary Button', 'click']} />,
|
||||
);
|
||||
|
||||
return <>
|
||||
{
|
||||
Array.from(rightPanelPhaseButtons.keys()).map((phase) =>
|
||||
( this.props.excludedRightPanelPhaseButtons.includes(phase)
|
||||
? null
|
||||
: rightPanelPhaseButtons.get(phase)))
|
||||
}
|
||||
</>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ import { Container, MAX_PINNED, WidgetLayoutStore } from "../../../stores/widget
|
|||
import RoomName from "../elements/RoomName";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import ExportDialog from "../dialogs/ExportDialog";
|
||||
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -139,14 +138,28 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
|||
mx_RoomSummaryCard_Button_pinned: isPinned,
|
||||
});
|
||||
|
||||
const isMaximised = WidgetLayoutStore.instance.isInContainer(room, app, Container.Center);
|
||||
const toggleMaximised = isMaximised
|
||||
? () => { WidgetLayoutStore.instance.moveToContainer(room, app, Container.Right); }
|
||||
: () => { WidgetLayoutStore.instance.moveToContainer(room, app, Container.Center); };
|
||||
|
||||
const maximiseTitle = isMaximised ? _t("Close") : _t("Maximise widget");
|
||||
|
||||
let openTitle = "";
|
||||
if (isPinned) {
|
||||
openTitle = _t("Unpin this widget to view it in this panel");
|
||||
} else if (isMaximised) {
|
||||
openTitle =_t("Close this widget to view it in this panel");
|
||||
}
|
||||
|
||||
return <div className={classes} ref={handle}>
|
||||
<AccessibleTooltipButton
|
||||
className="mx_RoomSummaryCard_icon_app"
|
||||
onClick={onOpenWidgetClick}
|
||||
// only show a tooltip if the widget is pinned
|
||||
title={isPinned ? _t("Unpin a widget to view it in this panel") : ""}
|
||||
forceHide={!isPinned}
|
||||
disabled={isPinned}
|
||||
title={openTitle}
|
||||
forceHide={!(isPinned || isMaximised)}
|
||||
disabled={isPinned || isMaximised}
|
||||
yOffset={-48}
|
||||
>
|
||||
<WidgetAvatar app={app} />
|
||||
|
@ -155,7 +168,10 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
|||
</AccessibleTooltipButton>
|
||||
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_RoomSummaryCard_app_options"
|
||||
className={classNames({
|
||||
"mx_RoomSummaryCard_app_options": true,
|
||||
"mx_RoomSummaryCard_maximised_widget": SettingsStore.getValue("feature_maximised_widgets"),
|
||||
})}
|
||||
isExpanded={menuDisplayed}
|
||||
onClick={openMenu}
|
||||
title={_t("Options")}
|
||||
|
@ -169,6 +185,13 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
|
|||
disabled={cannotPin}
|
||||
yOffset={-24}
|
||||
/>
|
||||
{ SettingsStore.getValue("feature_maximised_widgets") &&
|
||||
<AccessibleTooltipButton
|
||||
className={isMaximised ? "mx_RoomSummaryCard_app_minimise" : "mx_RoomSummaryCard_app_maximise"}
|
||||
onClick={toggleMaximised}
|
||||
title={maximiseTitle}
|
||||
yOffset={-24}
|
||||
/> }
|
||||
|
||||
{ contextMenu }
|
||||
</div>;
|
||||
|
@ -208,17 +231,19 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
|
|||
</Group>;
|
||||
};
|
||||
|
||||
const onRoomMembersClick = () => {
|
||||
export const onRoomMembersClick = (allowClose = true) => {
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.RoomMemberList,
|
||||
allowClose,
|
||||
});
|
||||
};
|
||||
|
||||
const onRoomFilesClick = () => {
|
||||
export const onRoomFilesClick = (allowClose = true) => {
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.FilePanel,
|
||||
allowClose,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -276,19 +301,17 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
|||
return <BaseCard header={header} className="mx_RoomSummaryCard" onClose={onClose}>
|
||||
<Group title={_t("About")} className="mx_RoomSummaryCard_aboutGroup">
|
||||
<Button className="mx_RoomSummaryCard_icon_people" onClick={onRoomMembersClick}>
|
||||
{ _t("%(count)s people", { count: memberCount }) }
|
||||
{ _t("People") }
|
||||
<span className="mx_BaseCard_Button_sublabel">
|
||||
{ memberCount }
|
||||
</span>
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}>
|
||||
{ _t("Show files") }
|
||||
{ _t("Files") }
|
||||
</Button>
|
||||
<Button className="mx_RoomSummaryCard_icon_export" onClick={onRoomExportClick}>
|
||||
{ _t("Export chat") }
|
||||
</Button>
|
||||
{ SettingsStore.getValue("feature_thread") && (
|
||||
<Button className="mx_RoomSummaryCard_icon_threads" onClick={dispatchShowThreadsPanelEvent}>
|
||||
{ _t("Show threads") }
|
||||
</Button>
|
||||
) }
|
||||
<Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}>
|
||||
{ _t("Share room") }
|
||||
</Button>
|
||||
|
|
207
src/components/views/right_panel/TimelineCard.tsx
Normal file
207
src/components/views/right_panel/TimelineCard.tsx
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
Copyright 2021 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 from 'react';
|
||||
import { EventSubscription } from "fbemitter";
|
||||
import { EventTimelineSet, IEventRelation, MatrixEvent, Room } from 'matrix-js-sdk/src';
|
||||
import { Thread } from 'matrix-js-sdk/src/models/thread';
|
||||
|
||||
import BaseCard from "./BaseCard";
|
||||
|
||||
import ResizeNotifier from '../../../utils/ResizeNotifier';
|
||||
import MessageComposer from '../rooms/MessageComposer';
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import { Layout } from '../../../settings/enums/Layout';
|
||||
import TimelinePanel from '../../structures/TimelinePanel';
|
||||
import { E2EStatus } from '../../../utils/ShieldUtils';
|
||||
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from '../../../utils/replaceableComponent';
|
||||
import { ActionPayload } from '../../../dispatcher/payloads';
|
||||
import { Action } from '../../../dispatcher/actions';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
import UploadBar from '../../structures/UploadBar';
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
onClose: () => void;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
e2eStatus?: E2EStatus;
|
||||
timelineSet?: EventTimelineSet;
|
||||
timelineRenderingType?: TimelineRenderingType;
|
||||
showComposer?: boolean;
|
||||
composerRelation?: IEventRelation;
|
||||
}
|
||||
interface IState {
|
||||
thread?: Thread;
|
||||
editState?: EditorStateTransfer;
|
||||
replyToEvent?: MatrixEvent;
|
||||
initialEventId?: string;
|
||||
initialEventHighlighted?: boolean;
|
||||
|
||||
// settings:
|
||||
showReadReceipts?: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("structures.TimelineCard")
|
||||
export default class TimelineCard extends React.Component<IProps, IState> {
|
||||
static contextType = RoomContext;
|
||||
|
||||
private dispatcherRef: string;
|
||||
private timelinePanelRef: React.RefObject<TimelinePanel> = React.createRef();
|
||||
private roomStoreToken: EventSubscription;
|
||||
private settingWatchers: string[];
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showReadReceipts: false,
|
||||
};
|
||||
this.settingWatchers = [];
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
// Remove RoomStore listener
|
||||
if (this.roomStoreToken) {
|
||||
this.roomStoreToken.remove();
|
||||
}
|
||||
dis.unregister(this.dispatcherRef);
|
||||
for (const watcher of this.settingWatchers) {
|
||||
SettingsStore.unwatchSetting(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
private onRoomViewStoreUpdate = async (initial?: boolean): Promise<void> => {
|
||||
const roomId = this.props.room.roomId;
|
||||
const newState: Pick<IState, any> = {
|
||||
// roomLoading: RoomViewStore.isRoomLoading(),
|
||||
// roomLoadError: RoomViewStore.getRoomLoadError(),
|
||||
|
||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
|
||||
initialEventId: RoomViewStore.getInitialEventId(),
|
||||
initialEventHighlighted: RoomViewStore.isInitialEventHighlighted(),
|
||||
replyToEvent: RoomViewStore.getQuotingEvent(),
|
||||
};
|
||||
|
||||
this.settingWatchers = this.settingWatchers.concat([
|
||||
SettingsStore.watchSetting("showReadReceipts", roomId, (...[,,, value]) =>
|
||||
this.setState({ showReadReceipts: value as boolean }),
|
||||
),
|
||||
]);
|
||||
this.setState(newState);
|
||||
};
|
||||
|
||||
private onAction = (payload: ActionPayload): void => {
|
||||
switch (payload.action) {
|
||||
case Action.EditEvent:
|
||||
this.setState({
|
||||
editState: payload.event ? new EditorStateTransfer(payload.event) : null,
|
||||
}, () => {
|
||||
if (payload.event) {
|
||||
this.timelinePanelRef.current?.scrollToEventIfNeeded(payload.event.getId());
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private onScroll = (): void => {
|
||||
if (this.state.initialEventId && this.state.initialEventHighlighted) {
|
||||
dis.dispatch({
|
||||
action: Action.ViewRoom,
|
||||
room_id: this.props.room.roomId,
|
||||
event_id: this.state.initialEventId,
|
||||
highlighted: false,
|
||||
replyingToEvent: this.state.replyToEvent,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private renderTimelineCardHeader = (): JSX.Element => {
|
||||
return <div className="mx_TimelineCard__header">
|
||||
<span>{ _t("Chat") }</span>
|
||||
</div>;
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const highlightedEventId = this.state.initialEventHighlighted
|
||||
? this.state.initialEventId
|
||||
: null;
|
||||
|
||||
return (
|
||||
<RoomContext.Provider value={{
|
||||
...this.context,
|
||||
timelineRenderingType: this.props.timelineRenderingType ?? this.context.timelineRenderingType,
|
||||
liveTimeline: this.props.timelineSet.getLiveTimeline(),
|
||||
}}>
|
||||
<BaseCard
|
||||
className="mx_ThreadPanel mx_TimelineCard"
|
||||
onClose={this.props.onClose}
|
||||
withoutScrollContainer={true}
|
||||
header={this.renderTimelineCardHeader()}
|
||||
>
|
||||
<TimelinePanel
|
||||
ref={this.timelinePanelRef}
|
||||
showReadReceipts={/*this.state.showReadReceipts*/ false} // TODO: RR's cause issues with limited horizontal space
|
||||
manageReadReceipts={true}
|
||||
manageReadMarkers={false} // No RM support in the TimelineCard
|
||||
sendReadReceiptOnLoad={true}
|
||||
timelineSet={this.props.timelineSet}
|
||||
showUrlPreview={true}
|
||||
layout={Layout.Group}
|
||||
hideThreadedMessages={false}
|
||||
hidden={false}
|
||||
showReactions={true}
|
||||
className="mx_RoomView_messagePanel mx_GroupLayout"
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
membersLoaded={true}
|
||||
editState={this.state.editState}
|
||||
eventId={this.state.initialEventId}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
highlightedEventId={highlightedEventId}
|
||||
onUserScroll={this.onScroll}
|
||||
/>
|
||||
|
||||
{ ContentMessages.sharedInstance().getCurrentUploads(this.props.composerRelation).length > 0 && (
|
||||
<UploadBar room={this.props.room} relation={this.props.composerRelation} />
|
||||
) }
|
||||
|
||||
<MessageComposer
|
||||
room={this.props.room}
|
||||
relation={this.props.composerRelation}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
replyToEvent={this.state.replyToEvent}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
e2eStatus={this.props.e2eStatus}
|
||||
compact={true}
|
||||
/>
|
||||
</BaseCard>
|
||||
</RoomContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ import RoomName from "../elements/RoomName";
|
|||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import ConfirmSpaceUserActionDialog from "../dialogs/ConfirmSpaceUserActionDialog";
|
||||
import { bulkSpaceBehaviour } from "../../../utils/space";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
|
@ -125,7 +125,7 @@ async function openDMForUser(matrixClient: MatrixClient, userId: string) {
|
|||
|
||||
if (lastActiveRoom) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
room_id: lastActiveRoom.roomId,
|
||||
});
|
||||
return;
|
||||
|
@ -367,7 +367,7 @@ const UserOptionsSection: React.FC<{
|
|||
const onReadReceiptButton = function() {
|
||||
const room = cli.getRoom(member.roomId);
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
action: Action.ViewRoom,
|
||||
highlighted: true,
|
||||
event_id: room.getEventReadUpTo(member.userId),
|
||||
room_id: member.roomId,
|
||||
|
@ -1121,6 +1121,10 @@ const PowerLevelEditor: React.FC<{
|
|||
const cli = useContext(MatrixClientContext);
|
||||
|
||||
const [selectedPowerLevel, setSelectedPowerLevel] = useState(user.powerLevel);
|
||||
useEffect(() => {
|
||||
setSelectedPowerLevel(user.powerLevel);
|
||||
}, [user]);
|
||||
|
||||
const onPowerChange = useCallback(async (powerLevel: number) => {
|
||||
setSelectedPowerLevel(powerLevel);
|
||||
|
||||
|
|
|
@ -222,7 +222,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
if (this.props.isRoomEncrypted) {
|
||||
text = _t("Verify all users in a room to ensure it's secure.");
|
||||
} else {
|
||||
text = _t("In encrypted rooms, verify all users to ensure it’s secure.");
|
||||
text = _t("In encrypted rooms, verify all users to ensure it's secure.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue