@@ -22,8 +22,12 @@ import {
22
22
ISupportedVersionsActionRequest ,
23
23
ISupportedVersionsActionResponseData ,
24
24
} from "./interfaces/SupportedVersionsAction" ;
25
- import { CurrentApiVersions } from "./interfaces/ApiVersion" ;
26
- import { ICapabilitiesActionRequest , ICapabilitiesActionResponseData } from "./interfaces/CapabilitiesAction" ;
25
+ import { ApiVersion , CurrentApiVersions , UnstableApiVersion } from "./interfaces/ApiVersion" ;
26
+ import {
27
+ ICapabilitiesActionRequest ,
28
+ ICapabilitiesActionResponseData ,
29
+ INotifyCapabilitiesActionRequest ,
30
+ } from "./interfaces/CapabilitiesAction" ;
27
31
import { ITransport } from "./transport/ITransport" ;
28
32
import { PostmessageTransport } from "./transport/PostmessageTransport" ;
29
33
import { WidgetApiFromWidgetAction , WidgetApiToWidgetAction } from "./interfaces/WidgetApiAction" ;
@@ -39,11 +43,11 @@ import {
39
43
import { IOpenIDCredentialsActionRequest } from "./interfaces/OpenIDCredentialsAction" ;
40
44
import { MatrixWidgetType , WidgetType } from "./interfaces/WidgetType" ;
41
45
import {
46
+ BuiltInModalButtonID ,
42
47
IModalWidgetCreateData ,
43
48
IModalWidgetOpenRequestData ,
44
49
IModalWidgetOpenRequestDataButton ,
45
50
IModalWidgetReturnData ,
46
- BuiltInModalButtonID ,
47
51
ModalButtonID ,
48
52
} from "./interfaces/ModalWidgetActions" ;
49
53
import { ISetModalButtonEnabledActionRequestData } from "./interfaces/SetModalButtonEnabledAction" ;
@@ -71,6 +75,8 @@ export class WidgetApi extends EventEmitter {
71
75
72
76
private capabilitiesFinished = false ;
73
77
private requestedCapabilities : Capability [ ] = [ ] ;
78
+ private approvedCapabilities : Capability [ ] ;
79
+ private cachedClientVersions : ApiVersion [ ] ;
74
80
75
81
/**
76
82
* Creates a new API handler for the given widget.
@@ -93,6 +99,20 @@ export class WidgetApi extends EventEmitter {
93
99
this . transport . on ( "message" , this . handleMessage . bind ( this ) ) ;
94
100
}
95
101
102
+ /**
103
+ * Determines if the widget was granted a particular capability. Note that on
104
+ * clients where the capabilities are not fed back to the widget this function
105
+ * will rely on requested capabilities instead.
106
+ * @param {Capability } capability The capability to check for approval of.
107
+ * @returns {boolean } True if the widget has approval for the given capability.
108
+ */
109
+ public hasCapability ( capability : Capability ) : boolean {
110
+ if ( Array . isArray ( this . approvedCapabilities ) ) {
111
+ return this . approvedCapabilities . includes ( capability ) ;
112
+ }
113
+ return this . requestedCapabilities . includes ( capability ) ;
114
+ }
115
+
96
116
/**
97
117
* Request a capability from the client. It is not guaranteed to be allowed,
98
118
* but will be asked for if the negotiation has not already happened.
@@ -277,6 +297,8 @@ export class WidgetApi extends EventEmitter {
277
297
return this . handleCapabilities ( < ICapabilitiesActionRequest > ev . detail ) ;
278
298
case WidgetApiToWidgetAction . UpdateVisibility :
279
299
return this . transport . reply ( ev . detail , < IWidgetApiRequestEmptyData > { } ) ; // ack to avoid error spam
300
+ case WidgetApiToWidgetAction . NotifyCapabilities :
301
+ return this . transport . reply ( ev . detail , < IWidgetApiRequestEmptyData > { } ) ; // ack to avoid error spam
280
302
default :
281
303
return this . transport . reply ( ev . detail , < IWidgetApiErrorResponseData > {
282
304
error : {
@@ -293,6 +315,22 @@ export class WidgetApi extends EventEmitter {
293
315
} ) ;
294
316
}
295
317
318
+ private getClientVersions ( ) : Promise < ApiVersion [ ] > {
319
+ if ( Array . isArray ( this . cachedClientVersions ) ) {
320
+ return Promise . resolve ( this . cachedClientVersions ) ;
321
+ }
322
+
323
+ return this . transport . send < IWidgetApiRequestEmptyData , ISupportedVersionsActionResponseData > (
324
+ WidgetApiFromWidgetAction . SupportedApiVersions , { } ,
325
+ ) . then ( r => {
326
+ this . cachedClientVersions = r . supported_versions ;
327
+ return r . supported_versions ;
328
+ } ) . catch ( e => {
329
+ console . warn ( "non-fatal error getting supported client versions: " , e ) ;
330
+ return [ ] ;
331
+ } ) ;
332
+ }
333
+
296
334
private handleCapabilities ( request : ICapabilitiesActionRequest ) {
297
335
if ( this . capabilitiesFinished ) {
298
336
return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
@@ -301,10 +339,27 @@ export class WidgetApi extends EventEmitter {
301
339
} ,
302
340
} ) ;
303
341
}
304
- this . capabilitiesFinished = true ;
305
- this . emit ( "ready" ) ;
306
- return this . transport . reply < ICapabilitiesActionResponseData > ( request , {
307
- capabilities : this . requestedCapabilities ,
342
+
343
+ // See if we can expect a capabilities notification or not
344
+ return this . getClientVersions ( ) . then ( v => {
345
+ if ( v . includes ( UnstableApiVersion . MSC2871 ) ) {
346
+ this . once (
347
+ `action:${ WidgetApiToWidgetAction . NotifyCapabilities } ` ,
348
+ ( ev : CustomEvent < INotifyCapabilitiesActionRequest > ) => {
349
+ this . approvedCapabilities = ev . detail . data . approved ;
350
+ this . emit ( "ready" ) ;
351
+ } ,
352
+ ) ;
353
+ } else {
354
+ // if we can't expect notification, we're as done as we can be
355
+ this . emit ( "ready" ) ;
356
+ }
357
+
358
+ // in either case, reply to that capabilities request
359
+ this . capabilitiesFinished = true ;
360
+ return this . transport . reply < ICapabilitiesActionResponseData > ( request , {
361
+ capabilities : this . requestedCapabilities ,
362
+ } ) ;
308
363
} ) ;
309
364
}
310
365
}
0 commit comments