@applicaster/zapp-react-native-utils 13.0.7-alpha.2991546311 → 13.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,8 +65,4 @@ export const BUTTON_ACCESSIBILITY_KEYS = {
65
65
  label: "accessibility_menu_item_label",
66
66
  hint: "accessibility_menu_item_hint",
67
67
  },
68
- skip_intro: {
69
- label: "accessibility_skip_intro_label",
70
- hint: "accessibility_skip_intro_hint",
71
- },
72
68
  } as const;
@@ -8,10 +8,8 @@ import _ from "lodash";
8
8
  export class AccessibilityManager {
9
9
  private static _instance: AccessibilityManager | null = null;
10
10
  private headingTimeout: NodeJS.Timeout | null = null;
11
- private announcementDelayTimeout: NodeJS.Timeout | null = null;
12
11
  private WORDS_PER_MINUTE = 160;
13
12
  private MINIMUM_PAUSE = 500;
14
- private ANNOUNCEMENT_DELAY = 500;
15
13
  private state$ = new BehaviorSubject<AccessibilityState>({
16
14
  screenReaderEnabled: false,
17
15
  reduceMotionEnabled: false,
@@ -27,9 +25,6 @@ export class AccessibilityManager {
27
25
  private ttsManager = TTSManager.getInstance();
28
26
  private localizations: { [key: string]: string } = {};
29
27
  private headingQueue: string[] = [];
30
- private currentFocusId: string | null = null;
31
- private headingFocusMap: Map<string, string> = new Map();
32
- private pendingFocusId: string | null = null;
33
28
 
34
29
  private constructor() {}
35
30
 
@@ -70,11 +65,9 @@ export class AccessibilityManager {
70
65
  private calculateReadingTime(text: string): number {
71
66
  const words = text.trim().split(/\s+/).length;
72
67
 
73
- return (
74
- Math.max(
75
- this.MINIMUM_PAUSE,
76
- (words / this.WORDS_PER_MINUTE) * 60 * 1000
77
- ) + 300
68
+ return Math.max(
69
+ this.MINIMUM_PAUSE,
70
+ (words / this.WORDS_PER_MINUTE) * 60 * 1000
78
71
  );
79
72
  }
80
73
 
@@ -83,20 +76,12 @@ export class AccessibilityManager {
83
76
  * Each heading will be read once and removed from the queue
84
77
  */
85
78
  public addHeading(heading: string) {
86
- if (!this.pendingFocusId) {
87
- this.pendingFocusId = Date.now().toString();
88
- }
89
-
90
- this.headingFocusMap.set(heading, this.pendingFocusId);
91
79
  this.headingQueue.push(heading);
92
80
  }
93
81
 
94
82
  /**
95
83
  * text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
96
84
  * keyOfLocalizedText is the key to the localized text
97
- *
98
- * Implements a delay mechanism to reduce noise during rapid navigation.
99
- * Only the most recent announcement will be read after the delay period.
100
85
  */
101
86
  public readText({
102
87
  text,
@@ -127,27 +112,19 @@ export class AccessibilityManager {
127
112
  textToRead = localizedMessage;
128
113
  }
129
114
 
130
- const focusId = this.pendingFocusId || Date.now().toString();
131
- this.currentFocusId = focusId;
132
- this.pendingFocusId = null;
115
+ if (this.headingQueue.length > 0) {
116
+ const heading = this.headingQueue.shift()!;
117
+ this.ttsManager?.readText(heading);
133
118
 
134
- this.clearAnnouncement();
119
+ if (this.headingTimeout) {
120
+ clearTimeout(this.headingTimeout);
121
+ }
135
122
 
136
- this.announcementDelayTimeout = setTimeout(() => {
137
- this.executeAnnouncement(textToRead, keyOfLocalizedText, focusId);
138
- }, this.ANNOUNCEMENT_DELAY);
139
- }
123
+ const pauseTime = this.calculateReadingTime(heading);
140
124
 
141
- /**
142
- * Internal method to execute the actual announcement after the delay
143
- */
144
- private executeAnnouncement(
145
- textToRead: string,
146
- keyOfLocalizedText?: string,
147
- focusId?: string
148
- ) {
149
- if (this.headingQueue.length > 0) {
150
- this.processHeadingQueue(textToRead, focusId);
125
+ this.headingTimeout = setTimeout(() => {
126
+ this.ttsManager?.readText(textToRead);
127
+ }, pauseTime);
151
128
  } else {
152
129
  this.ttsManager?.readText(textToRead);
153
130
  }
@@ -159,49 +136,6 @@ export class AccessibilityManager {
159
136
  });
160
137
  }
161
138
 
162
- /**
163
- * Recursively processes all headings in the queue, reading them one by one
164
- */
165
- private processHeadingQueue(textToRead: string, focusId?: string) {
166
- // If focus has changed, abort this announcement
167
- if (focusId && this.currentFocusId !== focusId) {
168
- return;
169
- }
170
-
171
- if (this.headingQueue.length === 0) {
172
- if (focusId && this.currentFocusId === focusId) {
173
- this.ttsManager?.readText(textToRead);
174
- }
175
-
176
- return;
177
- }
178
-
179
- const heading = this.headingQueue.shift()!;
180
-
181
- const headingFocusId = this.headingFocusMap.get(heading);
182
-
183
- if (headingFocusId && headingFocusId !== focusId) {
184
- // This heading belongs to a previous focus, skip it
185
- this.headingFocusMap.delete(heading);
186
- this.processHeadingQueue(textToRead, focusId);
187
-
188
- return;
189
- }
190
-
191
- this.ttsManager?.readText(heading);
192
- this.headingFocusMap.delete(heading); // Clean up after reading
193
-
194
- if (this.headingTimeout) {
195
- clearTimeout(this.headingTimeout);
196
- }
197
-
198
- const pauseTime = this.calculateReadingTime(heading);
199
-
200
- this.headingTimeout = setTimeout(() => {
201
- this.processHeadingQueue(textToRead, focusId);
202
- }, pauseTime);
203
- }
204
-
205
139
  public getButtonAccessibilityProps(name: string): AccessibilityProps {
206
140
  const buttonName = _.toString(name);
207
141
 
@@ -209,15 +143,12 @@ export class AccessibilityManager {
209
143
 
210
144
  if (!buttonConfig) {
211
145
  return {
212
- accessible: true,
213
146
  accessibilityLabel: buttonName,
214
147
  accessibilityHint: `Press button to perform action on ${buttonName}`,
215
148
  "aria-label": buttonName,
216
149
  "aria-description": `Press button to perform action on ${buttonName}`,
217
150
  accessibilityRole: "button" as AccessibilityRole,
218
151
  "aria-role": "button",
219
- role: "button",
220
- tabindex: 0,
221
152
  };
222
153
  }
223
154
 
@@ -231,52 +162,23 @@ export class AccessibilityManager {
231
162
  `Press button to perform action on ${buttonName}`;
232
163
 
233
164
  return {
234
- accessible: true,
235
165
  accessibilityLabel: label,
236
166
  accessibilityHint: hint,
237
167
  "aria-label": label,
238
168
  "aria-description": hint,
239
169
  accessibilityRole: "button" as AccessibilityRole,
240
170
  "aria-role": "button",
241
- role: "button",
242
- tabindex: 0,
243
171
  };
244
172
  }
245
173
 
246
174
  public getInputAccessibilityProps(inputName: string): AccessibilityProps {
247
175
  return {
248
- accessible: true,
249
176
  accessibilityLabel: inputName,
250
177
  accessibilityHint: `Enter text into ${inputName}`,
251
178
  "aria-label": inputName,
252
179
  "aria-description": `Enter text into ${inputName}`,
253
- accessibilityRole: "text" as AccessibilityRole,
254
- "aria-role": "text",
255
- role: "text",
256
- tabindex: 0,
257
- };
258
- }
259
-
260
- /**
261
- * Extracts accessibility props from component props and returns them as HTML attributes
262
- * @param props - Component props containing accessibility properties
263
- * @returns Object with accessibility HTML attributes
264
- */
265
- public getWebAccessibilityProps(props: any): AccessibilityProps {
266
- const {
267
- "aria-label": ariaLabel,
268
- "aria-description": ariaDescription,
269
- "aria-role": ariaRole,
270
- role,
271
- tabindex,
272
- } = props;
273
-
274
- return {
275
- "aria-label": ariaLabel,
276
- "aria-description": ariaDescription,
277
- "aria-role": ariaRole,
278
- role: role || ariaRole,
279
- tabindex,
180
+ accessibilityRole: "textbox" as AccessibilityRole,
181
+ "aria-role": "textbox",
280
182
  };
281
183
  }
282
184
 
@@ -294,11 +196,4 @@ export class AccessibilityManager {
294
196
 
295
197
  return this.localizations[key];
296
198
  }
297
-
298
- private clearAnnouncement() {
299
- if (this.announcementDelayTimeout) {
300
- clearTimeout(this.announcementDelayTimeout);
301
- this.announcementDelayTimeout = null;
302
- }
303
- }
304
199
  }
@@ -214,8 +214,6 @@ export class TTSManager {
214
214
  readText(text: string) {
215
215
  if (isSamsungPlatform() && window.speechSynthesis) {
216
216
  const utterance = new SpeechSynthesisUtterance(text);
217
-
218
- window.speechSynthesis.cancel(); // Cancel previous speech before speaking new text
219
217
  window.speechSynthesis.speak(utterance);
220
218
  }
221
219
 
@@ -235,7 +233,6 @@ export class TTSManager {
235
233
  },
236
234
  parameters: {
237
235
  text,
238
- clear: true, // Clear any pending speech before speaking new text
239
236
  },
240
237
  });
241
238
  } catch (error) {
package/index.d.ts CHANGED
@@ -139,13 +139,10 @@ declare type AccessibilityState = {
139
139
  };
140
140
 
141
141
  declare type AccessibilityProps = {
142
- accessible?: boolean;
143
142
  accessibilityLabel?: string;
144
143
  accessibilityHint?: string;
145
144
  "aria-label"?: string;
146
145
  "aria-description"?: string;
147
146
  accessibilityRole?: string;
148
147
  "aria-role"?: string;
149
- role?: string;
150
- tabindex?: number;
151
148
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.7-alpha.2991546311",
3
+ "version": "13.0.7",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "13.0.7-alpha.2991546311",
30
+ "@applicaster/applicaster-types": "13.0.7",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -1,213 +0,0 @@
1
- import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
2
- import uuidv4 from "uuid/v4";
3
- import { Player } from "../../appUtils/playerManager/player";
4
-
5
- enum SEEK_DIRECTION {
6
- FORWARD = "forward",
7
- REWIND = "back",
8
- }
9
-
10
- export class PlayerTTS {
11
- private player: Player;
12
- private accessibilityManager: AccessibilityManager;
13
- private seekStartPosition: number | null = null;
14
- private isSeeking: boolean = false;
15
- private listenerId: string;
16
- private isInitialPlayerOpen: boolean = true;
17
-
18
- constructor(player: Player, accessibilityManager: AccessibilityManager) {
19
- this.player = player;
20
- this.accessibilityManager = accessibilityManager;
21
- this.listenerId = `player-tts-${uuidv4()}`;
22
- }
23
-
24
- private numberToWords(num: number): string {
25
- const ones = [
26
- "",
27
- "one",
28
- "two",
29
- "three",
30
- "four",
31
- "five",
32
- "six",
33
- "seven",
34
- "eight",
35
- "nine",
36
- "ten",
37
- "eleven",
38
- "twelve",
39
- "thirteen",
40
- "fourteen",
41
- "fifteen",
42
- "sixteen",
43
- "seventeen",
44
- "eighteen",
45
- "nineteen",
46
- ];
47
-
48
- const tens = [
49
- "",
50
- "",
51
- "twenty",
52
- "thirty",
53
- "forty",
54
- "fifty",
55
- "sixty",
56
- "seventy",
57
- "eighty",
58
- "ninety",
59
- ];
60
-
61
- if (num === 0) return "zero";
62
- if (num < 20) return ones[num];
63
-
64
- const ten = Math.floor(num / 10);
65
- const one = num % 10;
66
-
67
- if (one === 0) return tens[ten];
68
-
69
- return `${tens[ten]} ${ones[one]}`;
70
- }
71
-
72
- private secondsToTime(
73
- seconds: number,
74
- format: "natural" | "standard" = "natural"
75
- ): string {
76
- if (seconds < 0) return format === "natural" ? "zero" : "0";
77
-
78
- const minutes = Math.floor(seconds / 60);
79
- const remainingSeconds = Math.floor(seconds % 60);
80
-
81
- if (format === "standard") {
82
- if (minutes === 0) {
83
- if (remainingSeconds === 0) return "0";
84
-
85
- return `${remainingSeconds} second${remainingSeconds !== 1 ? "s" : ""}`;
86
- }
87
-
88
- if (remainingSeconds === 0) {
89
- return `${minutes} minute${minutes !== 1 ? "s" : ""}`;
90
- }
91
-
92
- return `${minutes} minute${minutes !== 1 ? "s" : ""} ${remainingSeconds} second${remainingSeconds !== 1 ? "s" : ""}`;
93
- } else {
94
- if (minutes === 0) {
95
- if (remainingSeconds === 0) return "zero";
96
-
97
- if (remainingSeconds < 10) {
98
- return `zero o ${this.numberToWords(remainingSeconds)}`;
99
- }
100
-
101
- return `zero ${this.numberToWords(remainingSeconds)}`;
102
- }
103
-
104
- if (remainingSeconds === 0) {
105
- return `${this.numberToWords(minutes)}`;
106
- }
107
-
108
- if (remainingSeconds < 10) {
109
- return `${this.numberToWords(minutes)} o ${this.numberToWords(remainingSeconds)}`;
110
- }
111
-
112
- return `${this.numberToWords(minutes)} ${this.numberToWords(remainingSeconds)}`;
113
- }
114
- }
115
-
116
- private announceLoading = () => {
117
- this.accessibilityManager.addHeading("Loading");
118
- };
119
-
120
- private announcePause = () => {
121
- if (!this.isSeeking) {
122
- this.accessibilityManager.addHeading(
123
- `Paused - ${this.secondsToTime(this.player.playerState.contentPosition, "standard")}`
124
- );
125
- }
126
- };
127
-
128
- private announceBufferComplete = (event: any) => {
129
- if (this.isInitialPlayerOpen) {
130
- const timeRemaining = event.duration - event.currentTime;
131
- const title = (this.player.entry.title as string) || "";
132
- const summary = (this.player.entry.summary as string) || "";
133
- this.accessibilityManager.addHeading(`Playing - ${title}`);
134
- this.accessibilityManager.addHeading(summary);
135
-
136
- this.accessibilityManager.addHeading(
137
- `Playing from ${this.secondsToTime(event.currentTime, "standard")}`
138
- );
139
-
140
- this.accessibilityManager.addHeading(
141
- `${this.secondsToTime(timeRemaining, "standard")} remaining.`
142
- );
143
-
144
- this.isInitialPlayerOpen = false;
145
- }
146
- };
147
-
148
- private announceResume = () => {
149
- if (!this.isSeeking && !this.isInitialPlayerOpen) {
150
- this.accessibilityManager.addHeading(
151
- `Playing - ${this.secondsToTime(this.player.playerState.contentPosition, "standard")}`
152
- );
153
- }
154
- };
155
-
156
- private handleVideoProgress = (event: any) => {
157
- if (event.currentTime > 0) {
158
- this.seekStartPosition = event.currentTime;
159
- }
160
- };
161
-
162
- private handleSeekComplete = (event: any) => {
163
- if (this.seekStartPosition !== null) {
164
- const seekDirection =
165
- event.currentTime > this.seekStartPosition
166
- ? SEEK_DIRECTION.FORWARD
167
- : SEEK_DIRECTION.REWIND;
168
-
169
- const seekAmount = Math.round(
170
- Math.abs(event.currentTime - this.seekStartPosition)
171
- );
172
-
173
- this.accessibilityManager.readText({
174
- text: `Skipped ${seekDirection} ${this.secondsToTime(seekAmount, "standard")}`,
175
- });
176
-
177
- this.seekStartPosition = event.currentTime;
178
- }
179
-
180
- this.isSeeking = false;
181
- };
182
-
183
- private handleSeekStart = () => {
184
- this.isSeeking = true;
185
- };
186
-
187
- public init(): () => void {
188
- if (!this.player) {
189
- return () => {};
190
- }
191
-
192
- return this.player.addListener({
193
- id: this.listenerId,
194
- listener: {
195
- onBufferStart: this.announceLoading,
196
- onBufferComplete: this.announceBufferComplete,
197
- onPlayerResume: this.announceResume,
198
- onPlayerPause: this.announcePause,
199
- onVideoProgress: this.handleVideoProgress,
200
- onPlayerSeekStart: this.handleSeekStart,
201
- onPlayerSeekComplete: this.handleSeekComplete,
202
- },
203
- });
204
- }
205
-
206
- public destroy(): void {
207
- if (this.player) {
208
- this.player.removeListener(this.listenerId);
209
- }
210
-
211
- this.seekStartPosition = null;
212
- }
213
- }
@@ -1 +0,0 @@
1
- export { PlayerTTS } from "./PlayerTTS";
@@ -1,21 +0,0 @@
1
- import * as React from "react";
2
- import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
3
- import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
4
- import { PlayerTTS } from "@applicaster/zapp-react-native-utils/playerUtils/PlayerTTS";
5
-
6
- export const usePlayerTTS = () => {
7
- const player = usePlayer();
8
- const accessibilityManager = useAccessibilityManager({});
9
-
10
- React.useEffect(() => {
11
- if (player && accessibilityManager) {
12
- const playerTTS = new PlayerTTS(player, accessibilityManager);
13
- const unsubscribe = playerTTS.init();
14
-
15
- return () => {
16
- unsubscribe();
17
- playerTTS.destroy();
18
- };
19
- }
20
- }, [player, accessibilityManager]);
21
- };