1 |
|
2 | import * as React from "react";
|
3 | import * as R from "ramda";
|
4 |
|
5 | import {
|
6 | sendQuickBrickEvent,
|
7 | QUICK_BRICK_EVENTS,
|
8 | } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
9 |
|
10 | import {
|
11 | debounce,
|
12 | noop,
|
13 | } from "@applicaster/zapp-react-native-utils/functionUtils";
|
14 |
|
15 | import {
|
16 | keyCode,
|
17 | KEYS,
|
18 | ARROW_KEYS,
|
19 | focusManager,
|
20 | playerManager,
|
21 | } from "@applicaster/zapp-react-native-utils/appUtils";
|
22 |
|
23 | import {
|
24 | DisplayStateContext,
|
25 | DISPLAY_STATES,
|
26 | } from "@applicaster/zapp-react-native-ui-components/Contexts/DisplayStateContext";
|
27 |
|
28 | import { PlayerContentContext } from "@applicaster/zapp-react-dom-ui-components/Contexts";
|
29 |
|
30 | import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator";
|
31 |
|
32 | type Props = {
|
33 | displayState: string,
|
34 | setDisplayState: string => void,
|
35 | setPlayerContent: ({} | null) => void,
|
36 | navigator: {
|
37 | goBack: () => void,
|
38 | canGoBack: () => boolean,
|
39 | replace: ({}) => void,
|
40 | currentRoute: string,
|
41 | },
|
42 | rivers: {},
|
43 | };
|
44 |
|
45 | type KeyCode = { code: string, keyCode: number | string };
|
46 |
|
47 | const DEFAULT_TRANSPORT_CONTROLS_TIMEOUT = 5 * 1000;
|
48 | const LONG_KEY_PRESS_TIMEOUT = 2 * 1000;
|
49 |
|
50 | export function withInteractionManager(
|
51 | Component: React.ComponentType<any>
|
52 | ): React.ComponentType<any> {
|
53 | class InteractionManager extends React.Component<Props> {
|
54 | onKeyDown: KeyCode => void;
|
55 | onKeyUp: KeyCode => void;
|
56 | onKeyDownListener: KeyCode => void;
|
57 | onKeyUpListener: KeyCode => void;
|
58 |
|
59 | longKeyPressTimeout: any | null;
|
60 | timeout: any | null;
|
61 |
|
62 | constructor(props) {
|
63 | super(props);
|
64 | this.onKeyDown = this.onKeyDown.bind(this);
|
65 | this.onKeyDownListener = debounce({ fn: this.onKeyDown, context: this });
|
66 | this.onKeyUpListener = debounce({ fn: this.onKeyUp, context: this });
|
67 |
|
68 | this.longKeyPressTimeout = null;
|
69 | }
|
70 |
|
71 | componentDidMount() {
|
72 | window.addEventListener("keydown", this.onKeyDownListener);
|
73 | window.addEventListener("keyup", this.onKeyUpListener);
|
74 | }
|
75 |
|
76 | componentWillUnmount() {
|
77 | window.removeEventListener("keydown", this.onKeyDownListener);
|
78 | window.removeEventListener("keyup", this.onKeyUpListener);
|
79 |
|
80 | if (this.timeout) {
|
81 | this.clearTimeout();
|
82 | }
|
83 | }
|
84 |
|
85 | isHomeScreen() {
|
86 | const { navigator } = this.props;
|
87 | const { rivers } = this.props;
|
88 |
|
89 | const homeId = R.compose(
|
90 | R.prop("id"),
|
91 | R.find(R.prop("home")),
|
92 | R.values
|
93 | )(rivers);
|
94 |
|
95 | const homePath = `/river/${homeId}`;
|
96 |
|
97 | return homePath === navigator.currentRoute;
|
98 | }
|
99 |
|
100 | goToHome() {
|
101 | const { navigator } = this.props;
|
102 | const { rivers } = this.props;
|
103 | const homeRiver = R.find(R.propEq("home", true), R.values(rivers));
|
104 |
|
105 | navigator.replace(homeRiver);
|
106 | }
|
107 |
|
108 | resetHudTimeout() {
|
109 | const { setDisplayState } = this.props;
|
110 |
|
111 | if (this.timeout) {
|
112 | clearTimeout(this.timeout);
|
113 | }
|
114 |
|
115 | this.timeout = setTimeout(() => {
|
116 | setDisplayState(DISPLAY_STATES.PLAYER);
|
117 | this.timeout = null;
|
118 | }, DEFAULT_TRANSPORT_CONTROLS_TIMEOUT);
|
119 | }
|
120 |
|
121 | clearTimeout() {
|
122 | if (this.timeout) {
|
123 | clearTimeout(this.timeout);
|
124 | this.timeout = null;
|
125 | }
|
126 | }
|
127 |
|
128 | onKeyUp(event) {
|
129 | this.clearLongPressTimeout();
|
130 | }
|
131 |
|
132 | clearLongPressTimeout() {
|
133 | if (this.longKeyPressTimeout) {
|
134 | clearTimeout(this.longKeyPressTimeout);
|
135 | this.longKeyPressTimeout = null;
|
136 | }
|
137 | }
|
138 |
|
139 | runIfLongPress(action, delay = LONG_KEY_PRESS_TIMEOUT) {
|
140 | this.clearLongPressTimeout();
|
141 |
|
142 | this.longKeyPressTimeout = setTimeout(() => {
|
143 | (action || noop)();
|
144 | this.clearLongPressTimeout();
|
145 | }, delay);
|
146 | }
|
147 |
|
148 | onKeyDown(event) {
|
149 | const {
|
150 | displayState,
|
151 | setDisplayState,
|
152 | setPlayerContent,
|
153 | navigator,
|
154 | } = this.props;
|
155 |
|
156 | if (keyCode(event).matches(KEYS.Exit)) {
|
157 | this.runIfLongPress(
|
158 | sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND)
|
159 | );
|
160 | }
|
161 |
|
162 | |
163 |
|
164 |
|
165 |
|
166 | if (
|
167 | displayState === DISPLAY_STATES.PLAYER ||
|
168 | displayState === DISPLAY_STATES.HUD
|
169 | ) {
|
170 | this.resetHudTimeout();
|
171 |
|
172 | |
173 |
|
174 |
|
175 | if (keyCode(event).matches(KEYS.TogglePlayPause)) {
|
176 | const { playing } = playerManager.getState() || {};
|
177 | if (!playing) {
|
178 | return playerManager.togglePlayPause();
|
179 | }
|
180 | }
|
181 |
|
182 | if (keyCode(event).matches(KEYS.Play)) {
|
183 | return playerManager.play();
|
184 | }
|
185 |
|
186 | if (keyCode(event).matches(KEYS.Pause)) {
|
187 | setDisplayState(DISPLAY_STATES.HUD);
|
188 | return playerManager.pause();
|
189 | }
|
190 |
|
191 | if (keyCode(event).matches(KEYS.Stop)) {
|
192 | setDisplayState(DISPLAY_STATES.DEFAULT);
|
193 | setPlayerContent(null);
|
194 | navigator.goBack();
|
195 | return;
|
196 | }
|
197 |
|
198 | if (keyCode(event).matches(KEYS.Back)) {
|
199 | setDisplayState(DISPLAY_STATES.DEFAULT);
|
200 | setPlayerContent(null);
|
201 | navigator.goBack();
|
202 | return;
|
203 | }
|
204 |
|
205 | if (keyCode(event).matches(KEYS.Forward)) {
|
206 | setDisplayState(DISPLAY_STATES.HUD);
|
207 | return playerManager.forward();
|
208 | }
|
209 |
|
210 | if (keyCode(event).matches(KEYS.Rewind)) {
|
211 | setDisplayState(DISPLAY_STATES.HUD);
|
212 | return playerManager.rewind();
|
213 | }
|
214 |
|
215 | if (displayState === DISPLAY_STATES.PLAYER) {
|
216 | return setDisplayState(DISPLAY_STATES.HUD);
|
217 | }
|
218 | }
|
219 |
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | if (keyCode(event).matches(KEYS.Back)) {
|
228 | switch (displayState) {
|
229 | case DISPLAY_STATES.HUD:
|
230 | setDisplayState(DISPLAY_STATES.PLAYER);
|
231 | break;
|
232 |
|
233 | case DISPLAY_STATES.PLAYER:
|
234 | setDisplayState(DISPLAY_STATES.DEFAULT);
|
235 | break;
|
236 |
|
237 | case DISPLAY_STATES.DEFAULT:
|
238 | if (navigator.canGoBack()) {
|
239 | navigator.goBack();
|
240 | } else if (this.isHomeScreen()) {
|
241 | sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
|
242 | } else {
|
243 | this.goToHome();
|
244 | }
|
245 | break;
|
246 |
|
247 | default:
|
248 | break;
|
249 | }
|
250 | }
|
251 |
|
252 | if (keyCode(event).matches(KEYS.Enter)) {
|
253 | focusManager.press();
|
254 | return true;
|
255 | }
|
256 |
|
257 | if (keyCode(event).matchesAny(...ARROW_KEYS)) {
|
258 | focusManager.moveFocus(keyCode(event).direction());
|
259 | return true;
|
260 | }
|
261 | }
|
262 |
|
263 | render() {
|
264 | return <Component {...this.props} />;
|
265 | }
|
266 | }
|
267 |
|
268 | return R.compose(
|
269 | withNavigator,
|
270 | PlayerContentContext.withConsumer,
|
271 | DisplayStateContext.withConsumer
|
272 | )(InteractionManager);
|
273 | }
|
274 |
|
\ | No newline at end of file |