Merge remote-tracking branch 'origin/develop' into jryans/defer-cross-signing-setup
commit
0219824d6f
|
@ -94,6 +94,7 @@
|
||||||
"react-focus-lock": "^2.4.1",
|
"react-focus-lock": "^2.4.1",
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-html": "^1.27.1",
|
"sanitize-html": "^1.27.1",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"text-encoding-utf-8": "^1.0.2",
|
"text-encoding-utf-8": "^1.0.2",
|
||||||
|
|
|
@ -15,18 +15,39 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
$MiniAppTileHeight: 114px;
|
||||||
the tile title bar is 5 (top border) + 12 (title, buttons) + 5 (bottom padding) px = 22px
|
|
||||||
the body is assumed to be 300px (assumed by at least the sticker pickerm, perhaps elsewhere),
|
|
||||||
so the body height would be 300px - 22px (room for title bar) = 278px
|
|
||||||
BUT! the sticker picker also assumes it's a little less high than that because the iframe
|
|
||||||
for the sticker picker doesn't have any padding or margin on it's bottom.
|
|
||||||
so subtracking another 5px, which brings us at 273px.
|
|
||||||
*/
|
|
||||||
$AppsDrawerBodyHeight: 273px;
|
|
||||||
|
|
||||||
.mx_AppsDrawer {
|
.mx_AppsDrawer {
|
||||||
margin: 5px;
|
margin: 5px 5px 5px 18px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.mx_AppsContainer_resizerHandle {
|
||||||
|
cursor: ns-resize;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
// Override styles from library
|
||||||
|
width: unset !important;
|
||||||
|
height: 4px !important;
|
||||||
|
|
||||||
|
// This is positioned directly below frame
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8px !important; // override from library
|
||||||
|
|
||||||
|
// Together, these make the bar 64px wide
|
||||||
|
// These are also overridden from the library
|
||||||
|
left: calc(50% - 32px) !important;
|
||||||
|
right: calc(50% - 32px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.mx_AppsContainer_resizerHandle {
|
||||||
|
opacity: 0.8;
|
||||||
|
background: $primary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppsDrawer_hidden {
|
.mx_AppsDrawer_hidden {
|
||||||
|
@ -36,15 +57,23 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
.mx_AppsContainer {
|
.mx_AppsContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppsDrawer_minimised .mx_AppsContainer {
|
||||||
|
// override the re-resizable inline styles
|
||||||
|
height: inherit !important;
|
||||||
|
min-height: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddWidget_button {
|
.mx_AddWidget_button {
|
||||||
order: 2;
|
order: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 5px auto 5px auto;
|
margin: -3px auto 5px 0;
|
||||||
color: $accent-color;
|
color: $accent-color;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
}
|
}
|
||||||
|
@ -65,40 +94,52 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
.mx_AppTile {
|
.mx_AppTile {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-right: 5px;
|
|
||||||
border: 5px solid $widget-menu-bar-bg-color;
|
border: 5px solid $widget-menu-bar-bg-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.mx_AppTile:last-child {
|
& + .mx_AppTile {
|
||||||
margin-right: 1px;
|
margin-left: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileFullWidth {
|
.mx_AppTileFullWidth {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 5px solid $widget-menu-bar-bg-color;
|
border: 5px solid $widget-menu-bar-bg-color;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTile_mini {
|
.mx_AppTile_mini {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: $MiniAppTileHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTile_persistedWrapper {
|
.mx_AppTile.mx_AppTile_minimised,
|
||||||
height: $AppsDrawerBodyHeight;
|
.mx_AppTileFullWidth.mx_AppTile_minimised,
|
||||||
|
.mx_AppTile_mini.mx_AppTile_minimised {
|
||||||
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTile .mx_AppTile_persistedWrapper,
|
||||||
|
.mx_AppTileFullWidth .mx_AppTile_persistedWrapper,
|
||||||
.mx_AppTile_mini .mx_AppTile_persistedWrapper {
|
.mx_AppTile_mini .mx_AppTile_persistedWrapper {
|
||||||
height: 114px;
|
flex: 1;
|
||||||
min-width: 300px;
|
}
|
||||||
|
|
||||||
|
.mx_AppTile_persistedWrapper div {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBar {
|
.mx_AppTileMenuBar {
|
||||||
|
@ -110,6 +151,7 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBar_expanded {
|
.mx_AppTileMenuBar_expanded {
|
||||||
|
@ -172,7 +214,7 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileBody {
|
.mx_AppTileBody {
|
||||||
height: $AppsDrawerBodyHeight;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +225,13 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTile .mx_AppTileBody,
|
||||||
|
.mx_AppTileFullWidth .mx_AppTileBody,
|
||||||
|
.mx_AppTile_mini .mx_AppTileBody_mini {
|
||||||
|
height: inherit;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AppTileBody_mini iframe {
|
.mx_AppTileBody_mini iframe {
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -191,7 +240,7 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
|
|
||||||
.mx_AppTileBody iframe {
|
.mx_AppTileBody iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: $AppsDrawerBodyHeight;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -331,7 +380,7 @@ form.mx_Custom_Widget_Form div {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: $AppsDrawerBodyHeight;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppLoading .mx_Spinner {
|
.mx_AppLoading .mx_Spinner {
|
||||||
|
@ -358,3 +407,16 @@ form.mx_Custom_Widget_Form div {
|
||||||
.mx_AppLoading iframe {
|
.mx_AppLoading iframe {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppsDrawer_minimised .mx_AppsContainer_resizerHandle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Avoid apptile iframes capturing mouse event focus when resizing */
|
||||||
|
.mx_AppsDrawer_resizing iframe {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppsDrawer_resizing .mx_AppTile_persistedWrapper {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTile_persistedWrapper div {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_IncomingCallBox {
|
.mx_IncomingCallBox {
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -67,6 +67,7 @@ import {generateHumanReadableId} from "./utils/NamingUtils";
|
||||||
import {Jitsi} from "./widgets/Jitsi";
|
import {Jitsi} from "./widgets/Jitsi";
|
||||||
import {WidgetType} from "./widgets/WidgetType";
|
import {WidgetType} from "./widgets/WidgetType";
|
||||||
import {SettingLevel} from "./settings/SettingLevel";
|
import {SettingLevel} from "./settings/SettingLevel";
|
||||||
|
import {base32} from "rfc4648";
|
||||||
|
|
||||||
global.mxCalls = {
|
global.mxCalls = {
|
||||||
//room_id: MatrixCall
|
//room_id: MatrixCall
|
||||||
|
@ -388,10 +389,21 @@ async function _startCallApp(roomId, type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confId = `JitsiConference${generateHumanReadableId()}`;
|
|
||||||
const jitsiDomain = Jitsi.getInstance().preferredDomain;
|
const jitsiDomain = Jitsi.getInstance().preferredDomain;
|
||||||
|
const jitsiAuth = await Jitsi.getInstance().getJitsiAuth();
|
||||||
|
let confId;
|
||||||
|
if (jitsiAuth === 'openidtoken-jwt') {
|
||||||
|
// Create conference ID from room ID
|
||||||
|
// For compatibility with Jitsi, use base32 without padding.
|
||||||
|
// More details here:
|
||||||
|
// https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||||
|
confId = base32.stringify(Buffer.from(roomId), { pad: false });
|
||||||
|
} else {
|
||||||
|
// Create a random human readable conference ID
|
||||||
|
confId = `JitsiConference${generateHumanReadableId()}`;
|
||||||
|
}
|
||||||
|
|
||||||
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl();
|
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl({auth: jitsiAuth});
|
||||||
|
|
||||||
// TODO: Remove URL hacks when the mobile clients eventually support v2 widgets
|
// TODO: Remove URL hacks when the mobile clients eventually support v2 widgets
|
||||||
const parsedUrl = new URL(widgetUrl);
|
const parsedUrl = new URL(widgetUrl);
|
||||||
|
@ -403,6 +415,7 @@ async function _startCallApp(roomId, type) {
|
||||||
conferenceId: confId,
|
conferenceId: confId,
|
||||||
isAudioOnly: type === 'voice',
|
isAudioOnly: type === 'voice',
|
||||||
domain: jitsiDomain,
|
domain: jitsiDomain,
|
||||||
|
auth: jitsiAuth,
|
||||||
};
|
};
|
||||||
|
|
||||||
const widgetId = (
|
const widgetId = (
|
||||||
|
|
|
@ -154,6 +154,19 @@ export const Commands = [
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
|
command: 'lenny',
|
||||||
|
args: '<message>',
|
||||||
|
description: _td('Prepends ( ͡° ͜ʖ ͡°) to a plain-text message'),
|
||||||
|
runFn: function(roomId, args) {
|
||||||
|
let message = '( ͡° ͜ʖ ͡°)';
|
||||||
|
if (args) {
|
||||||
|
message = message + ' ' + args;
|
||||||
|
}
|
||||||
|
return success(MatrixClientPeg.get().sendTextMessage(roomId, message));
|
||||||
|
},
|
||||||
|
category: CommandCategories.messages,
|
||||||
|
}),
|
||||||
new Command({
|
new Command({
|
||||||
command: 'plain',
|
command: 'plain',
|
||||||
args: '<message>',
|
args: '<message>',
|
||||||
|
|
|
@ -186,7 +186,14 @@ export default class WidgetMessaging {
|
||||||
isUserWidget: this.isUserWidget,
|
isUserWidget: this.isUserWidget,
|
||||||
|
|
||||||
onFinished: async (confirm) => {
|
onFinished: async (confirm) => {
|
||||||
const responseBody = {success: confirm};
|
const responseBody = {
|
||||||
|
// Legacy (early draft) fields
|
||||||
|
success: confirm,
|
||||||
|
|
||||||
|
// New style MSC1960 fields
|
||||||
|
state: confirm ? "allowed" : "blocked",
|
||||||
|
original_request_id: ev.requestId, // eslint-disable-line camelcase
|
||||||
|
};
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
const credentials = await MatrixClientPeg.get().getOpenIdToken();
|
const credentials = await MatrixClientPeg.get().getOpenIdToken();
|
||||||
Object.assign(responseBody, credentials);
|
Object.assign(responseBody, credentials);
|
||||||
|
|
|
@ -1322,7 +1322,7 @@ export default class GroupView extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<GroupHeaderButtons />
|
<GroupHeaderButtons />
|
||||||
</div>
|
</div>
|
||||||
<MainSplit panel={rightPanel}>
|
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||||
<AutoHideScrollbar className="mx_GroupView_body">
|
<AutoHideScrollbar className="mx_GroupView_body">
|
||||||
{ this._getMembershipSection() }
|
{ this._getMembershipSection() }
|
||||||
{ this._getGroupSection() }
|
{ this._getGroupSection() }
|
||||||
|
|
|
@ -257,6 +257,12 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
window.localStorage.setItem("mx_lhs_size", '' + size);
|
window.localStorage.setItem("mx_lhs_size", '' + size);
|
||||||
this.props.resizeNotifier.notifyLeftHandleResized();
|
this.props.resizeNotifier.notifyLeftHandleResized();
|
||||||
},
|
},
|
||||||
|
onResizeStart: () => {
|
||||||
|
this.props.resizeNotifier.startResizing();
|
||||||
|
},
|
||||||
|
onResizeStop: () => {
|
||||||
|
this.props.resizeNotifier.stopResizing();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
const resizer = new Resizer(
|
const resizer = new Resizer(
|
||||||
this._resizeContainer.current,
|
this._resizeContainer.current,
|
||||||
|
@ -650,12 +656,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.UserView:
|
case PageTypes.UserView:
|
||||||
pageElement = <UserView userId={this.props.currentUserId} />;
|
pageElement = <UserView userId={this.props.currentUserId} resizeNotifier={this.props.resizeNotifier} />;
|
||||||
break;
|
break;
|
||||||
case PageTypes.GroupView:
|
case PageTypes.GroupView:
|
||||||
pageElement = <GroupView
|
pageElement = <GroupView
|
||||||
groupId={this.props.currentGroupId}
|
groupId={this.props.currentGroupId}
|
||||||
isNew={this.props.currentGroupIsNew}
|
isNew={this.props.currentGroupIsNew}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,18 @@ import React from 'react';
|
||||||
import { Resizable } from 're-resizable';
|
import { Resizable } from 're-resizable';
|
||||||
|
|
||||||
export default class MainSplit extends React.Component {
|
export default class MainSplit extends React.Component {
|
||||||
_onResized = (event, direction, refToElement, delta) => {
|
_onResizeStart = () => {
|
||||||
|
this.props.resizeNotifier.startResizing();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onResize = () => {
|
||||||
|
this.props.resizeNotifier.notifyRightHandleResized();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onResizeStop = (event, direction, refToElement, delta) => {
|
||||||
|
this.props.resizeNotifier.stopResizing();
|
||||||
window.localStorage.setItem("mx_rhs_size", this._loadSidePanelSize().width + delta.width);
|
window.localStorage.setItem("mx_rhs_size", this._loadSidePanelSize().width + delta.width);
|
||||||
}
|
};
|
||||||
|
|
||||||
_loadSidePanelSize() {
|
_loadSidePanelSize() {
|
||||||
let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size"), 10);
|
let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size"), 10);
|
||||||
|
@ -58,7 +67,9 @@ export default class MainSplit extends React.Component {
|
||||||
bottomLeft: false,
|
bottomLeft: false,
|
||||||
topLeft: false,
|
topLeft: false,
|
||||||
}}
|
}}
|
||||||
onResizeStop={this._onResized}
|
onResizeStart={this._onResizeStart}
|
||||||
|
onResize={this._onResize}
|
||||||
|
onResizeStop={this._onResizeStop}
|
||||||
className="mx_RightPanel_ResizeWrapper"
|
className="mx_RightPanel_ResizeWrapper"
|
||||||
handleClasses={{left: "mx_RightPanel_ResizeHandle"}}
|
handleClasses={{left: "mx_RightPanel_ResizeHandle"}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -225,11 +225,12 @@ export default class RightPanel extends React.Component {
|
||||||
const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo');
|
const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo');
|
||||||
|
|
||||||
let panel = <div />;
|
let panel = <div />;
|
||||||
|
const roomId = this.props.room ? this.props.room.roomId : undefined;
|
||||||
|
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case RightPanelPhases.RoomMemberList:
|
case RightPanelPhases.RoomMemberList:
|
||||||
if (this.props.room.roomId) {
|
if (roomId) {
|
||||||
panel = <MemberList roomId={this.props.room.roomId} key={this.props.room.roomId} />;
|
panel = <MemberList roomId={roomId} key={roomId} />;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.GroupMemberList:
|
case RightPanelPhases.GroupMemberList:
|
||||||
|
@ -244,8 +245,8 @@ export default class RightPanel extends React.Component {
|
||||||
case RightPanelPhases.EncryptionPanel:
|
case RightPanelPhases.EncryptionPanel:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
user={this.state.member}
|
user={this.state.member}
|
||||||
roomId={this.props.room.roomId}
|
room={this.props.room}
|
||||||
key={this.props.room.roomId || this.state.member.userId}
|
key={roomId || this.state.member.userId}
|
||||||
onClose={this.onCloseUserInfo}
|
onClose={this.onCloseUserInfo}
|
||||||
phase={this.state.phase}
|
phase={this.state.phase}
|
||||||
verificationRequest={this.state.verificationRequest}
|
verificationRequest={this.state.verificationRequest}
|
||||||
|
@ -253,7 +254,7 @@ export default class RightPanel extends React.Component {
|
||||||
/>;
|
/>;
|
||||||
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={roomId} />;
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.GroupMemberInfo:
|
case RightPanelPhases.GroupMemberInfo:
|
||||||
panel = <UserInfo
|
panel = <UserInfo
|
||||||
|
@ -272,7 +273,7 @@ export default class RightPanel extends React.Component {
|
||||||
panel = <NotificationPanel />;
|
panel = <NotificationPanel />;
|
||||||
break;
|
break;
|
||||||
case RightPanelPhases.FilePanel:
|
case RightPanelPhases.FilePanel:
|
||||||
panel = <FilePanel roomId={this.props.room.roomId} resizeNotifier={this.props.resizeNotifier} />;
|
panel = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1545,9 +1545,9 @@ export default class RoomView extends React.Component {
|
||||||
|
|
||||||
// header + footer + status + give us at least 120px of scrollback at all times.
|
// header + footer + status + give us at least 120px of scrollback at all times.
|
||||||
let auxPanelMaxHeight = window.innerHeight -
|
let auxPanelMaxHeight = window.innerHeight -
|
||||||
(83 + // height of RoomHeader
|
(54 + // height of RoomHeader
|
||||||
36 + // height of the status area
|
36 + // height of the status area
|
||||||
72 + // minimum height of the message compmoser
|
51 + // minimum height of the message compmoser
|
||||||
120); // amount of desired scrollback
|
120); // amount of desired scrollback
|
||||||
|
|
||||||
// XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway
|
// XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway
|
||||||
|
@ -1884,7 +1884,8 @@ export default class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auxPanel = (
|
const auxPanel = (
|
||||||
<AuxPanel room={this.state.room}
|
<AuxPanel
|
||||||
|
room={this.state.room}
|
||||||
fullHeight={false}
|
fullHeight={false}
|
||||||
userId={this.context.credentials.userId}
|
userId={this.context.credentials.userId}
|
||||||
conferenceHandler={this.props.ConferenceHandler}
|
conferenceHandler={this.props.ConferenceHandler}
|
||||||
|
@ -1892,7 +1893,10 @@ export default class RoomView extends React.Component {
|
||||||
displayConfCallNotification={this.state.displayConfCallNotification}
|
displayConfCallNotification={this.state.displayConfCallNotification}
|
||||||
maxHeight={this.state.auxPanelMaxHeight}
|
maxHeight={this.state.auxPanelMaxHeight}
|
||||||
showApps={this.state.showApps}
|
showApps={this.state.showApps}
|
||||||
hideAppsDrawer={false} >
|
hideAppsDrawer={false}
|
||||||
|
onResize={this.onResize}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
>
|
||||||
{ aux }
|
{ aux }
|
||||||
</AuxPanel>
|
</AuxPanel>
|
||||||
);
|
);
|
||||||
|
@ -2090,10 +2094,7 @@ export default class RoomView extends React.Component {
|
||||||
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
||||||
e2eStatus={this.state.e2eStatus}
|
e2eStatus={this.state.e2eStatus}
|
||||||
/>
|
/>
|
||||||
<MainSplit
|
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||||
panel={rightPanel}
|
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
|
||||||
>
|
|
||||||
<div className={fadableSectionClasses}>
|
<div className={fadableSectionClasses}>
|
||||||
{auxPanel}
|
{auxPanel}
|
||||||
<div className={timelineClasses}>
|
<div className={timelineClasses}>
|
||||||
|
|
|
@ -163,7 +163,7 @@ export default class ScrollPanel extends React.Component {
|
||||||
this._pendingFillRequests = {b: null, f: null};
|
this._pendingFillRequests = {b: null, f: null};
|
||||||
|
|
||||||
if (this.props.resizeNotifier) {
|
if (this.props.resizeNotifier) {
|
||||||
this.props.resizeNotifier.on("middlePanelResized", this.onResize);
|
this.props.resizeNotifier.on("middlePanelResizedNoisy", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resetScrollState();
|
this.resetScrollState();
|
||||||
|
@ -193,11 +193,12 @@ export default class ScrollPanel extends React.Component {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
|
|
||||||
if (this.props.resizeNotifier) {
|
if (this.props.resizeNotifier) {
|
||||||
this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize);
|
this.props.resizeNotifier.removeListener("middlePanelResizedNoisy", this.onResize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onScroll = ev => {
|
onScroll = ev => {
|
||||||
|
if (this.props.resizeNotifier.isResizing) return; // skip scroll events caused by resizing
|
||||||
debuglog("onScroll", this._getScrollNode().scrollTop);
|
debuglog("onScroll", this._getScrollNode().scrollTop);
|
||||||
this._scrollTimeout.restart();
|
this._scrollTimeout.restart();
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
|
@ -207,6 +208,7 @@ export default class ScrollPanel extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
onResize = () => {
|
onResize = () => {
|
||||||
|
debuglog("onResize");
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
// update preventShrinkingState if present
|
// update preventShrinkingState if present
|
||||||
if (this.preventShrinkingState) {
|
if (this.preventShrinkingState) {
|
||||||
|
@ -236,7 +238,6 @@ export default class ScrollPanel extends React.Component {
|
||||||
// when scrolled all the way down. E.g. Chrome 72 on debian.
|
// when scrolled all the way down. E.g. Chrome 72 on debian.
|
||||||
// so check difference <= 1;
|
// so check difference <= 1;
|
||||||
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// returns the vertical height in the given direction that can be removed from
|
// returns the vertical height in the given direction that can be removed from
|
||||||
|
|
|
@ -80,7 +80,9 @@ export default class UserView extends React.Component {
|
||||||
const RightPanel = sdk.getComponent('structures.RightPanel');
|
const RightPanel = sdk.getComponent('structures.RightPanel');
|
||||||
const MainSplit = sdk.getComponent('structures.MainSplit');
|
const MainSplit = sdk.getComponent('structures.MainSplit');
|
||||||
const panel = <RightPanel user={this.state.member} />;
|
const panel = <RightPanel user={this.state.member} />;
|
||||||
return (<MainSplit panel={panel}><HomePage /></MainSplit>);
|
return (<MainSplit panel={panel} resizeNotifier={this.props.resizeNotifier}>
|
||||||
|
<HomePage />
|
||||||
|
</MainSplit>);
|
||||||
} else {
|
} else {
|
||||||
return (<div />);
|
return (<div />);
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,11 @@ export default class LoginComponent extends React.Component {
|
||||||
'm.login.cas': () => this._renderSsoStep("cas"),
|
'm.login.cas': () => this._renderSsoStep("cas"),
|
||||||
'm.login.sso': () => this._renderSsoStep("sso"),
|
'm.login.sso': () => this._renderSsoStep("sso"),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
this._initLoginLogic();
|
this._initLoginLogic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -310,20 +310,7 @@ export default class AppTile extends React.Component {
|
||||||
if (this.props.onEditClick) {
|
if (this.props.onEditClick) {
|
||||||
this.props.onEditClick();
|
this.props.onEditClick();
|
||||||
} else {
|
} else {
|
||||||
// TODO: Open the right manager for the widget
|
WidgetUtils.editWidget(this.props.room, this.props.app);
|
||||||
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
|
||||||
IntegrationManagers.sharedInstance().openAll(
|
|
||||||
this.props.room,
|
|
||||||
'type_' + this.props.app.type,
|
|
||||||
this.props.app.id,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
|
||||||
this.props.room,
|
|
||||||
'type_' + this.props.app.type,
|
|
||||||
this.props.app.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +613,10 @@ export default class AppTile extends React.Component {
|
||||||
|
|
||||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||||
console.log("Replacing Jitsi widget URL with local wrapper");
|
console.log("Replacing Jitsi widget URL with local wrapper");
|
||||||
url = WidgetUtils.getLocalJitsiWrapperUrl({forLocalRender: true});
|
url = WidgetUtils.getLocalJitsiWrapperUrl({
|
||||||
|
forLocalRender: true,
|
||||||
|
auth: this.props.app.data ? this.props.app.data.auth : null,
|
||||||
|
});
|
||||||
url = this._addWurlParams(url);
|
url = this._addWurlParams(url);
|
||||||
} else {
|
} else {
|
||||||
url = this._getSafeUrl(this.state.widgetUrl);
|
url = this._getSafeUrl(this.state.widgetUrl);
|
||||||
|
@ -637,7 +627,10 @@ export default class AppTile extends React.Component {
|
||||||
_getPopoutUrl() {
|
_getPopoutUrl() {
|
||||||
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
if (WidgetType.JITSI.matches(this.props.app.type)) {
|
||||||
return this._templatedUrl(
|
return this._templatedUrl(
|
||||||
WidgetUtils.getLocalJitsiWrapperUrl({forLocalRender: false}),
|
WidgetUtils.getLocalJitsiWrapperUrl({
|
||||||
|
forLocalRender: false,
|
||||||
|
auth: this.props.app.data ? this.props.app.data.auth : null,
|
||||||
|
}),
|
||||||
this.props.app.type,
|
this.props.app.type,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -804,14 +797,16 @@ export default class AppTile extends React.Component {
|
||||||
const showMinimiseButton = this.props.showMinimise && this.props.show;
|
const showMinimiseButton = this.props.showMinimise && this.props.show;
|
||||||
const showMaximiseButton = this.props.showMinimise && !this.props.show;
|
const showMaximiseButton = this.props.showMinimise && !this.props.show;
|
||||||
|
|
||||||
let appTileClass;
|
let appTileClasses;
|
||||||
if (this.props.miniMode) {
|
if (this.props.miniMode) {
|
||||||
appTileClass = 'mx_AppTile_mini';
|
appTileClasses = {mx_AppTile_mini: true};
|
||||||
} else if (this.props.fullWidth) {
|
} else if (this.props.fullWidth) {
|
||||||
appTileClass = 'mx_AppTileFullWidth';
|
appTileClasses = {mx_AppTileFullWidth: true};
|
||||||
} else {
|
} else {
|
||||||
appTileClass = 'mx_AppTile';
|
appTileClasses = {mx_AppTile: true};
|
||||||
}
|
}
|
||||||
|
appTileClasses.mx_AppTile_minimised = !this.props.show;
|
||||||
|
appTileClasses = classNames(appTileClasses);
|
||||||
|
|
||||||
const menuBarClasses = classNames({
|
const menuBarClasses = classNames({
|
||||||
mx_AppTileMenuBar: true,
|
mx_AppTileMenuBar: true,
|
||||||
|
@ -843,7 +838,7 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <React.Fragment>
|
return <React.Fragment>
|
||||||
<div className={appTileClass} id={this.props.app.id}>
|
<div className={appTileClasses} id={this.props.app.id}>
|
||||||
{ this.props.showMenubar &&
|
{ this.props.showMenubar &&
|
||||||
<div ref={this._menu_bar} className={menuBarClasses} onClick={this.onClickMenuBar}>
|
<div ref={this._menu_bar} className={menuBarClasses} onClick={this.onClickMenuBar}>
|
||||||
<span className="mx_AppTileMenuBarTitle" style={{pointerEvents: (this.props.handleMinimisePointerEvents ? 'all' : false)}}>
|
<span className="mx_AppTileMenuBarTitle" style={{pointerEvents: (this.props.handleMinimisePointerEvents ? 'all' : false)}}>
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import {throttle} from "lodash";
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
|
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -156,7 +156,7 @@ export default class PersistedElement extends React.Component {
|
||||||
child.style.display = visible ? 'block' : 'none';
|
child.style.display = visible ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
updateChildPosition(child, parent) {
|
updateChildPosition = throttle((child, parent) => {
|
||||||
if (!child || !parent) return;
|
if (!child || !parent) return;
|
||||||
|
|
||||||
const parentRect = parent.getBoundingClientRect();
|
const parentRect = parent.getBoundingClientRect();
|
||||||
|
@ -167,9 +167,9 @@ export default class PersistedElement extends React.Component {
|
||||||
width: parentRect.width + 'px',
|
width: parentRect.width + 'px',
|
||||||
height: parentRect.height + 'px',
|
height: parentRect.height + 'px',
|
||||||
});
|
});
|
||||||
}
|
}, 100, {trailing: true, leading: true});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div ref={this.collectChildContainer}></div>;
|
return <div ref={this.collectChildContainer} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,11 +57,14 @@ export default class PowerSelector extends React.Component {
|
||||||
customValue: this.props.value,
|
customValue: this.props.value,
|
||||||
selectValue: 0,
|
selectValue: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._initStateFromProps(this.props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
this._initStateFromProps(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
this._initStateFromProps(newProps);
|
this._initStateFromProps(newProps);
|
||||||
|
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
||||||
import React, {useCallback, useMemo, useState, useEffect, useContext} from 'react';
|
import React, {useCallback, useMemo, useState, useEffect, useContext} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {Group, RoomMember, User} from 'matrix-js-sdk';
|
import {Group, RoomMember, User, Room} from 'matrix-js-sdk';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -1471,11 +1471,9 @@ const UserInfoHeader = ({onClose, member, e2eStatus}) => {
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserInfo = ({user, groupId, roomId, onClose, phase=RightPanelPhases.RoomMemberInfo, ...props}) => {
|
const UserInfo = ({user, groupId, room, onClose, phase=RightPanelPhases.RoomMemberInfo, ...props}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
// Load room if we are given a room id and memoize it - this can be undefined for User Info/Group Member Info
|
|
||||||
const room = useMemo(() => roomId ? cli.getRoom(roomId) : null, [cli, roomId]);
|
|
||||||
// fetch latest room member if we have a room, so we don't show historical information, falling back to user
|
// fetch latest room member if we have a room, so we don't show historical information, falling back to user
|
||||||
const member = useMemo(() => room ? (room.getMember(user.userId) || user) : user, [room, user]);
|
const member = useMemo(() => room ? (room.getMember(user.userId) || user) : user, [room, user]);
|
||||||
|
|
||||||
|
@ -1529,7 +1527,7 @@ UserInfo.propTypes = {
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
group: PropTypes.instanceOf(Group),
|
group: PropTypes.instanceOf(Group),
|
||||||
groupId: PropTypes.string,
|
groupId: PropTypes.string,
|
||||||
roomId: PropTypes.string,
|
room: PropTypes.instanceOf(Room),
|
||||||
|
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {useState} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import AppTile from '../elements/AppTile';
|
import AppTile from '../elements/AppTile';
|
||||||
|
@ -29,6 +29,10 @@ import WidgetEchoStore from "../../../stores/WidgetEchoStore";
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {Resizable} from "re-resizable";
|
||||||
|
import {useLocalStorageState} from "../../../hooks/useLocalStorageState";
|
||||||
|
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||||
|
|
||||||
// The maximum number of widgets that can be added in a room
|
// The maximum number of widgets that can be added in a room
|
||||||
const MAX_WIDGETS = 2;
|
const MAX_WIDGETS = 2;
|
||||||
|
@ -37,6 +41,7 @@ export default class AppsDrawer extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
|
resizeNotifier: PropTypes.instanceOf(ResizeNotifier).isRequired,
|
||||||
showApps: PropTypes.bool, // Should apps be rendered
|
showApps: PropTypes.bool, // Should apps be rendered
|
||||||
hide: PropTypes.bool, // If rendered, should apps drawer be visible
|
hide: PropTypes.bool, // If rendered, should apps drawer be visible
|
||||||
};
|
};
|
||||||
|
@ -161,7 +166,7 @@ export default class AppsDrawer extends React.Component {
|
||||||
return (<AppTile
|
return (<AppTile
|
||||||
key={app.id}
|
key={app.id}
|
||||||
app={app}
|
app={app}
|
||||||
fullWidth={arr.length<2 ? true : false}
|
fullWidth={arr.length < 2}
|
||||||
room={this.props.room}
|
room={this.props.room}
|
||||||
userId={this.props.userId}
|
userId={this.props.userId}
|
||||||
show={this.props.showApps}
|
show={this.props.showApps}
|
||||||
|
@ -172,8 +177,8 @@ export default class AppsDrawer extends React.Component {
|
||||||
/>);
|
/>);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apps.length == 0) {
|
if (apps.length === 0) {
|
||||||
return <div></div>;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let addWidget;
|
let addWidget;
|
||||||
|
@ -202,14 +207,68 @@ export default class AppsDrawer extends React.Component {
|
||||||
spinner = <Loader />;
|
spinner = <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
"mx_AppsDrawer": true,
|
||||||
|
"mx_AppsDrawer_hidden": this.props.hide,
|
||||||
|
"mx_AppsDrawer_fullWidth": apps.length < 2,
|
||||||
|
"mx_AppsDrawer_minimised": !this.props.showApps,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'mx_AppsDrawer' + (this.props.hide ? ' mx_AppsDrawer_hidden' : '')}>
|
<div className={classes}>
|
||||||
<div id='apps' className='mx_AppsContainer'>
|
<PersistentVResizer
|
||||||
|
id={"apps-drawer_" + this.props.room.roomId}
|
||||||
|
minHeight={100}
|
||||||
|
maxHeight={this.props.maxHeight ? this.props.maxHeight - 50 : undefined}
|
||||||
|
handleClass="mx_AppsContainer_resizerHandle"
|
||||||
|
className="mx_AppsContainer"
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
>
|
||||||
{ apps }
|
{ apps }
|
||||||
{ spinner }
|
{ spinner }
|
||||||
</div>
|
</PersistentVResizer>
|
||||||
{ this._canUserModify() && addWidget }
|
{ this._canUserModify() && addWidget }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PersistentVResizer = ({
|
||||||
|
id,
|
||||||
|
minHeight,
|
||||||
|
maxHeight,
|
||||||
|
className,
|
||||||
|
handleWrapperClass,
|
||||||
|
handleClass,
|
||||||
|
resizeNotifier,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [height, setHeight] = useLocalStorageState("pvr_" + id, 100);
|
||||||
|
const [resizing, setResizing] = useState(false);
|
||||||
|
|
||||||
|
return <Resizable
|
||||||
|
size={{height: Math.min(height, maxHeight)}}
|
||||||
|
minHeight={minHeight}
|
||||||
|
maxHeight={maxHeight}
|
||||||
|
onResizeStart={() => {
|
||||||
|
if (!resizing) setResizing(true);
|
||||||
|
resizeNotifier.startResizing();
|
||||||
|
}}
|
||||||
|
onResize={() => {
|
||||||
|
resizeNotifier.notifyTimelineHeightChanged();
|
||||||
|
}}
|
||||||
|
onResizeStop={(e, dir, ref, d) => {
|
||||||
|
setHeight(height + d.height);
|
||||||
|
if (resizing) setResizing(false);
|
||||||
|
resizeNotifier.stopResizing();
|
||||||
|
}}
|
||||||
|
handleWrapperClass={handleWrapperClass}
|
||||||
|
handleClasses={{bottom: handleClass}}
|
||||||
|
className={classNames(className, {
|
||||||
|
mx_AppsDrawer_resizing: resizing,
|
||||||
|
})}
|
||||||
|
enable={{bottom: true}}
|
||||||
|
>
|
||||||
|
{ children }
|
||||||
|
</Resizable>;
|
||||||
|
};
|
||||||
|
|
|
@ -204,6 +204,7 @@ export default class AuxPanel extends React.Component {
|
||||||
maxHeight={this.props.maxHeight}
|
maxHeight={this.props.maxHeight}
|
||||||
showApps={this.props.showApps}
|
showApps={this.props.showApps}
|
||||||
hide={this.props.hideAppsDrawer}
|
hide={this.props.hideAppsDrawer}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
let stateViews = null;
|
let stateViews = null;
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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 {Dispatch, SetStateAction, useCallback, useEffect, useState} from "react";
|
||||||
|
|
||||||
|
const getValue = <T>(key: string, initialValue: T): T => {
|
||||||
|
try {
|
||||||
|
const item = window.localStorage.getItem(key);
|
||||||
|
return item ? JSON.parse(item) : initialValue;
|
||||||
|
} catch (error) {
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook behaving like useState but persisting the value to localStorage. Returns same as useState
|
||||||
|
export const useLocalStorageState = <T>(key: string, initialValue: T) => {
|
||||||
|
const lsKey = "mx_" + key;
|
||||||
|
|
||||||
|
const [value, setValue] = useState<T>(getValue(lsKey, initialValue));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValue(getValue(lsKey, initialValue));
|
||||||
|
}, [lsKey, initialValue]);
|
||||||
|
|
||||||
|
const _setValue: Dispatch<SetStateAction<T>> = useCallback((v: T) => {
|
||||||
|
window.localStorage.setItem(lsKey, JSON.stringify(v));
|
||||||
|
setValue(v);
|
||||||
|
}, [lsKey]);
|
||||||
|
|
||||||
|
return [value, _setValue];
|
||||||
|
};
|
|
@ -149,6 +149,7 @@
|
||||||
"Command error": "Command error",
|
"Command error": "Command error",
|
||||||
"Usage": "Usage",
|
"Usage": "Usage",
|
||||||
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
|
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
|
||||||
|
"Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message",
|
||||||
"Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown",
|
"Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown",
|
||||||
"Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown",
|
"Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown",
|
||||||
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
"Searches DuckDuckGo for results": "Searches DuckDuckGo for results",
|
||||||
|
|
|
@ -105,6 +105,9 @@ export default class Resizer {
|
||||||
if (this.classNames.resizing) {
|
if (this.classNames.resizing) {
|
||||||
this.container.classList.add(this.classNames.resizing);
|
this.container.classList.add(this.classNames.resizing);
|
||||||
}
|
}
|
||||||
|
if (this.config.onResizeStart) {
|
||||||
|
this.config.onResizeStart();
|
||||||
|
}
|
||||||
|
|
||||||
const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
|
const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
|
||||||
distributor.start();
|
distributor.start();
|
||||||
|
@ -119,6 +122,9 @@ export default class Resizer {
|
||||||
if (this.classNames.resizing) {
|
if (this.classNames.resizing) {
|
||||||
this.container.classList.remove(this.classNames.resizing);
|
this.container.classList.remove(this.classNames.resizing);
|
||||||
}
|
}
|
||||||
|
if (this.config.onResizeStop) {
|
||||||
|
this.config.onResizeStop();
|
||||||
|
}
|
||||||
distributor.finish();
|
distributor.finish();
|
||||||
body.removeEventListener("mouseup", finishResize, false);
|
body.removeEventListener("mouseup", finishResize, false);
|
||||||
document.removeEventListener("mouseleave", finishResize, false);
|
document.removeEventListener("mouseleave", finishResize, false);
|
||||||
|
|
|
@ -56,6 +56,18 @@ export default class Sizer {
|
||||||
return this.vertical ? this.container.offsetTop : this.container.offsetLeft;
|
return this.vertical ? this.container.offsetTop : this.container.offsetLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return {number} container offset to document */
|
||||||
|
_getPageOffset() {
|
||||||
|
let element = this.container;
|
||||||
|
let offset = 0;
|
||||||
|
while (element) {
|
||||||
|
const pos = this.vertical ? element.offsetTop : element.offsetLeft;
|
||||||
|
offset = offset + pos;
|
||||||
|
element = element.offsetParent;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
setItemSize(item, size) {
|
setItemSize(item, size) {
|
||||||
if (this.vertical) {
|
if (this.vertical) {
|
||||||
item.style.height = `${Math.round(size)}px`;
|
item.style.height = `${Math.round(size)}px`;
|
||||||
|
@ -80,9 +92,9 @@ export default class Sizer {
|
||||||
offsetFromEvent(event) {
|
offsetFromEvent(event) {
|
||||||
const pos = this.vertical ? event.pageY : event.pageX;
|
const pos = this.vertical ? event.pageY : event.pageX;
|
||||||
if (this.reverse) {
|
if (this.reverse) {
|
||||||
return (this._getOffset() + this.getTotalSize()) - pos;
|
return (this._getPageOffset() + this.getTotalSize()) - pos;
|
||||||
} else {
|
} else {
|
||||||
return pos - this._getOffset();
|
return pos - this._getPageOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,10 @@ import { SettingLevel } from "./SettingLevel";
|
||||||
|
|
||||||
export type CallbackFn = (changedInRoomId: string, atLevel: SettingLevel, newValAtLevel: any) => void;
|
export type CallbackFn = (changedInRoomId: string, atLevel: SettingLevel, newValAtLevel: any) => void;
|
||||||
|
|
||||||
const IRRELEVANT_ROOM = Symbol("any room");
|
const IRRELEVANT_ROOM: string = null;
|
||||||
|
|
||||||
interface RoomWatcherMap {
|
interface RoomWatcherMap {
|
||||||
// @ts-ignore - TS wants string-only keys but we know better - https://github.com/Microsoft/TypeScript/issues/1863
|
[roomId: string]: CallbackFn[];
|
||||||
[roomId: string | symbol]: CallbackFn[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +68,7 @@ export class WatchManager {
|
||||||
if (!inRoomId) {
|
if (!inRoomId) {
|
||||||
// Fire updates to all the individual room watchers too, as they probably
|
// Fire updates to all the individual room watchers too, as they probably
|
||||||
// care about the change higher up.
|
// care about the change higher up.
|
||||||
callbacks.push(...Object.values(roomWatchers).reduce((r, a) => [...r, ...a], []));
|
callbacks.push(...Object.values(roomWatchers).flat(1));
|
||||||
} else if (roomWatchers[IRRELEVANT_ROOM]) {
|
} else if (roomWatchers[IRRELEVANT_ROOM]) {
|
||||||
callbacks.push(...roomWatchers[IRRELEVANT_ROOM]);
|
callbacks.push(...roomWatchers[IRRELEVANT_ROOM]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,13 +93,13 @@ class WidgetEchoStore extends EventEmitter {
|
||||||
if (this._roomWidgetEcho[roomId] === undefined) this._roomWidgetEcho[roomId] = {};
|
if (this._roomWidgetEcho[roomId] === undefined) this._roomWidgetEcho[roomId] = {};
|
||||||
|
|
||||||
this._roomWidgetEcho[roomId][widgetId] = state;
|
this._roomWidgetEcho[roomId][widgetId] = state;
|
||||||
this.emit('update');
|
this.emit('update', roomId, widgetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRoomWidgetEcho(roomId, widgetId) {
|
removeRoomWidgetEcho(roomId, widgetId) {
|
||||||
delete this._roomWidgetEcho[roomId][widgetId];
|
delete this._roomWidgetEcho[roomId][widgetId];
|
||||||
if (Object.keys(this._roomWidgetEcho[roomId]).length === 0) delete this._roomWidgetEcho[roomId];
|
if (Object.keys(this._roomWidgetEcho[roomId]).length === 0) delete this._roomWidgetEcho[roomId];
|
||||||
this.emit('update');
|
this.emit('update', roomId, widgetId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,19 @@ export default class ResizeNotifier extends EventEmitter {
|
||||||
// with default options, will call fn once at first call, and then every x ms
|
// with default options, will call fn once at first call, and then every x ms
|
||||||
// if there was another call in that timespan
|
// if there was another call in that timespan
|
||||||
this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
|
this._throttledMiddlePanel = throttle(() => this.emit("middlePanelResized"), 200);
|
||||||
|
this._isResizing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isResizing() {
|
||||||
|
return this._isResizing;
|
||||||
|
}
|
||||||
|
|
||||||
|
startResizing() {
|
||||||
|
this._isResizing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopResizing() {
|
||||||
|
this._isResizing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_noisyMiddlePanel() {
|
_noisyMiddlePanel() {
|
||||||
|
|
|
@ -405,6 +405,7 @@ export default class WidgetUtils {
|
||||||
app.creatorUserId = senderUserId;
|
app.creatorUserId = senderUserId;
|
||||||
|
|
||||||
app.id = appId;
|
app.id = appId;
|
||||||
|
app.roomId = roomId;
|
||||||
app.eventId = eventId;
|
app.eventId = eventId;
|
||||||
app.name = app.name || app.type;
|
app.name = app.name || app.type;
|
||||||
|
|
||||||
|
@ -448,16 +449,21 @@ export default class WidgetUtils {
|
||||||
return encodeURIComponent(`${widgetLocation}::${widgetUrl}`);
|
return encodeURIComponent(`${widgetLocation}::${widgetUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean}={}) {
|
static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean, auth?: string}={}) {
|
||||||
// NB. we can't just encodeURIComponent all of these because the $ signs need to be there
|
// NB. we can't just encodeURIComponent all of these because the $ signs need to be there
|
||||||
const queryString = [
|
const queryStringParts = [
|
||||||
'conferenceDomain=$domain',
|
'conferenceDomain=$domain',
|
||||||
'conferenceId=$conferenceId',
|
'conferenceId=$conferenceId',
|
||||||
'isAudioOnly=$isAudioOnly',
|
'isAudioOnly=$isAudioOnly',
|
||||||
'displayName=$matrix_display_name',
|
'displayName=$matrix_display_name',
|
||||||
'avatarUrl=$matrix_avatar_url',
|
'avatarUrl=$matrix_avatar_url',
|
||||||
'userId=$matrix_user_id',
|
'userId=$matrix_user_id',
|
||||||
].join('&');
|
'roomId=$matrix_room_id',
|
||||||
|
];
|
||||||
|
if (opts.auth) {
|
||||||
|
queryStringParts.push(`auth=${opts.auth}`);
|
||||||
|
}
|
||||||
|
const queryString = queryStringParts.join('&');
|
||||||
|
|
||||||
let baseUrl = window.location;
|
let baseUrl = window.location;
|
||||||
if (window.location.protocol !== "https:" && !opts.forLocalRender) {
|
if (window.location.protocol !== "https:" && !opts.forLocalRender) {
|
||||||
|
@ -471,4 +477,13 @@ export default class WidgetUtils {
|
||||||
const url = new URL("jitsi.html#" + queryString, baseUrl); // this strips hash fragment from baseUrl
|
const url = new URL("jitsi.html#" + queryString, baseUrl); // this strips hash fragment from baseUrl
|
||||||
return url.href;
|
return url.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static editWidget(room, app) {
|
||||||
|
// TODO: Open the right manager for the widget
|
||||||
|
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||||
|
IntegrationManagers.sharedInstance().openAll(room, 'type_' + app.type, app.id);
|
||||||
|
} else {
|
||||||
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(room, 'type_' + app.type, app.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,6 +332,9 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string {
|
||||||
if (permalinkParts.roomIdOrAlias) {
|
if (permalinkParts.roomIdOrAlias) {
|
||||||
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
||||||
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
||||||
|
if (permalinkParts.viaServers.length > 0) {
|
||||||
|
permalink += new SpecPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers);
|
||||||
|
}
|
||||||
} else if (permalinkParts.groupId) {
|
} else if (permalinkParts.groupId) {
|
||||||
permalink = `#/group/${permalinkParts.groupId}`;
|
permalink = `#/group/${permalinkParts.groupId}`;
|
||||||
} else if (permalinkParts.userId) {
|
} else if (permalinkParts.userId) {
|
||||||
|
|
|
@ -34,6 +34,30 @@ export class Jitsi {
|
||||||
return this.domain || 'jitsi.riot.im';
|
return this.domain || 'jitsi.riot.im';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for auth needed by looking up a well-known file
|
||||||
|
*
|
||||||
|
* If the file does not exist, we assume no auth.
|
||||||
|
*
|
||||||
|
* See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||||
|
*/
|
||||||
|
public async getJitsiAuth(): Promise<string|null> {
|
||||||
|
if (!this.preferredDomain) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://${this.preferredDomain}/.well-known/element/jitsi`);
|
||||||
|
data = await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (data.auth) {
|
||||||
|
return data.auth;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.on("WellKnown.client", this.update);
|
cli.on("WellKnown.client", this.update);
|
||||||
|
|
|
@ -34,6 +34,7 @@ export enum KnownWidgetActions {
|
||||||
GetCapabilities = "capabilities",
|
GetCapabilities = "capabilities",
|
||||||
SendEvent = "send_event",
|
SendEvent = "send_event",
|
||||||
UpdateVisibility = "visibility",
|
UpdateVisibility = "visibility",
|
||||||
|
GetOpenIDCredentials = "get_openid",
|
||||||
ReceiveOpenIDCredentials = "openid_credentials",
|
ReceiveOpenIDCredentials = "openid_credentials",
|
||||||
SetAlwaysOnScreen = "set_always_on_screen",
|
SetAlwaysOnScreen = "set_always_on_screen",
|
||||||
ClientReady = "im.vector.ready",
|
ClientReady = "im.vector.ready",
|
||||||
|
@ -64,6 +65,13 @@ export interface FromWidgetRequest extends WidgetRequest {
|
||||||
response: any;
|
response: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OpenIDCredentials {
|
||||||
|
accessToken: string;
|
||||||
|
tokenType: string;
|
||||||
|
matrixServerName: string;
|
||||||
|
expiresIn: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles Element <--> Widget interactions for embedded/standalone widgets.
|
* Handles Element <--> Widget interactions for embedded/standalone widgets.
|
||||||
*
|
*
|
||||||
|
@ -73,10 +81,12 @@ export interface FromWidgetRequest extends WidgetRequest {
|
||||||
* the given promise resolves.
|
* the given promise resolves.
|
||||||
*/
|
*/
|
||||||
export class WidgetApi extends EventEmitter {
|
export class WidgetApi extends EventEmitter {
|
||||||
private origin: string;
|
private readonly origin: string;
|
||||||
private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {};
|
private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {};
|
||||||
private readyPromise: Promise<any>;
|
private readonly readyPromise: Promise<any>;
|
||||||
private readyPromiseResolve: () => void;
|
private readyPromiseResolve: () => void;
|
||||||
|
private openIDCredentialsCallback: () => void;
|
||||||
|
public openIDCredentials: OpenIDCredentials;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this to true if your widget is expecting a ready message from the client. False otherwise (default).
|
* Set this to true if your widget is expecting a ready message from the client. False otherwise (default).
|
||||||
|
@ -120,6 +130,10 @@ export class WidgetApi extends EventEmitter {
|
||||||
// Acknowledge that we're shut down now
|
// Acknowledge that we're shut down now
|
||||||
this.replyToRequest(<ToWidgetRequest>payload, {});
|
this.replyToRequest(<ToWidgetRequest>payload, {});
|
||||||
});
|
});
|
||||||
|
} else if (payload.action === KnownWidgetActions.ReceiveOpenIDCredentials) {
|
||||||
|
// Save OpenID credentials
|
||||||
|
this.setOpenIDCredentials(<ToWidgetRequest>payload);
|
||||||
|
this.replyToRequest(<ToWidgetRequest>payload, {});
|
||||||
} else {
|
} else {
|
||||||
console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`);
|
console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`);
|
||||||
}
|
}
|
||||||
|
@ -134,6 +148,32 @@ export class WidgetApi extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setOpenIDCredentials(value: WidgetRequest) {
|
||||||
|
const data = value.data;
|
||||||
|
if (data.state === 'allowed') {
|
||||||
|
this.openIDCredentials = {
|
||||||
|
accessToken: data.access_token,
|
||||||
|
tokenType: data.token_type,
|
||||||
|
matrixServerName: data.matrix_server_name,
|
||||||
|
expiresIn: data.expires_in,
|
||||||
|
}
|
||||||
|
} else if (data.state === 'blocked') {
|
||||||
|
this.openIDCredentials = null;
|
||||||
|
}
|
||||||
|
if (['allowed', 'blocked'].includes(data.state) && this.openIDCredentialsCallback) {
|
||||||
|
this.openIDCredentialsCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public requestOpenIDCredentials(credentialsResponseCallback: () => void) {
|
||||||
|
this.openIDCredentialsCallback = credentialsResponseCallback;
|
||||||
|
this.callAction(
|
||||||
|
KnownWidgetActions.GetOpenIDCredentials,
|
||||||
|
{},
|
||||||
|
this.setOpenIDCredentials,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public waitReady(): Promise<any> {
|
public waitReady(): Promise<any> {
|
||||||
return this.readyPromise;
|
return this.readyPromise;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7557,6 +7557,11 @@ retry@^0.10.0:
|
||||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
|
||||||
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
|
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
|
||||||
|
|
||||||
|
rfc4648@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd"
|
||||||
|
integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg==
|
||||||
|
|
||||||
rimraf@2.6.3:
|
rimraf@2.6.3:
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||||
|
|
Loading…
Reference in New Issue