Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 42c59b5

Browse files
committed
Make AppTile in Stickerpicker persistent using PersistedElement
1 parent fee4802 commit 42c59b5

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
Copyright 2018 New Vector Ltd.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
const classNames = require('classnames');
18+
const React = require('react');
19+
const ReactDOM = require('react-dom');
20+
import PropTypes from 'prop-types';
21+
22+
// Shamelessly ripped off Modal.js. There's probably a better way
23+
// of doing reusable widgets like dialog boxes & menus where we go and
24+
// pass in a custom control as the actual body.
25+
26+
const ContainerId = "mx_PersistedElement";
27+
28+
function getOrCreateContainer() {
29+
let container = document.getElementById(ContainerId);
30+
31+
if (!container) {
32+
container = document.createElement("div");
33+
container.id = ContainerId;
34+
document.body.appendChild(container);
35+
}
36+
37+
return container;
38+
}
39+
40+
// Greater than that of the ContextualMenu
41+
const PE_Z_INDEX = 3000;
42+
43+
/*
44+
* Class of component that renders its children in a separate ReactDOM virtual tree
45+
* in a container element appended to document.body.
46+
*
47+
* This prevents the children from being unmounted when the parent of PersistedElement
48+
* unmounts, allowing them to persist.
49+
*
50+
* When PE is unmounted, it hides the children using CSS. When mounted or updated, the
51+
* children are made visible and are positioned into a div that is given the same
52+
* bounding rect as the parent of PE.
53+
*/
54+
export default class PersistedElement extends React.Component {
55+
constructor() {
56+
super();
57+
this.collectChildContainer = this.collectChildContainer.bind(this);
58+
this.collectChild = this.collectChild.bind(this);
59+
}
60+
61+
collectChildContainer(ref) {
62+
this.childContainer = ref;
63+
}
64+
65+
collectChild(ref) {
66+
this.child = ref;
67+
this.updateChild();
68+
}
69+
70+
componentDidMount() {
71+
this.updateChild();
72+
}
73+
74+
componentDidUpdate() {
75+
this.updateChild();
76+
}
77+
78+
componentWillUnmount() {
79+
this.updateChildVisibility(this.child, false);
80+
}
81+
82+
updateChild() {
83+
this.updateChildPosition(this.child, this.childContainer);
84+
this.updateChildVisibility(this.child, true);
85+
}
86+
87+
updateChildVisibility(child, visible) {
88+
if (!child) return;
89+
child.style.display = visible ? 'block' : 'none';
90+
}
91+
92+
updateChildPosition(child, parent) {
93+
if (!child || !parent) return;
94+
95+
const parentRect = parent.getBoundingClientRect();
96+
Object.assign(child.style, {
97+
position: 'absolute',
98+
top: parentRect.top + 'px',
99+
left: parentRect.left + 'px',
100+
width: parentRect.width + 'px',
101+
height: parentRect.height + 'px',
102+
zIndex: PE_Z_INDEX,
103+
});
104+
}
105+
106+
render() {
107+
const content = <div ref={this.collectChild}>
108+
{this.props.children}
109+
</div>;
110+
111+
ReactDOM.render(content, getOrCreateContainer());
112+
113+
return <div ref={this.collectChildContainer}></div>;
114+
}
115+
}
116+

src/components/views/rooms/Stickerpicker.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ export default class Stickerpicker extends React.Component {
150150
const stickerpickerWidget = this.state.stickerpickerWidget;
151151
let stickersContent;
152152

153+
// Use a separate ReactDOM tree to render the AppTile separately so that it persists and does
154+
// not unmount when we (a) close the sticker picker (b) switch rooms. It's properties are still
155+
// updated.
156+
const PersistedElement = sdk.getComponent("elements.PersistedElement");
157+
153158
// Load stickerpack content
154159
if (stickerpickerWidget && stickerpickerWidget.content && stickerpickerWidget.content.url) {
155160
// Set default name
@@ -166,6 +171,7 @@ export default class Stickerpicker extends React.Component {
166171
width: this.popoverWidth,
167172
}}
168173
>
174+
<PersistedElement>
169175
<AppTile
170176
id={stickerpickerWidget.id}
171177
url={stickerpickerWidget.content.url}
@@ -188,6 +194,7 @@ export default class Stickerpicker extends React.Component {
188194
handleMinimisePointerEvents={true}
189195
whitelistCapabilities={['m.sticker']}
190196
/>
197+
</PersistedElement>
191198
</div>
192199
</div>
193200
);

0 commit comments

Comments
 (0)