@applicaster/quick-brick-player 13.0.0-rc.93 → 13.0.0-rc.95

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 (30) hide show
  1. package/package.json +5 -6
  2. package/src/Player/AudioLayer/AudioDescription.tsx +4 -6
  3. package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +36 -66
  4. package/src/Player/AudioLayer/Layout/Controls/VerticalGutter.tsx +27 -0
  5. package/src/Player/AudioLayer/Layout/Controls/index.tsx +305 -0
  6. package/src/Player/AudioLayer/Layout/{defaultLayoutsConts.ts → Controls/utils.ts} +2 -12
  7. package/src/Player/AudioLayer/Layout/{DockedLayout.tsx → DockedControls/index.tsx} +28 -39
  8. package/src/Player/AudioLayer/Layout/MobileLayout.tsx +212 -0
  9. package/src/Player/AudioLayer/Layout/PlayerImage/AnimatedImage.tsx +83 -0
  10. package/src/Player/AudioLayer/Layout/PlayerImage/FlexImage.tsx +32 -0
  11. package/src/Player/AudioLayer/Layout/PlayerImage/index.tsx +95 -0
  12. package/src/Player/AudioLayer/Layout/PlayerImage/styles.ts +17 -0
  13. package/src/Player/AudioLayer/Layout/TabletLandscapeLayout.tsx +214 -0
  14. package/src/Player/AudioLayer/Layout/TabletPortraitLayout.tsx +224 -0
  15. package/src/Player/AudioLayer/Layout/TransportInfo/index.tsx +102 -0
  16. package/src/Player/AudioLayer/Layout/VerticalGutter/index.tsx +8 -0
  17. package/src/Player/AudioLayer/Layout/const.ts +20 -0
  18. package/src/Player/AudioLayer/Layout/debug.ts +6 -0
  19. package/src/Player/AudioLayer/Layout/hooks/index.ts +3 -0
  20. package/src/Player/AudioLayer/Layout/hooks/useImageSize.ts +101 -0
  21. package/src/Player/AudioLayer/Layout/hooks/useTransportInfoHeight.ts +54 -0
  22. package/src/Player/AudioLayer/Layout/index.tsx +21 -48
  23. package/src/Player/AudioLayer/Layout/types.ts +7 -0
  24. package/src/Player/AudioLayer/utils.ts +6 -3
  25. package/src/Player/Const/sleepTimerOptions.ts +10 -0
  26. package/src/Player/Const/speedOptions.ts +4 -0
  27. package/src/Player/Utils/Utils.ts +0 -5
  28. package/src/Player/Utils/index.tsx +117 -0
  29. package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +0 -495
  30. package/src/Player/Helpers/VideoResizeMode.ts +0 -7
@@ -0,0 +1,214 @@
1
+ import * as React from "react";
2
+ import { View, ViewStyle, StyleSheet } from "react-native";
3
+ import {
4
+ useSafeAreaInsets,
5
+ useSafeAreaFrame,
6
+ } from "react-native-safe-area-context";
7
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+ import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
9
+
10
+ import { TABLET_LANDSCAPE } from "./const";
11
+ import { TransportInfo } from "./TransportInfo";
12
+ import { PlayerImage } from "./PlayerImage";
13
+ import { Controls } from "./Controls";
14
+
15
+ import { useImageSize, useTransportInfoHeight } from "./hooks";
16
+ import { DockedControls } from "./DockedControls";
17
+ import { withDebug } from "./debug";
18
+
19
+ import { Props } from "./types";
20
+
21
+ const isIOS = isApplePlatform();
22
+
23
+ const styles = StyleSheet.create({
24
+ container: {
25
+ flex: 1,
26
+ ...withDebug({
27
+ borderWidth: 2,
28
+ borderColor: "magenta",
29
+ }),
30
+ },
31
+ flex: {
32
+ flex: 1,
33
+ },
34
+ audioPlayerContainer: {
35
+ flex: 1,
36
+ maxHeight: TABLET_LANDSCAPE.MAX_HEIGHT,
37
+ flexDirection: "column",
38
+ alignItems: "center",
39
+
40
+ ...withDebug({
41
+ borderWidth: 2,
42
+ borderColor: "blue",
43
+ }),
44
+ },
45
+ });
46
+
47
+ const tabletStyles = ({
48
+ screenHeight,
49
+ topInset,
50
+ bottomInset,
51
+ imageSize,
52
+ }): ViewStyle => ({
53
+ alignItems: "center",
54
+
55
+ height: screenHeight - topInset - bottomInset,
56
+ paddingBottom: TABLET_LANDSCAPE.MARGIN_VERTICAL,
57
+
58
+ opacity: imageSize ? 1 : 0, // hide content until imageSize is calculated
59
+ });
60
+
61
+ const isTabletWithHomeButton = (bottomInset: number): boolean =>
62
+ bottomInset === 0;
63
+
64
+ const withAdditionalOffset = ({ progressBarHeight, bottomInset }) => {
65
+ if (isIOS && isTabletWithHomeButton(bottomInset)) {
66
+ return progressBarHeight === 0 ? 8 : progressBarHeight + 2;
67
+ }
68
+
69
+ if (isIOS && !isTabletWithHomeButton(bottomInset)) {
70
+ return progressBarHeight === 0 ? 4 : 1;
71
+ }
72
+
73
+ // android
74
+ return progressBarHeight === 0 ? 4 : 0.5;
75
+ };
76
+
77
+ export function TabletLandscapeLayout({
78
+ entry,
79
+ layoutState,
80
+ playerState,
81
+ value,
82
+ onPlaybackButtonPress,
83
+ onRewindButtonPress,
84
+ onForwardButtonPress,
85
+ onPlayerSeek,
86
+ onSkipStart,
87
+ onSkipLive,
88
+ onSpeedButtonPress,
89
+ onSleepButtonPress,
90
+ onCloseVideoModal,
91
+ playbackSpeedName,
92
+ playbackSleepName,
93
+ configuration,
94
+ dimensions,
95
+ playNextData,
96
+ cancelSleepTimer,
97
+ sleepTimerDate,
98
+ title,
99
+ summary,
100
+ }: Props) {
101
+ const { docked } = layoutState;
102
+
103
+ const { bottom: bottomInset, top: topInset } = useSafeAreaInsets();
104
+ const { height: screenHeight } = useSafeAreaFrame();
105
+ const { progressBarHeight } = useModalAnimationContext();
106
+
107
+ const { imageSize, onLayout } = useImageSize({ isPortraitMode: false });
108
+
109
+ const { height: transportInfoHeight, onLayout: onTransportInfoLayout } =
110
+ useTransportInfoHeight();
111
+
112
+ return (
113
+ <View
114
+ style={[
115
+ styles.container,
116
+ tabletStyles({
117
+ screenHeight,
118
+ topInset,
119
+ bottomInset,
120
+ imageSize,
121
+ }),
122
+ ]}
123
+ >
124
+ {/* TransportInfo */}
125
+ <TransportInfo
126
+ entry={entry}
127
+ layoutState={layoutState}
128
+ value={value}
129
+ configuration={configuration}
130
+ onLayout={onTransportInfoLayout}
131
+ />
132
+
133
+ {/* TransportInfo */}
134
+
135
+ <View style={styles.audioPlayerContainer}>
136
+ {/* PlayerImage */}
137
+ <View
138
+ style={[
139
+ styles.flex,
140
+ withDebug({
141
+ borderWidth: 2,
142
+ borderColor: "yellow",
143
+ }),
144
+ ]}
145
+ onLayout={onLayout}
146
+ >
147
+ <PlayerImage
148
+ entry={entry}
149
+ layoutState={layoutState}
150
+ playerState={playerState}
151
+ value={value}
152
+ configuration={configuration}
153
+ onPlayerSeek={onPlayerSeek}
154
+ onCloseVideoModal={onCloseVideoModal}
155
+ dimensions={dimensions}
156
+ playNextData={playNextData}
157
+ title={title}
158
+ summary={summary}
159
+ docked={docked}
160
+ imageWidth={imageSize}
161
+ verticalOffset={
162
+ transportInfoHeight +
163
+ topInset +
164
+ withAdditionalOffset({ progressBarHeight, bottomInset })
165
+ }
166
+ />
167
+ </View>
168
+ {/* PlayerImage */}
169
+
170
+ <View
171
+ style={{
172
+ maxWidth: imageSize,
173
+ }}
174
+ >
175
+ <Controls
176
+ entry={entry}
177
+ layoutState={layoutState}
178
+ playerState={playerState}
179
+ value={value}
180
+ title={title}
181
+ summary={summary}
182
+ sleepTimerDate={sleepTimerDate}
183
+ cancelSleepTimer={cancelSleepTimer}
184
+ playbackSpeedName={playbackSpeedName}
185
+ playbackSleepName={playbackSleepName}
186
+ onPlaybackButtonPress={onPlaybackButtonPress}
187
+ onRewindButtonPress={onRewindButtonPress}
188
+ onForwardButtonPress={onForwardButtonPress}
189
+ onPlayerSeek={onPlayerSeek}
190
+ onSkipStart={onSkipStart}
191
+ onSkipLive={onSkipLive}
192
+ onSpeedButtonPress={onSpeedButtonPress}
193
+ onSleepButtonPress={onSleepButtonPress}
194
+ docked={docked}
195
+ />
196
+ </View>
197
+ </View>
198
+
199
+ <DockedControls
200
+ entry={entry}
201
+ layoutState={layoutState}
202
+ playerState={playerState}
203
+ value={value}
204
+ configuration={configuration}
205
+ dimensions={dimensions}
206
+ onCloseVideoModal={onCloseVideoModal}
207
+ onPlayerSeek={onPlayerSeek}
208
+ playNextData={playNextData}
209
+ title={title}
210
+ summary={summary}
211
+ />
212
+ </View>
213
+ );
214
+ }
@@ -0,0 +1,224 @@
1
+ import * as React from "react";
2
+ import { View, ViewStyle, StyleSheet } from "react-native";
3
+ import {
4
+ useSafeAreaInsets,
5
+ useSafeAreaFrame,
6
+ } from "react-native-safe-area-context";
7
+ import { useModalAnimationContext } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
8
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
9
+
10
+ import { withDebug } from "./debug";
11
+ import { TABLET_PORTRAIT } from "./const";
12
+
13
+ import { TransportInfo } from "./TransportInfo";
14
+ import { PlayerImage } from "./PlayerImage";
15
+ import { Controls } from "./Controls";
16
+
17
+ import { useImageSize } from "./hooks";
18
+ import { DockedControls } from "./DockedControls";
19
+ import { VerticalGutter } from "./VerticalGutter";
20
+
21
+ import { Props } from "./types";
22
+
23
+ const styles = StyleSheet.create({
24
+ container: {
25
+ flex: 1,
26
+
27
+ ...withDebug({
28
+ borderWidth: 2,
29
+ borderColor: "magenta",
30
+ }),
31
+ },
32
+
33
+ flex: {
34
+ flex: 1,
35
+ },
36
+
37
+ fillContainer: {
38
+ ...StyleSheet.absoluteFillObject,
39
+
40
+ ...withDebug({
41
+ borderWidth: 2,
42
+ borderColor: "red",
43
+ }),
44
+ },
45
+
46
+ audioPlayerContainer: {
47
+ flex: 1,
48
+ maxWidth: TABLET_PORTRAIT.MAX_WIDTH,
49
+ marginLeft: TABLET_PORTRAIT.MARGIN_HORIZONTAL,
50
+ marginRight: TABLET_PORTRAIT.MARGIN_HORIZONTAL,
51
+
52
+ ...withDebug({
53
+ borderWidth: 2,
54
+ borderColor: "blue",
55
+ }),
56
+ },
57
+ });
58
+
59
+ const tabletStyles = ({
60
+ screenHeight,
61
+ topInset,
62
+ bottomInset,
63
+ topOffset,
64
+ }): ViewStyle => ({
65
+ alignItems: "center",
66
+ height: screenHeight - topInset - bottomInset,
67
+ opacity: topOffset ? 1 : 0, // hide content until topOffset is calculated
68
+ });
69
+
70
+ const justifyContent = (topOffset: Option<number>) =>
71
+ topOffset ? "flex-start" : "center";
72
+
73
+ export function TabletPortraitLayout({
74
+ entry,
75
+ layoutState,
76
+ playerState,
77
+ value,
78
+ onPlaybackButtonPress,
79
+ onRewindButtonPress,
80
+ onForwardButtonPress,
81
+ onPlayerSeek,
82
+ onSkipStart,
83
+ onSkipLive,
84
+ onSpeedButtonPress,
85
+ onSleepButtonPress,
86
+ onCloseVideoModal,
87
+ playbackSpeedName,
88
+ playbackSleepName,
89
+ configuration,
90
+ dimensions,
91
+ playNextData,
92
+ cancelSleepTimer,
93
+ sleepTimerDate,
94
+ title,
95
+ summary,
96
+ }: Props) {
97
+ const { docked } = layoutState;
98
+
99
+ const { bottom: bottomInset, top: topInset } = useSafeAreaInsets();
100
+ const { height: screenHeight } = useSafeAreaFrame();
101
+ const { progressBarHeight } = useModalAnimationContext();
102
+
103
+ const {
104
+ imageSize,
105
+ offsetY: topOffset,
106
+ onLayout,
107
+ } = useImageSize({ isPortraitMode: true });
108
+
109
+ return (
110
+ <View
111
+ style={[
112
+ styles.container,
113
+ tabletStyles({
114
+ screenHeight,
115
+ topInset,
116
+ bottomInset,
117
+ topOffset,
118
+ }),
119
+ ]}
120
+ >
121
+ {/* TransportInfo */}
122
+ <TransportInfo
123
+ entry={entry}
124
+ layoutState={layoutState}
125
+ value={value}
126
+ configuration={configuration}
127
+ />
128
+
129
+ {/* TransportInfo */}
130
+
131
+ {/* PlayerImage */}
132
+ <View style={styles.fillContainer} pointerEvents="box-none">
133
+ <View
134
+ style={[
135
+ styles.audioPlayerContainer,
136
+ {
137
+ justifyContent: justifyContent(topOffset),
138
+ width: imageSize,
139
+ },
140
+ ]}
141
+ >
142
+ {topOffset ? <VerticalGutter height={topOffset} /> : null}
143
+
144
+ <View
145
+ style={[
146
+ styles.flex,
147
+ {
148
+ maxHeight: imageSize,
149
+
150
+ ...withDebug({
151
+ borderWidth: 2,
152
+ borderColor: "yellow",
153
+ }),
154
+ },
155
+ ]}
156
+ onLayout={onLayout}
157
+ >
158
+ <PlayerImage
159
+ entry={entry}
160
+ layoutState={layoutState}
161
+ playerState={playerState}
162
+ value={value}
163
+ configuration={configuration}
164
+ onPlayerSeek={onPlayerSeek}
165
+ onCloseVideoModal={onCloseVideoModal}
166
+ dimensions={dimensions}
167
+ playNextData={playNextData}
168
+ title={title}
169
+ summary={summary}
170
+ docked={docked}
171
+ imageWidth={imageSize}
172
+ verticalOffset={
173
+ toNumberWithDefaultZero(topOffset) - progressBarHeight
174
+ }
175
+ />
176
+ </View>
177
+
178
+ <View
179
+ style={{
180
+ maxWidth: imageSize,
181
+ }}
182
+ >
183
+ <Controls
184
+ entry={entry}
185
+ layoutState={layoutState}
186
+ playerState={playerState}
187
+ value={value}
188
+ title={title}
189
+ summary={summary}
190
+ sleepTimerDate={sleepTimerDate}
191
+ cancelSleepTimer={cancelSleepTimer}
192
+ playbackSpeedName={playbackSpeedName}
193
+ playbackSleepName={playbackSleepName}
194
+ onPlaybackButtonPress={onPlaybackButtonPress}
195
+ onRewindButtonPress={onRewindButtonPress}
196
+ onForwardButtonPress={onForwardButtonPress}
197
+ onPlayerSeek={onPlayerSeek}
198
+ onSkipStart={onSkipStart}
199
+ onSkipLive={onSkipLive}
200
+ onSpeedButtonPress={onSpeedButtonPress}
201
+ onSleepButtonPress={onSleepButtonPress}
202
+ docked={docked}
203
+ />
204
+ </View>
205
+ </View>
206
+ </View>
207
+ {/* PlayerImage */}
208
+
209
+ <DockedControls
210
+ entry={entry}
211
+ layoutState={layoutState}
212
+ playerState={playerState}
213
+ value={value}
214
+ configuration={configuration}
215
+ dimensions={dimensions}
216
+ onCloseVideoModal={onCloseVideoModal}
217
+ onPlayerSeek={onPlayerSeek}
218
+ playNextData={playNextData}
219
+ title={title}
220
+ summary={summary}
221
+ />
222
+ </View>
223
+ );
224
+ }
@@ -0,0 +1,102 @@
1
+ import * as React from "react";
2
+ import { compose, take, map, split, propOr, trim } from "ramda";
3
+ import { StyleSheet, View, LayoutChangeEvent } from "react-native";
4
+
5
+ import { TransportInfo as MobileTransportInfo } from "@applicaster/quick-brick-mobile-transport-controls";
6
+ import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
7
+ import { useLanguageData } from "@applicaster/zapp-react-native-utils/reactHooks/player";
8
+ import {
9
+ AnimationComponent,
10
+ ComponentAnimationType,
11
+ } from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
12
+ import { toPositiveNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
13
+ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
14
+
15
+ import { TABLET_LANDSCAPE } from "../const";
16
+ import { withDebug } from "../debug";
17
+
18
+ const styles = StyleSheet.create({
19
+ container: {
20
+ width: "100%",
21
+ alignItems: "center",
22
+ justifyContent: "space-between",
23
+ },
24
+ });
25
+
26
+ const applyMarginBottom = ({ isTablet, marginBottom }) =>
27
+ isTablet ? marginBottom : 0;
28
+
29
+ type Props = {
30
+ entry: ZappEntry;
31
+ layoutState: QuickBrickPlayer.LayoutState;
32
+ value: GetValue;
33
+ configuration: QuickBrickPlayer.AudioPlayerProps["configuration"];
34
+ onLayout?: (event: LayoutChangeEvent) => void;
35
+ };
36
+
37
+ export const TransportInfo = ({
38
+ entry,
39
+ layoutState,
40
+ value,
41
+ configuration,
42
+ onLayout = noop,
43
+ }: Props) => {
44
+ const isTablet = useIsTablet();
45
+ const languageData = useLanguageData("audio-player");
46
+
47
+ const [marginBottom, setMarginBottom] = React.useState(0);
48
+
49
+ const handleLayout = React.useCallback(
50
+ (event: LayoutChangeEvent) => {
51
+ const height = toPositiveNumberWithDefaultZero(
52
+ event?.nativeEvent?.layout?.height
53
+ );
54
+
55
+ const minHeight = Math.min(height, TABLET_LANDSCAPE.MARGIN_VERTICAL);
56
+ setMarginBottom(TABLET_LANDSCAPE.MARGIN_VERTICAL - minHeight);
57
+
58
+ // pass layoutChangeEvent to upper level
59
+ onLayout(event);
60
+ },
61
+ [onLayout]
62
+ );
63
+
64
+ const actionButtons = React.useMemo(
65
+ () =>
66
+ compose(
67
+ take(2),
68
+ map(trim),
69
+ split(","),
70
+ propOr("", "player_action_buttons")
71
+ )(configuration),
72
+ [configuration.player_action_buttons]
73
+ );
74
+
75
+ return (
76
+ <View
77
+ style={{
78
+ marginBottom: applyMarginBottom({ isTablet, marginBottom }),
79
+
80
+ ...withDebug({
81
+ borderWidth: 2,
82
+ borderColor: "red",
83
+ }),
84
+ }}
85
+ onLayout={handleLayout}
86
+ >
87
+ <AnimationComponent
88
+ animationType={ComponentAnimationType.componentFade}
89
+ style={styles.container}
90
+ >
91
+ <MobileTransportInfo
92
+ content={entry}
93
+ value={value}
94
+ visible
95
+ languageData={languageData}
96
+ layoutState={layoutState}
97
+ actionButtons={actionButtons}
98
+ />
99
+ </AnimationComponent>
100
+ </View>
101
+ );
102
+ };
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ import { View } from "react-native";
3
+
4
+ import { withDebug } from "../debug";
5
+
6
+ export const VerticalGutter = ({ height }: { height: number }) => (
7
+ <View style={{ height, ...withDebug({ backgroundColor: "red" }) }} />
8
+ );
@@ -0,0 +1,20 @@
1
+ export const PADDING_HORIZONTAL = 15;
2
+
3
+ export const TABLET_VERTICAL_SEPARATOR_HEIGHT = 32;
4
+
5
+ export const TABLET_LANDSCAPE = {
6
+ MARGIN_VERTICAL: 80, // gap between safe-area edge and audio-player container
7
+ MAX_HEIGHT: 750,
8
+ };
9
+
10
+ export const TABLET_PORTRAIT = {
11
+ MARGIN_HORIZONTAL: 180,
12
+ MAX_WIDTH: 550,
13
+ };
14
+
15
+ export const MOBILE = {
16
+ MARGIN_HORIZONTAL: 30,
17
+ MARGIN_BOTTOM: 48,
18
+ MARGIN_TOP: 10,
19
+ MIN_HEIGHT: 458,
20
+ };
@@ -0,0 +1,6 @@
1
+ // use this variable to activate borders on AudioPlayer. It will help you to fix layout if needed.
2
+ const DEBUG = false;
3
+
4
+ export const withDebug = (style) => {
5
+ return DEBUG ? style : {};
6
+ };
@@ -0,0 +1,3 @@
1
+ export { useImageSize } from "./useImageSize";
2
+
3
+ export { useTransportInfoHeight } from "./useTransportInfoHeight";
@@ -0,0 +1,101 @@
1
+ import * as React from "react";
2
+ import { LayoutChangeEvent } from "react-native";
3
+
4
+ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
5
+ import { Subject } from "rxjs";
6
+ import { first, debounceTime } from "rxjs/operators";
7
+ import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
8
+
9
+ const TIMEOUT = 200; // ms
10
+
11
+ type Layout = {
12
+ x: number;
13
+ y: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+
18
+ const builder = () => {
19
+ const layoutChange$ = new Subject<Layout>();
20
+
21
+ const emitLayoutChange = (layout: Layout): void => {
22
+ layoutChange$.next(layout);
23
+ };
24
+
25
+ return {
26
+ emitLayoutChange,
27
+ layoutChange$,
28
+ };
29
+ };
30
+
31
+ type Props = {
32
+ isPortraitMode: boolean;
33
+ };
34
+
35
+ type Return = {
36
+ imageSize: Option<number>;
37
+ offsetY: Option<number>;
38
+ onLayout: (event: LayoutChangeEvent) => void;
39
+ };
40
+
41
+ export const useImageSize = ({ isPortraitMode }: Props): Return => {
42
+ const [imageSize, setImageSize] = React.useState(undefined);
43
+ const [offsetY, setOffsetY] = React.useState(undefined);
44
+
45
+ const layoutRef = useRefWithInitialValue<ReturnType<typeof builder>>(builder);
46
+
47
+ React.useEffect(() => {
48
+ const subscription = layoutRef.current.layoutChange$
49
+ // for landscape layout we need to wait the second layout change in order to get proper size of image
50
+ .pipe(debounceTime(TIMEOUT), first())
51
+ .subscribe({
52
+ next: (layout: Layout) => {
53
+ const size = isPortraitMode
54
+ ? Math.min(layout.height, layout.width)
55
+ : layout.height;
56
+
57
+ setImageSize(size);
58
+ },
59
+ });
60
+
61
+ return () => {
62
+ subscription.unsubscribe();
63
+ };
64
+ }, [isPortraitMode]);
65
+
66
+ React.useEffect(() => {
67
+ const subscription = layoutRef.current.layoutChange$
68
+ .pipe(debounceTime(TIMEOUT), first())
69
+ .subscribe({
70
+ next: (layout: Layout) => {
71
+ setOffsetY(layout.y);
72
+ },
73
+ });
74
+
75
+ return () => {
76
+ subscription.unsubscribe();
77
+ };
78
+ }, []);
79
+
80
+ const handleLayout = React.useCallback((event: LayoutChangeEvent) => {
81
+ const height = toNumber(event?.nativeEvent?.layout?.height);
82
+ const width = toNumber(event?.nativeEvent?.layout?.width);
83
+ const y = toNumber(event?.nativeEvent?.layout?.y);
84
+ const x = toNumber(event?.nativeEvent?.layout?.x);
85
+
86
+ const layout: Layout = {
87
+ x,
88
+ y,
89
+ width,
90
+ height,
91
+ };
92
+
93
+ layoutRef.current.emitLayoutChange(layout);
94
+ }, []);
95
+
96
+ return {
97
+ imageSize,
98
+ offsetY,
99
+ onLayout: handleLayout,
100
+ };
101
+ };