Create new right panel cards
This commit is contained in:
parent
eb7f6f4c4b
commit
31cca5e0f2
14 changed files with 844 additions and 43 deletions
|
@ -155,9 +155,12 @@
|
||||||
@import "./views/messages/_UnknownBody.scss";
|
@import "./views/messages/_UnknownBody.scss";
|
||||||
@import "./views/messages/_ViewSourceEvent.scss";
|
@import "./views/messages/_ViewSourceEvent.scss";
|
||||||
@import "./views/messages/_common_CryptoEvent.scss";
|
@import "./views/messages/_common_CryptoEvent.scss";
|
||||||
|
@import "./views/right_panel/_BaseCard.scss";
|
||||||
@import "./views/right_panel/_EncryptionInfo.scss";
|
@import "./views/right_panel/_EncryptionInfo.scss";
|
||||||
|
@import "./views/right_panel/_RoomSummaryCard.scss";
|
||||||
@import "./views/right_panel/_UserInfo.scss";
|
@import "./views/right_panel/_UserInfo.scss";
|
||||||
@import "./views/right_panel/_VerificationPanel.scss";
|
@import "./views/right_panel/_VerificationPanel.scss";
|
||||||
|
@import "./views/right_panel/_WidgetCard.scss";
|
||||||
@import "./views/room_settings/_AliasSettings.scss";
|
@import "./views/room_settings/_AliasSettings.scss";
|
||||||
@import "./views/rooms/_AppsDrawer.scss";
|
@import "./views/rooms/_AppsDrawer.scss";
|
||||||
@import "./views/rooms/_Autocomplete.scss";
|
@import "./views/rooms/_Autocomplete.scss";
|
||||||
|
|
164
res/css/views/right_panel/_BaseCard.scss
Normal file
164
res/css/views/right_panel/_BaseCard.scss
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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_BaseCard {
|
||||||
|
padding: 0 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.mx_BaseCard_header {
|
||||||
|
margin: 8px 0;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0 44px;
|
||||||
|
font-size: $font-18px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_back, .mx_BaseCard_close {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(141, 151, 165, 0.2); // TODO
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
margin: 12px;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
background-color: $rightpanel-button-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_back {
|
||||||
|
border-radius: 4px;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
mask-size: 20px;
|
||||||
|
mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_close {
|
||||||
|
border-radius: 10px;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
mask-image: url('$(res)/img/icons-close.svg'); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AutoHideScrollbar {
|
||||||
|
// collapse the margin into a padding to move the scrollbar into the right gutter
|
||||||
|
margin-right: -8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_Group {
|
||||||
|
margin: 20px 0 16px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: $tertiary-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_Button {
|
||||||
|
padding: 10px 38px 10px 12px;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
font-size: $font-13px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(141, 151, 165, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 6px;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
background-color: $icon-button-color;
|
||||||
|
transform: rotate(270deg);
|
||||||
|
mask-size: 20px;
|
||||||
|
mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_footer {
|
||||||
|
padding-top: 4px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_secondary {
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
background-color: rgba(141, 151, 165, 0.2); // TODO
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-14px;
|
||||||
|
min-width: 70px;
|
||||||
|
|
||||||
|
& + .mx_AccessibleButton_kind_secondary {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_FilePanel,
|
||||||
|
.mx_UserInfo,
|
||||||
|
.mx_NotificationPanel,
|
||||||
|
.mx_MemberList {
|
||||||
|
&.mx_BaseCard {
|
||||||
|
padding: 32px 0 0;
|
||||||
|
|
||||||
|
.mx_AutoHideScrollbar {
|
||||||
|
margin-right: unset;
|
||||||
|
padding-right: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
res/css/views/right_panel/_RoomSummaryCard.scss
Normal file
157
res/css/views/right_panel/_RoomSummaryCard.scss
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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_RoomSummaryCard {
|
||||||
|
// shrink left gutter by 12 and instead add it as margin to all things except the buttons
|
||||||
|
// as their hover effect should go into the gutter
|
||||||
|
& > * { // TODO consolidate this as the standard effect
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.mx_AutoHideScrollbar {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseCard_header {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-18px;
|
||||||
|
margin: 12px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_alias {
|
||||||
|
font-size: $font-13px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, .mx_RoomSummaryCard_alias {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_avatar {
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_e2ee {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #737D8C;
|
||||||
|
margin-top: -3px; // alignment
|
||||||
|
margin-left: -10px; // overlap
|
||||||
|
border: 3px solid $dark-panel-bg-color;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: 13px;
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
mask-size: cover;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-image: url('$(res)/img/e2e/disabled.svg');
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_e2ee_secure{
|
||||||
|
background-color: #5ABFF2;
|
||||||
|
&::before {
|
||||||
|
mask-image: url('$(res)/img/e2e/normal.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_aboutGroup {
|
||||||
|
.mx_RoomSummaryCard_Button {
|
||||||
|
padding-left: 48px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
background-color: $icon-button-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_appsGroup {
|
||||||
|
.mx_RoomSummaryCard_Button {
|
||||||
|
padding-left: 10px;
|
||||||
|
color: $tertiary-fg-color;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: top;
|
||||||
|
margin-right: 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_icon_app_pinned::after {
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/pin2.svg');
|
||||||
|
background-color: $accent-color;
|
||||||
|
transform: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_link {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: $font-13px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_icon_people::before {
|
||||||
|
mask-image: url("$(res)/img/element-icons/room/members.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_icon_files::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/files.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_icon_share::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/share.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSummaryCard_icon_settings::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/settings.svg');
|
||||||
|
}
|
25
res/css/views/right_panel/_WidgetCard.scss
Normal file
25
res/css/views/right_panel/_WidgetCard.scss
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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_WidgetCard {
|
||||||
|
height: 100%;
|
||||||
|
display: contents;
|
||||||
|
|
||||||
|
.mx_AppTileFullWidth {
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -236,10 +236,6 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_settingsButton::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/settings.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomHeader_forgetButton::before {
|
.mx_RoomHeader_forgetButton::before {
|
||||||
mask-image: url('$(res)/img/element-icons/leave.svg');
|
mask-image: url('$(res)/img/element-icons/leave.svg');
|
||||||
width: 26px;
|
width: 26px;
|
||||||
|
@ -249,14 +245,6 @@ limitations under the License.
|
||||||
mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
|
mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_shareButton::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/room/share.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomHeader_manageIntegsButton::before {
|
|
||||||
mask-image: url('$(res)/img/element-icons/room/integrations.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomHeader_showPanel {
|
.mx_RoomHeader_showPanel {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ import {RightPanelPhases, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPa
|
||||||
import RightPanelStore from "../../stores/RightPanelStore";
|
import RightPanelStore from "../../stores/RightPanelStore";
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import {Action} from "../../dispatcher/actions";
|
import {Action} from "../../dispatcher/actions";
|
||||||
|
import RoomSummaryCard from "../views/right_panel/RoomSummaryCard";
|
||||||
|
import WidgetCard from "../views/right_panel/WidgetCard";
|
||||||
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||||
|
|
||||||
export default class RightPanel extends React.Component {
|
export default class RightPanel extends React.Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
|
@ -182,6 +185,7 @@ export default class RightPanel extends React.Component {
|
||||||
event: payload.event,
|
event: payload.event,
|
||||||
verificationRequest: payload.verificationRequest,
|
verificationRequest: payload.verificationRequest,
|
||||||
verificationRequestPromise: payload.verificationRequestPromise,
|
verificationRequestPromise: payload.verificationRequestPromise,
|
||||||
|
widgetId: payload.widgetId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,6 +213,14 @@ export default class RightPanel extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onClose = () => {
|
||||||
|
// the RightPanelStore has no way of knowing which mode room/group it is in, so we handle closing here
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: Action.ToggleRightPanel,
|
||||||
|
type: this.props.groupId ? "group" : "room",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const MemberList = sdk.getComponent('rooms.MemberList');
|
const MemberList = sdk.getComponent('rooms.MemberList');
|
||||||
const UserInfo = sdk.getComponent('right_panel.UserInfo');
|
const UserInfo = sdk.getComponent('right_panel.UserInfo');
|
||||||
|
@ -225,17 +237,24 @@ export default class RightPanel extends React.Component {
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case RightPanelPhases.RoomMemberList:
|
case RightPanelPhases.RoomMemberList:
|
||||||
if (this.props.room.roomId) {
|
if (this.props.room.roomId) {
|
||||||
panel = <MemberList roomId={this.props.room.roomId} key={this.props.room.roomId} />;
|
panel = <MemberList
|
||||||
|
roomId={this.props.room.roomId}
|
||||||
|
key={this.props.room.roomId}
|
||||||
|
onClose={this.onClose}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.GroupMemberList:
|
case RightPanelPhases.GroupMemberList:
|
||||||
if (this.props.groupId) {
|
if (this.props.groupId) {
|
||||||
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
panel = <GroupMemberList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.GroupRoomList:
|
case RightPanelPhases.GroupRoomList:
|
||||||
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
panel = <GroupRoomList groupId={this.props.groupId} key={this.props.groupId} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.RoomMemberInfo:
|
case RightPanelPhases.RoomMemberInfo:
|
||||||
case RightPanelPhases.EncryptionPanel:
|
case RightPanelPhases.EncryptionPanel:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
|
@ -248,9 +267,11 @@ export default class RightPanel extends React.Component {
|
||||||
verificationRequestPromise={this.state.verificationRequestPromise}
|
verificationRequestPromise={this.state.verificationRequestPromise}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.Room3pidMemberInfo:
|
case RightPanelPhases.Room3pidMemberInfo:
|
||||||
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.room.roomId} />;
|
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.room.roomId} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.GroupMemberInfo:
|
case RightPanelPhases.GroupMemberInfo:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
user={this.state.member}
|
user={this.state.member}
|
||||||
|
@ -258,17 +279,31 @@ export default class RightPanel extends React.Component {
|
||||||
key={this.state.member.userId}
|
key={this.state.member.userId}
|
||||||
onClose={this.onCloseUserInfo} />;
|
onClose={this.onCloseUserInfo} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.GroupRoomInfo:
|
case RightPanelPhases.GroupRoomInfo:
|
||||||
panel = <GroupRoomInfo
|
panel = <GroupRoomInfo
|
||||||
groupRoomId={this.state.groupRoomId}
|
groupRoomId={this.state.groupRoomId}
|
||||||
groupId={this.props.groupId}
|
groupId={this.props.groupId}
|
||||||
key={this.state.groupRoomId} />;
|
key={this.state.groupRoomId} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.NotificationPanel:
|
case RightPanelPhases.NotificationPanel:
|
||||||
panel = <NotificationPanel />;
|
panel = <NotificationPanel onClose={this.onClose} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RightPanelPhases.FilePanel:
|
case RightPanelPhases.FilePanel:
|
||||||
panel = <FilePanel roomId={this.props.room.roomId} resizeNotifier={this.props.resizeNotifier} />;
|
panel = <FilePanel
|
||||||
|
roomId={this.props.room.roomId}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
onClose={this.onClose} />;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RightPanelPhases.RoomSummary:
|
||||||
|
panel = <RoomSummaryCard room={this.props.room} onClose={this.onClose} />;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RightPanelPhases.Widget:
|
||||||
|
panel = <WidgetCard room={this.props.room} widgetId={this.state.widgetId} onClose={this.onClose} />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
93
src/components/views/right_panel/BaseCard.tsx
Normal file
93
src/components/views/right_panel/BaseCard.tsx
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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, {ReactNode} from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
import {_t} from "../../../languageHandler";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||||
|
import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
header?: ReactNode;
|
||||||
|
footer?: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
withoutScrollContainer?: boolean;
|
||||||
|
previousPhase?: RightPanelPhases;
|
||||||
|
onClose?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGroupProps {
|
||||||
|
className?: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Group: React.FC<IGroupProps> = ({ className, title, children }) => {
|
||||||
|
return <div className={classNames("mx_BaseCard_Group", className)}>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
{children}
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BaseCard: React.FC<IProps> = ({
|
||||||
|
onClose,
|
||||||
|
className,
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
withoutScrollContainer,
|
||||||
|
previousPhase,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
let backButton;
|
||||||
|
if (previousPhase) {
|
||||||
|
const onBackClick = () => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: previousPhase,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={_t("Back")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let closeButton;
|
||||||
|
if (onClose) {
|
||||||
|
closeButton = <AccessibleButton className="mx_BaseCard_close" onClick={onClose} title={_t("Close")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!withoutScrollContainer) {
|
||||||
|
children = <AutoHideScrollbar>
|
||||||
|
{ children }
|
||||||
|
</AutoHideScrollbar>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames("mx_BaseCard", className)}>
|
||||||
|
<div className="mx_BaseCard_header">
|
||||||
|
{ backButton }
|
||||||
|
{ closeButton }
|
||||||
|
{ header }
|
||||||
|
</div>
|
||||||
|
{ children }
|
||||||
|
{ footer && <div className="mx_BaseCard_footer">{ footer }</div> }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseCard;
|
|
@ -96,8 +96,7 @@ export default abstract class HeaderButtons extends React.Component<IProps, ISta
|
||||||
public abstract renderButtons(): JSX.Element[];
|
public abstract renderButtons(): JSX.Element[];
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
// inline style as this will be swapped around in future commits
|
return <div className="mx_HeaderButtons">
|
||||||
return <div className="mx_HeaderButtons" role="tablist">
|
|
||||||
{this.renderButtons()}
|
{this.renderButtons()}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,10 @@ import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
import {Action} from "../../../dispatcher/actions";
|
||||||
import {ActionPayload} from "../../../dispatcher/payloads";
|
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||||
|
|
||||||
const MEMBER_PHASES = [
|
const ROOM_INFO_PHASES = [
|
||||||
|
RightPanelPhases.RoomSummary,
|
||||||
|
RightPanelPhases.Widget,
|
||||||
|
RightPanelPhases.FilePanel,
|
||||||
RightPanelPhases.RoomMemberList,
|
RightPanelPhases.RoomMemberList,
|
||||||
RightPanelPhases.RoomMemberInfo,
|
RightPanelPhases.RoomMemberInfo,
|
||||||
RightPanelPhases.EncryptionPanel,
|
RightPanelPhases.EncryptionPanel,
|
||||||
|
@ -54,20 +57,10 @@ export default class RoomHeaderButtons extends HeaderButtons {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMembersClicked = () => {
|
// TODO make it restore whatever widget they were on last
|
||||||
if (this.state.phase === RightPanelPhases.RoomMemberInfo) {
|
private onRoomSummaryClicked = () => {
|
||||||
// send the active phase to trigger a toggle
|
|
||||||
// XXX: we should pass refireParams here but then it won't collapse as we desire it to
|
|
||||||
this.setPhase(RightPanelPhases.RoomMemberInfo);
|
|
||||||
} else {
|
|
||||||
// This toggles for us, if needed
|
// This toggles for us, if needed
|
||||||
this.setPhase(RightPanelPhases.RoomMemberList);
|
this.setPhase(RightPanelPhases.RoomSummary);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private onFilesClicked = () => {
|
|
||||||
// This toggles for us, if needed
|
|
||||||
this.setPhase(RightPanelPhases.FilePanel);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private onNotificationsClicked = () => {
|
private onNotificationsClicked = () => {
|
||||||
|
@ -77,19 +70,17 @@ export default class RoomHeaderButtons extends HeaderButtons {
|
||||||
|
|
||||||
public renderButtons() {
|
public renderButtons() {
|
||||||
return [
|
return [
|
||||||
<HeaderButton key="membersButton" name="membersButton"
|
<HeaderButton
|
||||||
title={_t('Members')}
|
key="roomSummaryButton"
|
||||||
isHighlighted={this.isPhase(MEMBER_PHASES)}
|
name="roomSummaryButton"
|
||||||
onClick={this.onMembersClicked}
|
title={_t('Room Info')}
|
||||||
analytics={['Right Panel', 'Member List Button', 'click']}
|
isHighlighted={this.isPhase(ROOM_INFO_PHASES)}
|
||||||
|
onClick={this.onRoomSummaryClicked}
|
||||||
|
analytics={['Right Panel', 'Room Summary Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="filesButton" name="filesButton"
|
<HeaderButton
|
||||||
title={_t('Files')}
|
key="notifsButton"
|
||||||
isHighlighted={this.isPhase(RightPanelPhases.FilePanel)}
|
name="notifsButton"
|
||||||
onClick={this.onFilesClicked}
|
|
||||||
analytics={['Right Panel', 'File List Button', 'click']}
|
|
||||||
/>,
|
|
||||||
<HeaderButton key="notifsButton" name="notifsButton"
|
|
||||||
title={_t('Notifications')}
|
title={_t('Notifications')}
|
||||||
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
|
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
|
||||||
onClick={this.onNotificationsClicked}
|
onClick={this.onNotificationsClicked}
|
||||||
|
|
231
src/components/views/right_panel/RoomSummaryCard.tsx
Normal file
231
src/components/views/right_panel/RoomSummaryCard.tsx
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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, useEffect, useContext} from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
|
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||||
|
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import { useIsEncrypted } from '../../../hooks/useIsEncrypted';
|
||||||
|
import BaseCard, { Group } from "./BaseCard";
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
|
import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||||
|
import Modal from "../../../Modal";
|
||||||
|
import ShareDialog from '../dialogs/ShareDialog';
|
||||||
|
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||||
|
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
|
||||||
|
import WidgetUtils from "../../../utils/WidgetUtils";
|
||||||
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import TextWithTooltip from "../elements/TextWithTooltip";
|
||||||
|
import BaseAvatar from "../avatars/BaseAvatar";
|
||||||
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
import {IApp, WidgetStore} from "../../../stores/WidgetStore";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
room: Room;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAppsSectionProps {
|
||||||
|
room: Room;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IButtonProps {
|
||||||
|
className: string;
|
||||||
|
onClick(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button: React.FC<IButtonProps> = ({ children, className, onClick }) => {
|
||||||
|
return <AccessibleButton
|
||||||
|
className={classNames("mx_BaseCard_Button mx_RoomSummaryCard_Button", className)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{ children }
|
||||||
|
</AccessibleButton>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useWidgets = (room: Room) => {
|
||||||
|
const [apps, setApps] = useState<IApp[]>(WidgetStore.instance.getApps(room));
|
||||||
|
|
||||||
|
const updateApps = useCallback(() => {
|
||||||
|
// Copy the array so that we always trigger a re-render, as some updates mutate the array of apps/settings
|
||||||
|
setApps([...WidgetStore.instance.getApps(room)]);
|
||||||
|
}, [room]);
|
||||||
|
|
||||||
|
useEffect(updateApps, [room]);
|
||||||
|
useEventEmitter(WidgetEchoStore, "update", updateApps);
|
||||||
|
useEventEmitter(WidgetStore.instance, room.roomId, updateApps);
|
||||||
|
|
||||||
|
return apps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
|
||||||
|
const cli = useContext(MatrixClientContext);
|
||||||
|
const apps = useWidgets(room);
|
||||||
|
|
||||||
|
const onManageIntegrations = () => {
|
||||||
|
const managers = IntegrationManagers.sharedInstance();
|
||||||
|
if (!managers.hasManager()) {
|
||||||
|
managers.openNoManagerDialog();
|
||||||
|
} else {
|
||||||
|
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||||
|
managers.openAll(room);
|
||||||
|
} else {
|
||||||
|
managers.getPrimaryManager().open(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Group className="mx_RoomSummaryCard_appsGroup" title={_t("Apps")}>
|
||||||
|
{ apps.map(app => {
|
||||||
|
const name = WidgetUtils.getWidgetName(app);
|
||||||
|
const dataTitle = WidgetUtils.getWidgetDataTitle(app);
|
||||||
|
const subtitle = dataTitle && " - " + dataTitle;
|
||||||
|
|
||||||
|
let iconUrls = [require("../../../../res/img/element-icons/room/default_app.svg")];
|
||||||
|
// heuristics for some better icons until Widgets support their own icons
|
||||||
|
if (app.type.includes("meeting") || app.type.includes("calendar")) {
|
||||||
|
iconUrls = [require("../../../../res/img/element-icons/room/default_cal.svg")];
|
||||||
|
} else if (app.type.includes("pad") || app.type.includes("doc") || app.type.includes("calc")) {
|
||||||
|
iconUrls = [require("../../../../res/img/element-icons/room/default_doc.svg")];
|
||||||
|
} else if (app.type.includes("clock")) {
|
||||||
|
iconUrls = [require("../../../../res/img/element-icons/room/default_clock.svg")];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.avatar_url) { // MSC2765
|
||||||
|
iconUrls.unshift(getHttpUriForMxc(cli.getHomeserverUrl(), app.avatar_url, 20, 20, "crop"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPinned = WidgetStore.instance.isPinned(app.id);
|
||||||
|
const classes = classNames("mx_RoomSummaryCard_icon_app", {
|
||||||
|
mx_RoomSummaryCard_icon_app_pinned: isPinned,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isPinned) {
|
||||||
|
const onClick = () => {
|
||||||
|
WidgetStore.instance.unpinWidget(app.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <AccessibleTooltipButton
|
||||||
|
key={app.id}
|
||||||
|
className={classNames("mx_BaseCard_Button mx_RoomSummaryCard_Button", classes)}
|
||||||
|
onClick={onClick}
|
||||||
|
title={_t("Unpin app")}
|
||||||
|
>
|
||||||
|
<BaseAvatar name={app.id} urls={iconUrls} width={20} height={20} />
|
||||||
|
<span>{name}</span>
|
||||||
|
{ subtitle }
|
||||||
|
</AccessibleTooltipButton>
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOpenWidgetClick = () => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: RightPanelPhases.Widget,
|
||||||
|
refireParams: {
|
||||||
|
widgetId: app.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Button key={app.id} className={classes} onClick={onOpenWidgetClick}>
|
||||||
|
<BaseAvatar name={app.id} urls={iconUrls} width={20} height={20} />
|
||||||
|
<span>{name}</span>
|
||||||
|
{ subtitle }
|
||||||
|
</Button>;
|
||||||
|
}) }
|
||||||
|
|
||||||
|
<AccessibleButton kind="link" onClick={onManageIntegrations}>
|
||||||
|
{ apps.length > 0 ? _t("Edit apps") : _t("Add applications") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</Group>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRoomMembersClick = () => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: RightPanelPhases.RoomMemberList,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRoomFilesClick = () => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: RightPanelPhases.FilePanel,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRoomSettingsClick = () => {
|
||||||
|
defaultDispatcher.dispatch({ action: "open_room_settings" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
||||||
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
|
const onShareRoomClick = () => {
|
||||||
|
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
||||||
|
target: room,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRoomEncrypted = useIsEncrypted(cli, room);
|
||||||
|
|
||||||
|
const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || "";
|
||||||
|
const header = <React.Fragment>
|
||||||
|
<div className="mx_RoomSummaryCard_avatar" role="presentation">
|
||||||
|
<RoomAvatar room={room} height={54} width={54} viewAvatarOnClick />
|
||||||
|
<TextWithTooltip
|
||||||
|
tooltip={isRoomEncrypted ? _t("Encrypted") : _t("Not encrypted")}
|
||||||
|
class={classNames("mx_RoomSummaryCard_e2ee", {
|
||||||
|
mx_RoomSummaryCard_e2ee_secure: isRoomEncrypted,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 title={room.name}>{ room.name }</h2>
|
||||||
|
<div className="mx_RoomSummaryCard_alias" title={alias}>
|
||||||
|
{ alias }
|
||||||
|
</div>
|
||||||
|
</React.Fragment>;
|
||||||
|
|
||||||
|
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: room.getJoinedMembers().length })}
|
||||||
|
</Button>
|
||||||
|
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}>
|
||||||
|
{_t("Show files")}
|
||||||
|
</Button>
|
||||||
|
<Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}>
|
||||||
|
{_t("Share room")}
|
||||||
|
</Button>
|
||||||
|
<Button className="mx_RoomSummaryCard_icon_settings" onClick={onRoomSettingsClick}>
|
||||||
|
{_t("Room settings")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<AppsSection room={room} />
|
||||||
|
</BaseCard>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RoomSummaryCard;
|
107
src/components/views/right_panel/WidgetCard.tsx
Normal file
107
src/components/views/right_panel/WidgetCard.tsx
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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, {useContext, useEffect} from "react";
|
||||||
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
|
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import BaseCard from "./BaseCard";
|
||||||
|
import WidgetUtils from "../../../utils/WidgetUtils";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import AppTile from "../elements/AppTile";
|
||||||
|
import {_t} from "../../../languageHandler";
|
||||||
|
import {useWidgets} from "./RoomSummaryCard";
|
||||||
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||||
|
import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
import {WidgetStore} from "../../../stores/WidgetStore";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
room: Room;
|
||||||
|
widgetId: string;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFinished = () => {
|
||||||
|
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||||
|
action: Action.SetRightPanelPhase,
|
||||||
|
phase: RightPanelPhases.RoomSummary,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||||
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
|
const apps = useWidgets(room);
|
||||||
|
const app = apps.find(a => a.id === widgetId);
|
||||||
|
const isPinned = app && WidgetStore.instance.isPinned(app.id);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!app || isPinned) {
|
||||||
|
// TODO maybe we should do this in the ActiveWidgetStore instead
|
||||||
|
onFinished();
|
||||||
|
}
|
||||||
|
}, [app, isPinned]);
|
||||||
|
|
||||||
|
// Don't render anything as we are about to transition
|
||||||
|
if (!app || isPinned) return null;
|
||||||
|
|
||||||
|
const header = <React.Fragment>
|
||||||
|
<h2>{ WidgetUtils.getWidgetName(app) }</h2>
|
||||||
|
</React.Fragment>;
|
||||||
|
|
||||||
|
const onPinClick = () => {
|
||||||
|
WidgetStore.instance.pinWidget(app.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEditClick = () => {
|
||||||
|
WidgetUtils.editWidget(room, app);
|
||||||
|
};
|
||||||
|
|
||||||
|
const footer = <React.Fragment>
|
||||||
|
<AccessibleButton kind="secondary" onClick={onPinClick}>
|
||||||
|
{ _t("Pin to room") }
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton kind="secondary" onClick={onEditClick}>
|
||||||
|
{ _t("Edit") }
|
||||||
|
</AccessibleButton>
|
||||||
|
</React.Fragment>;
|
||||||
|
|
||||||
|
return <BaseCard
|
||||||
|
header={header}
|
||||||
|
footer={footer}
|
||||||
|
className="mx_WidgetCard"
|
||||||
|
onClose={onClose}
|
||||||
|
previousPhase={RightPanelPhases.RoomSummary}
|
||||||
|
withoutScrollContainer
|
||||||
|
>
|
||||||
|
<AppTile
|
||||||
|
app={app}
|
||||||
|
fullWidth
|
||||||
|
show
|
||||||
|
showMenubar={false}
|
||||||
|
room={room}
|
||||||
|
userId={cli.getUserId()}
|
||||||
|
creatorUserId={app.creatorUserId}
|
||||||
|
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||||
|
waitForIframeLoad={app.waitForIframeLoad}
|
||||||
|
whitelistCapabilities={WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, room.roomId)}
|
||||||
|
/>
|
||||||
|
</BaseCard>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WidgetCard;
|
|
@ -34,4 +34,5 @@ export interface SetRightPanelPhaseRefireParams {
|
||||||
groupRoomId?: string;
|
groupRoomId?: string;
|
||||||
// XXX: The type for event should 'view_3pid_invite' action's payload
|
// XXX: The type for event should 'view_3pid_invite' action's payload
|
||||||
event?: any;
|
event?: any;
|
||||||
|
widgetId?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -607,4 +607,8 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
displayName: _td("Enable experimental, compact IRC style layout"),
|
displayName: _td("Enable experimental, compact IRC style layout"),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
"Widgets.pinned": {
|
||||||
|
supportedLevels: LEVELS_ROOM_OR_ACCOUNT,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,8 @@ export enum RightPanelPhases {
|
||||||
NotificationPanel = 'NotificationPanel',
|
NotificationPanel = 'NotificationPanel',
|
||||||
RoomMemberInfo = 'RoomMemberInfo',
|
RoomMemberInfo = 'RoomMemberInfo',
|
||||||
EncryptionPanel = 'EncryptionPanel',
|
EncryptionPanel = 'EncryptionPanel',
|
||||||
|
RoomSummary = 'RoomSummary',
|
||||||
|
Widget = 'Widget',
|
||||||
|
|
||||||
Room3pidMemberInfo = 'Room3pidMemberInfo',
|
Room3pidMemberInfo = 'Room3pidMemberInfo',
|
||||||
// Group stuff
|
// Group stuff
|
||||||
|
@ -34,6 +36,7 @@ export enum RightPanelPhases {
|
||||||
// These are the phases that are safe to persist (the ones that don't require additional
|
// These are the phases that are safe to persist (the ones that don't require additional
|
||||||
// arguments).
|
// arguments).
|
||||||
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
export const RIGHT_PANEL_PHASES_NO_ARGS = [
|
||||||
|
RightPanelPhases.RoomSummary,
|
||||||
RightPanelPhases.NotificationPanel,
|
RightPanelPhases.NotificationPanel,
|
||||||
RightPanelPhases.FilePanel,
|
RightPanelPhases.FilePanel,
|
||||||
RightPanelPhases.RoomMemberList,
|
RightPanelPhases.RoomMemberList,
|
||||||
|
|
Loading…
Reference in a new issue