index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <template>
  2. <modal
  3. v-if="station"
  4. :title="
  5. !isOwnerOrAdmin() && station.partyMode
  6. ? 'Add Song to Queue'
  7. : 'Manage Station'
  8. "
  9. :style="`--primary-color: var(--${station.theme})`"
  10. class="manage-station-modal"
  11. >
  12. <template #body>
  13. <div class="custom-modal-body" v-if="station && station._id">
  14. <div class="left-section">
  15. <div class="section">
  16. <div id="about-station-container">
  17. <div id="station-info">
  18. <div id="station-name">
  19. <h1>{{ station.displayName }}</h1>
  20. <i
  21. v-if="station.type === 'official'"
  22. class="material-icons verified-station"
  23. content="Verified Station"
  24. v-tippy
  25. >
  26. check_circle
  27. </i>
  28. <i
  29. class="material-icons stationMode"
  30. :content="
  31. station.partyMode
  32. ? 'Station in Party mode'
  33. : 'Station in Playlist mode'
  34. "
  35. v-tippy
  36. >{{
  37. station.partyMode
  38. ? "emoji_people"
  39. : "playlist_play"
  40. }}</i
  41. >
  42. </div>
  43. <p>{{ station.description }}</p>
  44. </div>
  45. <div id="admin-buttons" v-if="isOwnerOrAdmin()">
  46. <!-- (Admin) Pause/Resume Button -->
  47. <button
  48. class="button is-danger"
  49. v-if="stationPaused"
  50. @click="resumeStation()"
  51. >
  52. <i class="material-icons icon-with-button"
  53. >play_arrow</i
  54. >
  55. <span> Resume Station </span>
  56. </button>
  57. <button
  58. class="button is-danger"
  59. @click="pauseStation()"
  60. v-else
  61. >
  62. <i class="material-icons icon-with-button"
  63. >pause</i
  64. >
  65. <span> Pause Station </span>
  66. </button>
  67. <!-- (Admin) Skip Button -->
  68. <button
  69. class="button is-danger"
  70. @click="skipStation()"
  71. >
  72. <i class="material-icons icon-with-button"
  73. >skip_next</i
  74. >
  75. <span> Force Skip </span>
  76. </button>
  77. <!-- Station Settings Button -->
  78. <!-- <button
  79. class="button is-primary"
  80. @click="openModal('manageStation')"
  81. >
  82. <i class="material-icons icon-with-button"
  83. >settings</i
  84. >
  85. <span>
  86. Manage Station
  87. </span>
  88. </button> -->
  89. <router-link
  90. v-if="sector !== 'station' && station.name"
  91. :to="{
  92. name: 'station',
  93. params: { id: station.name }
  94. }"
  95. class="button is-primary"
  96. >
  97. Go To Station
  98. </router-link>
  99. </div>
  100. </div>
  101. <div class="tab-selection">
  102. <button
  103. v-if="isOwnerOrAdmin()"
  104. class="button is-default"
  105. :class="{ selected: tab === 'settings' }"
  106. ref="settings-tab"
  107. @click="showTab('settings')"
  108. >
  109. Settings
  110. </button>
  111. <button
  112. v-if="isAllowedToParty() || isOwnerOrAdmin()"
  113. class="button is-default"
  114. :class="{ selected: tab === 'playlists' }"
  115. ref="playlists-tab"
  116. @click="showTab('playlists')"
  117. >
  118. Playlists
  119. </button>
  120. <button
  121. v-if="isAllowedToParty() || isOwnerOrAdmin()"
  122. class="button is-default"
  123. :class="{ selected: tab === 'songs' }"
  124. ref="songs-tab"
  125. @click="showTab('songs')"
  126. >
  127. Songs
  128. </button>
  129. </div>
  130. <settings
  131. v-if="isOwnerOrAdmin()"
  132. class="tab"
  133. v-show="tab === 'settings'"
  134. />
  135. <playlists
  136. v-if="isAllowedToParty() || isOwnerOrAdmin()"
  137. class="tab"
  138. v-show="tab === 'playlists'"
  139. />
  140. <songs
  141. v-if="isAllowedToParty() || isOwnerOrAdmin()"
  142. class="tab"
  143. v-show="tab === 'songs'"
  144. />
  145. </div>
  146. </div>
  147. <div class="right-section">
  148. <div class="section">
  149. <div class="queue-title">
  150. <h4 class="section-title">Queue</h4>
  151. </div>
  152. <hr class="section-horizontal-rule" />
  153. <song-item
  154. v-if="currentSong._id"
  155. :song="currentSong"
  156. :requested-by="
  157. station.type === 'community' &&
  158. station.partyMode === true
  159. "
  160. header="Currently Playing.."
  161. class="currently-playing"
  162. />
  163. <queue sector="manageStation" />
  164. </div>
  165. </div>
  166. </div>
  167. </template>
  168. <template #footer>
  169. <!-- <router-link
  170. v-if="sector !== 'station' && station.name"
  171. :to="{
  172. name: 'station',
  173. params: { id: station.name }
  174. }"
  175. class="button is-primary"
  176. >
  177. Go To Station
  178. </router-link> -->
  179. <button
  180. class="button is-primary tab-actionable-button"
  181. v-if="loggedIn && station.type === 'official'"
  182. @click="openModal('requestSong')"
  183. >
  184. <i class="material-icons icon-with-button">queue</i>
  185. <span> Request Song </span>
  186. </button>
  187. <div v-if="isOwnerOrAdmin()" class="right">
  188. <confirm @confirm="clearAndRefillStationQueue()">
  189. <a class="button is-danger">
  190. Clear and refill station queue
  191. </a>
  192. </confirm>
  193. <confirm
  194. v-if="station && station.type === 'community'"
  195. @confirm="removeStation()"
  196. >
  197. <button class="button is-danger">Delete station</button>
  198. </confirm>
  199. </div>
  200. </template>
  201. </modal>
  202. </template>
  203. <script>
  204. import { mapState, mapGetters, mapActions } from "vuex";
  205. import Toast from "toasters";
  206. import Confirm from "@/components/Confirm.vue";
  207. import Queue from "@/components/Queue.vue";
  208. import SongItem from "@/components/SongItem.vue";
  209. import Modal from "../../Modal.vue";
  210. import Settings from "./Tabs/Settings.vue";
  211. import Playlists from "./Tabs/Playlists.vue";
  212. import Songs from "./Tabs/Songs.vue";
  213. export default {
  214. components: {
  215. Modal,
  216. Confirm,
  217. Queue,
  218. SongItem,
  219. Settings,
  220. Playlists,
  221. Songs
  222. },
  223. props: {
  224. stationId: { type: String, default: "" },
  225. sector: { type: String, default: "admin" }
  226. },
  227. computed: {
  228. ...mapState({
  229. loggedIn: state => state.user.auth.loggedIn,
  230. userId: state => state.user.auth.userId,
  231. role: state => state.user.auth.role
  232. }),
  233. ...mapState("modals/manageStation", {
  234. tab: state => state.tab,
  235. station: state => state.station,
  236. originalStation: state => state.originalStation,
  237. songsList: state => state.songsList,
  238. stationPlaylist: state => state.stationPlaylist,
  239. includedPlaylists: state => state.includedPlaylists,
  240. excludedPlaylists: state => state.excludedPlaylists,
  241. stationPaused: state => state.stationPaused,
  242. currentSong: state => state.currentSong
  243. }),
  244. ...mapGetters({
  245. socket: "websockets/getSocket"
  246. })
  247. },
  248. mounted() {
  249. this.socket.dispatch(`stations.getStationById`, this.stationId, res => {
  250. if (res.status === "success") {
  251. const { station } = res.data;
  252. this.editStation(station);
  253. if (!this.isOwnerOrAdmin() && this.station.partyMode)
  254. this.showTab("songs");
  255. const currentSong = res.data.station.currentSong
  256. ? res.data.station.currentSong
  257. : {};
  258. this.updateCurrentSong(currentSong);
  259. this.updateStationPaused(res.data.station.paused);
  260. this.socket.dispatch(
  261. "stations.getStationIncludedPlaylistsById",
  262. this.stationId,
  263. res => {
  264. if (res.status === "success")
  265. this.setIncludedPlaylists(res.data.playlists);
  266. }
  267. );
  268. this.socket.dispatch(
  269. "stations.getStationExcludedPlaylistsById",
  270. this.stationId,
  271. res => {
  272. if (res.status === "success")
  273. this.setExcludedPlaylists(res.data.playlists);
  274. }
  275. );
  276. if (this.isOwnerOrAdmin()) {
  277. this.socket.dispatch(
  278. "playlists.getPlaylistForStation",
  279. this.station._id,
  280. true,
  281. res => {
  282. if (res.status === "success") {
  283. this.updateStationPlaylist(res.data.playlist);
  284. }
  285. }
  286. );
  287. }
  288. this.socket.dispatch(
  289. "stations.getQueue",
  290. this.stationId,
  291. res => {
  292. if (res.status === "success")
  293. this.updateSongsList(res.data.queue);
  294. }
  295. );
  296. this.socket.dispatch(
  297. "apis.joinRoom",
  298. `manage-station.${this.stationId}`
  299. );
  300. this.socket.on(
  301. "event:station.name.updated",
  302. res => {
  303. this.station.name = res.data.name;
  304. },
  305. { modal: "manageStation" }
  306. );
  307. this.socket.on(
  308. "event:station.displayName.updated",
  309. res => {
  310. this.station.displayName = res.data.displayName;
  311. },
  312. { modal: "manageStation" }
  313. );
  314. this.socket.on(
  315. "event:station.description.updated",
  316. res => {
  317. this.station.description = res.data.description;
  318. },
  319. { modal: "manageStation" }
  320. );
  321. this.socket.on(
  322. "event:station.partyMode.updated",
  323. res => {
  324. if (this.station.type === "community")
  325. this.station.partyMode = res.data.partyMode;
  326. },
  327. { modal: "manageStation" }
  328. );
  329. this.socket.on(
  330. "event:station.playMode.updated",
  331. res => {
  332. this.station.playMode = res.data.playMode;
  333. },
  334. { modal: "manageStation" }
  335. );
  336. this.socket.on(
  337. "event:station.theme.updated",
  338. res => {
  339. const { theme } = res.data;
  340. this.station.theme = theme;
  341. },
  342. { modal: "manageStation" }
  343. );
  344. this.socket.on(
  345. "event:station.privacy.updated",
  346. res => {
  347. this.station.privacy = res.data.privacy;
  348. },
  349. { modal: "manageStation" }
  350. );
  351. this.socket.on(
  352. "event:station.queue.lock.toggled",
  353. res => {
  354. this.station.locked = res.data.locked;
  355. },
  356. { modal: "manageStation" }
  357. );
  358. this.socket.on(
  359. "event:station.includedPlaylist",
  360. res => {
  361. const { playlist } = res.data;
  362. const playlistIndex = this.includedPlaylists
  363. .map(includedPlaylist => includedPlaylist._id)
  364. .indexOf(playlist._id);
  365. if (playlistIndex === -1)
  366. this.includedPlaylists.push(playlist);
  367. },
  368. { modal: "manageStation" }
  369. );
  370. this.socket.on(
  371. "event:station.excludedPlaylist",
  372. res => {
  373. const { playlist } = res.data;
  374. const playlistIndex = this.excludedPlaylists
  375. .map(excludedPlaylist => excludedPlaylist._id)
  376. .indexOf(playlist._id);
  377. if (playlistIndex === -1)
  378. this.excludedPlaylists.push(playlist);
  379. },
  380. { modal: "manageStation" }
  381. );
  382. this.socket.on(
  383. "event:station.removedIncludedPlaylist",
  384. res => {
  385. const { playlistId } = res.data;
  386. const playlistIndex = this.includedPlaylists
  387. .map(playlist => playlist._id)
  388. .indexOf(playlistId);
  389. if (playlistIndex >= 0)
  390. this.includedPlaylists.splice(playlistIndex, 1);
  391. },
  392. { modal: "manageStation" }
  393. );
  394. this.socket.on(
  395. "event:station.removedExcludedPlaylist",
  396. res => {
  397. const { playlistId } = res.data;
  398. const playlistIndex = this.excludedPlaylists
  399. .map(playlist => playlist._id)
  400. .indexOf(playlistId);
  401. if (playlistIndex >= 0)
  402. this.excludedPlaylists.splice(playlistIndex, 1);
  403. },
  404. { modal: "manageStation" }
  405. );
  406. } else {
  407. new Toast(`Station with that ID not found`);
  408. this.closeModal("manageStation");
  409. }
  410. });
  411. this.socket.on(
  412. "event:station.queue.updated",
  413. res => this.updateSongsList(res.data.queue),
  414. { modal: "manageStation" }
  415. );
  416. this.socket.on(
  417. "event:station.queue.song.repositioned",
  418. res => this.repositionSongInList(res.data.song),
  419. { modal: "manageStation" }
  420. );
  421. this.socket.on(
  422. "event:station.pause",
  423. () => this.updateStationPaused(true),
  424. { modal: "manageStation" }
  425. );
  426. this.socket.on(
  427. "event:station.resume",
  428. () => this.updateStationPaused(false),
  429. { modal: "manageStation" }
  430. );
  431. this.socket.on(
  432. "event:station.nextSong",
  433. res => {
  434. const { currentSong } = res.data;
  435. this.updateCurrentSong(currentSong || {});
  436. },
  437. { modal: "manageStation" }
  438. );
  439. if (this.isOwnerOrAdmin()) {
  440. this.socket.on(
  441. "event:playlist.song.added",
  442. res => {
  443. if (this.stationPlaylist._id === res.data.playlistId)
  444. this.stationPlaylist.songs.push(res.data.song);
  445. },
  446. {
  447. modal: "manageStation"
  448. }
  449. );
  450. this.socket.on(
  451. "event:playlist.song.removed",
  452. res => {
  453. if (this.stationPlaylist._id === res.data.playlistId) {
  454. // remove song from array of playlists
  455. this.stationPlaylist.songs.forEach((song, index) => {
  456. if (song.youtubeId === res.data.youtubeId)
  457. this.stationPlaylist.songs.splice(index, 1);
  458. });
  459. }
  460. },
  461. {
  462. modal: "manageStation"
  463. }
  464. );
  465. this.socket.on(
  466. "event:playlist.songs.repositioned",
  467. res => {
  468. if (this.stationPlaylist._id === res.data.playlistId) {
  469. // for each song that has a new position
  470. res.data.songsBeingChanged.forEach(changedSong => {
  471. this.stationPlaylist.songs.forEach(
  472. (song, index) => {
  473. // find song locally
  474. if (
  475. song.youtubeId === changedSong.youtubeId
  476. ) {
  477. // change song position attribute
  478. this.stationPlaylist.songs[
  479. index
  480. ].position = changedSong.position;
  481. // reposition in array if needed
  482. if (index !== changedSong.position - 1)
  483. this.stationPlaylist.songs.splice(
  484. changedSong.position - 1,
  485. 0,
  486. this.stationPlaylist.songs.splice(
  487. index,
  488. 1
  489. )[0]
  490. );
  491. }
  492. }
  493. );
  494. });
  495. }
  496. },
  497. {
  498. modal: "manageStation"
  499. }
  500. );
  501. }
  502. },
  503. beforeUnmount() {
  504. this.socket.dispatch(
  505. "apis.leaveRoom",
  506. `manage-station.${this.stationId}`,
  507. () => {}
  508. );
  509. this.clearStation();
  510. this.showTab("settings");
  511. },
  512. methods: {
  513. isOwner() {
  514. return (
  515. this.loggedIn &&
  516. this.station &&
  517. this.userId === this.station.owner
  518. );
  519. },
  520. isAdmin() {
  521. return this.loggedIn && this.role === "admin";
  522. },
  523. isOwnerOrAdmin() {
  524. return this.isOwner() || this.isAdmin();
  525. },
  526. isPartyMode() {
  527. return (
  528. this.station &&
  529. this.station.type === "community" &&
  530. this.station.partyMode
  531. );
  532. },
  533. isAllowedToParty() {
  534. return (
  535. this.station &&
  536. this.isPartyMode() &&
  537. (!this.station.locked || this.isOwnerOrAdmin()) &&
  538. this.loggedIn
  539. );
  540. },
  541. isPlaylistMode() {
  542. return this.station && !this.isPartyMode();
  543. },
  544. removeStation() {
  545. this.socket.dispatch("stations.remove", this.station._id, res => {
  546. new Toast(res.message);
  547. if (res.status === "success") {
  548. this.closeModal("manageStation");
  549. }
  550. });
  551. },
  552. resumeStation() {
  553. this.socket.dispatch("stations.resume", this.station._id, res => {
  554. if (res.status !== "success")
  555. new Toast(`Error: ${res.message}`);
  556. else new Toast("Successfully resumed the station.");
  557. });
  558. },
  559. pauseStation() {
  560. this.socket.dispatch("stations.pause", this.station._id, res => {
  561. if (res.status !== "success")
  562. new Toast(`Error: ${res.message}`);
  563. else new Toast("Successfully paused the station.");
  564. });
  565. },
  566. skipStation() {
  567. this.socket.dispatch(
  568. "stations.forceSkip",
  569. this.station._id,
  570. res => {
  571. if (res.status !== "success")
  572. new Toast(`Error: ${res.message}`);
  573. else
  574. new Toast(
  575. "Successfully skipped the station's current song."
  576. );
  577. }
  578. );
  579. },
  580. clearAndRefillStationQueue() {
  581. this.socket.dispatch(
  582. "stations.clearAndRefillStationQueue",
  583. this.station._id,
  584. res => {
  585. if (res.status !== "success")
  586. new Toast({
  587. content: `Error: ${res.message}`,
  588. timeout: 8000
  589. });
  590. else new Toast({ content: res.message, timeout: 4000 });
  591. }
  592. );
  593. },
  594. ...mapActions("modals/manageStation", [
  595. "editStation",
  596. "setIncludedPlaylists",
  597. "setExcludedPlaylists",
  598. "clearStation",
  599. "updateSongsList",
  600. "updateStationPlaylist",
  601. "repositionSongInList",
  602. "updateStationPaused",
  603. "updateCurrentSong"
  604. ]),
  605. ...mapActions({
  606. showTab(dispatch, payload) {
  607. this.$refs[`${payload}-tab`].scrollIntoView();
  608. return dispatch("modals/manageStation/showTab", payload);
  609. }
  610. }),
  611. ...mapActions("modalVisibility", ["openModal", "closeModal"]),
  612. ...mapActions("user/playlists", ["editPlaylist"])
  613. }
  614. };
  615. </script>
  616. <style lang="scss">
  617. .manage-station-modal.modal {
  618. z-index: 1800;
  619. .modal-card {
  620. width: 1300px;
  621. height: 100%;
  622. overflow: auto;
  623. .tab > button {
  624. width: 100%;
  625. margin-bottom: 10px;
  626. }
  627. .currently-playing.song-item {
  628. .song-info {
  629. width: calc(100% - 150px);
  630. }
  631. .thumbnail {
  632. min-width: 130px;
  633. width: 130px;
  634. height: 130px;
  635. }
  636. }
  637. }
  638. }
  639. </style>
  640. <style lang="scss" scoped>
  641. .night-mode {
  642. .manage-station-modal.modal .modal-card-body .custom-modal-body {
  643. .left-section {
  644. #about-station-container {
  645. background-color: var(--dark-grey-3) !important;
  646. border: 0;
  647. }
  648. .section {
  649. background-color: transparent !important;
  650. }
  651. .tab-selection .button {
  652. background: var(--dark-grey);
  653. color: var(--white);
  654. }
  655. .tab {
  656. background-color: var(--dark-grey-3);
  657. border: 0;
  658. }
  659. }
  660. .right-section .section,
  661. #queue {
  662. border-radius: 5px;
  663. background-color: transparent !important;
  664. }
  665. }
  666. }
  667. .manage-station-modal.modal .modal-card-body .custom-modal-body {
  668. display: flex;
  669. flex-wrap: wrap;
  670. height: 100%;
  671. .section {
  672. display: flex;
  673. flex-direction: column;
  674. flex-grow: 1;
  675. width: auto;
  676. padding: 15px !important;
  677. margin: 0 10px;
  678. }
  679. .left-section {
  680. flex-basis: 50%;
  681. height: 100%;
  682. overflow-y: auto;
  683. flex-grow: 1;
  684. .section:first-child {
  685. padding: 0 15px 15px !important;
  686. }
  687. #about-station-container {
  688. padding: 20px;
  689. display: flex;
  690. flex-direction: column;
  691. flex-grow: unset;
  692. border-radius: 5px;
  693. margin: 0 0 20px 0;
  694. background-color: var(--white);
  695. border: 1px solid var(--light-grey-3);
  696. #station-info {
  697. #station-name {
  698. flex-direction: row !important;
  699. display: flex;
  700. flex-direction: row;
  701. max-width: 100%;
  702. h1 {
  703. margin: 0;
  704. font-size: 36px;
  705. line-height: 0.8;
  706. }
  707. i {
  708. margin-left: 10px;
  709. font-size: 30px;
  710. color: var(--yellow);
  711. &.stationMode {
  712. padding-left: 10px;
  713. margin-left: auto;
  714. color: var(--primary-color);
  715. }
  716. }
  717. .verified-station {
  718. color: var(--primary-color);
  719. }
  720. }
  721. p {
  722. max-width: 700px;
  723. margin-bottom: 10px;
  724. }
  725. }
  726. #admin-buttons {
  727. display: flex;
  728. .button {
  729. margin: 3px;
  730. }
  731. }
  732. }
  733. .tab-selection {
  734. display: flex;
  735. overflow-x: auto;
  736. .button {
  737. border-radius: 5px 5px 0 0;
  738. border: 0;
  739. text-transform: uppercase;
  740. font-size: 14px;
  741. color: var(--dark-grey-3);
  742. background-color: var(--light-grey-2);
  743. flex-grow: 1;
  744. height: 32px;
  745. &:not(:first-of-type) {
  746. margin-left: 5px;
  747. }
  748. }
  749. .selected {
  750. background-color: var(--primary-color) !important;
  751. color: var(--white) !important;
  752. font-weight: 600;
  753. }
  754. }
  755. .tab {
  756. border: 1px solid var(--light-grey-3);
  757. padding: 15px;
  758. border-radius: 0 0 5px 5px;
  759. }
  760. }
  761. .right-section {
  762. flex-basis: 50%;
  763. height: 100%;
  764. overflow-y: auto;
  765. flex-grow: 1;
  766. .section {
  767. .queue-title {
  768. display: flex;
  769. line-height: 30px;
  770. .material-icons {
  771. margin-left: 5px;
  772. margin-bottom: 5px;
  773. font-size: 28px;
  774. cursor: pointer;
  775. &:first-of-type {
  776. margin-left: auto;
  777. }
  778. &.skip-station {
  779. color: var(--red);
  780. }
  781. &.resume-station,
  782. &.pause-station {
  783. color: var(--primary-color);
  784. }
  785. }
  786. }
  787. .currently-playing {
  788. margin-bottom: 10px;
  789. }
  790. }
  791. }
  792. }
  793. @media screen and (max-width: 1100px) {
  794. .manage-station-modal.modal .modal-card-body .custom-modal-body {
  795. .left-section,
  796. .right-section {
  797. flex-basis: unset;
  798. height: auto;
  799. }
  800. }
  801. }
  802. </style>