Browse Source

feat: add toggleWhiteboard to Jitsi API (#13292)

* add toggleWhiteboard to Jitsi API

* eslint recommendations applied

* Prevent to send whiteboard status change notifications for mobile

* Fix code style errors (eslint)

* Requested changes (by mihhu) have been made.

---------

Co-authored-by: Fikret Huseynkhanov <fikret.huseynkhanov@simbrella.com>
factor2
FIKRAT HUSEYNKHANOV 2 years ago
parent
commit
aaeb1a90e5
No account linked to committer's email address

+ 19
- 0
modules/API/API.js View File

113
 import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
113
 import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
114
 import { muteAllParticipants } from '../../react/features/video-menu/actions';
114
 import { muteAllParticipants } from '../../react/features/video-menu/actions';
115
 import { setVideoQuality } from '../../react/features/video-quality/actions';
115
 import { setVideoQuality } from '../../react/features/video-quality/actions';
116
+import { toggleWhiteboard } from '../../react/features/whiteboard/actions.any';
117
+import { WhiteboardStatus } from '../../react/features/whiteboard/types';
116
 import { getJitsiMeetTransport } from '../transport';
118
 import { getJitsiMeetTransport } from '../transport';
117
 
119
 
118
 import {
120
 import {
833
             } else {
835
             } else {
834
                 logger.error(' End Conference not supported');
836
                 logger.error(' End Conference not supported');
835
             }
837
             }
838
+        },
839
+        'toggle-whiteboard': () => {
840
+            APP.store.dispatch(toggleWhiteboard());
836
         }
841
         }
837
     };
842
     };
838
     transport.on('event', ({ data, name }) => {
843
     transport.on('event', ({ data, name }) => {
2014
         });
2019
         });
2015
     }
2020
     }
2016
 
2021
 
2022
+    /**
2023
+     * Notify external application (if API is enabled) if whiteboard state is
2024
+     * changed.
2025
+     *
2026
+     * @param {WhiteboardStatus} status - The new whiteboard status.
2027
+     * @returns {void}
2028
+     */
2029
+    notifyWhiteboardStatusChanged(status: WhiteboardStatus) {
2030
+        this._sendEvent({
2031
+            name: 'whiteboard-status-changed',
2032
+            status
2033
+        });
2034
+    }
2035
+
2017
     /**
2036
     /**
2018
      * Disposes the allocated resources.
2037
      * Disposes the allocated resources.
2019
      *
2038
      *

+ 4
- 2
modules/API/external/external_api.js View File

90
     toggleSubtitles: 'toggle-subtitles',
90
     toggleSubtitles: 'toggle-subtitles',
91
     toggleTileView: 'toggle-tile-view',
91
     toggleTileView: 'toggle-tile-view',
92
     toggleVirtualBackgroundDialog: 'toggle-virtual-background',
92
     toggleVirtualBackgroundDialog: 'toggle-virtual-background',
93
-    toggleVideo: 'toggle-video'
93
+    toggleVideo: 'toggle-video',
94
+    toggleWhiteboard: 'toggle-whiteboard'
94
 };
95
 };
95
 
96
 
96
 /**
97
 /**
154
     'subject-change': 'subjectChange',
155
     'subject-change': 'subjectChange',
155
     'suspend-detected': 'suspendDetected',
156
     'suspend-detected': 'suspendDetected',
156
     'tile-view-changed': 'tileViewChanged',
157
     'tile-view-changed': 'tileViewChanged',
157
-    'toolbar-button-clicked': 'toolbarButtonClicked'
158
+    'toolbar-button-clicked': 'toolbarButtonClicked',
159
+    'whiteboard-status-changed': 'whiteboardStatusChanged'
158
 };
160
 };
159
 
161
 
160
 /**
162
 /**

+ 31
- 0
react/features/whiteboard/actions.any.ts View File

1
+import { IStore } from '../app/types';
2
+
3
+import { setWhiteboardOpen } from './actions';
4
+import { isWhiteboardAllowed, isWhiteboardOpen, isWhiteboardVisible } from './functions';
5
+import { WhiteboardStatus } from './types';
6
+
7
+
8
+/**
9
+ * API to toggle the whiteboard.
10
+ *
11
+ * @returns {Function}
12
+ */
13
+export function toggleWhiteboard() {
14
+    return async (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
15
+        const state = getState();
16
+        const isAllowed = isWhiteboardAllowed(state);
17
+        const isOpen = isWhiteboardOpen(state);
18
+
19
+        if (isAllowed) {
20
+            if (isOpen && !isWhiteboardVisible(state)) {
21
+                dispatch(setWhiteboardOpen(true));
22
+            } else if (isOpen && isWhiteboardVisible(state)) {
23
+                dispatch(setWhiteboardOpen(false));
24
+            } else if (!isOpen) {
25
+                dispatch(setWhiteboardOpen(true));
26
+            }
27
+        } else if (typeof APP !== 'undefined') {
28
+            APP.API.notifyWhiteboardStatusChanged(WhiteboardStatus.FORBIDDEN);
29
+        }
30
+    };
31
+}

+ 9
- 0
react/features/whiteboard/functions.ts View File

89
 export const isWhiteboardVisible = (state: IReduxState): boolean =>
89
 export const isWhiteboardVisible = (state: IReduxState): boolean =>
90
     getPinnedParticipant(state)?.id === WHITEBOARD_ID
90
     getPinnedParticipant(state)?.id === WHITEBOARD_ID
91
     || state['features/large-video'].participantId === WHITEBOARD_ID;
91
     || state['features/large-video'].participantId === WHITEBOARD_ID;
92
+
93
+/**
94
+* Indicates whether the whiteboard is accessible to a participant that has a moderator role.
95
+*
96
+* @param {IReduxState} state - The state from the Redux store.
97
+* @returns {boolean}
98
+*/
99
+export const isWhiteboardAllowed = (state: IReduxState): boolean =>
100
+    isWhiteboardEnabled(state) && isLocalParticipantModerator(state);

+ 20
- 1
react/features/whiteboard/middleware.ts View File

9
 import StateListenerRegistry from '../base/redux/StateListenerRegistry';
9
 import StateListenerRegistry from '../base/redux/StateListenerRegistry';
10
 import { getCurrentRoomId } from '../breakout-rooms/functions';
10
 import { getCurrentRoomId } from '../breakout-rooms/functions';
11
 import { addStageParticipant } from '../filmstrip/actions.web';
11
 import { addStageParticipant } from '../filmstrip/actions.web';
12
-import { isStageFilmstripAvailable } from '../filmstrip/functions';
12
+import { isStageFilmstripAvailable } from '../filmstrip/functions.web';
13
 
13
 
14
 import { RESET_WHITEBOARD, SET_WHITEBOARD_OPEN } from './actionTypes';
14
 import { RESET_WHITEBOARD, SET_WHITEBOARD_OPEN } from './actionTypes';
15
 import { resetWhiteboard, setWhiteboardOpen, setupWhiteboard } from './actions';
15
 import { resetWhiteboard, setWhiteboardOpen, setupWhiteboard } from './actions';
16
 import { WHITEBOARD_ID, WHITEBOARD_PARTICIPANT_NAME } from './constants';
16
 import { WHITEBOARD_ID, WHITEBOARD_PARTICIPANT_NAME } from './constants';
17
 import { getCollabDetails, getCollabServerUrl, isWhiteboardPresent } from './functions';
17
 import { getCollabDetails, getCollabServerUrl, isWhiteboardPresent } from './functions';
18
+import { WhiteboardStatus } from './types';
18
 
19
 
19
 const focusWhiteboard = (store: IStore) => {
20
 const focusWhiteboard = (store: IStore) => {
20
     const { dispatch, getState } = store;
21
     const { dispatch, getState } = store;
68
                 collabServerUrl,
69
                 collabServerUrl,
69
                 collabDetails
70
                 collabDetails
70
             });
71
             });
72
+            raiseWhiteboardNotification(WhiteboardStatus.INSTANTIATED);
71
 
73
 
72
             return;
74
             return;
73
         }
75
         }
74
 
76
 
75
         if (action.isOpen) {
77
         if (action.isOpen) {
76
             focusWhiteboard(store);
78
             focusWhiteboard(store);
79
+            raiseWhiteboardNotification(WhiteboardStatus.SHOWN);
77
 
80
 
78
             return;
81
             return;
79
         }
82
         }
80
 
83
 
81
         dispatch(participantLeft(WHITEBOARD_ID, conference, { fakeParticipant: FakeParticipant.Whiteboard }));
84
         dispatch(participantLeft(WHITEBOARD_ID, conference, { fakeParticipant: FakeParticipant.Whiteboard }));
85
+        raiseWhiteboardNotification(WhiteboardStatus.HIDDEN);
86
+
82
         break;
87
         break;
83
     }
88
     }
84
     case RESET_WHITEBOARD: {
89
     case RESET_WHITEBOARD: {
85
         dispatch(participantLeft(WHITEBOARD_ID, conference, { fakeParticipant: FakeParticipant.Whiteboard }));
90
         dispatch(participantLeft(WHITEBOARD_ID, conference, { fakeParticipant: FakeParticipant.Whiteboard }));
91
+        raiseWhiteboardNotification(WhiteboardStatus.RESET);
92
+
86
         break;
93
         break;
87
     }
94
     }
88
     }
95
     }
90
     return next(action);
97
     return next(action);
91
 });
98
 });
92
 
99
 
100
+/**
101
+ * Raises the whiteboard status notifications changes (if API is enabled).
102
+ *
103
+ * @param {WhiteboardStatus} status - The whiteboard changed status.
104
+ * @returns {Function}
105
+ */
106
+function raiseWhiteboardNotification(status: WhiteboardStatus) {
107
+    if (typeof APP !== 'undefined') {
108
+        APP.API.notifyWhiteboardStatusChanged(status);
109
+    }
110
+}
111
+
93
 /**
112
 /**
94
  * Set up state change listener to perform maintenance tasks when the conference
113
  * Set up state change listener to perform maintenance tasks when the conference
95
  * is left or failed, e.g. Disable the whiteboard if it's left open.
114
  * is left or failed, e.g. Disable the whiteboard if it's left open.

+ 13
- 0
react/features/whiteboard/types.ts View File

1
+
2
+/**
3
+ * Whiteboard statuses used to raise the notification when it's changed.
4
+ *
5
+ * @enum
6
+ */
7
+export enum WhiteboardStatus {
8
+    FORBIDDEN = 'FORBIDDEN',
9
+    HIDDEN = 'HIDDEN',
10
+    INSTANTIATED = 'INSTANTIATED',
11
+    RESET = 'RESET',
12
+    SHOWN = 'SHOWN'
13
+}

Loading…
Cancel
Save