Skip to content

Commit 44deb75

Browse files
committed
feat: add 'Surface: When connected' feedback #4124
1 parent f439614 commit 44deb75

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

‎companion/lib/Internal/Surface.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,12 @@ export class InternalSurface extends EventEmitter<InternalModuleFragmentEvents>
135135
this.#surfaceController.on('surface-add', () => {
136136
debounceUpdateVariableDefinitions()
137137
debounceUpdateVariables()
138+
this.emit('checkFeedbacks', 'surface_connected')
138139
})
139140
this.#surfaceController.on('surface-delete', () => {
140141
debounceUpdateVariableDefinitions()
141142
debounceUpdateVariables()
143+
this.emit('checkFeedbacks', 'surface_connected')
142144
})
143145
this.#surfaceController.on('surface-in-group', () => debounceUpdateVariables())
144146
this.#surfaceController.on('surface_name', () => debounceUpdateVariables())
@@ -727,6 +729,28 @@ export class InternalSurface extends EventEmitter<InternalModuleFragmentEvents>
727729
optionsSupportExpressions: true,
728730
},
729731

732+
surface_connected: {
733+
feedbackType: FeedbackEntitySubType.Boolean,
734+
label: 'Surface: When connected',
735+
description: 'Change style when a surface is connected',
736+
feedbackStyle: {
737+
color: 0xffffff,
738+
bgcolor: 0x00aa00,
739+
},
740+
showInvert: true,
741+
options: [
742+
{
743+
type: 'internal:surface_serial',
744+
label: 'Surface',
745+
id: 'surfaceId',
746+
includeSelf: false,
747+
default: '',
748+
listMode: 'surfaces',
749+
},
750+
],
751+
optionsSupportExpressions: true,
752+
},
753+
730754
outbound_surface_enabled: {
731755
feedbackType: FeedbackEntitySubType.Boolean,
732756
label: 'Remote surface: When enabled',
@@ -753,6 +777,12 @@ export class InternalSurface extends EventEmitter<InternalModuleFragmentEvents>
753777

754778
return currentPage == thePage
755779
}
780+
if (feedback.definitionId == 'surface_connected') {
781+
const surfaceId = stringifyVariableValue(feedback.options.surfaceId)?.trim()
782+
if (!surfaceId) return false
783+
784+
return this.#surfaceController.isSurfaceConnected(surfaceId, true)
785+
}
756786
if (feedback.definitionId == 'outbound_surface_enabled') {
757787
const surfaceId = stringifyVariableValue(feedback.options.surfaceId)?.trim()
758788
if (!surfaceId) return false

‎companion/lib/Surface/Controller.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,10 @@ export class SurfaceController extends EventEmitter<SurfaceControllerEvents> {
19051905
/**
19061906
* Get the page id of a surface
19071907
*/
1908+
isSurfaceConnected(surfaceId: string, looseIdMatching: boolean): boolean {
1909+
return !!this.#getSurfaceHandlerForId(surfaceId, looseIdMatching)
1910+
}
1911+
19081912
devicePageGet(surfaceOrGroupId: string, looseIdMatching = false): string | undefined {
19091913
const surfaceGroup = this.#getGroupForId(surfaceOrGroupId, looseIdMatching)
19101914
if (surfaceGroup) {

‎companion/test/Internal/Surface.test.ts‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,41 @@ describe('InternalSurface', () => {
483483
expect(surface.executeFeedback(makeFeedback('surface_on_page', { surfaceId: '', page: 2 }))).toBe(false)
484484
})
485485

486+
test('surface_connected reflects the surface connection state', () => {
487+
const { surface, surfaceController } = createSurface()
488+
surfaceController.isSurfaceConnected.mockReturnValue(true)
489+
490+
expect(surface.executeFeedback(makeFeedback('surface_connected', { surfaceId: 'surface0' }))).toBe(true)
491+
expect(surfaceController.isSurfaceConnected).toHaveBeenCalledWith('surface0', true)
492+
})
493+
494+
test('surface_connected is false when the surface is not connected', () => {
495+
const { surface, surfaceController } = createSurface()
496+
surfaceController.isSurfaceConnected.mockReturnValue(false)
497+
498+
expect(surface.executeFeedback(makeFeedback('surface_connected', { surfaceId: 'surface0' }))).toBe(false)
499+
})
500+
501+
test('surface_connected is false when no surface is selected', () => {
502+
const { surface, surfaceController } = createSurface()
503+
surfaceController.isSurfaceConnected.mockReturnValue(true)
504+
505+
expect(surface.executeFeedback(makeFeedback('surface_connected', { surfaceId: '' }))).toBe(false)
506+
})
507+
508+
test('surface_connected is rechecked when a surface connects or disconnects', () => {
509+
const { surface, surfaceController } = createSurface()
510+
const checkFeedbacks = vi.fn()
511+
surface.on('checkFeedbacks', checkFeedbacks)
512+
513+
const handlers = new Map(surfaceController.on.mock.calls.map(([event, fn]) => [event, fn as () => void]))
514+
handlers.get('surface-add')!()
515+
handlers.get('surface-delete')!()
516+
517+
expect(checkFeedbacks).toHaveBeenCalledTimes(2)
518+
expect(checkFeedbacks).toHaveBeenCalledWith('surface_connected')
519+
})
520+
486521
test('outbound_surface_enabled reflects the surface enabled state', () => {
487522
const { surface, surfaceController } = createSurface()
488523
surfaceController.outbound.getById.mockReturnValue({ enabled: true } as any)
@@ -607,7 +642,7 @@ describe('InternalSurface', () => {
607642
const { surface } = createSurface()
608643

609644
expect(Object.keys(surface.getFeedbackDefinitions()).sort()).toEqual(
610-
['surface_on_page', 'outbound_surface_enabled'].sort()
645+
['surface_on_page', 'surface_connected', 'outbound_surface_enabled'].sort()
611646
)
612647
})
613648
})

0 commit comments

Comments
 (0)