From 74c7bb00efdd65954c399582b342d5849a6ef8f8 Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Fri, 9 May 2025 15:57:25 +0100 Subject: [PATCH 1/5] implement addIntentWithContextListener --- CHANGELOG.md | 1 + .../fdc3-agent-proxy/src/DesktopAgentProxy.ts | 5 + .../src/intents/DefaultIntentSupport.ts | 18 +++- .../src/intents/IntentSupport.ts | 5 + .../src/listeners/DefaultIntentListener.ts | 20 ++-- .../step-definitions/desktop-agent.steps.ts | 1 + .../fdc3-schema/generated/api/BrowserTypes.ts | 12 ++- .../api/addIntentListenerRequest.schema.json | 96 ++++++++++--------- .../fdc3-standard/src/api/DesktopAgent.ts | 17 ++++ .../src/handlers/IntentHandler.ts | 9 +- 10 files changed, 130 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17296a667..60bf88b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - resolves ([#1487](https://github.com/finos/FDC3/issues/1487)) - resolves ([#1488](https://github.com/finos/FDC3/issues/1488)) * Adjusted reference Desktop Agent implementation for FDC3 for Web to open a new app instance when raiseIntent is called with an appId but no instanceId ([#1556](https://github.com/finos/FDC3/pull/1556)) +* Added `addIntentWithContextListener` to `DesktopAgent` and implemented in `DesktopAgentProxy` ### Changed diff --git a/packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts b/packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts index fcf5fb49f..32c77e74f 100644 --- a/packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts +++ b/packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts @@ -66,6 +66,7 @@ export class DesktopAgentProxy implements DesktopAgent, Connectable { this.findIntentsByContext = this.findIntentsByContext.bind(this); this.raiseIntent = this.raiseIntent.bind(this); this.addIntentListener = this.addIntentListener.bind(this); + this.addIntentListenerWithContext = this.addIntentListenerWithContext.bind(this); this.raiseIntentForContext = this.raiseIntentForContext.bind(this); this.open = this.open.bind(this); this.findInstances = this.findInstances.bind(this); @@ -183,6 +184,10 @@ export class DesktopAgentProxy implements DesktopAgent, Connectable { return this.intents.addIntentListener(intent, handler); } + addIntentListenerWithContext(intent: string, contextType: string | string[], handler: IntentHandler) { + return this.intents.addIntentListenerWithContext(intent, contextType, handler); + } + raiseIntentForContext(context: Context, app?: string | AppIdentifier): Promise { return this.intents.raiseIntentForContext(context, this.ensureAppId(app)); } diff --git a/packages/fdc3-agent-proxy/src/intents/DefaultIntentSupport.ts b/packages/fdc3-agent-proxy/src/intents/DefaultIntentSupport.ts index 71111b8f4..0dd4a0d20 100644 --- a/packages/fdc3-agent-proxy/src/intents/DefaultIntentSupport.ts +++ b/packages/fdc3-agent-proxy/src/intents/DefaultIntentSupport.ts @@ -217,7 +217,23 @@ export class DefaultIntentSupport implements IntentSupport { } async addIntentListener(intent: string, handler: IntentHandler): Promise { - const out = new DefaultIntentListener(this.messaging, intent, handler, this.messageExchangeTimeout); + const out = new DefaultIntentListener(this.messaging, intent, undefined, handler, this.messageExchangeTimeout); + await out.register(); + return out; + } + + async addIntentListenerWithContext( + intent: string, + contextType: string | string[], + handler: IntentHandler + ): Promise { + const out = new DefaultIntentListener( + this.messaging, + intent, + Array.isArray(contextType) ? contextType : [contextType], + handler, + this.messageExchangeTimeout + ); await out.register(); return out; } diff --git a/packages/fdc3-agent-proxy/src/intents/IntentSupport.ts b/packages/fdc3-agent-proxy/src/intents/IntentSupport.ts index 9069ca0d0..7b346645c 100644 --- a/packages/fdc3-agent-proxy/src/intents/IntentSupport.ts +++ b/packages/fdc3-agent-proxy/src/intents/IntentSupport.ts @@ -7,4 +7,9 @@ export interface IntentSupport { raiseIntent(intent: string, context: Context, app?: AppIdentifier): Promise; raiseIntentForContext(context: Context, app?: AppIdentifier): Promise; addIntentListener(intent: string, handler: IntentHandler): Promise; + addIntentListenerWithContext( + intent: string, + contextType: string | string[], + handler: IntentHandler + ): Promise; } diff --git a/packages/fdc3-agent-proxy/src/listeners/DefaultIntentListener.ts b/packages/fdc3-agent-proxy/src/listeners/DefaultIntentListener.ts index 0e71bc2ed..e5b4cc9fb 100644 --- a/packages/fdc3-agent-proxy/src/listeners/DefaultIntentListener.ts +++ b/packages/fdc3-agent-proxy/src/listeners/DefaultIntentListener.ts @@ -7,28 +7,34 @@ import { IntentEvent, IntentResultRequest, IntentResultResponse, - //RaiseIntentResponse, } from '@finos/fdc3-schema/dist/generated/api/BrowserTypes'; export class DefaultIntentListener extends AbstractListener { - readonly intent: string; - - constructor(messaging: Messaging, intent: string, action: IntentHandler, messageExchangeTimeout: number) { + constructor( + messaging: Messaging, + private readonly intent: string, + private readonly contextTypes: string[] | undefined, + action: IntentHandler, + messageExchangeTimeout: number + ) { super( messaging, messageExchangeTimeout, - { intent }, + { intent, contextTypes }, action, 'addIntentListenerRequest', 'addIntentListenerResponse', 'intentListenerUnsubscribeRequest', 'intentListenerUnsubscribeResponse' ); - this.intent = intent; } filter(m: IntentEvent): boolean { - return m.type == 'intentEvent' && m.payload.intent == this.intent; + return ( + m.type == 'intentEvent' && + m.payload.intent == this.intent && + (this.contextTypes == null || this.contextTypes.includes(m.payload.context.type)) + ); } action(m: IntentEvent): void { diff --git a/packages/fdc3-get-agent/test/step-definitions/desktop-agent.steps.ts b/packages/fdc3-get-agent/test/step-definitions/desktop-agent.steps.ts index eeea07ffb..6b9ce0977 100644 --- a/packages/fdc3-get-agent/test/step-definitions/desktop-agent.steps.ts +++ b/packages/fdc3-get-agent/test/step-definitions/desktop-agent.steps.ts @@ -223,6 +223,7 @@ Given('A Dummy Desktop Agent in {string}', async function (this: CustomWorld, fi raiseIntent: notImplemented, raiseIntentForContext: notImplemented, addIntentListener: notImplemented, + addIntentListenerWithContext: notImplemented, addContextListener: notImplemented, addEventListener: notImplemented, getUserChannels: notImplemented, diff --git a/packages/fdc3-schema/generated/api/BrowserTypes.ts b/packages/fdc3-schema/generated/api/BrowserTypes.ts index 2e73252f7..934f0a100 100644 --- a/packages/fdc3-schema/generated/api/BrowserTypes.ts +++ b/packages/fdc3-schema/generated/api/BrowserTypes.ts @@ -950,6 +950,10 @@ export interface AddIntentListenerRequest { * The message payload typically contains the arguments to FDC3 API functions. */ export interface AddIntentListenerRequestPayload { + /** + * The types of context to listen for. + */ + contextTypes?: string[]; /** * The name of the intent to listen for. */ @@ -5171,7 +5175,13 @@ const typeMap: any = { ], false ), - AddIntentListenerRequestPayload: o([{ json: 'intent', js: 'intent', typ: '' }], false), + AddIntentListenerRequestPayload: o( + [ + { json: 'contextTypes', js: 'contextTypes', typ: u(undefined, a('')) }, + { json: 'intent', js: 'intent', typ: '' }, + ], + false + ), AddIntentListenerResponse: o( [ { json: 'meta', js: 'meta', typ: r('AddContextListenerResponseMeta') }, diff --git a/packages/fdc3-schema/schemas/api/addIntentListenerRequest.schema.json b/packages/fdc3-schema/schemas/api/addIntentListenerRequest.schema.json index b639d39aa..fcf486a11 100644 --- a/packages/fdc3-schema/schemas/api/addIntentListenerRequest.schema.json +++ b/packages/fdc3-schema/schemas/api/addIntentListenerRequest.schema.json @@ -1,46 +1,54 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json", - "type": "object", - "title": "AddIntentListener Request", - "description": "A request to add an Intent listener for a specified intent type.", - "allOf": [ - { - "$ref": "appRequest.schema.json" - }, - { - "type": "object", - "properties": { - "type": { - "$ref": "#/$defs/AddIntentListenerRequestType" - }, - "payload": { - "$ref": "#/$defs/AddIntentListenerRequestPayload" - }, - "meta": true - }, - "additionalProperties": false - } - ], - "$defs": { - "AddIntentListenerRequestType": { - "title": "AddIntentListener Request Message Type", - "const": "addIntentListenerRequest" - }, - "AddIntentListenerRequestPayload": { - "title": "AddIntentListener Request Payload", - "type": "object", - "properties": { - "intent": { - "title": "Intent name", - "description": "The name of the intent to listen for.", - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "intent" - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json", + "type": "object", + "title": "AddIntentListener Request", + "description": "A request to add an Intent listener for a specified intent type.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddIntentListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/AddIntentListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddIntentListenerRequestType": { + "title": "AddIntentListener Request Message Type", + "const": "addIntentListenerRequest" + }, + "AddIntentListenerRequestPayload": { + "title": "AddIntentListener Request Payload", + "type": "object", + "properties": { + "intent": { + "title": "Intent name", + "description": "The name of the intent to listen for.", + "type": "string" + }, + "contextTypes": { + "title": "Context types", + "description": "The types of context to listen for.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "intent" + ] + } + } } \ No newline at end of file diff --git a/packages/fdc3-standard/src/api/DesktopAgent.ts b/packages/fdc3-standard/src/api/DesktopAgent.ts index 49fa8935e..7c1c30bf8 100644 --- a/packages/fdc3-standard/src/api/DesktopAgent.ts +++ b/packages/fdc3-standard/src/api/DesktopAgent.ts @@ -353,6 +353,23 @@ export interface DesktopAgent { */ addIntentListener(intent: Intent, handler: IntentHandler): Promise; + /** + * Like `addIntentListener`, but allows filtering by one or more context types. + * The listener will only be triggered for the specified intent and context type(s). + * + * //Handle a raised intent + * const listener = fdc3.addIntentListenerWithContext('StartChat', 'fdc3.contact', context => { + * // start chat has been requested by another application + * return; + * }); + */ + + addIntentListenerWithContext( + intent: Intent, + contextType: string | string[], + handler: IntentHandler + ): Promise; + /** * Adds a listener for incoming context broadcasts from the Desktop Agent (via a User channel or `fdc3.open`API call. If the consumer is only interested in a context of a particular type, they can they can specify that type. If the consumer is able to receive context of any type or will inspect types received, then they can pass `null` as the `contextType` parameter to receive all context types. * diff --git a/toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts b/toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts index c48f8f97d..39ed840e8 100644 --- a/toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts +++ b/toolbox/fdc3-for-web/fdc3-web-impl/src/handlers/IntentHandler.ts @@ -28,6 +28,7 @@ type ListenerRegistration = { appId: string; instanceId: string; intentName: string; + contextTypes?: string[]; listenerUUID: string; }; @@ -114,6 +115,7 @@ class PendingIntent { if ( arg0.appId == this.appId.appId && arg0.intentName == this.r.intent && + (arg0.contextTypes == undefined || arg0.contextTypes.includes(this.r.context.type)) && (arg0.instanceId == this.appId.instanceId || this.appId.instanceId == undefined) ) { this.complete = true; @@ -248,6 +250,7 @@ export class IntentHandler implements MessageHandler { appId: from.appId, instanceId: from.instanceId, intentName: arg0.payload.intent, + contextTypes: arg0.payload.contextTypes, listenerUUID: sc.createUUID(), }; @@ -377,7 +380,11 @@ export class IntentHandler implements MessageHandler { async raiseIntentToAnyApp(arg0: IntentRequest[], sc: ServerContext): Promise { const connectedApps = await sc.getConnectedApps(); const matchingIntents = arg0.flatMap(i => this.directory.retrieveIntents(i.context.type, i.intent, undefined)); - const matchingRegistrations = arg0.flatMap(i => this.registrations.filter(r => r.intentName == i.intent)); + const matchingRegistrations = arg0.flatMap(i => + this.registrations.filter( + r => r.intentName == i.intent && (r.contextTypes == null || r.contextTypes.includes(i.context.type)) + ) + ); // Get a list of intent listeners that match the intent and context type const uniqueIntentNames = [ ...matchingIntents.map(i => i.intentName), ...matchingRegistrations.map(r => r.intentName), From 9c706cac70108e26cdb9f69b4df458fbaab46eda Mon Sep 17 00:00:00 2001 From: Roaders <10414642+Roaders@users.noreply.github.com> Date: Mon, 12 May 2025 13:50:01 +0100 Subject: [PATCH 2/5] Update CHANGELOG.md Co-authored-by: Kris West --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bf88b57..8c5a0809b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,7 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - resolves ([#1487](https://github.com/finos/FDC3/issues/1487)) - resolves ([#1488](https://github.com/finos/FDC3/issues/1488)) * Adjusted reference Desktop Agent implementation for FDC3 for Web to open a new app instance when raiseIntent is called with an appId but no instanceId ([#1556](https://github.com/finos/FDC3/pull/1556)) -* Added `addIntentWithContextListener` to `DesktopAgent` and implemented in `DesktopAgentProxy` +* Added `addIntentListenerWithContext` to `DesktopAgent` and implemented in `DesktopAgentProxy`. Added handling of optional `contextTypes` to reference implementation ### Changed From 4eebe7f569c44322a207bdc94dabb20bd15a69a5 Mon Sep 17 00:00:00 2001 From: Roaders <10414642+Roaders@users.noreply.github.com> Date: Thu, 22 May 2025 15:24:05 +0100 Subject: [PATCH 3/5] Update packages/fdc3-standard/src/api/DesktopAgent.ts Co-authored-by: Julianna Langston <74684272+julianna-ciq@users.noreply.github.com> --- packages/fdc3-standard/src/api/DesktopAgent.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/fdc3-standard/src/api/DesktopAgent.ts b/packages/fdc3-standard/src/api/DesktopAgent.ts index 7c1c30bf8..30877deeb 100644 --- a/packages/fdc3-standard/src/api/DesktopAgent.ts +++ b/packages/fdc3-standard/src/api/DesktopAgent.ts @@ -354,8 +354,7 @@ export interface DesktopAgent { addIntentListener(intent: Intent, handler: IntentHandler): Promise; /** - * Like `addIntentListener`, but allows filtering by one or more context types. - * The listener will only be triggered for the specified intent and context type(s). + * Adds a listener for incoming intents raised by other applications, via calls to `fdc3.raiseIntent` or `fdc3.raiseIntentForContext, but filters intents for one or more context types. See `addIntentListener` for details and restrictions for both usage and implementation. * * //Handle a raised intent * const listener = fdc3.addIntentListenerWithContext('StartChat', 'fdc3.contact', context => { From 2adc653ac17dd1500d46a3cf1d82a6186362d8cd Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:07:31 +0100 Subject: [PATCH 4/5] add tests --- .../test/features/intent-listener.feature | 32 +++++++++++++++++++ packages/testing/src/steps/generic.steps.ts | 7 ++++ 2 files changed, 39 insertions(+) diff --git a/packages/fdc3-agent-proxy/test/features/intent-listener.feature b/packages/fdc3-agent-proxy/test/features/intent-listener.feature index bcaa3bba1..c41bf1065 100644 --- a/packages/fdc3-agent-proxy/test/features/intent-listener.feature +++ b/packages/fdc3-agent-proxy/test/features/intent-listener.feature @@ -17,6 +17,38 @@ Feature: Intent Listeners | type | | intentResultRequest | + Scenario: Intent Listeners With non-matching Context does not handle message + Given "resultHandler" pipes intent to "intents" + When I call "{api1}" with "addIntentListenerWithContext" with parameters "BuyStock" and "fdc3.order" and "{resultHandler}" + And messaging receives "{intentMessageOne}" + Then "{intents}" is an array of objects with the following contents + | context.type | context.name | metadata.source.appId | + And messaging will have posts + | type | + + Scenario: Intent Listeners With matching Context string does handle message + Given "resultHandler" pipes intent to "intents" + When I call "{api1}" with "addIntentListenerWithContext" with parameters "BuyStock" and "fdc3.instrument" and "{resultHandler}" + And messaging receives "{intentMessageOne}" + Then "{intents}" is an array of objects with the following contents + | context.type | context.name | metadata.source.appId | + | fdc3.instrument | Apple | some-app-id | + And messaging will have posts + | type | + | intentResultRequest | + + Scenario: Intent Listeners With matching Context array does handle message + Given "resultHandler" pipes intent to "intents" + And "contextArray" is an array of contexts including "fdc3.instrument" and "fdc3.instrumentList" + When I call "{api1}" with "addIntentListenerWithContext" with parameters "BuyStock" and "{contextArray}" and "{resultHandler}" + And messaging receives "{intentMessageOne}" + Then "{intents}" is an array of objects with the following contents + | context.type | context.name | metadata.source.appId | + | fdc3.instrument | Apple | some-app-id | + And messaging will have posts + | type | + | intentResultRequest | + Scenario: Intent Listeners Can Return Results (Context) Given "resultHandler" returns a context item When I call "{api1}" with "addIntentListener" with parameters "BuyStock" and "{resultHandler}" diff --git a/packages/testing/src/steps/generic.steps.ts b/packages/testing/src/steps/generic.steps.ts index 957758c26..d344e3ae4 100644 --- a/packages/testing/src/steps/generic.steps.ts +++ b/packages/testing/src/steps/generic.steps.ts @@ -9,6 +9,13 @@ import fs from 'fs'; import path from 'path'; export function setupGenericSteps() { + Given( + '{string} is an array of contexts including {string} and {string}', + function (field: string, valueOne: string, valueTwo: string) { + this.props[field] = [valueOne, valueTwo]; + } + ); + Then('the promise {string} should resolve', async function (this: PropsWorld, field: string) { try { const promise = handleResolve(field, this); From e81cfe73e3b2b728843084ec2415f0c87d6e6039 Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:30:38 +0100 Subject: [PATCH 5/5] Support add intent with context in workbench --- .../fdc3-workbench/src/components/Intents.tsx | 90 +++++++++++++++++ .../src/fixtures/codeExamples.ts | 4 + .../fdc3-workbench/src/store/IntentStore.ts | 96 ++++++++++--------- 3 files changed, 145 insertions(+), 45 deletions(-) diff --git a/toolbox/fdc3-workbench/src/components/Intents.tsx b/toolbox/fdc3-workbench/src/components/Intents.tsx index 88130c26d..cd678cd22 100644 --- a/toolbox/fdc3-workbench/src/components/Intents.tsx +++ b/toolbox/fdc3-workbench/src/components/Intents.tsx @@ -184,6 +184,10 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) const [targetApp, setTargetApp] = useState('None'); const [contextTargetApp, setContextTargetApp] = useState('None'); const [raiseIntentContext, setRaiseIntentContext] = useState(null); + const [addIntentListenerWithContextContext, setAddIntentListenerWithContextContext] = useState( + null + ); + const [raiseIntentWithContextContext, setRaiseIntentWithContextContext] = useState(null); const [intentError, setIntentError] = useState(false); const [intentResolution, setIntentResolution] = useState(null); @@ -205,6 +209,8 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) const [targetOptions, setTargetOptions] = useState([]); const [targetOptionsforContext, setTargetOptionsforContext] = useState([]); + const [addListenerWithContext, setAddListenerWithContext] = useState(false); + const handleRaiseIntent = async () => { setIntentResolution(null); if (!intentValue) { @@ -455,6 +461,28 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) } else { intentStore.addIntentListener( intentListener.value, + undefined, + sendIntentResult && resultType === 'context-result' ? toJS(resultTypeContext) : null, + sendIntentResult && resultType === 'channel-result' ? currentAppChannelId : undefined, + sendIntentResult && resultType === 'channel-result' ? channelType === 'private-channel' : undefined, + sendIntentResult && resultType === 'channel-result' ? resultOverChannelContextList : undefined, + sendIntentResult && resultType === 'channel-result' ? resultOverChannelContextDelays : undefined + ); + setIntentListener(null); + } + setSendIntentResult(false); + }; + + const handleAddIntentListenerWithContext = () => { + if (!intentListener) { + setIntentError('Enter intent'); + return; + } else if (!addIntentListenerWithContextContext) { + setIntentError('Enter Context'); + } else { + intentStore.addIntentListener( + intentListener.value, + addIntentListenerWithContextContext, sendIntentResult && resultType === 'context-result' ? toJS(resultTypeContext) : null, sendIntentResult && resultType === 'channel-result' ? currentAppChannelId : undefined, sendIntentResult && resultType === 'channel-result' ? channelType === 'private-channel' : undefined, @@ -462,6 +490,7 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) sendIntentResult && resultType === 'channel-result' ? resultOverChannelContextDelays : undefined ); setIntentListener(null); + setAddIntentListenerWithContextContext(null); } setSendIntentResult(false); }; @@ -880,6 +909,66 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) + + + + setAddListenerWithContext(e.target.checked)} + /> + } + label="Add intent listener with context" + /> + + + + {addListenerWithContext && ( + + + + + + + + + { + let exampleToUse = codeExamples.intentListenerWithContext; + copyToClipboard(exampleToUse, 'addIntentListenerWithContext')(); + }} + > + + + + + + + + + + )} + {window.fdc3Version === '2.0' && ( @@ -897,6 +986,7 @@ export const Intents = observer(({ handleTabChange }: { handleTabChange: any }) )} + {sendIntentResult && ( setResultType(e.target.value)}> diff --git a/toolbox/fdc3-workbench/src/fixtures/codeExamples.ts b/toolbox/fdc3-workbench/src/fixtures/codeExamples.ts index de1ceb8fd..d8317dca5 100644 --- a/toolbox/fdc3-workbench/src/fixtures/codeExamples.ts +++ b/toolbox/fdc3-workbench/src/fixtures/codeExamples.ts @@ -106,6 +106,10 @@ const listener = fdc3.addIntentListener('StartChat', context => { return channel; });`, + intentListenerWithContext: `const listener = fdc3.addIntentListenerWithContext('StartChat', 'fdc3.contact', context => { + // start chat has been requested by another application +});`, + raiseIntentForContext: (context: string) => `let context = ${ context !== 'null' ? context : '{type: "fdc3.instrument", name: "Tesla, inc.", id: {ticker: "TSLA"}}' diff --git a/toolbox/fdc3-workbench/src/store/IntentStore.ts b/toolbox/fdc3-workbench/src/store/IntentStore.ts index 29dcfc465..ccea97f3a 100644 --- a/toolbox/fdc3-workbench/src/store/IntentStore.ts +++ b/toolbox/fdc3-workbench/src/store/IntentStore.ts @@ -16,7 +16,7 @@ import { intentTypes } from '../fixtures/intentTypes'; import systemLogStore from './SystemLogStore'; import appChannelStore from './AppChannelStore'; import privateChannelStore from './PrivateChannelStore'; -import { Channel, IntentResult } from '@finos/fdc3'; +import { Channel, IntentHandler, IntentResult, Listener } from '@finos/fdc3'; type IntentItem = { title: string; value: string }; @@ -38,6 +38,7 @@ class IntentStore { async addIntentListener( intent: string, + context?: ContextType, resultContext?: ContextType | null, channelName?: string | null, isPrivate?: boolean, @@ -49,60 +50,65 @@ class IntentStore { const agent = await getWorkbenchAgent(); - const intentListener = await agent.addIntentListener( - intent, - async (context: ContextType, metaData?: any): Promise => { - const currentListener = this.intentListeners.find(({ id }) => id === listenerId); - let channel: Channel | undefined; + const handler: IntentHandler = async (context: ContextType, metaData?: any): Promise => { + const currentListener = this.intentListeners.find(({ id }) => id === listenerId); + let channel: Channel | undefined; - //app channel - if (channelName && !isPrivate) { - channel = await appChannelStore.getOrCreateChannel(channelName); - } + //app channel + if (channelName && !isPrivate) { + channel = await appChannelStore.getOrCreateChannel(channelName); + } - //private channel - if (isPrivate && !channelName) { - channel = await privateChannelStore.createPrivateChannel(); - privateChannelStore.addChannelListener(channel, 'all'); + //private channel + if (isPrivate && !channelName) { + channel = await privateChannelStore.createPrivateChannel(); + privateChannelStore.addChannelListener(channel, 'all'); - privateChannelStore.onDisconnect(channel); - privateChannelStore.onUnsubscribe(channel); - privateChannelStore.onAddContextListener(channel, channelContexts, channelContextDelay); + privateChannelStore.onDisconnect(channel); + privateChannelStore.onUnsubscribe(channel); + privateChannelStore.onAddContextListener(channel, channelContexts, channelContextDelay); + } + + if (!isPrivate && channel) { + if (Object.keys(channelContexts).length !== 0) { + Object.keys(channelContexts).forEach(key => { + let broadcast = setTimeout(async () => { + appChannelStore.broadcast(channel, channelContexts[key]); + clearTimeout(broadcast); + }, channelContextDelay[key]); + }); + } else { + await channel.broadcast(context); } + } - if (!isPrivate && channel) { - if (Object.keys(channelContexts).length !== 0) { - Object.keys(channelContexts).forEach(key => { - let broadcast = setTimeout(async () => { - appChannelStore.broadcast(channel, channelContexts[key]); - clearTimeout(broadcast); - }, channelContextDelay[key]); - }); - } else { - await channel.broadcast(context); - } + runInAction(() => { + if (currentListener) { + currentListener.lastReceivedContext = context; + currentListener.metaData = metaData; } + }); - runInAction(() => { - if (currentListener) { - currentListener.lastReceivedContext = context; - currentListener.metaData = metaData; - } - }); + systemLogStore.addLog({ + name: 'receivedIntentListener', + type: 'info', + value: intent, + variant: 'code', + body: JSON.stringify(context, null, 4), + }); - systemLogStore.addLog({ - name: 'receivedIntentListener', - type: 'info', - value: intent, - variant: 'code', - body: JSON.stringify(context, null, 4), - }); + const result: IntentResult = channel || (resultContext ?? undefined); - const result: IntentResult = channel || (resultContext ?? undefined); + return result; + }; - return result; - } - ); + let intentListener: Listener; + + if (context == null) { + intentListener = await agent.addIntentListener(intent, handler); + } else { + intentListener = await agent.addIntentListenerWithContext(intent, context.type, handler); + } runInAction(() => { systemLogStore.addLog({