index.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. import React, { Component } from "react";
  2. import { NavLink } from "react-router-dom";
  3. import PropTypes from "prop-types";
  4. import { translate, Trans } from "react-i18next";
  5. import Player from "./Player";
  6. import Seekerbar from "./Seekerbar";
  7. import VolumeSlider from "./VolumeSlider";
  8. import Overlays from "./Views/Overlays";
  9. import { changeVolume } from "actions/volume";
  10. import { changeSong, setTimeElapsed, timePaused, receivedRatings, receivedOwnRatings } from "actions/songPlayer";
  11. import { pauseStation, resumeStation } from "actions/station";
  12. import { openOverlay1 } from "actions/stationOverlay";
  13. import { addSong } from "actions/playlistQueue";
  14. import { connect } from "react-redux";
  15. import io from "io";
  16. import config from "config";
  17. import {updateTimePaused} from "../../actions/songPlayer";
  18. const formatTime = (duration) => {
  19. let d = moment.duration(duration, "seconds");
  20. if (duration < 0) return "0:00";
  21. return ((d.hours() > 0) ? (d.hours() < 10 ? ("0" + d.hours() + ":") : (d.hours() + ":")) : "") + (d.minutes() + ":") + (d.seconds() < 10 ? ("0" + d.seconds()) : d.seconds());
  22. };
  23. @connect(state => ({
  24. user: {
  25. userId: state.user.get("userId"),
  26. role: state.user.get("role"),
  27. },
  28. loggedIn: state.user.get("loggedIn"),
  29. songId: state.songPlayer.get("songId"),
  30. songTitle: state.songPlayer.get("title"),
  31. songDuration: state.songPlayer.get("duration"),
  32. songTimeElapsed: state.songPlayer.get("timeElapsed"),
  33. songArtists: state.songPlayer.get("artists"),
  34. songLikes: state.songPlayer.get("likes"),
  35. songLiked: state.songPlayer.get("liked"),
  36. songDislikes: state.songPlayer.get("dislikes"),
  37. songDisliked: state.songPlayer.get("disliked"),
  38. simpleSong: state.songPlayer.get("simple"),
  39. songExists: state.songPlayer.get("exists"),
  40. queueLocked: state.station.get("locked"),
  41. partyEnabled: state.station.get("partyMode"),
  42. station: {
  43. stationId: state.station.get("id"),
  44. name: state.station.get("name"),
  45. displayName: state.station.get("displayName"),
  46. paused: state.station.get("paused"),
  47. pausedAt: state.station.get("pausedAt"),
  48. ownerId: state.station.get("ownerId"),
  49. },
  50. selectedPlaylistObject: {
  51. addedSongId: state.playlistQueue.get("addedSongId"),
  52. selectedPlaylistId: state.playlistQueue.get("playlistSelected"),
  53. },
  54. }))
  55. @translate(["station"], { wait: true })
  56. export default class Station extends Component {
  57. static propTypes = {
  58. t: PropTypes.func,
  59. };
  60. static defaultProps = {
  61. t: () => {},
  62. };
  63. constructor(props) {
  64. super();
  65. this.state = {
  66. mode: this.getModeTemp(props.partyEnabled, props.queueLocked),
  67. };
  68. io.getSocket(socket => {
  69. socket.emit("stations.join", props.station.name, res => {
  70. if (res.status === 'success') {
  71. if (res.data.currentSong) {
  72. res.data.currentSong.startedAt = res.data.startedAt;
  73. res.data.currentSong.timePaused = res.data.timePaused;
  74. }
  75. this.props.dispatch(changeSong(res.data.currentSong));
  76. this.getOwnRatings();
  77. }
  78. socket.on('event:songs.next', data => {
  79. this.addTopToQueue();
  80. if (data.currentSong) {
  81. data.currentSong.startedAt = data.startedAt;
  82. data.currentSong.timePaused = data.timePaused;
  83. }
  84. this.props.dispatch(changeSong(data.currentSong));
  85. this.getOwnRatings();
  86. });
  87. socket.on('event:stations.pause', pausedAt => {
  88. this.props.dispatch(pauseStation(pausedAt));
  89. });
  90. socket.on('event:stations.resume', data => {
  91. this.props.dispatch(updateTimePaused(data.timePaused));
  92. this.props.dispatch(resumeStation());
  93. });
  94. socket.on('event:song.like', data => {
  95. console.log("LIKE");
  96. if (this.props.songExists) {
  97. if (data.songId === this.props.songId) {
  98. this.props.dispatch(receivedRatings(data.likes, data.dislikes));
  99. }
  100. }
  101. });
  102. socket.on('event:song.dislike', data => {
  103. console.log("DISLIKE");
  104. if (this.props.songExists) {
  105. if (data.songId === this.props.songId) {
  106. this.props.dispatch(receivedRatings(data.likes, data.dislikes));
  107. }
  108. }
  109. });
  110. socket.on('event:song.unlike', data => {
  111. console.log("UNLIKE");
  112. if (this.props.songExists) {
  113. if (data.songId === this.props.songId) {
  114. this.props.dispatch(receivedRatings(data.likes, data.dislikes));
  115. }
  116. }
  117. });
  118. socket.on('event:song.undislike', data => {
  119. console.log("UNDISLIKE");
  120. if (this.props.songExists) {
  121. if (data.songId === this.props.songId) {
  122. this.props.dispatch(receivedRatings(data.likes, data.dislikes));
  123. }
  124. }
  125. });
  126. socket.on('event:song.newRatings', data => {
  127. if (this.props.songExists) {
  128. if (data.songId === this.props.songId) {
  129. this.props.dispatch(receivedOwnRatings(data.liked, data.disliked));
  130. }
  131. }
  132. });
  133. });
  134. });
  135. setInterval(() => {
  136. if (this.props.songExists) {
  137. this.props.dispatch(setTimeElapsed(this.props.station.paused, this.props.station.pausedAt)); // TODO Fix
  138. }
  139. }, 1000);
  140. }
  141. isInQueue = (songId, cb) => {
  142. io.getSocket((socket) => {
  143. socket.emit('stations.getQueue', this.props.stationId, data => {
  144. if (data.status === 'success') {
  145. data.queue.forEach((song) => {
  146. if (song._id === songId) {
  147. return cb(true);
  148. }
  149. });
  150. }
  151. return cb(false);
  152. });
  153. });
  154. };
  155. checkIfCanAdd = (cb) => {
  156. if (this.state.mode === "normal") return cb(false);
  157. let playlistId = this.props.selectedPlaylistObject.selectedPlaylistId;
  158. let songId = this.props.selectedPlaylistObject.addedSongId;
  159. console.log(playlistId, songId, this.props.songId);
  160. if (playlistId) {
  161. if (songId === this.props.songId) return cb(true);
  162. else if (songId === null) return cb(true);
  163. else {
  164. this.isInQueue(songId, (res) => {
  165. return cb(res);
  166. });
  167. }
  168. }
  169. }
  170. addTopToQueue = () => {
  171. console.log("ADD TOP TO QUEUE!!!");
  172. this.checkIfCanAdd((can) => {
  173. if (!can) return;
  174. let playlistId = this.props.selectedPlaylistObject.selectedPlaylistId;
  175. console.log(can);
  176. io.getSocket((socket) => {
  177. socket.emit('playlists.getFirstSong', this.props.selectedPlaylistObject.selectedPlaylistId, data => {
  178. if (data.status === 'success') {
  179. let songId = data.song.songId;
  180. if (data.song.duration < 15 * 60) {
  181. this.props.dispatch(addSong(songId));
  182. socket.emit('stations.addToQueue', this.props.station.stationId, songId, data2 => {
  183. if (data2.status === 'success') {
  184. this.moveToBottom(playlistId, songId, (data3) => {
  185. if (data3.status === 'success') {
  186. }
  187. });
  188. } else {
  189. this.messages.clearAddError("Could not automatically add top song of playlist to queue.", data2.message);
  190. }
  191. });
  192. } else {
  193. this.messages.clearAddError("Top song in playlist was too long to be added. Moving to the next song.");
  194. this.moveToBottom(playlistId, songId, (data3) => {
  195. if (data3.status === 'success') {
  196. setTimeout(() => {
  197. this.addTopToQueue();
  198. }, 2000);
  199. }
  200. });
  201. }
  202. }
  203. });
  204. });
  205. });
  206. };
  207. moveToBottom = (playlistId, songId, cb) => {
  208. io.getSocket((socket) => {
  209. socket.emit('playlists.moveSongToBottom', playlistId, songId, data => {
  210. cb(data);
  211. });
  212. });
  213. }
  214. getModeTemp = (partyEnabled, queueLocked) => {
  215. // If party enabled
  216. // If queue locked
  217. // Mode is DJ
  218. // If queue not locked
  219. // Mode party
  220. // If party not enabled
  221. // Mode is normal
  222. if (partyEnabled) {
  223. if (queueLocked) return "dj";
  224. else return "party";
  225. } else return "normal";
  226. }
  227. getOwnRatings = () => {
  228. io.getSocket((socket) => {
  229. if (!this.props.songExists) return;
  230. socket.emit('songs.getOwnSongRatings', this.props.songId, (data) => {
  231. if (this.props.songId === data.songId) this.props.dispatch(receivedOwnRatings(data.liked, data.disliked));
  232. });
  233. });
  234. };
  235. isOwner = () => {
  236. if (this.props.loggedIn) {
  237. if (this.props.user.role === "admin") return true;
  238. if (this.props.user.userId === this.props.station.ownerId) return true;
  239. }
  240. return false;
  241. };
  242. addSongTemp = () => {
  243. io.getSocket(socket => {
  244. socket.emit('stations.addToQueue', this.props.station.stationId, '60ItHLz5WEA', data => {
  245. console.log("ATQ Res", data);
  246. });
  247. });
  248. };
  249. resumeStation = () => {
  250. io.getSocket(socket => {
  251. socket.emit('stations.resume', this.props.station.stationId, data => {
  252. });
  253. });
  254. };
  255. pauseStation = () => {
  256. io.getSocket(socket => {
  257. socket.emit('stations.pause', this.props.station.stationId, data => {
  258. });
  259. });
  260. };
  261. getRatings = () => {
  262. const likes = <span>{ this.props.songLikes }</span>;
  263. const dislikes = <span>{ this.props.songDislikes }</span>;
  264. let likeButton = <i className="material-icons disabled">thumb_up</i>;
  265. let dislikeButton = <i className="material-icons disabled">thumb_down</i>;
  266. if (this.props.loggedIn) {
  267. if (this.props.songLiked) likeButton = <i className="material-icons liked" onClick={ this.unlike }>thumb_up</i>;
  268. else likeButton = <i className="material-icons" onClick={ this.like }>thumb_up</i>;
  269. if (this.props.songDisliked) dislikeButton = <i className="material-icons disliked" onClick={ this.undislike }>thumb_down</i>;
  270. else dislikeButton = <i className="material-icons" onClick={ this.dislike }>thumb_down</i>;
  271. }
  272. return <div className="ratings-container">
  273. <div>
  274. { likeButton }
  275. { likes }
  276. </div>
  277. <div>
  278. { dislikeButton }
  279. { dislikes }
  280. </div>
  281. </div>;
  282. };
  283. like = () => {
  284. io.getSocket(socket => {
  285. socket.emit('songs.like', this.props.songId, data => {});
  286. });
  287. };
  288. dislike = () => {
  289. io.getSocket(socket => {
  290. socket.emit('songs.dislike', this.props.songId, data => {});
  291. });
  292. };
  293. unlike = () => {
  294. io.getSocket(socket => {
  295. socket.emit('songs.unlike', this.props.songId, data => {});
  296. });
  297. };
  298. undislike = () => {
  299. io.getSocket(socket => {
  300. socket.emit('songs.undislike', this.props.songId, data => {});
  301. });
  302. };
  303. skipStation = () => {
  304. io.getSocket(socket => {
  305. socket.emit('stations.forceSkip', this.props.station.stationId, data => {});
  306. });
  307. }
  308. render() {
  309. const { t } = this.props;
  310. //TODO Make this not re-render a lot
  311. return (
  312. <main id="station">
  313. <Overlays t={ this.props.t } />
  314. <div id="sidebar">
  315. <button onClick={ () => { this.props.dispatch(openOverlay1("users")) } }><i className="material-icons">people</i></button>
  316. <button onClick={ () => { this.props.dispatch(openOverlay1("queueList")) } }><i className="material-icons">queue_music</i></button>
  317. <button onClick={ () => { this.props.dispatch(openOverlay1("playlists")) } }><i className="material-icons">library_music</i></button>
  318. <hr/>
  319. {
  320. (this.isOwner())
  321. ? (this.props.station.paused)
  322. ? <button onClick={ this.resumeStation }><i className="material-icons">play_arrow</i></button>
  323. : <button onClick={ this.pauseStation }><i className="material-icons">pause</i></button>
  324. : null
  325. }
  326. {
  327. (this.isOwner())
  328. ? <button onClick={ this.skipStation }><i className="material-icons">skip_next</i></button>
  329. : null
  330. }
  331. {
  332. (this.isOwner())
  333. ? <button onClick={ () => { this.props.dispatch(openOverlay1("settings")) } }><i className="material-icons">settings</i></button>
  334. : null
  335. }
  336. </div>
  337. <h1>{ this.props.station.displayName }</h1>
  338. <div className={(!this.props.songExists) ? "player-container hidden" : "player-container"}>
  339. <div className="iframe-container">
  340. <Player onRef={ ref => (this.player = ref) }/>
  341. { (this.props.station.paused) ? <div className="paused-overlay"><span>Paused</span><i className="material-icons">pause</i></div> : null }
  342. </div>
  343. <Seekerbar/>
  344. </div>
  345. { (this.props.songExists) ? (
  346. [
  347. <div key="content" className="content">
  348. <span className="title">{ this.props.songTitle }</span>
  349. <span className="artists">{ this.props.songArtists.join(", ") }</span>
  350. <span className="time">
  351. { formatTime(this.props.songTimeElapsed) } / { formatTime(this.props.songDuration) }
  352. </span>
  353. <VolumeSlider/>
  354. {
  355. (!this.props.simpleSong) ? this.getRatings() : null
  356. }
  357. </div>,
  358. ]) : (
  359. <h1>No song playing</h1>
  360. ) }
  361. </main>
  362. );
  363. }
  364. }