@plusscommunities/pluss-core-app 1.1.2 → 1.2.0
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 +4 -3
- package/src/components/Header.js +5 -1
- package/src/components/ImagePopup.js +1 -1
- package/src/components/LoadingIndicator.js +35 -105
- package/src/components/MediaPlayer.js +250 -0
- package/src/components/PlussChat.js +50 -1
- package/src/components/index.js +1 -0
- package/src/helper.js +51 -0
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plusscommunities/pluss-core-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Core extension package for Pluss Communities platform",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"patch": "npm version patch",
|
|
8
|
+
"upload": "npm publish --access public && rm -rf node_modules",
|
|
9
|
+
"upload:p": "npm run patch && npm run upload"
|
|
9
10
|
},
|
|
10
11
|
"author": "Phillip Suh",
|
|
11
12
|
"license": "ISC",
|
package/src/components/Header.js
CHANGED
|
@@ -276,6 +276,7 @@ class Header extends Component {
|
|
|
276
276
|
<Animated.View
|
|
277
277
|
style={[
|
|
278
278
|
styles.container,
|
|
279
|
+
!this.props.noShadow && styles.containerShadow,
|
|
279
280
|
this.props.transparent && styles.transparentContainer,
|
|
280
281
|
this.props.style,
|
|
281
282
|
{ backgroundColor: this.props.headerColourOffset, elevation: this.props.headerElev, shadowColor: this.props.headerShadow },
|
|
@@ -297,6 +298,7 @@ class Header extends Component {
|
|
|
297
298
|
<View
|
|
298
299
|
style={[
|
|
299
300
|
styles.container,
|
|
301
|
+
!this.props.noShadow && styles.containerShadow,
|
|
300
302
|
this.props.transparent && styles.transparentContainer,
|
|
301
303
|
this.props.lineSeparated && styles.lineSeparated,
|
|
302
304
|
this.props.popupHeader && styles.popupHeader,
|
|
@@ -331,6 +333,9 @@ const styles = {
|
|
|
331
333
|
container: {
|
|
332
334
|
alignSelf: 'stretch',
|
|
333
335
|
backgroundColor: '#fff',
|
|
336
|
+
zIndex: 10,
|
|
337
|
+
},
|
|
338
|
+
containerShadow: {
|
|
334
339
|
shadowColor: '#000',
|
|
335
340
|
shadowOffset: {
|
|
336
341
|
width: 0,
|
|
@@ -339,7 +344,6 @@ const styles = {
|
|
|
339
344
|
shadowOpacity: 0.1,
|
|
340
345
|
shadowRadius: 6,
|
|
341
346
|
elevation: 3,
|
|
342
|
-
zIndex: 10,
|
|
343
347
|
},
|
|
344
348
|
lineSeparated: {
|
|
345
349
|
borderBottomWidth: 1,
|
|
@@ -45,7 +45,7 @@ class ImagePopup extends Component {
|
|
|
45
45
|
return { url: get1400(image?.uri), original: image?.uri, isVideo: isVideo(image?.uri), date: image?.date, user: image?.user };
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
this.setState({ images, index: this.props.index || 0 });
|
|
48
|
+
this.setState({ images, index: this.state.index || this.props.index || 0 });
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
onChange = index => {
|
|
@@ -1,27 +1,17 @@
|
|
|
1
1
|
import React, { Component } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { StyleSheet, Animated, Dimensions } from 'react-native';
|
|
3
|
+
import { connect } from 'react-redux';
|
|
4
|
+
import { getMainBrandingColourFromState } from '../colours';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const OPACITY_END2 = 0.7;
|
|
8
|
-
const OPACITY_END3 = 1;
|
|
9
|
-
const LEFT_START = 1;
|
|
10
|
-
const LEFT_END1 = 16;
|
|
11
|
-
const LEFT_END2 = 32;
|
|
12
|
-
const CIRCLE_SIZE = 8;
|
|
13
|
-
const DEFAULT_HEIGHT = 38;
|
|
6
|
+
const DEFAULT_HEIGHT = 4;
|
|
7
|
+
const DEFAULT_WIDTH = Dimensions.get('window').width;
|
|
14
8
|
|
|
15
9
|
class LoadingIndicator extends Component {
|
|
16
10
|
constructor(props) {
|
|
17
11
|
super(props);
|
|
18
12
|
this.state = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
animatedOpacity3: new Animated.Value(OPACITY_START),
|
|
22
|
-
animatedOpacity4: new Animated.Value(0),
|
|
23
|
-
animatedLeft4: new Animated.Value(LEFT_START),
|
|
24
|
-
animatedHeight: new Animated.Value(this.props.visible ? DEFAULT_HEIGHT : 0),
|
|
13
|
+
animatedLeft: new Animated.Value(-DEFAULT_WIDTH),
|
|
14
|
+
animatedOpacity: new Animated.Value(0),
|
|
25
15
|
};
|
|
26
16
|
this.height = 0;
|
|
27
17
|
}
|
|
@@ -36,8 +26,8 @@ class LoadingIndicator extends Component {
|
|
|
36
26
|
|
|
37
27
|
componentDidUpdate(prevProps) {
|
|
38
28
|
if (prevProps.visible !== this.props.visible) {
|
|
39
|
-
Animated.timing(this.state.
|
|
40
|
-
toValue: this.props.visible ?
|
|
29
|
+
Animated.timing(this.state.animatedOpacity, {
|
|
30
|
+
toValue: this.props.visible ? 0.8 : 0,
|
|
41
31
|
duration: 300,
|
|
42
32
|
useNativeDriver: false,
|
|
43
33
|
}).start();
|
|
@@ -46,95 +36,32 @@ class LoadingIndicator extends Component {
|
|
|
46
36
|
|
|
47
37
|
animate = () => {
|
|
48
38
|
Animated.sequence([
|
|
49
|
-
Animated.timing(this.state.
|
|
50
|
-
toValue:
|
|
51
|
-
duration:
|
|
39
|
+
Animated.timing(this.state.animatedLeft, {
|
|
40
|
+
toValue: DEFAULT_WIDTH,
|
|
41
|
+
duration: 1500,
|
|
52
42
|
useNativeDriver: false,
|
|
53
43
|
}),
|
|
54
|
-
Animated.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
useNativeDriver: false,
|
|
59
|
-
}),
|
|
60
|
-
Animated.timing(this.state.animatedLeft4, {
|
|
61
|
-
toValue: LEFT_END1,
|
|
62
|
-
duration: 250,
|
|
63
|
-
useNativeDriver: false,
|
|
64
|
-
}),
|
|
65
|
-
]),
|
|
66
|
-
Animated.timing(this.state.animatedOpacity4, {
|
|
67
|
-
toValue: OPACITY_END2,
|
|
68
|
-
duration: 250,
|
|
69
|
-
useNativeDriver: false,
|
|
70
|
-
}),
|
|
71
|
-
Animated.parallel([
|
|
72
|
-
Animated.timing(this.state.animatedOpacity2, {
|
|
73
|
-
toValue: OPACITY_END2,
|
|
74
|
-
duration: 250,
|
|
75
|
-
useNativeDriver: false,
|
|
76
|
-
}),
|
|
77
|
-
Animated.timing(this.state.animatedLeft4, {
|
|
78
|
-
toValue: LEFT_END2,
|
|
79
|
-
duration: 250,
|
|
80
|
-
useNativeDriver: false,
|
|
81
|
-
}),
|
|
82
|
-
]),
|
|
83
|
-
Animated.parallel([
|
|
84
|
-
Animated.timing(this.state.animatedOpacity4, {
|
|
85
|
-
toValue: OPACITY_END3,
|
|
86
|
-
duration: 250,
|
|
87
|
-
useNativeDriver: false,
|
|
88
|
-
}),
|
|
89
|
-
Animated.timing(this.state.animatedOpacity3, {
|
|
90
|
-
toValue: OPACITY_END3,
|
|
91
|
-
duration: 250,
|
|
92
|
-
useNativeDriver: false,
|
|
93
|
-
}),
|
|
94
|
-
]),
|
|
95
|
-
Animated.parallel([
|
|
96
|
-
Animated.timing(this.state.animatedLeft4, {
|
|
97
|
-
toValue: LEFT_START,
|
|
98
|
-
duration: 1,
|
|
99
|
-
useNativeDriver: false,
|
|
100
|
-
}),
|
|
101
|
-
Animated.timing(this.state.animatedOpacity4, {
|
|
102
|
-
toValue: 0,
|
|
103
|
-
duration: 1,
|
|
104
|
-
useNativeDriver: false,
|
|
105
|
-
}),
|
|
106
|
-
]),
|
|
107
|
-
Animated.timing(this.state.animatedOpacity3, {
|
|
108
|
-
toValue: OPACITY_START,
|
|
109
|
-
duration: 250,
|
|
110
|
-
useNativeDriver: false,
|
|
111
|
-
}),
|
|
112
|
-
Animated.timing(this.state.animatedOpacity2, {
|
|
113
|
-
toValue: OPACITY_START,
|
|
114
|
-
duration: 250,
|
|
115
|
-
useNativeDriver: false,
|
|
116
|
-
}),
|
|
117
|
-
Animated.timing(this.state.animatedOpacity1, {
|
|
118
|
-
toValue: OPACITY_START,
|
|
119
|
-
duration: 250,
|
|
44
|
+
Animated.delay(500),
|
|
45
|
+
Animated.timing(this.state.animatedLeft, {
|
|
46
|
+
toValue: -DEFAULT_WIDTH,
|
|
47
|
+
duration: 1500,
|
|
120
48
|
useNativeDriver: false,
|
|
121
49
|
}),
|
|
50
|
+
Animated.delay(500),
|
|
122
51
|
]).start(() => {
|
|
123
52
|
this.animate();
|
|
124
53
|
});
|
|
125
54
|
};
|
|
126
55
|
|
|
127
56
|
render() {
|
|
128
|
-
const { animatedOpacity1, animatedOpacity2, animatedOpacity3, animatedOpacity4, animatedLeft4, animatedHeight } = this.state;
|
|
129
|
-
|
|
130
57
|
return (
|
|
131
|
-
<Animated.View
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
58
|
+
<Animated.View
|
|
59
|
+
style={[styles.container, this.props.style, { height: DEFAULT_HEIGHT, opacity: this.state.animatedOpacity }]}
|
|
60
|
+
onLayout={this.onLayout}
|
|
61
|
+
>
|
|
62
|
+
<Animated.View
|
|
63
|
+
style={[styles.line, { backgroundColor: this.props.colourBrandingMain, left: this.state.animatedLeft, height: DEFAULT_HEIGHT }]}
|
|
64
|
+
/>
|
|
138
65
|
</Animated.View>
|
|
139
66
|
);
|
|
140
67
|
}
|
|
@@ -145,20 +72,23 @@ const styles = StyleSheet.create({
|
|
|
145
72
|
alignItems: 'center',
|
|
146
73
|
justifyContent: 'center',
|
|
147
74
|
overflow: 'hidden',
|
|
148
|
-
backgroundColor: '#
|
|
75
|
+
backgroundColor: '#fff',
|
|
149
76
|
width: '100%',
|
|
150
77
|
height: 38,
|
|
151
78
|
},
|
|
152
79
|
innerContainer: {
|
|
153
80
|
flexDirection: 'row',
|
|
154
81
|
},
|
|
155
|
-
|
|
156
|
-
width:
|
|
157
|
-
height: CIRCLE_SIZE,
|
|
158
|
-
borderRadius: CIRCLE_SIZE / 2,
|
|
159
|
-
backgroundColor: TEXT_BLUEGREY,
|
|
160
|
-
marginHorizontal: 4,
|
|
82
|
+
line: {
|
|
83
|
+
width: DEFAULT_WIDTH,
|
|
161
84
|
},
|
|
162
85
|
});
|
|
163
86
|
|
|
164
|
-
|
|
87
|
+
const mapStateToProps = state => {
|
|
88
|
+
return {
|
|
89
|
+
colourBrandingMain: getMainBrandingColourFromState(state),
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default connect(mapStateToProps, {})(LoadingIndicator);
|
|
94
|
+
// export { loadingIndicator as Default };
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import { View, StyleSheet, Dimensions } from 'react-native';
|
|
3
|
+
import YoutubePlayer, { getYoutubeMeta } from 'react-native-youtube-iframe';
|
|
4
|
+
// import { Vimeo } from 'react-native-vimeo-iframe';
|
|
5
|
+
import WebView from 'react-native-webview';
|
|
6
|
+
import { Video } from 'expo-av';
|
|
7
|
+
import { Spinner } from '../../../../src/components/common';
|
|
8
|
+
|
|
9
|
+
const SCREEN_HEIGHT = Dimensions.get('window').height;
|
|
10
|
+
const SCREEN_WIDTH = Dimensions.get('window').width;
|
|
11
|
+
const EXPO_VIDEO_PROPS = { rate: 1, isMuted: false, volume: 1, shouldPlay: true };
|
|
12
|
+
const RETRY_RECOVER = 30000;
|
|
13
|
+
const RETRY_LOADING = 10000;
|
|
14
|
+
|
|
15
|
+
class MediaPlayer extends Component {
|
|
16
|
+
constructor(props) {
|
|
17
|
+
super(props);
|
|
18
|
+
this.state = {
|
|
19
|
+
isUrlLink: true,
|
|
20
|
+
isYoutube: false,
|
|
21
|
+
forceWebview: false,
|
|
22
|
+
heightFactor: 0.565,
|
|
23
|
+
playbackLoaded: false,
|
|
24
|
+
playbackBuffering: false,
|
|
25
|
+
playbackPlaying: false,
|
|
26
|
+
};
|
|
27
|
+
this.youtubePlayer = null;
|
|
28
|
+
this.videoPlayer = null;
|
|
29
|
+
this.checkStreamLoaded = null;
|
|
30
|
+
this.retryLoading = false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
componentDidMount() {
|
|
34
|
+
this.setupForLiveStreaming();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
componentWillUnmount() {
|
|
38
|
+
if (this.checkStreamLoaded) clearInterval(this.checkStreamLoaded);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getYoutubeVideoId = url => {
|
|
42
|
+
url = url.replace(/(>|<)/gi, '').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
|
|
43
|
+
if (url[2] !== undefined) {
|
|
44
|
+
const ids = url[2].split(/[^0-9a-z_\-]/i);
|
|
45
|
+
return ids[0];
|
|
46
|
+
} else {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
setupForLiveStreaming = async () => {
|
|
52
|
+
// 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 });
|
|
57
|
+
|
|
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
|
+
// }
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
getStreamPlaybackUrl = () => {
|
|
66
|
+
// return 'https://stream.mux.com/nflNhyFQMlrSt00II01Hjh2BLSW009PH1tl023pdpJ01s1vc.m3u8';
|
|
67
|
+
return `https://stream.mux.com/${this.props.streamInfo.playbackId}.m3u8`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
checkReloadStream = (wait = RETRY_LOADING, force = false) => {
|
|
71
|
+
if (!force && this.retryLoading) return;
|
|
72
|
+
|
|
73
|
+
console.log(`Check loading in ${wait} milliseconds...`);
|
|
74
|
+
this.retryLoading = true;
|
|
75
|
+
setTimeout(async () => {
|
|
76
|
+
const { playbackLoaded, playbackBuffering } = this.state;
|
|
77
|
+
try {
|
|
78
|
+
if (playbackLoaded === false || playbackBuffering === true) {
|
|
79
|
+
console.log('Reloading started');
|
|
80
|
+
await this.videoPlayer.loadAsync(
|
|
81
|
+
{ uri: this.getStreamPlaybackUrl(), overrideFileExtensionAndroid: 'm3u8' },
|
|
82
|
+
EXPO_VIDEO_PROPS,
|
|
83
|
+
false,
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
console.log('Already loaded - not reloading');
|
|
87
|
+
}
|
|
88
|
+
this.retryLoading = false;
|
|
89
|
+
} catch {}
|
|
90
|
+
}, wait);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
onStreamLoadStart = () => {
|
|
94
|
+
console.log('Stream load started');
|
|
95
|
+
this.checkStreamLoaded = setInterval(() => {
|
|
96
|
+
const { playbackLoaded } = this.state;
|
|
97
|
+
if (playbackLoaded === false) {
|
|
98
|
+
console.log('Stream loading failed unexpectedly');
|
|
99
|
+
this.checkReloadStream(1000, true);
|
|
100
|
+
} else {
|
|
101
|
+
clearInterval(this.checkStreamLoaded);
|
|
102
|
+
}
|
|
103
|
+
}, RETRY_RECOVER);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
onStreamLoaded = status => {
|
|
107
|
+
console.log(`Stream loaded - isLoaded:${status.isLoaded}, isBuffering:${status.isBuffering}, isPlaying:${status.isPlaying}`);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
onStreamError = error => {
|
|
111
|
+
console.log('Stream error', error);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
onStreamStatusUpdate = status => {
|
|
115
|
+
this.setState(
|
|
116
|
+
{
|
|
117
|
+
playbackLoaded: status.isLoaded,
|
|
118
|
+
playbackBuffering: status.isBuffering,
|
|
119
|
+
playbackPlaying: status.isPlaying,
|
|
120
|
+
},
|
|
121
|
+
() => {
|
|
122
|
+
const { playbackLoaded, playbackBuffering, playbackPlaying, shouldPlay } = this.state;
|
|
123
|
+
// console.log(`Status updated - isLoaded:${playbackLoaded}, isBuffering:${playbackBuffering}, isPlaying:${playbackPlaying}`);
|
|
124
|
+
if (playbackLoaded === false && playbackBuffering === undefined && playbackPlaying === undefined) {
|
|
125
|
+
this.checkReloadStream();
|
|
126
|
+
} else if (playbackLoaded === true && playbackBuffering === true) {
|
|
127
|
+
this.checkReloadStream(RETRY_RECOVER);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
onYoutubeError = e => {
|
|
134
|
+
// console.log('onYoutubeError', e);
|
|
135
|
+
this.setState({ forceWebview: true });
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
onYoutubeReady = () => {
|
|
139
|
+
// console.log('onYoutubeReady');
|
|
140
|
+
this.setState({ playbackLoaded: true });
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
getYoutubePlayer = (youtubeId, width, height) => (
|
|
144
|
+
<YoutubePlayer
|
|
145
|
+
ref={ref => (this.youtubePlayer = ref)}
|
|
146
|
+
height={height}
|
|
147
|
+
width={width}
|
|
148
|
+
videoId={youtubeId}
|
|
149
|
+
play
|
|
150
|
+
// onChangeState={event => console.log('onChangeState', event)}
|
|
151
|
+
onReady={this.onYoutubeReady}
|
|
152
|
+
onError={this.onYoutubeError}
|
|
153
|
+
// onPlaybackQualityChange={q => console.log('onPlaybackQualityChange', q)}
|
|
154
|
+
volume={50}
|
|
155
|
+
playbackRate={1}
|
|
156
|
+
playerParams={{
|
|
157
|
+
cc_lang_pref: 'us',
|
|
158
|
+
showClosedCaptions: true,
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
getWebviewPlayer = embedUrl => (
|
|
164
|
+
<WebView
|
|
165
|
+
startInLoadingState
|
|
166
|
+
javaScriptEnabled
|
|
167
|
+
scrollEnabled={false}
|
|
168
|
+
automaticallyAdjustContentInsets={false}
|
|
169
|
+
mediaPlaybackRequiresUserAction
|
|
170
|
+
style={styles.webView}
|
|
171
|
+
source={{ uri: embedUrl }}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
getVideoPlayer = (embedUrl, width, height) => (
|
|
176
|
+
<Video
|
|
177
|
+
ref={vp => {
|
|
178
|
+
this.videoPlayer = vp;
|
|
179
|
+
}}
|
|
180
|
+
source={{ uri: embedUrl }}
|
|
181
|
+
{...EXPO_VIDEO_PROPS}
|
|
182
|
+
resizeMode={Video.RESIZE_MODE_CONTAIN}
|
|
183
|
+
useNativeControls
|
|
184
|
+
style={{ width, height }}
|
|
185
|
+
onLoadStart={this.onStreamLoadStart}
|
|
186
|
+
onLoad={this.onStreamLoaded}
|
|
187
|
+
onError={this.onStreamError}
|
|
188
|
+
onPlaybackStatusUpdate={this.onStreamStatusUpdate}
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
render() {
|
|
193
|
+
let width, height;
|
|
194
|
+
const { landscape, streamInfo } = this.props;
|
|
195
|
+
if (landscape) {
|
|
196
|
+
width = SCREEN_HEIGHT;
|
|
197
|
+
height = SCREEN_WIDTH;
|
|
198
|
+
} else {
|
|
199
|
+
width = SCREEN_WIDTH;
|
|
200
|
+
height = SCREEN_WIDTH * this.state.heightFactor;
|
|
201
|
+
}
|
|
202
|
+
|
|
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;
|
|
212
|
+
player = this.getWebviewPlayer(embedUrl);
|
|
213
|
+
} else if (playbackId) {
|
|
214
|
+
embedUrl = this.getStreamPlaybackUrl();
|
|
215
|
+
player = this.getVideoPlayer(embedUrl, width, height);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// console.log('Streaming:', embedUrl, 'landscape', landscape, 'playbackLoaded', playbackLoaded, 'playbackBuffering', playbackBuffering);
|
|
219
|
+
return (
|
|
220
|
+
<View style={[styles.container, !isUrlLink && { alignItems: 'center' }, { width, height }]}>
|
|
221
|
+
{player}
|
|
222
|
+
{(!playbackLoaded || playbackBuffering) && (
|
|
223
|
+
<View style={styles.loadingContainer}>
|
|
224
|
+
<Spinner color={'#fff'} />
|
|
225
|
+
</View>
|
|
226
|
+
)}
|
|
227
|
+
</View>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const styles = StyleSheet.create({
|
|
233
|
+
container: {
|
|
234
|
+
backgroundColor: '#000',
|
|
235
|
+
},
|
|
236
|
+
webView: {
|
|
237
|
+
flex: 1,
|
|
238
|
+
},
|
|
239
|
+
loadingContainer: {
|
|
240
|
+
position: 'absolute',
|
|
241
|
+
top: 50,
|
|
242
|
+
left: 0,
|
|
243
|
+
right: 0,
|
|
244
|
+
bottom: 50,
|
|
245
|
+
justifyContent: 'center',
|
|
246
|
+
alignItems: 'center',
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
export default MediaPlayer;
|
|
@@ -9,6 +9,8 @@ import { getBottomSpace } from 'react-native-iphone-x-helper';
|
|
|
9
9
|
import { Spinner } from './Spinner';
|
|
10
10
|
import { ProfilePic } from './ProfilePic';
|
|
11
11
|
import PlussChatTime from './PlussChatTime';
|
|
12
|
+
import { PDFPopup } from './PDFPopup';
|
|
13
|
+
import { Attachment } from './Attachment';
|
|
12
14
|
import PlussChatMessage from './PlussChatMessage';
|
|
13
15
|
import {
|
|
14
16
|
TEXT_DARK,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
BG_GREY,
|
|
20
22
|
TEXT_BLUEGREY,
|
|
21
23
|
} from '../colours';
|
|
22
|
-
import { getEnabledTabsFromState, get1400, getThumb300, imageExists, isVideo, getImageSource } from '../helper';
|
|
24
|
+
import { getEnabledTabsFromState, get1400, getThumb300, imageExists, isVideo, getImageSource, getFileName } from '../helper';
|
|
23
25
|
import Config, { Services } from '../config';
|
|
24
26
|
import ImageUploader from './ImageUploader';
|
|
25
27
|
import ImageUploadProgress from './ImageUploadProgress';
|
|
@@ -143,6 +145,18 @@ class PlussChat extends Component {
|
|
|
143
145
|
}, 2000);
|
|
144
146
|
};
|
|
145
147
|
|
|
148
|
+
onOpenAttachment = a => {
|
|
149
|
+
this.setState({
|
|
150
|
+
selectedPDF: a,
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
onCloseAttachment = () => {
|
|
155
|
+
this.setState({
|
|
156
|
+
selectedPDF: null,
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
146
160
|
showUploadMenu() {
|
|
147
161
|
this.imageUploader.getWrappedInstance().showUploadMenu();
|
|
148
162
|
}
|
|
@@ -279,6 +293,11 @@ class PlussChat extends Component {
|
|
|
279
293
|
wrapperStyle.paddingBottom = 16;
|
|
280
294
|
wrapperStyle.minWidth = 142; // 110 image width + 16 padding either side
|
|
281
295
|
}
|
|
296
|
+
if (!_.isEmpty(bubbleProps.currentMessage.attachments)) {
|
|
297
|
+
wrapperStyle.paddingTop = 8;
|
|
298
|
+
wrapperStyle.paddingBottom = 16;
|
|
299
|
+
wrapperStyle.paddingHorizontal = 16;
|
|
300
|
+
}
|
|
282
301
|
return (
|
|
283
302
|
<Bubble
|
|
284
303
|
{...bubbleProps}
|
|
@@ -401,6 +420,25 @@ class PlussChat extends Component {
|
|
|
401
420
|
</View>
|
|
402
421
|
);
|
|
403
422
|
}
|
|
423
|
+
if (!_.isEmpty(currentMessage.attachments)) {
|
|
424
|
+
console.log('rendering attachments');
|
|
425
|
+
console.log(currentMessage.attachments);
|
|
426
|
+
return (
|
|
427
|
+
<View>
|
|
428
|
+
{currentMessage.attachments.map((url, i) => {
|
|
429
|
+
return (
|
|
430
|
+
<Attachment
|
|
431
|
+
onPress={() => {
|
|
432
|
+
this.onOpenAttachment(url);
|
|
433
|
+
}}
|
|
434
|
+
key={i}
|
|
435
|
+
title={getFileName(url)}
|
|
436
|
+
/>
|
|
437
|
+
);
|
|
438
|
+
})}
|
|
439
|
+
</View>
|
|
440
|
+
);
|
|
441
|
+
}
|
|
404
442
|
if (currentMessage.event) {
|
|
405
443
|
return (
|
|
406
444
|
<TouchableOpacity onPress={this.onPressEvent.bind(this, currentMessage.event)}>
|
|
@@ -680,6 +718,15 @@ class PlussChat extends Component {
|
|
|
680
718
|
);
|
|
681
719
|
}
|
|
682
720
|
|
|
721
|
+
renderPDF() {
|
|
722
|
+
if (_.isEmpty(this.state.selectedPDF)) {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
return (
|
|
726
|
+
<PDFPopup source={this.state.selectedPDF} onClose={this.onCloseAttachment} title={getFileName(this.state.selectedPDF)} pdfCount={1} />
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
|
|
683
730
|
render() {
|
|
684
731
|
if (Platform.OS === 'android' && !this.props.noAndroidAvoid) {
|
|
685
732
|
return (
|
|
@@ -688,6 +735,7 @@ class PlussChat extends Component {
|
|
|
688
735
|
{this.renderImageUploader()}
|
|
689
736
|
{this.renderImagePopup()}
|
|
690
737
|
{this.renderVideoPlayerPopup()}
|
|
738
|
+
{this.renderPDF()}
|
|
691
739
|
</KeyboardAvoidingView>
|
|
692
740
|
);
|
|
693
741
|
}
|
|
@@ -697,6 +745,7 @@ class PlussChat extends Component {
|
|
|
697
745
|
{this.renderImageUploader()}
|
|
698
746
|
{this.renderImagePopup()}
|
|
699
747
|
{this.renderVideoPlayerPopup()}
|
|
748
|
+
{this.renderPDF()}
|
|
700
749
|
</View>
|
|
701
750
|
);
|
|
702
751
|
}
|
package/src/components/index.js
CHANGED
|
@@ -47,3 +47,4 @@ export { default as UserListing } from './UserListing';
|
|
|
47
47
|
export { default as PlussChat } from './PlussChat';
|
|
48
48
|
export { default as PositionedImage } from './PositionedImage';
|
|
49
49
|
export { default as FormattedText } from './FormattedText';
|
|
50
|
+
export { default as MediaPlayer } from './MediaPlayer';
|
package/src/helper.js
CHANGED
|
@@ -74,11 +74,37 @@ const getExtension = url => {
|
|
|
74
74
|
return fileSplit[fileSplit.length - 1].toLowerCase();
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
+
const getFileName = (url, noExtension) => {
|
|
78
|
+
if (!url) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const fileSplit = url.split('/');
|
|
82
|
+
const name = fileSplit[fileSplit.length - 1].toLowerCase();
|
|
83
|
+
if (!noExtension) {
|
|
84
|
+
return name;
|
|
85
|
+
}
|
|
86
|
+
return name.split('.')[0];
|
|
87
|
+
};
|
|
88
|
+
|
|
77
89
|
const isVideo = url => {
|
|
78
90
|
const extension = getExtension(url);
|
|
79
91
|
return ['mov', 'mp4'].includes(extension);
|
|
80
92
|
};
|
|
81
93
|
|
|
94
|
+
const isVideoUrl = url => {
|
|
95
|
+
const urlToCheck = getValidUrl(url);
|
|
96
|
+
if (isVideo(urlToCheck)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (isYoutube(urlToCheck)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (isVimeo(urlToCheck)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
};
|
|
107
|
+
|
|
82
108
|
const isStockImage = url => {
|
|
83
109
|
return url.indexOf('https://pluss60-dev-uploads.s3.ap-southeast-2.amazonaws.com/uploads/library') !== -1;
|
|
84
110
|
};
|
|
@@ -288,6 +314,29 @@ const isTablet = () => {
|
|
|
288
314
|
return Dimensions.get('screen').width > 600;
|
|
289
315
|
};
|
|
290
316
|
|
|
317
|
+
const isVimeo = url => {
|
|
318
|
+
if (_.isEmpty(url)) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
if (url.indexOf('https://vimeo.com/') === 0) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
if (url.indexOf('https://player.vimeo.com/video/') === 0) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const getVimeoEmbed = url => {
|
|
330
|
+
if (_.isEmpty(url)) {
|
|
331
|
+
return '';
|
|
332
|
+
}
|
|
333
|
+
if (url.indexOf('https://vimeo.com/') === 0) {
|
|
334
|
+
const vimeoId = 'lol'; //TODO
|
|
335
|
+
return `https://player.vimeo.com/video/${vimeoId}`;
|
|
336
|
+
}
|
|
337
|
+
return url; //TODO
|
|
338
|
+
};
|
|
339
|
+
|
|
291
340
|
const isYoutube = url => {
|
|
292
341
|
if (_.isUndefined(url) || _.isEmpty(url)) {
|
|
293
342
|
return false;
|
|
@@ -349,6 +398,7 @@ export {
|
|
|
349
398
|
getSiteSettingFromState,
|
|
350
399
|
getValueOrDefault,
|
|
351
400
|
getExtension,
|
|
401
|
+
getFileName,
|
|
352
402
|
isVideo,
|
|
353
403
|
getThumb300,
|
|
354
404
|
get1400,
|
|
@@ -373,4 +423,5 @@ export {
|
|
|
373
423
|
getValidUrl,
|
|
374
424
|
getUserPreview,
|
|
375
425
|
allowComments,
|
|
426
|
+
isVideoUrl,
|
|
376
427
|
};
|