Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/19192

This commit is contained in:
Michael Telatynski 2021-09-29 15:04:14 +01:00
commit c0b6c001ca
16 changed files with 223 additions and 195 deletions

View file

@ -48,7 +48,7 @@ Code should be committed as follows:
* CSS: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/css
* Theme specific CSS & resources: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/themes
React components in matrix-react-sdk are come in two different flavours:
React components in matrix-react-sdk come in two different flavours:
'structures' and 'views'. Structures are stateful components which handle the
more complicated business logic of the app, delegating their actual presentation
rendering to stateless 'view' components. For instance, the RoomView component

View file

@ -1,8 +1,10 @@
// autogenerated by rethemendex.sh
@import "./_animations.scss";
@import "./_common.scss";
@import "./_font-sizes.scss";
@import "./_font-weights.scss";
@import "./structures/_AutoHideScrollbar.scss";
@import "./structures/_BackdropPanel.scss";
@import "./structures/_CompatibilityPage.scss";
@import "./structures/_ContextualMenu.scss";
@import "./structures/_CreateRoom.scss";
@ -17,7 +19,6 @@
@import "./structures/_LeftPanelWidget.scss";
@import "./structures/_MainSplit.scss";
@import "./structures/_MatrixChat.scss";
@import "./structures/_BackdropPanel.scss";
@import "./structures/_MyGroups.scss";
@import "./structures/_NonUrgentToastContainer.scss";
@import "./structures/_NotificationPanel.scss";
@ -243,6 +244,7 @@
@import "./views/settings/_E2eAdvancedPanel.scss";
@import "./views/settings/_EmailAddresses.scss";
@import "./views/settings/_IntegrationManager.scss";
@import "./views/settings/_JoinRuleSettings.scss";
@import "./views/settings/_LayoutSwitcher.scss";
@import "./views/settings/_Notifications.scss";
@import "./views/settings/_PhoneNumbers.scss";

View file

@ -0,0 +1,88 @@
/*
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.
*/
.mx_JoinRuleSettings_upgradeRequired {
margin-left: 16px;
padding: 4px 16px;
border: 1px solid $accent-color;
border-radius: 8px;
color: $accent-color;
font-size: $font-12px;
line-height: $font-15px;
}
.mx_JoinRuleSettings_spacesWithAccess {
> h4 {
color: $secondary-content;
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
text-transform: uppercase;
}
> span {
font-weight: 500;
font-size: $font-14px;
line-height: 32px; // matches height of avatar for v-align
color: $secondary-content;
display: inline-block;
img.mx_RoomAvatar_isSpaceRoom,
.mx_RoomAvatar_isSpaceRoom img {
border-radius: 8px;
}
.mx_BaseAvatar {
margin-right: 8px;
}
& + span {
margin-left: 16px;
}
}
}
.mx_JoinRuleSettings_radioButton {
padding-top: 16px;
margin-bottom: 8px;
.mx_RadioButton_content {
margin-left: 14px;
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-24px;
color: $primary-content;
display: block;
}
& + span {
display: inline-block;
margin-left: 34px;
margin-bottom: 16px;
font-size: $font-15px;
line-height: $font-24px;
color: $secondary-content;
& + .mx_RadioButton {
border-top: 1px solid $menu-border-color;
}
}
}
.mx_JoinRuleSettings_linkButton {
padding: 0;
font-size: inherit;
}

View file

@ -19,37 +19,6 @@ limitations under the License.
padding: 0;
margin-bottom: 16px;
}
.mx_SecurityRoomSettingsTab_spacesWithAccess {
> h4 {
color: $secondary-content;
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
text-transform: uppercase;
}
> span {
font-weight: 500;
font-size: $font-14px;
line-height: 32px; // matches height of avatar for v-align
color: $secondary-content;
display: inline-block;
img.mx_RoomAvatar_isSpaceRoom,
.mx_RoomAvatar_isSpaceRoom img {
border-radius: 8px;
}
.mx_BaseAvatar {
margin-right: 8px;
}
& + span {
margin-left: 16px;
}
}
}
}
.mx_SecurityRoomSettingsTab_warning {
@ -68,47 +37,3 @@ limitations under the License.
border-bottom: 1px solid $menu-border-color;
margin-bottom: 32px;
}
.mx_SecurityRoomSettingsTab_upgradeRequired {
margin-left: 16px;
padding: 4px 16px;
border: 1px solid $accent-color;
border-radius: 8px;
color: $accent-color;
font-size: $font-12px;
line-height: $font-15px;
}
.mx_SecurityRoomSettingsTab_joinRule {
.mx_RadioButton {
padding-top: 16px;
margin-bottom: 8px;
.mx_RadioButton_content {
margin-left: 14px;
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-24px;
color: $primary-content;
display: block;
}
}
> span {
display: inline-block;
margin-left: 34px;
margin-bottom: 16px;
font-size: $font-15px;
line-height: $font-24px;
color: $secondary-content;
& + .mx_RadioButton {
border-top: 1px solid $menu-border-color;
}
}
.mx_AccessibleButton_kind_link {
padding: 0;
font-size: inherit;
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16"><path fill="#737D8C" fill-rule="evenodd" d="M1 2.75A.75.75 0 0 1 1.75 2h.005a.75.75 0 0 1 0 1.5H1.75A.75.75 0 0 1 1 2.75Zm2.495 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.005a.75.75 0 0 1 0 1.5h-.005a.75.75 0 0 1-.75-.75ZM1 6.75A.75.75 0 0 1 1.75 6h8.5a.75.75 0 0 1 0 1.5h-8.5A.75.75 0 0 1 1 6.75ZM1 9.75A.75.75 0 0 1 1.75 9h4.5a.75.75 0 0 1 0 1.5h-4.5A.75.75 0 0 1 1 9.75ZM1 13.75a.75.75 0 0 1 .75-.75h.005a.75.75 0 0 1 0 1.5H1.75a.75.75 0 0 1-.75-.75Zm2.495 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.01a.75.75 0 0 1 0 1.5h-.01a.75.75 0 0 1-.75-.75Zm2.5 0a.75.75 0 0 1 .75-.75h.005a.75.75 0 0 1 0 1.5h-.005a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -277,8 +277,15 @@ export default class ScrollPanel extends React.Component<IProps> {
// fractional values (both too big and too small)
// for scrollTop happen on certain browsers/platforms
// when scrolled all the way down. E.g. Chrome 72 on debian.
// so check difference <= 1;
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
//
// We therefore leave a bit of wiggle-room and assume we're at the
// bottom if the unscrolled area is less than one pixel high.
//
// non-standard DPI settings also seem to have effect here and can
// actually lead to scrollTop+clientHeight being *larger* than
// scrollHeight. (observed in element-desktop on Ubuntu 20.04)
//
return sn.scrollHeight - (sn.scrollTop + sn.clientHeight) <= 1;
};
// returns the vertical height in the given direction that can be removed from

View file

@ -139,7 +139,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
sendReadReceiptOnLoad={false} // No RR support in thread's MVP
timelineSet={this.state?.thread?.timelineSet}
showUrlPreview={true}
tileShape={TileShape.Notif}
tileShape={TileShape.Thread}
empty={<div>empty</div>}
alwaysShowTimestamps={true}
layout={Layout.Group}

View file

@ -34,8 +34,7 @@ import ForwardDialog from "../dialogs/ForwardDialog";
import { Action } from "../../../dispatcher/actions";
import ReportEventDialog from '../dialogs/ReportEventDialog';
import ViewSource from '../../structures/ViewSource';
import ConfirmRedactDialog from '../dialogs/ConfirmRedactDialog';
import ErrorDialog from '../dialogs/ErrorDialog';
import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog';
import ShareDialog from '../dialogs/ShareDialog';
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import { IPosition, ChevronFace } from '../../structures/ContextMenu';
@ -140,34 +139,11 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
};
private onRedactClick = (): void => {
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
onFinished: async (proceed: boolean, reason?: string) => {
if (!proceed) return;
const cli = MatrixClientPeg.get();
try {
this.props.onCloseDialog?.();
await cli.redactEvent(
this.props.mxEvent.getRoomId(),
this.props.mxEvent.getId(),
undefined,
reason ? { reason } : {},
);
} catch (e) {
const code = e.errcode || e.statusCode;
// only show the dialog if failing for something other than a network error
// (e.g. no errcode or statusCode) as in that case the redactions end up in the
// detached queue and we show the room status bar to allow retry
if (typeof code !== "undefined") {
// display error message stating you couldn't delete this.
Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, {
title: _t('Error'),
description: _t('You cannot delete this message. (%(code)s)', { code }),
});
}
}
},
}, 'mx_Dialog_confirmredact');
const { mxEvent, onCloseDialog } = this.props;
createRedactEventDialog({
mxEvent,
onCloseDialog,
});
this.closeMenu();
};

View file

@ -14,9 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import React from 'react';
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import ErrorDialog from './ErrorDialog';
import TextInputDialog from "./TextInputDialog";
interface IProps {
@ -42,3 +46,40 @@ export default class ConfirmRedactDialog extends React.Component<IProps> {
);
}
}
export function createRedactEventDialog({
mxEvent,
onCloseDialog = () => {},
}: {
mxEvent: MatrixEvent;
onCloseDialog?: () => void;
}) {
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
onFinished: async (proceed: boolean, reason?: string) => {
if (!proceed) return;
const cli = MatrixClientPeg.get();
try {
onCloseDialog?.();
await cli.redactEvent(
mxEvent.getRoomId(),
mxEvent.getId(),
undefined,
reason ? { reason } : {},
);
} catch (e) {
const code = e.errcode || e.statusCode;
// only show the dialog if failing for something other than a network error
// (e.g. no errcode or statusCode) as in that case the redactions end up in the
// detached queue and we show the room status bar to allow retry
if (typeof code !== "undefined") {
// display error message stating you couldn't delete this.
Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, {
title: _t('Error'),
description: _t('You cannot delete this message. (%(code)s)', { code }),
});
}
}
},
}, 'mx_Dialog_confirmredact');
}

View file

@ -128,6 +128,11 @@ const ReactButton: React.FC<IReactButtonProps> = ({ mxEvent, reactions, onFocusC
</React.Fragment>;
};
export enum ActionBarRenderingContext {
Room,
Thread
}
interface IMessageActionBarProps {
mxEvent: MatrixEvent;
reactions?: Relations;
@ -135,15 +140,20 @@ interface IMessageActionBarProps {
getTile: () => any | null;
getReplyThread: () => ReplyThread | undefined;
permalinkCreator?: RoomPermalinkCreator;
onFocusChange: (menuDisplayed: boolean) => void;
isQuoteExpanded?: boolean;
onFocusChange?: (menuDisplayed: boolean) => void;
toggleThreadExpanded: () => void;
renderingContext?: ActionBarRenderingContext;
isQuoteExpanded?: boolean;
}
@replaceableComponent("views.messages.MessageActionBar")
export default class MessageActionBar extends React.PureComponent<IMessageActionBarProps> {
public static contextType = RoomContext;
public static defaultProps = {
renderingContext: ActionBarRenderingContext.Room,
};
public componentDidMount(): void {
if (this.props.mxEvent.status && this.props.mxEvent.status !== EventStatus.SENT) {
this.props.mxEvent.on("Event.status", this.onSent);
@ -288,7 +298,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
// Like the resend button, the react and reply buttons need to appear before the edit.
// The only catch is we do the reply button first so that we can make sure the react
// button is the very first button without having to do length checks for `splice()`.
if (this.context.canReply) {
if (this.context.canReply && this.props.renderingContext === ActionBarRenderingContext.Room) {
toolbarOpts.splice(0, 0, <>
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"

View file

@ -42,6 +42,7 @@ import ErrorDialog from "../dialogs/ErrorDialog";
import QuestionDialog from "../dialogs/QuestionDialog";
import { ActionPayload } from "../../../dispatcher/payloads";
import AccessibleButton from '../elements/AccessibleButton';
import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog';
import SettingsStore from "../../../settings/SettingsStore";
import { logger } from "matrix-js-sdk/src/logger";
@ -331,6 +332,14 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
let shouldSend = true;
if (newContent?.body === '') {
this.cancelPreviousPendingEdit();
createRedactEventDialog({
mxEvent: editedEvent,
});
return;
}
// If content is modified then send an updated event into the room
if (this.isContentModified(newContent)) {
const roomId = editedEvent.getRoomId();

View file

@ -53,7 +53,7 @@ import SenderProfile from '../messages/SenderProfile';
import MessageTimestamp from '../messages/MessageTimestamp';
import TooltipButton from '../elements/TooltipButton';
import ReadReceiptMarker from "./ReadReceiptMarker";
import MessageActionBar from "../messages/MessageActionBar";
import MessageActionBar, { ActionBarRenderingContext } from "../messages/MessageActionBar";
import ReactionsRow from '../messages/ReactionsRow';
import { getEventDisplayInfo } from '../../../utils/EventUtils';
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
@ -192,6 +192,7 @@ export enum TileShape {
Notif = "notif",
FileGrid = "file_grid",
Pinned = "pinned",
Thread = "thread",
}
interface IProps {
@ -1055,6 +1056,9 @@ export default class EventTile extends React.Component<IProps, IState> {
}
}
const renderingContext = this.props.tileShape === TileShape.Thread
? ActionBarRenderingContext.Thread
: ActionBarRenderingContext.Room;
const actionBar = !isEditing ? <MessageActionBar
mxEvent={this.props.mxEvent}
reactions={this.state.reactions}
@ -1062,6 +1066,7 @@ export default class EventTile extends React.Component<IProps, IState> {
getTile={this.getTile}
getReplyThread={this.getReplyThread}
onFocusChange={this.onActionBarFocusChange}
renderingContext={renderingContext}
isQuoteExpanded={isQuoteExpanded}
toggleThreadExpanded={() => this.setQuoteExpanded(!isQuoteExpanded)}
/> : undefined;
@ -1170,6 +1175,40 @@ export default class EventTile extends React.Component<IProps, IState> {
</div>,
]);
}
case TileShape.Thread: {
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
return React.createElement(this.props.as || "li", {
"className": classes,
"aria-live": ariaLive,
"aria-atomic": true,
"data-scroll-tokens": scrollToken,
}, [
<div className="mx_EventTile_roomName" key="mx_EventTile_roomName">
<RoomAvatar room={room} width={28} height={28} />
<a href={permalink} onClick={this.onPermalinkClicked}>
{ room ? room.name : '' }
</a>
</div>,
<div className="mx_EventTile_senderDetails" key="mx_EventTile_senderDetails">
{ avatar }
<a href={permalink} onClick={this.onPermalinkClicked}>
{ sender }
{ timestamp }
</a>
</div>,
<div className="mx_EventTile_line" key="mx_EventTile_line">
<EventTileType ref={this.tile}
mxEvent={this.props.mxEvent}
highlights={this.props.highlights}
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
onHeightChanged={this.props.onHeightChanged}
tileShape={this.props.tileShape}
/>
{ actionBar }
</div>,
]);
}
case TileShape.FileGrid: {
return React.createElement(this.props.as || "li", {
"className": classes,

View file

@ -97,7 +97,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
if (roomSupportsRestricted || preferredRestrictionVersion || joinRule === JoinRule.Restricted) {
let upgradeRequiredPill;
if (preferredRestrictionVersion) {
upgradeRequiredPill = <span className="mx_SecurityRoomSettingsTab_upgradeRequired">
upgradeRequiredPill = <span className="mx_JoinRuleSettings_upgradeRequired">
{ _t("Upgrade required") }
</span>;
}
@ -159,13 +159,14 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
disabled={disabled}
onClick={onEditRestrictedClick}
kind="link"
className="mx_JoinRuleSettings_linkButton"
>
{ sub }
</AccessibleButton>,
}) }
</span>
<div className="mx_SecurityRoomSettingsTab_spacesWithAccess">
<div className="mx_JoinRuleSettings_spacesWithAccess">
<h4>{ _t("Spaces with access") }</h4>
{ shownSpaces.map(room => {
return <span key={room.roomId}>
@ -286,6 +287,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
onChange={onChange}
definitions={definitions}
disabled={disabled}
className="mx_JoinRuleSettings_radioButton"
/>
);
};

View file

@ -185,7 +185,7 @@ export function startsWith(model: EditorModel, prefix: string, caseSensitive = t
const firstPart = model.parts[0];
// part type will be "plain" while editing,
// and "command" while composing a message.
let text = firstPart && firstPart.text;
let text = firstPart?.text || '';
if (!caseSensitive) {
prefix = prefix.toLowerCase();
text = text.toLowerCase();

View file

@ -773,16 +773,6 @@
"The person who invited you already left the room.": "The person who invited you already left the room.",
"The person who invited you already left the room, or their server is offline.": "The person who invited you already left the room, or their server is offline.",
"Failed to join room": "Failed to join room",
"New in the Spaces beta": "New in the Spaces beta",
"Help people in spaces to find and join private rooms": "Help people in spaces to find and join private rooms",
"Learn more": "Learn more",
"Help space members find private rooms": "Help space members find private rooms",
"To help space members find and join a private room, go to that room's Security & Privacy settings.": "To help space members find and join a private room, go to that room's Security & Privacy settings.",
"General": "General",
"Security & Privacy": "Security & Privacy",
"Roles & Permissions": "Roles & Permissions",
"This makes it easy for rooms to stay private to a space, while letting people in the space find and join them. All new rooms in a space will have this option available.": "This makes it easy for rooms to stay private to a space, while letting people in the space find and join them. All new rooms in a space will have this option available.",
"Skip": "Skip",
"You joined the call": "You joined the call",
"%(senderName)s joined the call": "%(senderName)s joined the call",
"Call in progress": "Call in progress",
@ -1056,6 +1046,7 @@
"Invite people": "Invite people",
"Invite with email or username": "Invite with email or username",
"Failed to save space settings.": "Failed to save space settings.",
"General": "General",
"Edit settings relating to your space.": "Edit settings relating to your space.",
"Saving...": "Saving...",
"Save Changes": "Save Changes",
@ -1465,6 +1456,7 @@
"Muted Users": "Muted Users",
"Banned users": "Banned users",
"Send %(eventType)s events": "Send %(eventType)s events",
"Roles & Permissions": "Roles & Permissions",
"Permissions": "Permissions",
"Select the roles required to change various parts of the space": "Select the roles required to change various parts of the space",
"Select the roles required to change various parts of the room": "Select the roles required to change various parts of the room",
@ -1487,6 +1479,7 @@
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.",
"People with supported clients will be able to join the room without having a registered account.": "People with supported clients will be able to join the room without having a registered account.",
"Who can read history?": "Who can read history?",
"Security & Privacy": "Security & Privacy",
"Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.",
"Encrypted": "Encrypted",
"Access": "Access",
@ -2211,6 +2204,7 @@
"People you know on %(brand)s": "People you know on %(brand)s",
"Hide": "Hide",
"Show": "Show",
"Skip": "Skip",
"Send %(count)s invites|other": "Send %(count)s invites",
"Send %(count)s invites|one": "Send %(count)s invite",
"Invite people to join %(communityName)s": "Invite people to join %(communityName)s",
@ -2532,6 +2526,7 @@
"We call the places where you can host your account homeservers.": "We call the places where you can host your account homeservers.",
"Other homeserver": "Other homeserver",
"Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.",
"Learn more": "Learn more",
"About homeservers": "About homeservers",
"Reset event store?": "Reset event store?",
"You most likely do not want to reset your event index store": "You most likely do not want to reset your event index store",

View file

@ -14,13 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import { ListIteratee, Many, sortBy, throttle } from "lodash";
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces";
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
import { IRoomCapability } from "matrix-js-sdk/src/client";
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
@ -41,12 +39,6 @@ import { arrayHasDiff, arrayHasOrderChange } from "../utils/arrays";
import { objectDiff } from "../utils/objects";
import { reorderLexicographically } from "../utils/stringOrderField";
import { TAG_ORDER } from "../components/views/rooms/RoomList";
import { shouldShowSpaceSettings } from "../utils/space";
import ToastStore from "./ToastStore";
import { _t } from "../languageHandler";
import GenericToast from "../components/views/toasts/GenericToast";
import Modal from "../Modal";
import InfoDialog from "../components/views/dialogs/InfoDialog";
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
type SpaceKey = string | symbol;
@ -233,65 +225,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
window.localStorage.removeItem(ACTIVE_SPACE_LS_KEY);
}
// New in Spaces beta toast for Restricted Join Rule
const lsKey = "mx_SpaceBeta_restrictedJoinRuleToastSeen";
if (contextSwitch && space?.getJoinRule() === JoinRule.Invite && shouldShowSpaceSettings(space) &&
space.getJoinedMemberCount() > 1 && !localStorage.getItem(lsKey)
&& this.restrictedJoinRuleSupport?.preferred
) {
const toastKey = "restrictedjoinrule";
ToastStore.sharedInstance().addOrReplaceToast({
key: toastKey,
title: _t("New in the Spaces beta"),
props: {
description: _t("Help people in spaces to find and join private rooms"),
acceptLabel: _t("Learn more"),
onAccept: () => {
localStorage.setItem(lsKey, "true");
ToastStore.sharedInstance().dismissToast(toastKey);
Modal.createTrackedDialog("New in the Spaces beta", "restricted join rule", InfoDialog, {
title: _t("Help space members find private rooms"),
description: <>
<p>{ _t("To help space members find and join a private room, " +
"go to that room's Security & Privacy settings.") }</p>
{ /* Reuses classes from TabbedView for simplicity, non-interactive */ }
<div className="mx_TabbedView_tabsOnLeft" style={{ width: "190px", position: "relative" }}>
<div className="mx_TabbedView_tabLabel">
<span className="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_settingsIcon" />
<span className="mx_TabbedView_tabLabel_text">{ _t("General") }</span>
</div>
<div className="mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active">
<span className="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_securityIcon" />
<span className="mx_TabbedView_tabLabel_text">{ _t("Security & Privacy") }</span>
</div>
<div className="mx_TabbedView_tabLabel">
<span className="mx_TabbedView_maskedIcon mx_RoomSettingsDialog_rolesIcon" />
<span className="mx_TabbedView_tabLabel_text">{ _t("Roles & Permissions") }</span>
</div>
</div>
<p>{ _t("This makes it easy for rooms to stay private to a space, " +
"while letting people in the space find and join them. " +
"All new rooms in a space will have this option available.") }</p>
</>,
button: _t("OK"),
hasCloseButton: false,
fixedWidth: true,
});
},
rejectLabel: _t("Skip"),
onReject: () => {
localStorage.setItem(lsKey, "true");
ToastStore.sharedInstance().dismissToast(toastKey);
},
},
component: GenericToast,
priority: 35,
});
}
if (space) {
this.loadSuggestedRooms(space);
}
@ -595,7 +528,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// Update NotificationStates
this.getNotificationState(s).setRooms(visibleRooms.filter(room => {
if (!roomIds.has(room.roomId)) return false;
if (!roomIds.has(room.roomId) || room.isSpaceRoom()) return false;
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
return s === HOME_SPACE;