@applicaster/quick-brick-player 16.0.0-rc.6 → 16.0.0-rc.60

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.
Files changed (26) hide show
  1. package/package.json +6 -6
  2. package/src/Player/AudioLayer/AudioDescription.tsx +46 -11
  3. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +38 -48
  4. package/src/Player/AudioLayer/Layout/Controls/__tests__/utils.test.ts +66 -0
  5. package/src/Player/AudioLayer/Layout/Controls/index.tsx +34 -14
  6. package/src/Player/AudioLayer/Layout/Controls/utils.ts +26 -0
  7. package/src/Player/AudioLayer/Layout/DockedControls/index.tsx +1 -13
  8. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +0 -4
  9. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +0 -4
  10. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +0 -4
  11. package/src/Player/AudioLayer/SleepTimerManager.ts +5 -1
  12. package/src/Player/AudioLayer/SpeedManager.ts +4 -0
  13. package/src/Player/AudioLayer/__tests__/AudioDescription.test.tsx +147 -0
  14. package/src/Player/AudioLayer/__tests__/AudioPlayerWrapper.test.tsx +130 -0
  15. package/src/Player/AudioLayer/__tests__/SleepTimerManager.test.ts +78 -0
  16. package/src/Player/AudioLayer/__tests__/SpeedManager.test.ts +57 -0
  17. package/src/Player/AudioLayer/__tests__/playerActions.test.ts +351 -0
  18. package/src/Player/AudioLayer/__tests__/usePlaylistNavigation.test.ts +387 -0
  19. package/src/Player/AudioLayer/components/SleepTimerModalHeader/SleepTimerModalHeader.tsx +9 -8
  20. package/src/Player/AudioLayer/components/SleepTimerModalHeader/__tests__/SleepTimerModalHeader.test.tsx +53 -0
  21. package/src/Player/AudioLayer/playerActions.ts +280 -0
  22. package/src/Player/AudioLayer/usePlaylistNavigation.ts +134 -0
  23. package/src/Player/AudioLayer/useSleepTimerModal.ts +20 -57
  24. package/src/Player/__tests__/getNativeProps.test.ts +115 -0
  25. package/src/Player/__tests__/toNativeMetadata.test.ts +77 -0
  26. package/src/Player/index.tsx +72 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/quick-brick-player",
3
- "version": "16.0.0-rc.6",
3
+ "version": "16.0.0-rc.60",
4
4
  "description": "Quick Brick Player",
5
5
  "main": "./src/index.ts",
6
6
  "types": "index.d.ts",
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "homepage": "https://github.com/applicaster/quickbrick#readme",
37
37
  "dependencies": {
38
- "@applicaster/quick-brick-mobile-transport-controls": "16.0.0-rc.2",
39
- "@applicaster/quick-brick-tv-transport-controls": "16.0.0-rc.2",
40
- "@applicaster/zapp-react-native-tvos-app": "16.0.0-rc.6",
41
- "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.6",
42
- "@applicaster/zapp-react-native-utils": "16.0.0-rc.6",
38
+ "@applicaster/quick-brick-mobile-transport-controls": "16.0.0-rc.60",
39
+ "@applicaster/quick-brick-tv-transport-controls": "16.0.0-rc.60",
40
+ "@applicaster/zapp-react-native-tvos-app": "16.0.0-rc.60",
41
+ "@applicaster/zapp-react-native-ui-components": "16.0.0-rc.60",
42
+ "@applicaster/zapp-react-native-utils": "16.0.0-rc.60",
43
43
  "query-string": "7.1.3",
44
44
  "shaka-player": "4.3.5",
45
45
  "typeface-montserrat": "^0.0.54",
@@ -1,10 +1,9 @@
1
1
  import React from "react";
2
2
  import { StyleSheet, Text, TextStyle, View } from "react-native";
3
3
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { usePlayerContent } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent";
4
5
 
5
6
  type AudioDescriptionProps = {
6
- title: string | number;
7
- summary: string | number;
8
7
  layoutState: {
9
8
  inline: boolean;
10
9
  docked: boolean;
@@ -96,17 +95,48 @@ const getSummaryStyles = (value: GetValue, prefix: string): TextStyle =>
96
95
  }),
97
96
  }) as const;
98
97
 
99
- const getHeight = (titleLineHeight: number, subtitleLineHeight) =>
100
- titleLineHeight * TITLE_LINES + subtitleLineHeight + TITLE_PADDING_BOTTOM;
98
+ const getHeight = ({
99
+ showTitle,
100
+ showSummary,
101
+ titleLineHeight,
102
+ subtitleLineHeight,
103
+ }: {
104
+ showTitle: boolean;
105
+ showSummary: boolean;
106
+ titleLineHeight: number;
107
+ subtitleLineHeight: number;
108
+ }) => {
109
+ const titleHeight = showTitle
110
+ ? titleLineHeight * TITLE_LINES + TITLE_PADDING_BOTTOM
111
+ : 0;
112
+
113
+ const summaryHeight = showSummary ? subtitleLineHeight : 0;
114
+
115
+ return titleHeight + summaryHeight;
116
+ };
101
117
 
102
118
  export const AudioDescription = (props: AudioDescriptionProps) => {
103
- const { title, summary, layoutState, value, maxWidth } = props;
119
+ const { layoutState, value, maxWidth } = props;
120
+ const { title, summary } = usePlayerContent();
104
121
  const prefix = layoutState.docked ? "docked_player_" : "";
105
122
 
106
123
  const summaryStyles = getSummaryStyles(value, prefix);
107
124
  const titleStyles = getTitleStyles(value, prefix);
108
125
 
109
- const height = getHeight(titleStyles.lineHeight, summaryStyles.lineHeight);
126
+ // Both depend on the Studio toggle alone, never on whether the text has
127
+ // arrived yet. Live audio resolves its title/summary asynchronously (the
128
+ // channel feed is polled after playback starts), so gating on the value
129
+ // would collapse the row on first render and expand it a second later -
130
+ // the control has to hold its place and render an empty line instead.
131
+ const showTitle = Boolean(value(`${prefix}title`));
132
+ const showSummary = Boolean(value(`${prefix}subtitle`));
133
+
134
+ const height = getHeight({
135
+ showTitle,
136
+ showSummary,
137
+ titleLineHeight: titleStyles.lineHeight,
138
+ subtitleLineHeight: summaryStyles.lineHeight,
139
+ });
110
140
 
111
141
  return (
112
142
  <View
@@ -118,15 +148,20 @@ export const AudioDescription = (props: AudioDescriptionProps) => {
118
148
  },
119
149
  ]}
120
150
  >
121
- <Text style={[styles.titleText, titleStyles]} numberOfLines={TITLE_LINES}>
122
- {title}
123
- </Text>
124
- {summary ? (
151
+ {showTitle ? (
152
+ <Text
153
+ style={[styles.titleText, titleStyles]}
154
+ numberOfLines={TITLE_LINES}
155
+ >
156
+ {title ?? ""}
157
+ </Text>
158
+ ) : null}
159
+ {showSummary ? (
125
160
  <Text
126
161
  style={[styles.summaryText, summaryStyles]}
127
162
  numberOfLines={SUBTITLE_LINES}
128
163
  >
129
- {summary}
164
+ {summary ?? ""}
130
165
  </Text>
131
166
  ) : null}
132
167
  </View>
@@ -12,24 +12,17 @@ import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
12
12
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
13
13
  import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Components/PlayerContainer/PlayerContainer";
14
14
  import { formatSpeed } from "./utils";
15
- import {
16
- getBottomSheetModalDefaultItemIcon,
17
- getBottomSheetModalIconProps,
18
- getBottomSheetModalItemProps,
19
- getBottomSheetModalLabelProps,
20
- getBottomSheetModalSelectedItemIcon,
21
- } from "../Utils";
22
- import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
23
-
24
15
  import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
25
-
26
16
  import { DEFAULT_SPEED, PlaybackSpeed, SpeedController } from "./SpeedManager";
27
17
  import {
28
18
  dismissModal as dismissBottomSheetModal,
29
- openBottomSheetModal,
30
19
  useModalStoreState,
31
20
  } from "@applicaster/zapp-react-native-utils/modalState";
32
- import { speedOptions } from "../Const/speedOptions";
21
+ import { uiActionsRegistry } from "@applicaster/zapp-react-native-utils/uiActionsRegistrator";
22
+ import {
23
+ PLAYER_ACTION_IDENTIFIERS,
24
+ registerPlayerActions,
25
+ } from "./playerActions";
33
26
 
34
27
  const { log_debug } = createLogger({
35
28
  subsystem: "UIComponent",
@@ -89,11 +82,27 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
89
82
  playNextData = null,
90
83
  } = props;
91
84
 
92
- const { title, summary } = useTitleSummaryOverlay(
93
- entry,
94
- playerState?.isLive,
95
- configuration?.audio_live_player_update_interval
96
- );
85
+ // For live audio the programme changes while the entry stays the same, so
86
+ // the player polls the channel's programme feed and republishes its content.
87
+ // Everything about that - whether the content is live at all, where the feed
88
+ // lives, how often to poll - is decided by `OverlaysObserver`, which loads
89
+ // the feed without React. This effect owns only the lifecycle: keep the
90
+ // metadata fresh while this player is mounted. Consumers read the result
91
+ // through `usePlayerContent` like any other player content.
92
+ React.useEffect(() => {
93
+ if (!player) {
94
+ return;
95
+ }
96
+
97
+ const observer = player.getOverlayObservable();
98
+
99
+ observer.startLiveContentUpdates();
100
+
101
+ return () => observer.stopLiveContentUpdates();
102
+ // Keyed on the entry's id rather than the object: starting the updates
103
+ // fires an immediate request, so a re-created `entry` prop with identical
104
+ // contents would re-fetch the feed on every render.
105
+ }, [player, playerState?.isLive, entry?.id]);
97
106
 
98
107
  const [speedManager, setSpeedManager] = useState<SpeedController | null>(
99
108
  null
@@ -103,6 +112,14 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
103
112
  setSpeedManager(player ? new SpeedController(player) : null);
104
113
  }, [player]);
105
114
 
115
+ useEffect(() => {
116
+ if (!speedManager || playerState.isLive) {
117
+ return;
118
+ }
119
+
120
+ return registerPlayerActions({ speedController: speedManager });
121
+ }, [speedManager, playerState.isLive]);
122
+
106
123
  useEffect(() => {
107
124
  if (!player) return;
108
125
 
@@ -201,36 +218,11 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
201
218
  player?.forward(seekDuration);
202
219
  }, [seekDuration, player]);
203
220
 
204
- const onSpeedSelect = React.useCallback(
205
- async (item) => {
206
- await speedManager?.setPlaybackSpeed(item);
207
- },
208
- [speedManager]
209
- );
210
-
211
221
  const onSpeedButtonPress = React.useCallback(() => {
212
- openBottomSheetModal({
213
- modalBottomSheetContentProps: {
214
- items: speedOptions,
215
- current_selection: playbackSpeed,
216
- onPress: onSpeedSelect,
217
- title: configuration.playback_speed_title,
218
- itemProps: getBottomSheetModalItemProps,
219
- iconProps: getBottomSheetModalIconProps,
220
- labelProps: getBottomSheetModalLabelProps,
221
- getSelectedItemIcon: getBottomSheetModalSelectedItemIcon,
222
- getDefaultItemIcon: getBottomSheetModalDefaultItemIcon,
223
- iconPlacement: "right",
224
- },
225
- options: {
226
- supportedOrientations: [
227
- "portrait",
228
- "landscape",
229
- "portrait-upside-down",
230
- ],
231
- },
232
- });
233
- }, [playbackSpeed, onSpeedSelect, configuration.playback_speed_title]);
222
+ uiActionsRegistry
223
+ .getAction(PLAYER_ACTION_IDENTIFIERS.PLAYBACK_SPEED)
224
+ ?.invokeAction(entry);
225
+ }, [entry]);
234
226
 
235
227
  const onPlayerSeek = React.useCallback(
236
228
  (targetTime: number) => {
@@ -282,8 +274,6 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
282
274
  configuration={configuration}
283
275
  dimensions={dimensions}
284
276
  playNextData={playNextData}
285
- title={title}
286
- summary={summary}
287
277
  />
288
278
  </View>
289
279
  </SafeAreaView>
@@ -0,0 +1,66 @@
1
+ import {
2
+ nextTrackButton,
3
+ previousTrackButton,
4
+ usesPlaylistControls,
5
+ } from "../utils";
6
+
7
+ describe("usesPlaylistControls", () => {
8
+ const configuredAs = (layout?: unknown): GetValue =>
9
+ ((key: string) =>
10
+ key === "player_controls_layout" ? layout : null) as unknown as GetValue;
11
+
12
+ it("opts in only for the playlist controls value", () => {
13
+ expect(usesPlaylistControls(configuredAs("playlist_controls"))).toBe(true);
14
+ });
15
+
16
+ it("keeps the original controls for the default value", () => {
17
+ expect(
18
+ usesPlaylistControls(configuredAs("playback_speed_and_sleep_timer"))
19
+ ).toBe(false);
20
+ });
21
+
22
+ it.each([
23
+ ["an undeclared key", undefined],
24
+ ["a null value", null],
25
+ ["an empty string", ""],
26
+ ["an unrecognised value", "something_else"],
27
+ ])("keeps the original controls for %s", (_label, layout) => {
28
+ expect(usesPlaylistControls(configuredAs(layout))).toBe(false);
29
+ });
30
+ });
31
+
32
+ describe("track control buttons", () => {
33
+ it("builds a previous button using the previous_track icon", () => {
34
+ const onPress = jest.fn();
35
+
36
+ expect(previousTrackButton({ isTablet: false, onPress })).toMatchObject({
37
+ size: "small",
38
+ icon: "previous_track",
39
+ onPress,
40
+ });
41
+ });
42
+
43
+ it("builds a next button using the next_track icon", () => {
44
+ const onPress = jest.fn();
45
+
46
+ expect(nextTrackButton({ isTablet: false, onPress })).toMatchObject({
47
+ size: "small",
48
+ icon: "next_track",
49
+ onPress,
50
+ });
51
+ });
52
+
53
+ it("propagates the disabled flag", () => {
54
+ expect(
55
+ nextTrackButton({ isTablet: false, onPress: jest.fn(), disabled: true })
56
+ ).toMatchObject({ disabled: true });
57
+
58
+ expect(
59
+ previousTrackButton({
60
+ isTablet: false,
61
+ onPress: jest.fn(),
62
+ disabled: true,
63
+ })
64
+ ).toMatchObject({ disabled: true });
65
+ });
66
+ });
@@ -2,10 +2,7 @@ import * as React from "react";
2
2
  import { StyleSheet, View } from "react-native";
3
3
 
4
4
  import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
5
- import {
6
- useIsTablet,
7
- useZStore,
8
- } from "@applicaster/zapp-react-native-utils/reactHooks";
5
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
9
6
  import {
10
7
  Controls as MobileTransportControls,
11
8
  ProgressBar,
@@ -18,14 +15,18 @@ import { VerticalGutter } from "./VerticalGutter";
18
15
 
19
16
  import {
20
17
  forwardButton,
18
+ nextTrackButton,
21
19
  playbackButton,
20
+ previousTrackButton,
22
21
  rewindButton,
23
22
  skipToLiveButton,
24
23
  skipToStartButton,
25
24
  sleepButton,
26
25
  speedButton,
26
+ usesPlaylistControls,
27
27
  } from "./utils";
28
28
  import { useSleepTimerModal } from "../../useSleepTimerModal";
29
+ import { usePlaylistNavigation } from "../../usePlaylistNavigation";
29
30
 
30
31
  const styles = StyleSheet.create({
31
32
  flex: {
@@ -61,8 +62,6 @@ type Props = {
61
62
  layoutState: QuickBrickPlayer.LayoutState;
62
63
  playerState: QuickBrickPlayer.PlayerState;
63
64
  value: GetValue;
64
- title: string | number;
65
- summary: string | number;
66
65
 
67
66
  playbackSpeedName: string;
68
67
 
@@ -85,8 +84,6 @@ export const Controls = (props: Props) => {
85
84
  layoutState,
86
85
  playerState,
87
86
  value,
88
- title,
89
- summary,
90
87
 
91
88
  playbackSpeedName,
92
89
 
@@ -100,13 +97,17 @@ export const Controls = (props: Props) => {
100
97
  maxWidth = 320,
101
98
  } = props;
102
99
 
103
- const configuration = useZStore("playerConfiguration");
104
-
105
100
  const { sleepTimerState, onSleepButtonPress } = useSleepTimerModal(
106
- playerManager.getActivePlayer(),
107
- configuration
101
+ playerManager.getActivePlayer()
108
102
  );
109
103
 
104
+ const { hasNext, hasPrevious, goNext, goPrevious } = usePlaylistNavigation(
105
+ entry,
106
+ playerState
107
+ );
108
+
109
+ const isPlaylistControlsLayout = usesPlaylistControls(value);
110
+
110
111
  const timerRemaining =
111
112
  sleepTimerState.timerDate !== 0 && +sleepTimerState.timerDate > Date.now()
112
113
  ? sleepTimerState.timerDate
@@ -203,6 +204,22 @@ export const Controls = (props: Props) => {
203
204
  return [skipToStart, rewind, play, forward, skipToLive];
204
205
  }
205
206
 
207
+ if (isPlaylistControlsLayout) {
208
+ const previous = previousTrackButton({
209
+ isTablet,
210
+ onPress: goPrevious,
211
+ disabled: !hasPrevious,
212
+ });
213
+
214
+ const next = nextTrackButton({
215
+ isTablet,
216
+ onPress: goNext,
217
+ disabled: !hasNext,
218
+ });
219
+
220
+ return [previous, rewind, play, forward, next];
221
+ }
222
+
206
223
  return [speed, rewind, play, forward, sleep];
207
224
  }, [
208
225
  playerState,
@@ -220,6 +237,11 @@ export const Controls = (props: Props) => {
220
237
  playbackSleepName,
221
238
  controller,
222
239
  isLiveShowFullControls,
240
+ isPlaylistControlsLayout,
241
+ hasNext,
242
+ hasPrevious,
243
+ goNext,
244
+ goPrevious,
223
245
  ]);
224
246
 
225
247
  return (
@@ -227,8 +249,6 @@ export const Controls = (props: Props) => {
227
249
  <VerticalGutter />
228
250
 
229
251
  <AudioDescription
230
- title={title}
231
- summary={summary}
232
252
  value={value}
233
253
  layoutState={layoutState}
234
254
  maxWidth={maxWidth}
@@ -103,3 +103,29 @@ export const speedButton = (props: ButtonsProps): ControlsButton =>
103
103
 
104
104
  export const sleepButton = (props: ButtonsProps): ControlsButton =>
105
105
  sleepButtonByName(props);
106
+
107
+ // Opt-in: a player plugin that does not declare this key returns undefined,
108
+ // which has to read as the default rather than as an opt-in.
109
+ export const usesPlaylistControls = (value: GetValue): boolean =>
110
+ value("player_controls_layout") === "playlist_controls";
111
+
112
+ type TrackButtonProps = ButtonsProps & { disabled?: boolean };
113
+
114
+ const trackButtonByName = ({
115
+ name,
116
+ onPress,
117
+ style,
118
+ disabled,
119
+ }: TrackButtonProps & { name: string }): ControlsButton => ({
120
+ size: "small",
121
+ icon: name,
122
+ onPress,
123
+ style,
124
+ disabled,
125
+ });
126
+
127
+ export const previousTrackButton = (props: TrackButtonProps): ControlsButton =>
128
+ trackButtonByName({ ...props, name: "previous_track" });
129
+
130
+ export const nextTrackButton = (props: TrackButtonProps): ControlsButton =>
131
+ trackButtonByName({ ...props, name: "next_track" });
@@ -18,7 +18,6 @@ import { PlayNextData } from "@applicaster/zapp-react-native-ui-components/Compo
18
18
  import { useSafeAreaInsets } from "react-native-safe-area-context";
19
19
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
20
20
  import { getTabBarHeight } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/getTabBarHeight";
21
- import { useTitleSummaryOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerTitleSummaryOverlay";
22
21
  import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
23
22
  import { useConfiguration } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/utils";
24
23
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
@@ -200,12 +199,6 @@ export const DockedControls = (props: Props) => {
200
199
  [...Object.values(configuration), playerState.seekableDuration]
201
200
  );
202
201
 
203
- const { title, summary } = useTitleSummaryOverlay(
204
- entry,
205
- playerState?.isLive,
206
- configuration?.audio_live_player_update_interval
207
- );
208
-
209
202
  const isTablet = useIsTablet();
210
203
 
211
204
  const liveButton = Boolean(value("live_badge_enabled"));
@@ -261,11 +254,6 @@ export const DockedControls = (props: Props) => {
261
254
  };
262
255
  }, [bottomTabsVisible, insets.bottom]);
263
256
 
264
- const contentInfoContent = React.useMemo(
265
- () => ({ ...entry, title, summary }),
266
- [title, summary, entry]
267
- );
268
-
269
257
  return (
270
258
  <>
271
259
  <View
@@ -297,7 +285,7 @@ export const DockedControls = (props: Props) => {
297
285
  testID="content-info"
298
286
  style={[styles.contentInfo, backgroundColorStyle]}
299
287
  >
300
- <ContentInfo content={contentInfoContent} value={value} docked />
288
+ <ContentInfo value={value} docked />
301
289
  </View>
302
290
  </View>
303
291
  <View
@@ -78,8 +78,6 @@ export function MobileLayout({
78
78
  onSpeedButtonPress,
79
79
  playbackSpeedName,
80
80
  configuration,
81
- title,
82
- summary,
83
81
  }: Props) {
84
82
  const { docked } = layoutState;
85
83
 
@@ -165,8 +163,6 @@ export function MobileLayout({
165
163
  layoutState={layoutState}
166
164
  playerState={playerState}
167
165
  value={value}
168
- title={title}
169
- summary={summary}
170
166
  playbackSpeedName={playbackSpeedName}
171
167
  onPlaybackButtonPress={onPlaybackButtonPress}
172
168
  onRewindButtonPress={onRewindButtonPress}
@@ -75,8 +75,6 @@ export function TabletLandscapeLayout({
75
75
  onSpeedButtonPress,
76
76
  playbackSpeedName,
77
77
  configuration,
78
- title,
79
- summary,
80
78
  }: Props) {
81
79
  const [imageWidth, setImageWidth] = React.useState(0);
82
80
  const { docked } = layoutState;
@@ -132,8 +130,6 @@ export function TabletLandscapeLayout({
132
130
  layoutState={layoutState}
133
131
  playerState={playerState}
134
132
  value={value}
135
- title={title}
136
- summary={summary}
137
133
  playbackSpeedName={playbackSpeedName}
138
134
  onPlaybackButtonPress={onPlaybackButtonPress}
139
135
  onRewindButtonPress={onRewindButtonPress}
@@ -81,8 +81,6 @@ export function TabletPortraitLayout({
81
81
  onSpeedButtonPress,
82
82
  playbackSpeedName,
83
83
  configuration,
84
- title,
85
- summary,
86
84
  }: Props) {
87
85
  const { docked } = layoutState;
88
86
 
@@ -157,8 +155,6 @@ export function TabletPortraitLayout({
157
155
  layoutState={layoutState}
158
156
  playerState={playerState}
159
157
  value={value}
160
- title={title}
161
- summary={summary}
162
158
  playbackSpeedName={playbackSpeedName}
163
159
  onPlaybackButtonPress={onPlaybackButtonPress}
164
160
  onRewindButtonPress={onRewindButtonPress}
@@ -41,6 +41,10 @@ export class SleepTimerController {
41
41
  return this.sleepTimerStateSubject.asObservable();
42
42
  }
43
43
 
44
+ get currentState(): SleepTimerState {
45
+ return this.sleepTimerStateSubject.getValue();
46
+ }
47
+
44
48
  private async scheduleSleep(sleepDate): Promise<boolean> {
45
49
  log_debug(`Setting sleep interval to ${sleepDate}`);
46
50
 
@@ -118,7 +122,7 @@ export class SleepTimerController {
118
122
 
119
123
  log_debug("Sleep state restored");
120
124
 
121
- this.setSleepTimer(item as SleepTimer, futureSleepDate);
125
+ await this.setSleepTimer(item as SleepTimer, futureSleepDate);
122
126
  }
123
127
 
124
128
  // Will be called if user closed the player explicitly.
@@ -33,6 +33,10 @@ export class SpeedController {
33
33
  return this.playbackSpeedSubject.asObservable();
34
34
  }
35
35
 
36
+ get currentSpeed(): PlaybackSpeed {
37
+ return this.playbackSpeedSubject.getValue();
38
+ }
39
+
36
40
  async loadSpeed() {
37
41
  const speed = await localStorage.getItem(SPEED_PREFERENCE_KEY, NAMESPACE);
38
42