diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java index f4a5f5e573..14a3e6e9a1 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BroadcastEvent.java @@ -89,7 +89,8 @@ public class BroadcastEvent { CHAT_MESSAGE_RECEIVED("org.jitsi.meet.CHAT_MESSAGE_RECEIVED"), CHAT_TOGGLED("org.jitsi.meet.CHAT_TOGGLED"), VIDEO_MUTED_CHANGED("org.jitsi.meet.VIDEO_MUTED_CHANGED"), - READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE"); + READY_TO_CLOSE("org.jitsi.meet.READY_TO_CLOSE"), + TRANSCRIPTION_CHUNK_RECEIVED("org.jitsi.meet.TRANSCRIPTION_CHUNK_RECEIVED"); private static final String CONFERENCE_BLURRED_NAME = "CONFERENCE_BLURRED"; private static final String CONFERENCE_FOCUSED_NAME = "CONFERENCE_FOCUSED"; @@ -106,6 +107,7 @@ public class BroadcastEvent { private static final String CHAT_TOGGLED_NAME = "CHAT_TOGGLED"; private static final String VIDEO_MUTED_CHANGED_NAME = "VIDEO_MUTED_CHANGED"; private static final String READY_TO_CLOSE_NAME = "READY_TO_CLOSE"; + private static final String TRANSCRIPTION_CHUNK_RECEIVED_NAME = "TRANSCRIPTION_CHUNK_RECEIVED"; private final String action; @@ -158,6 +160,8 @@ public class BroadcastEvent { return VIDEO_MUTED_CHANGED; case READY_TO_CLOSE_NAME: return READY_TO_CLOSE; + case TRANSCRIPTION_CHUNK_RECEIVED_NAME: + return TRANSCRIPTION_CHUNK_RECEIVED; } return null; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java index 05da4bc070..047954d36f 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java @@ -255,6 +255,10 @@ public class JitsiMeetActivity extends AppCompatActivity finish(); } +// protected void onTranscriptionChunkReceived(HashMap extraData) { +// JitsiMeetLogger.i("Transcription chunk received: " + extraData); +// } + // Activity lifecycle methods // @@ -338,6 +342,9 @@ public class JitsiMeetActivity extends AppCompatActivity case READY_TO_CLOSE: onReadyToClose(); break; +// case TRANSCRIPTION_CHUNK_RECEIVED: +// onTranscriptionChunkReceived(event.getData()); +// break; } } } diff --git a/ios/app/src/ViewController.m b/ios/app/src/ViewController.m index a9dadf0f42..9845817411 100644 --- a/ios/app/src/ViewController.m +++ b/ios/app/src/ViewController.m @@ -98,6 +98,10 @@ [self _onJitsiMeetViewDelegateEvent:@"READY_TO_CLOSE" withData:data]; } +// - (void)transcriptionChunkReceived:(NSDictionary *)data { +// [self _onJitsiMeetViewDelegateEvent:@"TRANSCRIPTION_CHUNK_RECEIVED" withData:data]; +// } + - (void)participantJoined:(NSDictionary *)data { NSLog(@"%@%@", @"Participant joined: ", data[@"participantId"]); } @@ -130,6 +134,7 @@ NSLog(@"%@%@", @"Video muted changed: ", data[@"muted"]); } + #pragma mark - Helpers - (void)terminate { diff --git a/ios/sdk/src/JitsiMeetView.h b/ios/sdk/src/JitsiMeetView.h index 3ab9cb4dfb..323f67ccf5 100644 --- a/ios/sdk/src/JitsiMeetView.h +++ b/ios/sdk/src/JitsiMeetView.h @@ -26,7 +26,7 @@ @property (nonatomic, nullable, weak) id delegate; /** - * Joins the conference specified by the given options. The gievn options will + * Joins the conference specified by the given options. The given options will * be merged with the defaultConferenceOptions (if set) in JitsiMeet. If there * is an already active conference it will be automatically left prior to * joining the new one. diff --git a/ios/sdk/src/JitsiMeetViewDelegate.h b/ios/sdk/src/JitsiMeetViewDelegate.h index 1d9a72ba8b..5818730a68 100644 --- a/ios/sdk/src/JitsiMeetViewDelegate.h +++ b/ios/sdk/src/JitsiMeetViewDelegate.h @@ -116,4 +116,11 @@ */ - (void)readyToClose:(NSDictionary *)data; +/** + * Called when the transcription chunk was received. + * + * The `data` dictionary contains a `messageID`, `language`, `participant` key. + */ +- (void)transcriptionChunkReceived:(NSDictionary *)data; + @end diff --git a/react/features/mobile/external-api/middleware.ts b/react/features/mobile/external-api/middleware.ts index 7a99d7316d..55a7a24d3d 100644 --- a/react/features/mobile/external-api/middleware.ts +++ b/react/features/mobile/external-api/middleware.ts @@ -86,7 +86,7 @@ const CONFERENCE_TERMINATED = 'CONFERENCE_TERMINATED'; const ENDPOINT_TEXT_MESSAGE_RECEIVED = 'ENDPOINT_TEXT_MESSAGE_RECEIVED'; /** - * Event which will be emitted on the native side to indicate a participant togggles + * Event which will be emitted on the native side to indicate a participant toggles * the screen share. */ const SCREEN_SHARE_TOGGLED = 'SCREEN_SHARE_TOGGLED'; diff --git a/react/features/subtitles/components/AbstractClosedCaptionButton.tsx b/react/features/subtitles/components/AbstractClosedCaptionButton.tsx index cd5d305f29..e9e75d44e7 100644 --- a/react/features/subtitles/components/AbstractClosedCaptionButton.tsx +++ b/react/features/subtitles/components/AbstractClosedCaptionButton.tsx @@ -4,7 +4,7 @@ import { IReduxState } from '../../app/types'; import { MEET_FEATURES } from '../../base/jwt/constants'; import AbstractButton, { IProps as AbstractButtonProps } from '../../base/toolbox/components/AbstractButton'; import { maybeShowPremiumFeatureDialog } from '../../jaas/actions'; -import { canStartSubtitles } from '../functions'; +import { canStartSubtitles } from '../functions.any'; export interface IAbstractProps extends AbstractButtonProps { diff --git a/react/features/subtitles/functions.ts b/react/features/subtitles/functions.any.ts similarity index 100% rename from react/features/subtitles/functions.ts rename to react/features/subtitles/functions.any.ts diff --git a/react/features/subtitles/functions.native.ts b/react/features/subtitles/functions.native.ts new file mode 100644 index 0000000000..1e944f75e6 --- /dev/null +++ b/react/features/subtitles/functions.native.ts @@ -0,0 +1,30 @@ +/* eslint-disable max-params, max-len */ + +import { sendEvent } from '../mobile/external-api/functions'; + + +/** + * Event which will be emitted on the native side to indicate that the transcription chunk was received. + */ +const TRANSCRIPTION_CHUNK_RECEIVED = 'TRANSCRIPTION_CHUNK_RECEIVED'; + +/** + * Logs when about the received transcription chunk. + * + * @param {string} transcriptMessageID - Transcription message id. + * @param {string} language - The language of the transcribed message. + * @param {Object} participant - The participant who send the message. + * @param {any} text - The message text. + * @param {any} _store - The store. + * @returns {Event} + */ +export const notifyTranscriptionChunkReceived = (transcriptMessageID: string, language: string, participant: Object, text: any, _store?: any) => + sendEvent( + _store, + TRANSCRIPTION_CHUNK_RECEIVED, + { + messageID: transcriptMessageID, + language, + participant, + text + }); diff --git a/react/features/subtitles/functions.web.ts b/react/features/subtitles/functions.web.ts new file mode 100644 index 0000000000..405df800de --- /dev/null +++ b/react/features/subtitles/functions.web.ts @@ -0,0 +1,19 @@ +/* eslint-disable max-params, max-len */ + +/** + * Logs when about the received transcription chunk. + * + * @param {string} transcriptMessageID - Transcription message id. + * @param {string} language - The language of the transcribed message. + * @param {Object} participant - The participant who send the message. + * @param {any} text - The message text. + * @param {any} _store - The store. + * @returns {Event} + */ +export const notifyTranscriptionChunkReceived = (transcriptMessageID: string, language: string, participant: Object, text: any, _store?: any) => + APP.API.notifyTranscriptionChunkReceived({ + messageID: transcriptMessageID, + language, + participant, + ...text + }); diff --git a/react/features/subtitles/middleware.ts b/react/features/subtitles/middleware.ts index 2051277633..4971a47bc6 100644 --- a/react/features/subtitles/middleware.ts +++ b/react/features/subtitles/middleware.ts @@ -12,6 +12,8 @@ import { removeTranscriptMessage, updateTranscriptMessage } from './actions.any'; +import { notifyTranscriptionChunkReceived } from './functions'; + /** * The type of json-message which indicates that json carries a @@ -43,7 +45,7 @@ const P_NAME_TRANSLATION_LANGUAGE = 'translation_language'; const REMOVE_AFTER_MS = 3000; /** - * Stability factor for a trancription. We'll treat a transcript as stable + * Stability factor for a transcription. We'll treat a transcript as stable * beyond this value. */ const STABLE_TRANSCRIPTION_FACTOR = 0.85; @@ -89,13 +91,14 @@ MiddlewareRegistry.register(store => next => action => { * @private * @returns {Object} The value returned by {@code next(action)}. */ -function _endpointMessageReceived({ dispatch, getState }: IStore, next: Function, action: AnyAction) { +function _endpointMessageReceived(store: IStore, next: Function, action: AnyAction) { const { data: json } = action; if (![ JSON_TYPE_TRANSCRIPTION_RESULT, JSON_TYPE_TRANSLATION_RESULT ].includes(json?.type)) { return next(action); } + const { dispatch, getState } = store; const state = getState(); const language = state['features/base/conference'].conference @@ -129,7 +132,7 @@ function _endpointMessageReceived({ dispatch, getState }: IStore, next: Function const { text } = json.transcript[0]; // First, notify the external API. - if (typeof APP !== 'undefined' && !(json.is_interim && skipInterimTranscriptions)) { + if (!(json.is_interim && skipInterimTranscriptions)) { const txt: any = {}; if (!json.is_interim) { @@ -140,26 +143,31 @@ function _endpointMessageReceived({ dispatch, getState }: IStore, next: Function txt.unstable = text; } - APP.API.notifyTranscriptionChunkReceived({ - messageID: transcriptMessageID, - language: json.language, + notifyTranscriptionChunkReceived( + transcriptMessageID, + json.language, participant, - ...txt - }); + txt, + store + ); - // Dump transcript in a element for debugging purposes. - if (!json.is_interim && dumpTranscript) { - try { - let elem = document.body.getElementsByTagName('transcript')[0]; + if (navigator.product !== 'ReactNative') { - if (!elem) { - elem = document.createElement('transcript'); - document.body.appendChild(elem); + // Dump transcript in a element for debugging purposes. + if (!json.is_interim && dumpTranscript) { + try { + let elem = document.body.getElementsByTagName('transcript')[0]; + + // eslint-disable-next-line max-depth + if (!elem) { + elem = document.createElement('transcript'); + document.body.appendChild(elem); + } + + elem.append(`${new Date(json.timestamp).toISOString()} ${participant.name}: ${text}`); + } catch (_) { + // Ignored. } - - elem.append(`${new Date(json.timestamp).toISOString()} ${participant.name}: ${text}`); - } catch (_) { - // Ignored. } } } diff --git a/react/features/subtitles/reducer.ts b/react/features/subtitles/reducer.ts index e3ac58563d..44e07aeed6 100644 --- a/react/features/subtitles/reducer.ts +++ b/react/features/subtitles/reducer.ts @@ -2,7 +2,9 @@ import ReducerRegistry from '../base/redux/ReducerRegistry'; import { REMOVE_TRANSCRIPT_MESSAGE, - SET_REQUESTING_SUBTITLES, TOGGLE_REQUESTING_SUBTITLES, UPDATE_TRANSCRIPT_MESSAGE + SET_REQUESTING_SUBTITLES, + TOGGLE_REQUESTING_SUBTITLES, + UPDATE_TRANSCRIPT_MESSAGE } from './actionTypes'; /**