@plusscommunities/pluss-core-app 1.2.2 → 1.2.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-core-app",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "Core extension package for Pluss Communities platform",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -26,10 +26,11 @@ export const reactionActions = {
26
26
  },
27
27
  });
28
28
  },
29
- addComment: (entityId, entityType, site, comment, image) => {
29
+ addComment: (entityId, entityType, entityName, site, comment, image) => {
30
30
  const data = {
31
31
  entityId,
32
32
  entityType,
33
+ entityName,
33
34
  site,
34
35
  comment,
35
36
  };
@@ -96,7 +96,7 @@ class CommentReply extends Component {
96
96
  this.props.commentSection.getWrappedInstance().startedAddingComment();
97
97
  }
98
98
  reactionActions
99
- .addComment(this.props.entityId, this.props.entityType, this.props.site, text, image)
99
+ .addComment(this.props.entityId, this.props.entityType, this.props.entityName, this.props.site, text, image)
100
100
  .then(res => {
101
101
  if (this.props.commentSection) {
102
102
  this.props.commentSection.getWrappedInstance().commentAdded(res.data);
@@ -110,9 +110,7 @@ class ImagePopup extends Component {
110
110
  const { showFullscreenVideo, currentVideoUrl } = this.state;
111
111
  if (!currentVideoUrl) return;
112
112
 
113
- return (
114
- <VideoPopup uri={currentVideoUrl} visible={showFullscreenVideo} showFullscreenButton={false} onClose={this.toggleFullscreenVideo} />
115
- );
113
+ return <VideoPopup uri={currentVideoUrl} visible={showFullscreenVideo} onClose={this.toggleFullscreenVideo} />;
116
114
  }
117
115
 
118
116
  render() {
@@ -1,14 +1,17 @@
1
1
  import React, { Component } from 'react';
2
- import { View, StyleSheet, Dimensions } from 'react-native';
2
+ import { Platform, View, StyleSheet, Dimensions } from 'react-native';
3
+ import * as ScreenOrientation from 'expo-screen-orientation';
4
+ import { DeviceMotion } from 'expo-sensors';
3
5
  import YoutubePlayer, { getYoutubeMeta } from 'react-native-youtube-iframe';
4
- // import { Vimeo } from 'react-native-vimeo-iframe';
6
+ import { Vimeo } from 'react-native-vimeo-iframe';
5
7
  import WebView from 'react-native-webview';
6
8
  import { Video } from 'expo-av';
9
+ import VideoPlayer from 'expo-video-player';
7
10
  import { Spinner } from './Spinner';
8
11
 
9
12
  const SCREEN_HEIGHT = Dimensions.get('window').height;
10
13
  const SCREEN_WIDTH = Dimensions.get('window').width;
11
- const EXPO_VIDEO_PROPS = { rate: 1, isMuted: false, volume: 1, shouldPlay: true };
14
+ const EXPO_VIDEO_PROPS = { rate: 1, isMuted: false, volume: 1 };
12
15
  const RETRY_RECOVER = 30000;
13
16
  const RETRY_LOADING = 10000;
14
17
 
@@ -16,27 +19,42 @@ class MediaPlayer extends Component {
16
19
  constructor(props) {
17
20
  super(props);
18
21
  this.state = {
22
+ deviceOrientation: null,
23
+ isLandscape: false,
19
24
  isUrlLink: true,
20
25
  isYoutube: false,
26
+ isVimeo: false,
21
27
  forceWebview: false,
22
- heightFactor: 0.565,
28
+ heightFactor: 0,
23
29
  playbackLoaded: false,
24
30
  playbackBuffering: false,
25
31
  playbackPlaying: false,
32
+ readyToRender: false,
26
33
  };
27
34
  this.youtubePlayer = null;
28
35
  this.videoPlayer = null;
29
36
  this.checkStreamLoaded = null;
30
37
  this.retryLoading = false;
38
+ this.orientationQueue = [];
31
39
  }
32
40
 
33
- componentDidMount() {
34
- this.setupForLiveStreaming();
35
- }
41
+ componentDidMount = async () => {
42
+ this.setupForPlaying();
36
43
 
37
- componentWillUnmount() {
44
+ ScreenOrientation.lockAsync(Platform.OS === 'ios' ? ScreenOrientation.OrientationLock.DEFAULT : ScreenOrientation.OrientationLock.ALL);
45
+ const motionAvailalble = await DeviceMotion.isAvailableAsync();
46
+ // console.log('motionAvailalble', motionAvailalble);
47
+ if (motionAvailalble) {
48
+ DeviceMotion.setUpdateInterval(500);
49
+ DeviceMotion.addListener(this.onMotionChange);
50
+ }
51
+ };
52
+
53
+ componentWillUnmount = () => {
38
54
  if (this.checkStreamLoaded) clearInterval(this.checkStreamLoaded);
39
- }
55
+ DeviceMotion.removeAllListeners();
56
+ ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
57
+ };
40
58
 
41
59
  getYoutubeVideoId = url => {
42
60
  url = url.replace(/(>|<)/gi, '').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
@@ -48,23 +66,35 @@ class MediaPlayer extends Component {
48
66
  }
49
67
  };
50
68
 
51
- setupForLiveStreaming = async () => {
69
+ getVimeoId = url => {
70
+ const result = url.match(
71
+ /(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|video\/|)(\d+)(?:[a-zA-Z0-9_\-]+)?/i,
72
+ );
73
+ return result[1];
74
+ };
75
+
76
+ setupForPlaying = async () => {
52
77
  // Extract live stream info
53
- const { streamUrl } = this.props.streamInfo;
54
- const isUrlLink = streamUrl.startsWith('http');
55
- const isYoutube = streamUrl.includes('youtube.com/watch?v=') || streamUrl.includes('youtu.be/');
56
- this.setState({ isUrlLink, isYoutube });
78
+ const { source } = this.props;
79
+ const isUrlLink = source.startsWith('http');
80
+ const isYoutube = source.includes('youtube.com/watch?v=') || source.includes('youtu.be/');
81
+ const isVimeo = source.includes('vimeo.com/');
82
+ this.setState({ isUrlLink, isYoutube, isVimeo, playbackLoaded: isYoutube || isVimeo });
57
83
 
58
- // if (isYoutube) {
59
- // const metadata = await getYoutubeMeta(this.getYoutubeVideoId(streamUrl));
60
- // this.setState({ heightFactor: metadata.height / metadata.width });
61
- // console.log('youtube metadata', metadata);
62
- // }
84
+ if (isYoutube) {
85
+ const metadata = await getYoutubeMeta(this.getYoutubeVideoId(source));
86
+ this.setState({ heightFactor: metadata.height / metadata.width, readyToRender: true });
87
+ // console.log('youtube metadata', metadata);
88
+ } else if (isVimeo) {
89
+ this.setState({ heightFactor: 0.565, readyToRender: true });
90
+ } else {
91
+ this.setState({ readyToRender: true });
92
+ }
63
93
  };
64
94
 
65
95
  getStreamPlaybackUrl = () => {
66
96
  // return 'https://stream.mux.com/nflNhyFQMlrSt00II01Hjh2BLSW009PH1tl023pdpJ01s1vc.m3u8';
67
- return `https://stream.mux.com/${this.props.streamInfo.playbackId}.m3u8`;
97
+ return `https://stream.mux.com/${this.props.playbackId}.m3u8`;
68
98
  };
69
99
 
70
100
  checkReloadStream = (wait = RETRY_LOADING, force = false) => {
@@ -90,6 +120,48 @@ class MediaPlayer extends Component {
90
120
  }, wait);
91
121
  };
92
122
 
123
+ isLandscape = deviceRotation => {
124
+ const { beta, gamma } = deviceRotation;
125
+ const absGamma = Math.abs(gamma);
126
+ const absBeta = Math.abs(beta);
127
+ const isGammaNegative = Math.sign(gamma) === -1;
128
+
129
+ if (absGamma <= 0.04 && absBeta <= 0.24) {
130
+ //Portrait mode, on a flat surface.
131
+ return false;
132
+ } else if ((absGamma <= 1.0 || absGamma >= 2.3) && absBeta >= 0.5) {
133
+ //General Portrait mode, accounting for forward and back tilt on the top of the phone.
134
+ return false;
135
+ } else {
136
+ if (isGammaNegative) {
137
+ //Landscape mode with the top of the phone to the left.
138
+ return true;
139
+ } else {
140
+ //Landscape mode with the top of the phone to the right.
141
+ return true;
142
+ }
143
+ }
144
+ };
145
+
146
+ onMotionChange = async changeEvent => {
147
+ const deviceRotation = changeEvent.rotation;
148
+ const deviceOrientation = changeEvent.orientation;
149
+ const isLandscape = this.isLandscape(deviceRotation);
150
+ this.orientationQueue.push(isLandscape);
151
+ if (this.orientationQueue.length > 2) this.orientationQueue = this.orientationQueue.slice(-2);
152
+ // console.log('onMotionChange - orientationQueue', this.orientationQueue);
153
+ if (
154
+ isLandscape !== this.state.isLandscape &&
155
+ isLandscape === (Math.abs(deviceOrientation) === 90) &&
156
+ this.orientationQueue.every(i => i === isLandscape)
157
+ ) {
158
+ this.setState({ isLandscape }, () => {
159
+ // console.log('isLandscape', this.state.isLandscape);
160
+ if (this.props.orientationChanged) this.props.orientationChanged(this.state.isLandscape);
161
+ });
162
+ }
163
+ };
164
+
93
165
  onStreamLoadStart = () => {
94
166
  console.log('Stream load started');
95
167
  this.checkStreamLoaded = setInterval(() => {
@@ -137,16 +209,24 @@ class MediaPlayer extends Component {
137
209
 
138
210
  onYoutubeReady = () => {
139
211
  // console.log('onYoutubeReady');
140
- this.setState({ playbackLoaded: true });
141
212
  };
142
213
 
143
- getYoutubePlayer = (youtubeId, width, height) => (
214
+ onVideoPlayStatusUpdate = status => {
215
+ // console.log('onVideoPlayStatusUpdate', status);
216
+ };
217
+
218
+ onVideoPlayError = error => {
219
+ // console.log('onVideoPlayError', error);
220
+ this.setState({ forceWebview: true });
221
+ };
222
+
223
+ getYoutubePlayer = (youtubeId, width, height, autoPlay) => (
144
224
  <YoutubePlayer
145
225
  ref={ref => (this.youtubePlayer = ref)}
146
226
  height={height}
147
227
  width={width}
148
228
  videoId={youtubeId}
149
- play
229
+ play={autoPlay}
150
230
  // onChangeState={event => console.log('onChangeState', event)}
151
231
  onReady={this.onYoutubeReady}
152
232
  onError={this.onYoutubeError}
@@ -160,6 +240,24 @@ class MediaPlayer extends Component {
160
240
  />
161
241
  );
162
242
 
243
+ getVimeoPlayer = (vimeoId, width, height, autoPlay) => (
244
+ <View style={{ width, height }}>
245
+ <Vimeo
246
+ style={{ marginLeft: -8, marginRight: -5 }}
247
+ videoId={vimeoId}
248
+ // onReady={() => console.log('Video is ready')}
249
+ // onPlay={() => console.log('Video is playing')}
250
+ // onPlayProgress={data => console.log('Video progress data:', data)}
251
+ // onFinish={() => console.log('Video is finished')}
252
+ loop={false}
253
+ autoPlay={autoPlay}
254
+ controls={true}
255
+ speed={true}
256
+ time={'0m0s'}
257
+ />
258
+ </View>
259
+ );
260
+
163
261
  getWebviewPlayer = embedUrl => (
164
262
  <WebView
165
263
  startInLoadingState
@@ -172,13 +270,14 @@ class MediaPlayer extends Component {
172
270
  />
173
271
  );
174
272
 
175
- getVideoPlayer = (embedUrl, width, height) => (
273
+ getStreamPlayer = (embedUrl, width, height, autoPlay) => (
176
274
  <Video
177
275
  ref={vp => {
178
276
  this.videoPlayer = vp;
179
277
  }}
180
278
  source={{ uri: embedUrl }}
181
279
  {...EXPO_VIDEO_PROPS}
280
+ shouldPlay={autoPlay}
182
281
  resizeMode={Video.RESIZE_MODE_CONTAIN}
183
282
  useNativeControls
184
283
  style={{ width, height }}
@@ -189,37 +288,83 @@ class MediaPlayer extends Component {
189
288
  />
190
289
  );
191
290
 
291
+ getVideoPlayer = (embedUrl, width, height, autoPlay) => (
292
+ <VideoPlayer
293
+ videoProps={{
294
+ shouldPlay: autoPlay,
295
+ resizeMode: Video.RESIZE_MODE_CONTAIN,
296
+ source: { uri: embedUrl },
297
+ }}
298
+ showFullscreenButton={false}
299
+ errorCallback={this.onVideoPlayError}
300
+ playbackCallback={this.onVideoPlayStatusUpdate}
301
+ height={height}
302
+ width={width}
303
+ />
304
+ );
305
+
192
306
  render() {
193
307
  let width, height;
194
- const { landscape, streamInfo } = this.props;
195
- if (landscape) {
308
+ const { playbackId, source, autoPlay, useVideoPlayer } = this.props;
309
+ const {
310
+ readyToRender,
311
+ isLandscape,
312
+ heightFactor,
313
+ isUrlLink,
314
+ isYoutube,
315
+ isVimeo,
316
+ forceWebview,
317
+ playbackLoaded,
318
+ playbackBuffering,
319
+ } = this.state;
320
+ if (!readyToRender) return null;
321
+
322
+ if (isLandscape) {
196
323
  width = SCREEN_HEIGHT;
197
324
  height = SCREEN_WIDTH;
198
325
  } else {
199
326
  width = SCREEN_WIDTH;
200
- height = SCREEN_WIDTH * this.state.heightFactor;
327
+ height = heightFactor ? SCREEN_WIDTH * heightFactor : SCREEN_HEIGHT;
201
328
  }
202
329
 
203
- const { isUrlLink, isYoutube, forceWebview, playbackLoaded, playbackBuffering } = this.state;
204
- const { playbackId, streamUrl } = streamInfo;
205
- let embedUrl, player;
206
- if (isYoutube && !forceWebview) {
207
- const youtubeId = this.getYoutubeVideoId(streamUrl);
208
- embedUrl = youtubeId ? `https://www.youtube.com/embed/${youtubeId}` : streamUrl;
209
- player = this.getYoutubePlayer(youtubeId, width, height);
210
- } else if (isUrlLink) {
211
- embedUrl = streamUrl;
330
+ let embedUrl, player, showLoading;
331
+ if (!forceWebview) {
332
+ if (isYoutube) {
333
+ const youtubeId = this.getYoutubeVideoId(source);
334
+ embedUrl = youtubeId ? `https://www.youtube.com/embed/${youtubeId}` : source;
335
+ player = this.getYoutubePlayer(youtubeId, width, height, autoPlay);
336
+ } else if (isVimeo) {
337
+ const vimeoId = this.getVimeoId(source);
338
+ embedUrl = source;
339
+ player = this.getVimeoPlayer(vimeoId, width, height, autoPlay);
340
+ } else if (playbackId) {
341
+ embedUrl = this.getStreamPlaybackUrl();
342
+ player = this.getStreamPlayer(embedUrl, width, height, autoPlay);
343
+ showLoading = true;
344
+ } else if (useVideoPlayer) {
345
+ embedUrl = source;
346
+ player = this.getVideoPlayer(embedUrl, width, height, autoPlay);
347
+ }
348
+ }
349
+ if (!player && isUrlLink) {
350
+ embedUrl = source;
212
351
  player = this.getWebviewPlayer(embedUrl);
213
- } else if (playbackId) {
214
- embedUrl = this.getStreamPlaybackUrl();
215
- player = this.getVideoPlayer(embedUrl, width, height);
216
352
  }
217
353
 
218
- // console.log('Streaming:', embedUrl, 'landscape', landscape, 'playbackLoaded', playbackLoaded, 'playbackBuffering', playbackBuffering);
354
+ // console.log({
355
+ // Streaming: embedUrl,
356
+ // Landscape: isLandscape,
357
+ // Loaded: playbackLoaded,
358
+ // Buffering: playbackBuffering,
359
+ // LoadingIndicator: showLoading,
360
+ // Width: width,
361
+ // Height: height,
362
+ // HeightFactor: heightFactor,
363
+ // });
219
364
  return (
220
- <View style={[styles.container, !isUrlLink && { alignItems: 'center' }, { width, height }]}>
365
+ <View style={styles.container}>
221
366
  {player}
222
- {(!playbackLoaded || playbackBuffering) && (
367
+ {showLoading && (!playbackLoaded || playbackBuffering) && (
223
368
  <View style={styles.loadingContainer}>
224
369
  <Spinner color={'#fff'} />
225
370
  </View>
@@ -232,6 +377,8 @@ class MediaPlayer extends Component {
232
377
  const styles = StyleSheet.create({
233
378
  container: {
234
379
  backgroundColor: '#000',
380
+ alignItems: 'center',
381
+ justifyContent: 'center',
235
382
  },
236
383
  webView: {
237
384
  flex: 1,
@@ -713,9 +713,7 @@ class PlussChat extends Component {
713
713
  const { showFullscreenVideo, currentVideoUrl } = this.state;
714
714
  if (!currentVideoUrl) return;
715
715
 
716
- return (
717
- <VideoPopup uri={currentVideoUrl} visible={showFullscreenVideo} showFullscreenButton={false} onClose={this.toggleFullscreenVideo} />
718
- );
716
+ return <VideoPopup uri={currentVideoUrl} visible={showFullscreenVideo} onClose={this.toggleFullscreenVideo} />;
719
717
  }
720
718
 
721
719
  renderPDF() {
@@ -1,12 +1,9 @@
1
1
  import React, { Component } from 'react';
2
- import { Dimensions, Modal, TouchableOpacity, StyleSheet } from 'react-native';
3
- import { Video } from 'expo-av';
4
- import VideoPlayer from 'expo-video-player';
2
+ import { Platform, View, Modal, TouchableOpacity, StyleSheet } from 'react-native';
3
+ import * as ScreenOrientation from 'expo-screen-orientation';
5
4
  import { StatusBarHeight, getCompressed, imageExists } from '../helper';
6
5
  import { Pl60Icon } from '../fonts';
7
- import { SharingTools } from './SharingTools';
8
-
9
- const SCREEN_HEIGHT = Dimensions.get('window').height;
6
+ import { SharingTools, MediaPlayer } from './';
10
7
 
11
8
  class VideoPopup extends Component {
12
9
  constructor(props) {
@@ -14,18 +11,26 @@ class VideoPopup extends Component {
14
11
 
15
12
  this.state = {
16
13
  uri: '',
14
+ isFullScreen: false,
17
15
  };
18
16
  }
19
17
 
20
- UNSAFE_componentWillMount() {
18
+ UNSAFE_componentWillMount = async () => {
21
19
  this.checkCompressed(this.props.uri);
22
- }
20
+ await ScreenOrientation.lockAsync(
21
+ Platform.OS === 'ios' ? ScreenOrientation.OrientationLock.DEFAULT : ScreenOrientation.OrientationLock.ALL,
22
+ );
23
+ };
23
24
 
24
25
  UNSAFE_componentWillReceiveProps(nextProps) {
25
26
  if (this.props.uri === nextProps.uri) return;
26
27
  this.checkCompressed(nextProps.uri);
27
28
  }
28
29
 
30
+ componentWillUnmount = async () => {
31
+ await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
32
+ };
33
+
29
34
  checkCompressed = url => {
30
35
  const compressedUri = getCompressed(url);
31
36
  imageExists(compressedUri).then(compressedExists => {
@@ -33,11 +38,6 @@ class VideoPopup extends Component {
33
38
  });
34
39
  };
35
40
 
36
- playbackCallback = status => {
37
- const { playbackCallback } = this.props;
38
- if (playbackCallback) playbackCallback(status);
39
- };
40
-
41
41
  renderClose() {
42
42
  return (
43
43
  <TouchableOpacity style={styles.menuIconContainer} onPress={this.props.onClose} activeOpacity={0.6}>
@@ -47,35 +47,36 @@ class VideoPopup extends Component {
47
47
  }
48
48
 
49
49
  renderPlayer() {
50
- const { shouldPlay, showFullscreenButton, switchToPortrait, switchToLandscape } = this.props;
51
50
  const { uri } = this.state;
52
51
  if (!uri) return null;
53
- console.log(`playing video\ncurrent: ${uri}\noriginal: ${this.props.uri}`);
54
52
 
55
53
  return (
56
- <VideoPlayer
57
- videoProps={{
58
- shouldPlay: shouldPlay || true,
59
- resizeMode: Video.RESIZE_MODE_CONTAIN,
60
- source: { uri },
61
- }}
62
- showFullscreenButton={showFullscreenButton}
63
- switchToPortrait={switchToPortrait}
64
- switchToLandscape={switchToLandscape}
65
- playbackCallback={this.playbackCallback}
66
- height={SCREEN_HEIGHT}
67
- />
54
+ <View style={styles.playerContainer}>
55
+ <MediaPlayer
56
+ source={uri}
57
+ useVideoPlayer={true}
58
+ autoPlay={true}
59
+ orientationChanged={isFullScreen => this.setState({ isFullScreen })}
60
+ />
61
+ </View>
68
62
  );
69
63
  }
70
64
 
71
65
  render() {
72
- const { visible, onClose, style } = this.props;
66
+ const { isFullScreen } = this.state;
67
+ const { visible, onClose, animationType, style } = this.props;
73
68
 
74
69
  return (
75
- <Modal visible={visible} animationType="fade" onRequestClose={onClose} style={[styles.container, style]}>
70
+ <Modal
71
+ visible={visible}
72
+ animationType={animationType || 'fade'}
73
+ onRequestClose={onClose}
74
+ style={[styles.container, style]}
75
+ supportedOrientations={['portrait', 'landscape']}
76
+ >
76
77
  {this.renderPlayer()}
77
- {this.renderClose()}
78
- <SharingTools uri={this.props.uri} />
78
+ {!isFullScreen && this.renderClose()}
79
+ {!isFullScreen && <SharingTools uri={this.props.uri} />}
79
80
  </Modal>
80
81
  );
81
82
  }
@@ -101,6 +102,12 @@ const styles = StyleSheet.create({
101
102
  color: '#fff',
102
103
  zIndex: 3,
103
104
  },
105
+ playerContainer: {
106
+ flex: 1,
107
+ backgroundColor: '#000',
108
+ alignItems: 'center',
109
+ justifyContent: 'center',
110
+ },
104
111
  });
105
112
 
106
113
  export { VideoPopup };