Merge pull request #3645 from matrix-org/travis/widget-menu
Move many widget options to a context menu
This commit is contained in:
commit
86783e4439
11 changed files with 234 additions and 171 deletions
|
@ -48,6 +48,7 @@
|
||||||
@import "./views/context_menus/_StatusMessageContextMenu.scss";
|
@import "./views/context_menus/_StatusMessageContextMenu.scss";
|
||||||
@import "./views/context_menus/_TagTileContextMenu.scss";
|
@import "./views/context_menus/_TagTileContextMenu.scss";
|
||||||
@import "./views/context_menus/_TopLeftMenu.scss";
|
@import "./views/context_menus/_TopLeftMenu.scss";
|
||||||
|
@import "./views/context_menus/_WidgetContextMenu.scss";
|
||||||
@import "./views/dialogs/_AddressPickerDialog.scss";
|
@import "./views/dialogs/_AddressPickerDialog.scss";
|
||||||
@import "./views/dialogs/_Analytics.scss";
|
@import "./views/dialogs/_Analytics.scss";
|
||||||
@import "./views/dialogs/_ChangelogDialog.scss";
|
@import "./views/dialogs/_ChangelogDialog.scss";
|
||||||
|
|
36
res/css/views/context_menus/_WidgetContextMenu.scss
Normal file
36
res/css/views/context_menus/_WidgetContextMenu.scss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Matrix.org Foundaction 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_WidgetContextMenu {
|
||||||
|
padding: 6px;
|
||||||
|
|
||||||
|
.mx_WidgetContextMenu_option {
|
||||||
|
padding: 3px 6px 3px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_WidgetContextMenu_separator {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-style: none;
|
||||||
|
border-left-style: none;
|
||||||
|
border-right-style: none;
|
||||||
|
border-top-style: solid;
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-color: $menu-border-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -153,40 +153,12 @@ $AppsDrawerBodyHeight: 273px;
|
||||||
background-color: $accent-color;
|
background-color: $accent-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_reload {
|
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/refresh.svg');
|
|
||||||
mask-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout {
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout {
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/external-link.svg');
|
mask-image: url('$(res)/img/feather-customised/widget/external-link.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_snapshot {
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_menu {
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/camera.svg');
|
mask-image: url('$(res)/img/icon_context.svg');
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_edit {
|
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/edit.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_delete {
|
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/bin.svg');
|
|
||||||
background-color: $warning-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_cancel {
|
|
||||||
mask-image: url('$(res)/img/feather-customised/widget/x-circle.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* delete ? */
|
|
||||||
.mx_AppTileMenuBarWidget {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
padding: 1px;
|
|
||||||
transition-duration: 500ms;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBarWidgetDelete {
|
.mx_AppTileMenuBarWidgetDelete {
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="11.014242"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 11.014242 12"
|
|
||||||
version="1.1"
|
|
||||||
id="svg6"
|
|
||||||
sodipodi:docname="bin.svg"
|
|
||||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
|
||||||
<metadata
|
|
||||||
id="metadata12">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
|
||||||
id="defs10" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1"
|
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:window-width="750"
|
|
||||||
inkscape:window-height="480"
|
|
||||||
id="namedview8"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0.5"
|
|
||||||
fit-margin-left="0.5"
|
|
||||||
fit-margin-bottom="0.5"
|
|
||||||
fit-margin-right="0.5"
|
|
||||||
inkscape:zoom="19.666667"
|
|
||||||
inkscape:cx="5.5071212"
|
|
||||||
inkscape:cy="6"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg6" />
|
|
||||||
<g
|
|
||||||
id="g4"
|
|
||||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
|
||||||
transform="translate(1.0071212)">
|
|
||||||
<path
|
|
||||||
d="M 0,3 H 9 M 8,3 v 7 A 1,1 0 0 1 7,11 H 2 A 1,1 0 0 1 1,10 V 3 M 2.5,3 V 2 a 1,1 0 0 1 1,-1 h 2 a 1,1 0 0 1 1,1 v 1 m -3,2.5 v 3 m 2,-3 v 3"
|
|
||||||
id="path2"
|
|
||||||
style="stroke:#000000;stroke-opacity:1"
|
|
||||||
inkscape:connector-curvature="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2 KiB |
|
@ -1,6 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
|
|
||||||
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
|
||||||
<path d="M11 8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V2.5a1 1 0 0 1 1-1h2L4 0h3l1 1.5h2a1 1 0 0 1 1 1V8z"/>
|
|
||||||
<circle cx="5.5" cy="5" r="2"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 378 B |
|
@ -1,6 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="11" viewBox="0 0 12 11">
|
|
||||||
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M10 6.33V9a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h2.67"/>
|
|
||||||
<path d="M9 0l2 2-5 5H4V5z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 324 B |
|
@ -1,6 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
|
|
||||||
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M12 1.5v3H9M1 9.5v-3h3"/>
|
|
||||||
<path d="M2.255 4A4.5 4.5 0 0 1 9.68 2.32L12 4.5m-11 2l2.32 2.18A4.5 4.5 0 0 0 10.745 7"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 346 B |
|
@ -1,6 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
|
||||||
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
|
||||||
<circle cx="5" cy="5" r="5"/>
|
|
||||||
<path d="M6.5 3.5l-3 3M3.5 3.5l3 3"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 315 B |
134
src/components/views/context_menus/WidgetContextMenu.js
Normal file
134
src/components/views/context_menus/WidgetContextMenu.js
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import {_t} from '../../../languageHandler';
|
||||||
|
|
||||||
|
export default class WidgetContextMenu extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
onFinished: PropTypes.func,
|
||||||
|
|
||||||
|
// Callback for when the revoke button is clicked. Required.
|
||||||
|
onRevokeClicked: PropTypes.func.isRequired,
|
||||||
|
|
||||||
|
// Callback for when the snapshot button is clicked. Button not shown
|
||||||
|
// without a callback.
|
||||||
|
onSnapshotClicked: PropTypes.func,
|
||||||
|
|
||||||
|
// Callback for when the reload button is clicked. Button not shown
|
||||||
|
// without a callback.
|
||||||
|
onReloadClicked: PropTypes.func,
|
||||||
|
|
||||||
|
// Callback for when the edit button is clicked. Button not shown
|
||||||
|
// without a callback.
|
||||||
|
onEditClicked: PropTypes.func,
|
||||||
|
|
||||||
|
// Callback for when the delete button is clicked. Button not shown
|
||||||
|
// without a callback.
|
||||||
|
onDeleteClicked: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
proxyClick(fn) {
|
||||||
|
fn();
|
||||||
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: It's annoying that our context menus require us to hit onFinished() to close :(
|
||||||
|
|
||||||
|
onEditClicked = () => {
|
||||||
|
this.proxyClick(this.props.onEditClicked);
|
||||||
|
};
|
||||||
|
|
||||||
|
onReloadClicked = () => {
|
||||||
|
this.proxyClick(this.props.onReloadClicked);
|
||||||
|
};
|
||||||
|
|
||||||
|
onSnapshotClicked = () => {
|
||||||
|
this.proxyClick(this.props.onSnapshotClicked);
|
||||||
|
};
|
||||||
|
|
||||||
|
onDeleteClicked = () => {
|
||||||
|
this.proxyClick(this.props.onDeleteClicked);
|
||||||
|
};
|
||||||
|
|
||||||
|
onRevokeClicked = () => {
|
||||||
|
this.proxyClick(this.props.onRevokeClicked);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||||
|
|
||||||
|
const options = [];
|
||||||
|
|
||||||
|
if (this.props.onEditClicked) {
|
||||||
|
options.push(
|
||||||
|
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onEditClicked} key='edit'>
|
||||||
|
{_t("Edit")}
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.onReloadClicked) {
|
||||||
|
options.push(
|
||||||
|
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onReloadClicked}
|
||||||
|
key='reload'>
|
||||||
|
{_t("Reload")}
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.onSnapshotClicked) {
|
||||||
|
options.push(
|
||||||
|
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onSnapshotClicked}
|
||||||
|
key='snap'>
|
||||||
|
{_t("Take picture")}
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.onDeleteClicked) {
|
||||||
|
options.push(
|
||||||
|
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onDeleteClicked}
|
||||||
|
key='delete'>
|
||||||
|
{_t("Remove for everyone")}
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push this last so it appears last. It's always present.
|
||||||
|
options.push(
|
||||||
|
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onRevokeClicked} key='revoke'>
|
||||||
|
{_t("Remove for me")}
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Put separators between the options
|
||||||
|
if (options.length > 1) {
|
||||||
|
const length = options.length;
|
||||||
|
for (let i = 0; i < length - 1; i++) {
|
||||||
|
const sep = <hr key={i} className="mx_WidgetContextMenu_separator" />;
|
||||||
|
|
||||||
|
// Insert backwards so the insertions don't affect our math on where to place them.
|
||||||
|
// We also use our cached length to avoid worrying about options.length changing
|
||||||
|
options.splice(length - 1 - i, 0, sep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="mx_WidgetContextMenu">{options}</div>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||||
|
import {createMenu} from "../../structures/ContextualMenu";
|
||||||
|
|
||||||
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
||||||
const ENABLE_REACT_PERF = false;
|
const ENABLE_REACT_PERF = false;
|
||||||
|
@ -52,7 +53,7 @@ export default class AppTile extends React.Component {
|
||||||
this._onLoaded = this._onLoaded.bind(this);
|
this._onLoaded = this._onLoaded.bind(this);
|
||||||
this._onEditClick = this._onEditClick.bind(this);
|
this._onEditClick = this._onEditClick.bind(this);
|
||||||
this._onDeleteClick = this._onDeleteClick.bind(this);
|
this._onDeleteClick = this._onDeleteClick.bind(this);
|
||||||
this._onCancelClick = this._onCancelClick.bind(this);
|
this._onRevokeClicked = this._onRevokeClicked.bind(this);
|
||||||
this._onSnapshotClick = this._onSnapshotClick.bind(this);
|
this._onSnapshotClick = this._onSnapshotClick.bind(this);
|
||||||
this.onClickMenuBar = this.onClickMenuBar.bind(this);
|
this.onClickMenuBar = this.onClickMenuBar.bind(this);
|
||||||
this._onMinimiseClick = this._onMinimiseClick.bind(this);
|
this._onMinimiseClick = this._onMinimiseClick.bind(this);
|
||||||
|
@ -271,7 +272,7 @@ export default class AppTile extends React.Component {
|
||||||
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
|
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onEditClick(e) {
|
_onEditClick() {
|
||||||
console.log("Edit widget ID ", this.props.id);
|
console.log("Edit widget ID ", this.props.id);
|
||||||
if (this.props.onEditClick) {
|
if (this.props.onEditClick) {
|
||||||
this.props.onEditClick();
|
this.props.onEditClick();
|
||||||
|
@ -293,7 +294,7 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSnapshotClick(e) {
|
_onSnapshotClick() {
|
||||||
console.warn("Requesting widget snapshot");
|
console.warn("Requesting widget snapshot");
|
||||||
ActiveWidgetStore.getWidgetMessaging(this.props.id).getScreenshot()
|
ActiveWidgetStore.getWidgetMessaging(this.props.id).getScreenshot()
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -360,13 +361,9 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onCancelClick() {
|
_onRevokeClicked() {
|
||||||
if (this.props.onDeleteClick) {
|
console.log("Revoke widget permissions - %s", this.props.id);
|
||||||
this.props.onDeleteClick();
|
this._revokeWidgetPermission();
|
||||||
} else {
|
|
||||||
console.log("Revoke widget permissions - %s", this.props.id);
|
|
||||||
this._revokeWidgetPermission();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -544,18 +541,59 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPopoutWidgetClick(e) {
|
_onPopoutWidgetClick() {
|
||||||
// Using Object.assign workaround as the following opens in a new window instead of a new tab.
|
// Using Object.assign workaround as the following opens in a new window instead of a new tab.
|
||||||
// window.open(this._getSafeUrl(), '_blank', 'noopener=yes');
|
// window.open(this._getSafeUrl(), '_blank', 'noopener=yes');
|
||||||
Object.assign(document.createElement('a'),
|
Object.assign(document.createElement('a'),
|
||||||
{ target: '_blank', href: this._getSafeUrl(), rel: 'noopener'}).click();
|
{ target: '_blank', href: this._getSafeUrl(), rel: 'noopener'}).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onReloadWidgetClick(e) {
|
_onReloadWidgetClick() {
|
||||||
// Reload iframe in this way to avoid cross-origin restrictions
|
// Reload iframe in this way to avoid cross-origin restrictions
|
||||||
this.refs.appFrame.src = this.refs.appFrame.src;
|
this.refs.appFrame.src = this.refs.appFrame.src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getMenuOptions(ev) {
|
||||||
|
// TODO: This block of code gets copy/pasted a lot. We should make that happen less.
|
||||||
|
const menuOptions = {};
|
||||||
|
const buttonRect = ev.target.getBoundingClientRect();
|
||||||
|
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||||
|
const buttonLeft = buttonRect.left + window.pageXOffset;
|
||||||
|
const buttonTop = buttonRect.top + window.pageYOffset;
|
||||||
|
// Align the right edge of the menu to the left edge of the button
|
||||||
|
menuOptions.right = window.innerWidth - buttonLeft;
|
||||||
|
// Align the menu vertically on whichever side of the button has more
|
||||||
|
// space available.
|
||||||
|
if (buttonTop < window.innerHeight / 2) {
|
||||||
|
menuOptions.top = buttonTop;
|
||||||
|
} else {
|
||||||
|
menuOptions.bottom = window.innerHeight - buttonTop;
|
||||||
|
}
|
||||||
|
return menuOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onContextMenuClick = (ev) => {
|
||||||
|
const WidgetContextMenu = sdk.getComponent('views.context_menus.WidgetContextMenu');
|
||||||
|
const menuOptions = {
|
||||||
|
...this._getMenuOptions(ev),
|
||||||
|
|
||||||
|
// A revoke handler is always required
|
||||||
|
onRevokeClicked: this._onRevokeClicked,
|
||||||
|
};
|
||||||
|
|
||||||
|
const canUserModify = this._canUserModify();
|
||||||
|
const showEditButton = Boolean(this._scalarClient && canUserModify);
|
||||||
|
const showDeleteButton = (this.props.showDelete === undefined || this.props.showDelete) && canUserModify;
|
||||||
|
const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show;
|
||||||
|
|
||||||
|
if (showEditButton) menuOptions.onEditClicked = this._onEditClick;
|
||||||
|
if (showDeleteButton) menuOptions.onDeleteClicked = this._onDeleteClick;
|
||||||
|
if (showPictureSnapshotButton) menuOptions.onSnapshotClicked = this._onSnapshotClick;
|
||||||
|
if (this.props.showReload) menuOptions.onReloadClicked = this._onReloadWidgetClick;
|
||||||
|
|
||||||
|
createMenu(WidgetContextMenu, menuOptions);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let appTileBody;
|
let appTileBody;
|
||||||
|
|
||||||
|
@ -565,7 +603,7 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
|
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
|
||||||
// because that would allow the iframe to prgramatically remove the sandbox attribute, but
|
// because that would allow the iframe to programmatically remove the sandbox attribute, but
|
||||||
// this would only be for content hosted on the same origin as the riot client: anything
|
// this would only be for content hosted on the same origin as the riot client: anything
|
||||||
// hosted on the same origin as the client will get the same access as if you clicked
|
// hosted on the same origin as the client will get the same access as if you clicked
|
||||||
// a link to it.
|
// a link to it.
|
||||||
|
@ -645,13 +683,6 @@ export default class AppTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// editing is done in scalar
|
|
||||||
const canUserModify = this._canUserModify();
|
|
||||||
const showEditButton = Boolean(this._scalarClient && canUserModify);
|
|
||||||
const showDeleteButton = (this.props.showDelete === undefined || this.props.showDelete) && canUserModify;
|
|
||||||
const showCancelButton = (this.props.showCancel === undefined || this.props.showCancel) && !showDeleteButton;
|
|
||||||
// Picture snapshot - only show button when apps are maximised.
|
|
||||||
const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show;
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -690,41 +721,17 @@ export default class AppTile extends React.Component {
|
||||||
{ this.props.showTitle && this._getTileTitle() }
|
{ this.props.showTitle && this._getTileTitle() }
|
||||||
</span>
|
</span>
|
||||||
<span className="mx_AppTileMenuBarWidgets">
|
<span className="mx_AppTileMenuBarWidgets">
|
||||||
{ /* Reload widget */ }
|
|
||||||
{ this.props.showReload && <AccessibleButton
|
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_reload"
|
|
||||||
title={_t('Reload widget')}
|
|
||||||
onClick={this._onReloadWidgetClick}
|
|
||||||
/> }
|
|
||||||
{ /* Popout widget */ }
|
{ /* Popout widget */ }
|
||||||
{ this.props.showPopout && <AccessibleButton
|
{ this.props.showPopout && <AccessibleButton
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout"
|
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout"
|
||||||
title={_t('Popout widget')}
|
title={_t('Popout widget')}
|
||||||
onClick={this._onPopoutWidgetClick}
|
onClick={this._onPopoutWidgetClick}
|
||||||
/> }
|
/> }
|
||||||
{ /* Snapshot widget */ }
|
{ /* Context menu */ }
|
||||||
{ showPictureSnapshotButton && <AccessibleButton
|
{ <AccessibleButton
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_snapshot"
|
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_menu"
|
||||||
title={_t('Picture')}
|
title={_t('More options')}
|
||||||
onClick={this._onSnapshotClick}
|
onClick={this._onContextMenuClick}
|
||||||
/> }
|
|
||||||
{ /* Edit widget */ }
|
|
||||||
{ showEditButton && <AccessibleButton
|
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_edit"
|
|
||||||
title={_t('Edit')}
|
|
||||||
onClick={this._onEditClick}
|
|
||||||
/> }
|
|
||||||
{ /* Delete widget */ }
|
|
||||||
{ showDeleteButton && <AccessibleButton
|
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_delete"
|
|
||||||
title={_t('Delete widget')}
|
|
||||||
onClick={this._onDeleteClick}
|
|
||||||
/> }
|
|
||||||
{ /* Cancel widget */ }
|
|
||||||
{ showCancelButton && <AccessibleButton
|
|
||||||
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_cancel"
|
|
||||||
title={_t('Revoke widget access')}
|
|
||||||
onClick={this._onCancelClick}
|
|
||||||
/> }
|
/> }
|
||||||
</span>
|
</span>
|
||||||
</div> }
|
</div> }
|
||||||
|
|
|
@ -1205,10 +1205,8 @@
|
||||||
"An error ocurred whilst trying to remove the widget from the room": "An error ocurred whilst trying to remove the widget from the room",
|
"An error ocurred whilst trying to remove the widget from the room": "An error ocurred whilst trying to remove the widget from the room",
|
||||||
"Minimize apps": "Minimize apps",
|
"Minimize apps": "Minimize apps",
|
||||||
"Maximize apps": "Maximize apps",
|
"Maximize apps": "Maximize apps",
|
||||||
"Reload widget": "Reload widget",
|
|
||||||
"Popout widget": "Popout widget",
|
"Popout widget": "Popout widget",
|
||||||
"Picture": "Picture",
|
"More options": "More options",
|
||||||
"Revoke widget access": "Revoke widget access",
|
|
||||||
"Create new room": "Create new room",
|
"Create new room": "Create new room",
|
||||||
"Unblacklist": "Unblacklist",
|
"Unblacklist": "Unblacklist",
|
||||||
"Blacklist": "Blacklist",
|
"Blacklist": "Blacklist",
|
||||||
|
@ -1565,6 +1563,10 @@
|
||||||
"Hide": "Hide",
|
"Hide": "Hide",
|
||||||
"Home": "Home",
|
"Home": "Home",
|
||||||
"Sign in": "Sign in",
|
"Sign in": "Sign in",
|
||||||
|
"Reload": "Reload",
|
||||||
|
"Take picture": "Take picture",
|
||||||
|
"Remove for everyone": "Remove for everyone",
|
||||||
|
"Remove for me": "Remove for me",
|
||||||
"powered by Matrix": "powered by Matrix",
|
"powered by Matrix": "powered by Matrix",
|
||||||
"This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.",
|
"This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.",
|
||||||
"Custom Server Options": "Custom Server Options",
|
"Custom Server Options": "Custom Server Options",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue