Skip to content

Commit 6a7e04d

Browse files
authored
wip
1 parent 0c6bae3 commit 6a7e04d

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed

src/app/index.js

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import React, { PureComponent, Suspense, lazy } from 'react';
2+
import { createBrowserHistory } from 'history'
3+
import Home from '../pages/Home';
4+
import percent from '../helpers/percent';
5+
import Menu from '../components/Menu';
6+
import Page from '../components/Page';
7+
import Loader from '../components/Loader';
8+
import Audio from '../helpers/audio';
9+
import { initialState } from '../data';
10+
import './style.scss';
11+
12+
const List = lazy(() => import('../pages/List'));
13+
const About = lazy(() => import('../pages/About'));
14+
const Detail = lazy(() => import('../pages/Detail'));
15+
16+
class App extends PureComponent {
17+
constructor(props) {
18+
super(props);
19+
20+
this.playlistUrl = process.env.REACT_APP_API_URL;
21+
this.state = {
22+
...initialState
23+
};
24+
25+
this.history = createBrowserHistory();
26+
}
27+
28+
componentDidMount() {
29+
this.history.listen((history) => {
30+
this.setState(() => {
31+
return { currentView: history.location.state.view || '/' };
32+
});
33+
34+
if (history.location.state.view === 'list' && !this.state.tracks[0].id) {
35+
this.fetchPlayList();
36+
};
37+
});
38+
39+
this.setupAudio();
40+
41+
this.history.push('/', { view: 'home' });
42+
}
43+
44+
onStartClick = () => {
45+
this.changeView('list');
46+
}
47+
48+
fetchPlayList = async () => {
49+
const result = await fetch(this.playlistUrl);
50+
const tracks = await result.json();
51+
52+
this.updateSate(tracks);
53+
}
54+
55+
updateSate(tracks) {
56+
const updatedState = {
57+
tracks: [...tracks.map((track, index) => {
58+
return Object.assign({}, {
59+
...this.state.track,
60+
id: track.id,
61+
stream_url: `${this.playlistUrl}/stream/${track.id}`,
62+
uri: track.uri,
63+
duration: track.duration,
64+
favoritings_count: track.favoritings_count,
65+
artist: track.user.username,
66+
artwork_url: track.artwork_url ? track.artwork_url.replace('large', 't50x50') : '',
67+
title: track.title.toLowerCase(),
68+
permalink_url: track.permalink_url,
69+
index,
70+
});
71+
})],
72+
playlistLoaded: true,
73+
};
74+
75+
this.setState(() => updatedState);
76+
}
77+
78+
changeView(view) {
79+
this.history.push(`/${view}`, { view });
80+
}
81+
82+
setTrack(track) {
83+
this.setState(() => {
84+
return {
85+
track,
86+
currentTime: 0,
87+
paused: true,
88+
played: false,
89+
playing: false,
90+
changingTrack: true
91+
};
92+
});
93+
}
94+
95+
canChangeTrack() {
96+
return this.state.changingTrack === false;
97+
}
98+
99+
getNextTrack() {
100+
const nextTrack = this.state.tracks[this.state.track.index + 1];
101+
102+
return nextTrack ? { ...nextTrack } : null;
103+
}
104+
105+
getPreviousTrack() {
106+
const prevTrack = this.state.tracks[this.state.track.index - 1];
107+
108+
return prevTrack ? { ...prevTrack } : null;
109+
}
110+
111+
changeTrack(track) {
112+
this.audio.setAudioSource('');
113+
114+
if (this.canChangeTrack() && track) {
115+
this.setTrack(track);
116+
this.onPlayClick(track);
117+
}
118+
}
119+
120+
selectTrack = (id) => {
121+
return this.state.tracks.filter((track) => Number(id) === track.id)[0];
122+
}
123+
124+
setupAudio() {
125+
this.timeupdate = this.timeupdate.bind(this);
126+
this.audioStop = this.audioStop.bind(this);
127+
128+
this.audio = new Audio(document.querySelector('#audio'), this.props.audioContext);
129+
this.audio.setup();
130+
this.audio.setTimerHandler(this.timeupdate);
131+
this.audio.setStopHandler(this.audioStop);
132+
this.audio.canplay(() => {
133+
this.setState(() => {
134+
return {
135+
changingTrack: false
136+
};
137+
});
138+
})
139+
}
140+
141+
audioStop() {
142+
this.setState({
143+
track: {
144+
...this.state.track, currentTime: 0, percentage: 0, playing: false,
145+
played: false,
146+
paused: true,
147+
}
148+
});
149+
}
150+
151+
timeupdate = (evt) => {
152+
this.setState({
153+
track: {
154+
...this.state.track, currentTime: evt.target.currentTime,
155+
percentage: percent(evt.target.currentTime, evt.target.duration) / 100
156+
}
157+
});
158+
}
159+
160+
onListClick = (id) => {
161+
if (id !== this.state.track.id) {
162+
this.audio.setAudioSource('');
163+
}
164+
165+
const track = {
166+
...this.selectTrack(id),
167+
currentTime: 0,
168+
percentage: 0,
169+
playing: this.state.track.id === id ? this.state.track.playing : false,
170+
played: this.state.track.id === id ? this.state.track.played : false,
171+
paused: this.state.track.id === id ? this.state.track.paused : true,
172+
};
173+
174+
this.setState(() => {
175+
return { track };
176+
});
177+
178+
this.changeView('detail');
179+
180+
this.onPlayClick(track);
181+
}
182+
183+
onPlayClick = async (track) => {
184+
if (!track.played) {
185+
const result = await fetch(track.stream_url);
186+
const urlstream = await result.json();
187+
188+
this.audio.setAudioSource(urlstream.http_mp3_128_url);
189+
}
190+
191+
this.setState(() => {
192+
return {
193+
track: {
194+
...track,
195+
paused: false,
196+
playing: true,
197+
played: true
198+
}
199+
};
200+
});
201+
202+
this.audio.resume();
203+
this.audio.play();
204+
}
205+
206+
onPauseClick = (track) => {
207+
this.audio.pause();
208+
209+
this.setState(() => {
210+
return {
211+
track: {
212+
...track,
213+
paused: true,
214+
playing: false
215+
}
216+
}
217+
});
218+
}
219+
220+
onBackClick = () => {
221+
this.history.go(-1);
222+
223+
this.setState(() => {
224+
return { currentView: this.history.location.state.view || '/' };
225+
});
226+
}
227+
228+
onAboutClick = () => {
229+
this.changeView('about');
230+
}
231+
232+
onPlayNext = () => {
233+
this.changeTrack(this.getNextTrack());
234+
}
235+
236+
onPlayPrev = () => {
237+
this.changeTrack(this.getPreviousTrack());
238+
}
239+
240+
onRepeatClick = () => {
241+
const repeat = !this.state.repeat;
242+
243+
this.setState(() => {
244+
return { repeat };
245+
});
246+
247+
this.audio.repeat(repeat);
248+
}
249+
250+
render() {
251+
return (
252+
<main className="app">
253+
<audio id="audio" crossOrigin="anonymous"></audio>
254+
<div className="shell">
255+
<Menu history={this.history}
256+
activeView={this.state.currentView}
257+
onBackClick={this.onBackClick}
258+
onAboutClick={this.onAboutClick}
259+
onCloseClick={this.onBackClick} />
260+
<div className="page-wrapper">
261+
<Page className="home" active={this.state.currentView === 'home'}>
262+
<Home onStartClick={this.onStartClick} />
263+
</Page>
264+
<Suspense fallback={<Loader />}>
265+
<Page className="list" active={this.state.currentView === 'list'}>
266+
<List track={this.state.track} tracks={this.state.tracks} onClick={this.onListClick} />
267+
</Page>
268+
<Page className="detail" active={this.state.currentView === 'detail'}>
269+
<Detail track={this.state.track}
270+
repeat={this.state.repeat}
271+
onRepeatClick={this.onRepeatClick}
272+
onPlayClick={this.onPlayClick}
273+
onPlayNext={this.onPlayNext}
274+
onPlayPrev={this.onPlayPrev}
275+
onPauseClick={this.onPauseClick} />
276+
</Page>
277+
<Page className="about" active={this.state.currentView === 'about'}>
278+
<About />
279+
</Page>
280+
</Suspense>
281+
</div>
282+
</div>
283+
</main >
284+
);
285+
}
286+
}
287+
288+
export default App;

0 commit comments

Comments
 (0)