@@ -18,21 +18,25 @@ import Icon from 'react-native-vector-icons/Ionicons'
1818import Video from 'react-native-video'
1919import { Touchable } from '../components/touchable'
2020import { TabBarIcon } from '../components/tabbar-icon'
21+ import { promiseTimeout } from '../../lib/promise-timeout'
2122
2223const kstoStream = 'https://cdn.stobcm.com/radio/ksto1.stream/master.m3u8'
24+ const kstoStatus = 'https://cdn.stobcm.com/radio/ksto1.stream/chunklist.3mu8'
2325const image = require ( '../../../images/streaming/ksto/ksto-logo.png' )
2426
2527type Viewport = {
2628 width : number ,
2729 height : number ,
2830}
2931
32+ type PlayState = 'paused' | 'playing' | 'checking'
33+
3034type Props = { }
3135
3236type State = {
33- refreshing : boolean ,
34- paused : boolean ,
37+ playState : PlayState ,
3538 streamError : ?Object ,
39+ uplinkError : ?string ,
3640 viewport : Viewport ,
3741}
3842
@@ -43,9 +47,9 @@ export default class KSTOView extends React.PureComponent<void, Props, State> {
4347 }
4448
4549 state = {
46- refreshing : false ,
47- paused : true ,
50+ playState : 'paused' ,
4851 streamError : null ,
52+ uplinkError : null ,
4953 viewport : Dimensions . get ( 'window' ) ,
5054 }
5155
@@ -61,14 +65,67 @@ export default class KSTOView extends React.PureComponent<void, Props, State> {
6165 this . setState ( ( ) => ( { viewport : event . window } ) )
6266 }
6367
64- changeControl = ( ) => {
65- this . setState ( state => ( { paused : ! state . paused } ) )
68+ // check the stream uplink status
69+ isUplinkUp = async ( ) => {
70+ try {
71+ await promiseTimeout ( 6000 , fetch ( kstoStatus ) )
72+ return true
73+ } catch ( err ) {
74+ return false
75+ }
76+ }
77+
78+ onPlay = async ( ) => {
79+ this . setState ( ( ) => ( { playState : 'checking' } ) )
80+
81+ const uplinkStatus = await this . isUplinkUp ( )
82+
83+ if ( uplinkStatus ) {
84+ this . setState ( ( ) => ( { playState : 'playing' } ) )
85+ } else {
86+ this . setState ( ( ) => ( {
87+ playState : 'paused' ,
88+ uplinkError : 'The KSTO stream is down. Sorry!' ,
89+ } ) )
90+ }
91+ }
92+
93+ onPause = ( ) => {
94+ this . setState ( ( ) => ( {
95+ playState : 'paused' ,
96+ uplinkError : null ,
97+ } ) )
6698 }
6799
68100 // error from react-native-video
69101 onError = ( e : any ) => {
70- this . setState ( ( ) => ( { streamError : e , paused : true } ) )
71- console . log ( e )
102+ this . setState ( ( ) => ( { streamError : e , playState : 'paused' } ) )
103+ }
104+
105+ renderButton = ( state : PlayState ) = > {
106+ switch ( state ) {
107+ case 'paused' :
108+ return (
109+ < ActionButton icon = "ios-play" text = "Listen" onPress = { this . onPlay } />
110+ )
111+
112+ case 'checking' :
113+ return (
114+ < ActionButton
115+ icon = "ios-more"
116+ text = "Starting"
117+ onPress = { this . onPause }
118+ />
119+ )
120+
121+ case 'playing' :
122+ return (
123+ < ActionButton icon = "ios-pause" text = "Pause" onPress = { this . onPause } />
124+ )
125+
126+ default:
127+ return < ActionButton icon = "ios-bug" text = "Error" onPress = { ( ) => { } } />
128+ }
72129 }
73130
74131 render ( ) {
@@ -84,6 +141,12 @@ export default class KSTOView extends React.PureComponent<void, Props, State> {
84141 height : logoWidth ,
85142 }
86143
144+ const error = this . state . uplinkError
145+ ? < Text style = { styles . status } > { this . state . uplinkError } </ Text >
146+ : null
147+
148+ const button = this . renderButton ( this . state . playState )
149+
87150 return (
88151 < ScrollView
89152 contentContainerStyle = { [ styles . root , sideways && landscape . root ] }
@@ -99,17 +162,15 @@ export default class KSTOView extends React.PureComponent<void, Props, State> {
99162 < View style = { styles . container } >
100163 < Title />
101164
102- < PlayPauseButton
103- onPress = { this . changeControl }
104- paused = { this . state . paused }
105- />
165+ { error }
166+ { button }
106167
107- { ! this . state . paused
168+ { this . state . playState === 'playing'
108169 ? < Video
109170 source = { { uri : kstoStream } }
110171 playInBackground = { true }
111172 playWhenInactive = { true }
112- paused = { this . state . paused }
173+ paused = { this . state . playState !== 'playing' }
113174 onError = { this . onError }
114175 />
115176 : null }
@@ -119,46 +180,30 @@ export default class KSTOView extends React.PureComponent<void, Props, State> {
119180 }
120181}
121182
122- const Title = ( ) = > {
123- return (
124- < View style = { styles . titleWrapper } >
125- < Text selectable = { true } style = { styles . heading } >
126- St. Olaf College Radio
127- </ Text >
128- < Text selectable = { true } style = { styles . subHeading } >
129- KSTO 93.1 FM
130- </ Text >
131- </ View >
132- )
133- }
183+ const Title = ( ) = >
184+ < View style = { styles . titleWrapper } >
185+ < Text selectable = { true } style = { styles . heading } >
186+ St. Olaf College Radio
187+ </ Text >
188+ < Text selectable = { true } style = { styles . subHeading } >
189+ KSTO 93.1 FM
190+ </ Text >
191+ </ View >
134192
135- class PlayPauseButton extends React . PureComponent {
136- props : {
137- paused : boolean ,
138- onPress : ( ) => any ,
139- }
140-
141- render ( ) {
142- const { paused, onPress} = this . props
143- return (
144- < Touchable
145- style = { buttonStyles . button }
146- hightlight = { false }
147- onPress = { onPress }
148- >
149- < View style = { buttonStyles . buttonWrapper } >
150- < Icon
151- style = { buttonStyles . icon }
152- name = { paused ? 'ios-play' : 'ios-pause' }
153- />
154- < Text style = { buttonStyles . action } >
155- { paused ? 'Listen' : 'Pause' }
156- </ Text >
157- </ View >
158- </ Touchable >
159- )
160- }
193+ type ActionButtonProps = {
194+ icon : string ,
195+ text : string ,
196+ onPress : ( ) => any ,
161197}
198+ const ActionButton = ( { icon, text, onPress} : ActionButtonProps ) = >
199+ < Touchable style = { buttonStyles . button } hightlight = { false } onPress = { onPress } >
200+ < View style = { buttonStyles . buttonWrapper } >
201+ < Icon style = { buttonStyles . icon } name = { icon } />
202+ < Text style = { buttonStyles . action } >
203+ { text }
204+ </ Text >
205+ </ View >
206+ </ Touchable >
162207
163208const styles = StyleSheet . create ( {
164209 root : {
@@ -200,6 +245,12 @@ const styles = StyleSheet.create({
200245 fontSize : 28 ,
201246 textAlign : 'center' ,
202247 } ,
248+ status : {
249+ paddingTop : 10 ,
250+ fontWeight : '400' ,
251+ fontSize : 24 ,
252+ color : c . grapefruit ,
253+ } ,
203254} )
204255
205256const landscape = StyleSheet . create ( {
0 commit comments