1
1
/**
2
2
Copyright 2017 Vector Creations Ltd
3
+ Copyright 2018 New Vector Ltd
3
4
4
5
Licensed under the Apache License, Version 2.0 (the "License");
5
6
you may not use this file except in compliance with the License.
@@ -33,24 +34,28 @@ import AppWarning from './AppWarning';
33
34
import MessageSpinner from './MessageSpinner' ;
34
35
import WidgetUtils from '../../../utils/WidgetUtils' ;
35
36
import dis from '../../../dispatcher' ;
37
+ import ActiveWidgetStore from '../../../stores/ActiveWidgetStore' ;
36
38
37
39
const ALLOWED_APP_URL_SCHEMES = [ 'https:' , 'http:' ] ;
38
40
const ENABLE_REACT_PERF = false ;
39
41
40
42
export default class AppTile extends React . Component {
41
43
constructor ( props ) {
42
44
super ( props ) ;
45
+
46
+ // The key used for PersistedElement
47
+ this . _persistKey = 'widget_' + this . props . id ;
48
+
43
49
this . state = this . _getNewState ( props ) ;
44
50
45
- this . _onWidgetAction = this . _onWidgetAction . bind ( this ) ;
51
+ this . _onAction = this . _onAction . bind ( this ) ;
46
52
this . _onMessage = this . _onMessage . bind ( this ) ;
47
53
this . _onLoaded = this . _onLoaded . bind ( this ) ;
48
54
this . _onEditClick = this . _onEditClick . bind ( this ) ;
49
55
this . _onDeleteClick = this . _onDeleteClick . bind ( this ) ;
50
56
this . _onSnapshotClick = this . _onSnapshotClick . bind ( this ) ;
51
57
this . onClickMenuBar = this . onClickMenuBar . bind ( this ) ;
52
58
this . _onMinimiseClick = this . _onMinimiseClick . bind ( this ) ;
53
- this . _onInitialLoad = this . _onInitialLoad . bind ( this ) ;
54
59
this . _grantWidgetPermission = this . _grantWidgetPermission . bind ( this ) ;
55
60
this . _revokeWidgetPermission = this . _revokeWidgetPermission . bind ( this ) ;
56
61
this . _onPopoutWidgetClick = this . _onPopoutWidgetClick . bind ( this ) ;
@@ -66,9 +71,12 @@ export default class AppTile extends React.Component {
66
71
_getNewState ( newProps ) {
67
72
const widgetPermissionId = [ newProps . room . roomId , encodeURIComponent ( newProps . url ) ] . join ( '_' ) ;
68
73
const hasPermissionToLoad = localStorage . getItem ( widgetPermissionId ) ;
74
+
75
+ const PersistedElement = sdk . getComponent ( "elements.PersistedElement" ) ;
69
76
return {
70
77
initialising : true , // True while we are mangling the widget URL
71
- loading : this . props . waitForIframeLoad , // True while the iframe content is loading
78
+ // True while the iframe content is loading
79
+ loading : this . props . waitForIframeLoad && ! PersistedElement . isMounted ( this . _persistKey ) ,
72
80
widgetUrl : this . _addWurlParams ( newProps . url ) ,
73
81
widgetPermissionId : widgetPermissionId ,
74
82
// Assume that widget has permission to load if we are the user who
@@ -77,9 +85,6 @@ export default class AppTile extends React.Component {
77
85
error : null ,
78
86
deleting : false ,
79
87
widgetPageTitle : newProps . widgetPageTitle ,
80
- allowedCapabilities : ( this . props . whitelistCapabilities && this . props . whitelistCapabilities . length > 0 ) ?
81
- this . props . whitelistCapabilities : [ ] ,
82
- requestedCapabilities : [ ] ,
83
88
} ;
84
89
}
85
90
@@ -89,7 +94,7 @@ export default class AppTile extends React.Component {
89
94
* @return {Boolean } True if capability supported
90
95
*/
91
96
_hasCapability ( capability ) {
92
- return this . state . allowedCapabilities . some ( ( c ) => { return c === capability ; } ) ;
97
+ return ActiveWidgetStore . widgetHasCapability ( this . props . id , capability ) ;
93
98
}
94
99
95
100
/**
@@ -142,30 +147,24 @@ export default class AppTile extends React.Component {
142
147
window . addEventListener ( 'message' , this . _onMessage , false ) ;
143
148
144
149
// Widget action listeners
145
- this . dispatcherRef = dis . register ( this . _onWidgetAction ) ;
146
- }
147
-
148
- componentDidUpdate ( ) {
149
- // Allow parents to access widget messaging
150
- if ( this . props . collectWidgetMessaging ) {
151
- this . props . collectWidgetMessaging ( this . widgetMessaging ) ;
152
- }
150
+ this . dispatcherRef = dis . register ( this . _onAction ) ;
153
151
}
154
152
155
153
componentWillUnmount ( ) {
156
154
// Widget action listeners
157
155
dis . unregister ( this . dispatcherRef ) ;
158
156
159
- // Widget postMessage listeners
160
- try {
161
- if ( this . widgetMessaging ) {
162
- this . widgetMessaging . stop ( ) ;
163
- }
164
- } catch ( e ) {
165
- console . error ( 'Failed to stop listening for widgetMessaging events' , e . message ) ;
166
- }
167
157
// Jitsi listener
168
158
window . removeEventListener ( 'message' , this . _onMessage ) ;
159
+
160
+ // if it's not remaining on screen, get rid of the PersistedElement container
161
+ if ( ! ActiveWidgetStore . getWidgetPersistence ( this . props . id ) ) {
162
+ // FIXME: ActiveWidgetStore should probably worry about this?
163
+ const PersistedElement = sdk . getComponent ( "elements.PersistedElement" ) ;
164
+ PersistedElement . destroyElement ( this . _persistKey ) ;
165
+ ActiveWidgetStore . delWidgetMessaging ( this . props . id ) ;
166
+ ActiveWidgetStore . delWidgetCapabilities ( this . props . id ) ;
167
+ }
169
168
}
170
169
171
170
/**
@@ -286,7 +285,7 @@ export default class AppTile extends React.Component {
286
285
287
286
_onSnapshotClick ( e ) {
288
287
console . warn ( "Requesting widget snapshot" ) ;
289
- this . widgetMessaging . getScreenshot ( )
288
+ ActiveWidgetStore . getWidgetMessaging ( this . props . id ) . getScreenshot ( )
290
289
. catch ( ( err ) => {
291
290
console . error ( "Failed to get screenshot" , err ) ;
292
291
} )
@@ -341,19 +340,19 @@ export default class AppTile extends React.Component {
341
340
* Called when widget iframe has finished loading
342
341
*/
343
342
_onLoaded ( ) {
344
- if ( ! this . widgetMessaging ) {
345
- this . _onInitialLoad ( ) ;
343
+ if ( ! ActiveWidgetStore . getWidgetMessaging ( this . props . id ) ) {
344
+ this . _setupWidgetMessaging ( ) ;
346
345
}
347
346
this . setState ( { loading : false } ) ;
348
347
}
349
348
350
- /**
351
- * Called on initial load of the widget iframe
352
- */
353
- _onInitialLoad ( ) {
354
- this . widgetMessaging = new WidgetMessaging ( this . props . id , this . props . url , this . refs . appFrame . contentWindow ) ;
355
- this . widgetMessaging . getCapabilities ( ) . then ( ( requestedCapabilities ) => {
356
- console . log ( `Widget ${ this . props . id } requested capabilities:` , requestedCapabilities ) ;
349
+ _setupWidgetMessaging ( ) {
350
+ // FIXME: There's probably no reason to do this here: it should probably be done entirely
351
+ // in ActiveWidgetStore.
352
+ const widgetMessaging = new WidgetMessaging ( this . props . id , this . props . url , this . refs . appFrame . contentWindow ) ;
353
+ ActiveWidgetStore . setWidgetMessaging ( this . props . id , widgetMessaging ) ;
354
+ widgetMessaging . getCapabilities ( ) . then ( ( requestedCapabilities ) => {
355
+ console . log ( `Widget ${ this . props . id } requested capabilities: ` + requestedCapabilities ) ;
357
356
requestedCapabilities = requestedCapabilities || [ ] ;
358
357
359
358
// Allow whitelisted capabilities
@@ -365,16 +364,15 @@ export default class AppTile extends React.Component {
365
364
} , this . props . whitelistCapabilities ) ;
366
365
367
366
if ( requestedWhitelistCapabilies . length > 0 ) {
368
- console . warn ( `Widget ${ this . props . id } allowing requested, whitelisted properties:` ,
369
- requestedWhitelistCapabilies ) ;
367
+ console . warn ( `Widget ${ this . props . id } allowing requested, whitelisted properties: ` +
368
+ requestedWhitelistCapabilies ,
369
+ ) ;
370
370
}
371
371
}
372
372
373
373
// TODO -- Add UI to warn about and optionally allow requested capabilities
374
- this . setState ( {
375
- requestedCapabilities,
376
- allowedCapabilities : this . state . allowedCapabilities . concat ( requestedWhitelistCapabilies ) ,
377
- } ) ;
374
+
375
+ ActiveWidgetStore . setWidgetCapabilities ( this . props . id , requestedWhitelistCapabilies ) ;
378
376
379
377
if ( this . props . onCapabilityRequest ) {
380
378
this . props . onCapabilityRequest ( requestedCapabilities ) ;
@@ -384,7 +382,7 @@ export default class AppTile extends React.Component {
384
382
} ) ;
385
383
}
386
384
387
- _onWidgetAction ( payload ) {
385
+ _onAction ( payload ) {
388
386
if ( payload . widgetId === this . props . id ) {
389
387
switch ( payload . action ) {
390
388
case 'm.sticker' :
@@ -562,6 +560,15 @@ export default class AppTile extends React.Component {
562
560
> </ iframe >
563
561
</ div >
564
562
) ;
563
+ // if the widget would be allowed to remian on screen, we must put it in
564
+ // a PersistedElement from the get-go, otherwise the iframe will be
565
+ // re-mounted later when we do.
566
+ if ( this . props . whitelistCapabilities . includes ( 'm.always_on_screen' ) ) {
567
+ const PersistedElement = sdk . getComponent ( "elements.PersistedElement" ) ;
568
+ appTileBody = < PersistedElement persistKey = { this . _persistKey } >
569
+ { appTileBody }
570
+ </ PersistedElement > ;
571
+ }
565
572
}
566
573
} else {
567
574
const isRoomEncrypted = MatrixClientPeg . get ( ) . isRoomEncrypted ( this . props . room . roomId ) ;
0 commit comments