Skip to content

Commit c75490a

Browse files
r-farkhutdinovRuslan Farkhutdinov
andauthored
SpeechToText: Check if SpeechRecognition is listening before start/stop (DevExpress#31186)
Co-authored-by: Ruslan Farkhutdinov <[email protected]>
1 parent ac6022a commit c75490a

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

packages/devextreme/js/__internal/core/speech_recognition_adapter.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const EVENT_NAMES = ['onresult', 'onerror', 'onend'];
1919
export class SpeechRecognitionAdapter {
2020
private _speechRecognition?: SpeechRecognition | null;
2121

22+
private _isListening = false;
23+
2224
constructor(config: SpeechRecognitionConfig, events: SpeechRecognitionEvents) {
2325
const window = getWindow();
2426
// @ts-expect-error SpeechRecognition API is not supported in TS
@@ -42,7 +44,15 @@ export class SpeechRecognitionAdapter {
4244
}
4345

4446
// eslint-disable-next-line spellcheck/spell-checker
45-
this._speechRecognition.onend = events.onEnd;
47+
this._speechRecognition.onstart = (): void => {
48+
this._isListening = true;
49+
};
50+
// eslint-disable-next-line spellcheck/spell-checker
51+
this._speechRecognition.onend = (event: Event): void => {
52+
this._isListening = false;
53+
54+
events.onEnd(event);
55+
};
4656
// eslint-disable-next-line spellcheck/spell-checker
4757
this._speechRecognition.onresult = events.onResult;
4858
this._speechRecognition.onerror = events.onError;
@@ -57,10 +67,18 @@ export class SpeechRecognitionAdapter {
5767
}
5868

5969
start(): void {
70+
if (this._isListening) {
71+
return;
72+
}
73+
6074
this._speechRecognition?.start();
6175
}
6276

6377
stop(): void {
78+
if (!this._isListening) {
79+
return;
80+
}
81+
6482
this._speechRecognition?.stop();
6583
}
6684

packages/devextreme/testing/tests/DevExpress.core/speechRecognitionAdapter.tests.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ QUnit.module('SpeechRecognitionAdapter', {
1313
constructor() {
1414
this.start = sinon.spy();
1515
this.stop = sinon.spy();
16+
this.onstart = null;
1617
this.onresult = null;
1718
this.onerror = null;
1819
this.onend = null;
@@ -118,6 +119,7 @@ QUnit.module('SpeechRecognitionAdapter', {
118119
const speechRecognition = adapter._speechRecognition;
119120

120121
adapter.start();
122+
speechRecognition.onstart();
121123
adapter.stop();
122124

123125
assert.ok(speechRecognition.start.calledOnce, 'start called');
@@ -131,5 +133,37 @@ QUnit.module('SpeechRecognitionAdapter', {
131133
adapter.dispose();
132134
assert.strictEqual(adapter._speechRecognition, null, 'cleared after dispose');
133135
});
136+
137+
QUnit.test('should not call start when listening', function(assert) {
138+
const adapter = this.createAdapter();
139+
const speechRecognition = adapter._speechRecognition;
140+
141+
speechRecognition.onstart();
142+
adapter.start();
143+
144+
assert.ok(speechRecognition.start.notCalled, 'start not called');
145+
});
146+
147+
QUnit.test('should not call stop if not listening', function(assert) {
148+
const adapter = this.createAdapter();
149+
const speechRecognition = adapter._speechRecognition;
150+
151+
adapter.stop();
152+
153+
assert.ok(speechRecognition.stop.notCalled, 'stop not called');
154+
});
155+
156+
QUnit.test('should update _isListening on onstart/onend', function(assert) {
157+
const adapter = this.createAdapter();
158+
const speechRecognition = adapter._speechRecognition;
159+
160+
assert.strictEqual(adapter._isListening, false, 'initially not listening');
161+
162+
speechRecognition.onstart();
163+
assert.strictEqual(adapter._isListening, true, 'set to listening after onstart');
164+
165+
speechRecognition.onend();
166+
assert.strictEqual(adapter._isListening, false, 'reset to false after onend');
167+
});
134168
});
135169

0 commit comments

Comments
 (0)