index.vue 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. <template>
  2. <div>
  3. <metadata title="Home" />
  4. <div class="app">
  5. <main-header
  6. :hide-logo="true"
  7. :transparent="true"
  8. :hide-logged-out="true"
  9. />
  10. <div class="header" :class="{ loggedIn }">
  11. <img class="background" src="/assets/homebg.jpeg" />
  12. <div class="overlay"></div>
  13. <div class="content-container">
  14. <div class="content">
  15. <img
  16. class="logo"
  17. src="/assets/white_wordmark.png"
  18. :alt="`${this.siteName}` || `Musare`"
  19. />
  20. <div v-if="!loggedIn" class="buttons">
  21. <button
  22. class="button login"
  23. @click="
  24. openModal({
  25. sector: 'header',
  26. modal: 'login'
  27. })
  28. "
  29. >
  30. Login
  31. </button>
  32. <button
  33. class="button register"
  34. @click="
  35. openModal({
  36. sector: 'header',
  37. modal: 'register'
  38. })
  39. "
  40. >
  41. Register
  42. </button>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. <div v-if="favoriteStations.length > 0" class="group">
  48. <div class="group-title">
  49. <div>
  50. <h2>My Favorites</h2>
  51. </div>
  52. </div>
  53. <router-link
  54. v-for="(station, index) in favoriteStations"
  55. :key="index"
  56. :to="{
  57. name: 'station',
  58. params: { id: station.name }
  59. }"
  60. class="card station-card"
  61. :class="{
  62. isPrivate: station.privacy === 'private',
  63. isMine: isOwner(station)
  64. }"
  65. :style="'--station-theme: ' + station.themeCode"
  66. >
  67. <div class="card-image">
  68. <figure class="image is-square">
  69. <div
  70. v-if="station.currentSong.ytThumbnail"
  71. class="ytThumbnailBg"
  72. :style="{
  73. 'background-image':
  74. 'url(' +
  75. station.currentSong.ytThumbnail +
  76. ')'
  77. }"
  78. ></div>
  79. <img
  80. v-if="station.currentSong.ytThumbnail"
  81. :src="station.currentSong.ytThumbnail"
  82. onerror="this.src='/assets/notes-transparent.png'"
  83. />
  84. <img
  85. v-else
  86. :src="station.currentSong.thumbnail"
  87. onerror="this.src='/assets/notes-transparent.png'"
  88. />
  89. </figure>
  90. </div>
  91. <div class="card-content">
  92. <div class="media">
  93. <div class="media-left displayName">
  94. <i
  95. v-if="loggedIn && !station.isFavorited"
  96. @click.prevent="favoriteStation(station)"
  97. class="favorite material-icons"
  98. >star_border</i
  99. >
  100. <i
  101. v-if="loggedIn && station.isFavorited"
  102. @click.prevent="unfavoriteStation(station)"
  103. class="favorite material-icons"
  104. >star</i
  105. >
  106. <h5>{{ station.displayName }}</h5>
  107. <i
  108. v-if="station.type === 'official'"
  109. class="material-icons verified-station"
  110. title="Verified station"
  111. >
  112. check_circle
  113. </i>
  114. </div>
  115. </div>
  116. <div class="content">
  117. {{ station.description }}
  118. </div>
  119. <div class="under-content">
  120. <p class="hostedBy">
  121. Hosted by
  122. <span class="host">
  123. <span
  124. v-if="station.type === 'official'"
  125. title="Musare"
  126. >Musare</span
  127. >
  128. <user-id-to-username
  129. v-else
  130. :user-id="station.owner"
  131. :link="true"
  132. />
  133. </span>
  134. </p>
  135. <div class="icons">
  136. <i
  137. v-if="
  138. station.type === 'community' &&
  139. isOwner(station)
  140. "
  141. class="homeIcon material-icons"
  142. title="This is your station."
  143. >home</i
  144. >
  145. <i
  146. v-if="station.privacy === 'private'"
  147. class="privateIcon material-icons"
  148. title="This station is not visible to other users."
  149. >lock</i
  150. >
  151. <i
  152. v-if="station.privacy === 'unlisted'"
  153. class="unlistedIcon material-icons"
  154. title="Unlisted Station"
  155. >link</i
  156. >
  157. </div>
  158. </div>
  159. </div>
  160. <div class="bottomBar">
  161. <i
  162. v-if="station.paused && station.currentSong.title"
  163. class="material-icons"
  164. title="Station Paused"
  165. >pause</i
  166. >
  167. <i
  168. v-else-if="station.currentSong.title"
  169. class="material-icons"
  170. >music_note</i
  171. >
  172. <i v-else class="material-icons">music_off</i>
  173. <span
  174. v-if="station.currentSong.title"
  175. class="songTitle"
  176. :title="
  177. station.currentSong.artists.length > 0
  178. ? 'Now Playing: ' +
  179. station.currentSong.title +
  180. ' by ' +
  181. station.currentSong.artists.join(',')
  182. : 'Now Playing: ' +
  183. station.currentSong.title
  184. "
  185. >{{ station.currentSong.title }}
  186. {{
  187. station.currentSong.artists.length > 0
  188. ? " by " +
  189. station.currentSong.artists.join(",")
  190. : ""
  191. }}</span
  192. >
  193. <span v-else class="songTitle">No Songs Playing</span>
  194. </div>
  195. </router-link>
  196. </div>
  197. <div class="group bottom">
  198. <div class="group-title">
  199. <div>
  200. <h1>Stations</h1>
  201. </div>
  202. </div>
  203. <a
  204. v-if="loggedIn"
  205. @click="
  206. openModal({
  207. sector: 'home',
  208. modal: 'createCommunityStation'
  209. })
  210. "
  211. class="card station-card createStation"
  212. :style="'--station-theme: rgb(2, 166, 242)'"
  213. >
  214. <div class="card-image">
  215. <figure class="image is-square">
  216. <i class="material-icons">radio</i>
  217. <!-- <img src="/assets/notes-transparent.png" /> -->
  218. </figure>
  219. </div>
  220. <div class="card-content">
  221. <div class="media">
  222. <div class="media-left displayName">
  223. <h5>Create Station</h5>
  224. </div>
  225. </div>
  226. <div class="content">
  227. Click here to create your own station!
  228. </div>
  229. </div>
  230. <div class="bottomBar"></div>
  231. </a>
  232. <router-link
  233. v-for="(station, index) in filteredStations"
  234. :key="index"
  235. :to="{
  236. name: 'station',
  237. params: { id: station.name }
  238. }"
  239. class="card station-card"
  240. :class="{
  241. isPrivate: station.privacy === 'private',
  242. isMine: isOwner(station)
  243. }"
  244. :style="'--station-theme: ' + station.themeCode"
  245. >
  246. <div class="card-image">
  247. <figure class="image is-square">
  248. <div
  249. v-if="station.currentSong.ytThumbnail"
  250. class="ytThumbnailBg"
  251. :style="{
  252. 'background-image':
  253. 'url(' +
  254. station.currentSong.ytThumbnail +
  255. ')'
  256. }"
  257. ></div>
  258. <img
  259. v-if="station.currentSong.ytThumbnail"
  260. :src="station.currentSong.ytThumbnail"
  261. onerror="this.src='/assets/notes-transparent.png'"
  262. />
  263. <img
  264. v-else
  265. :src="station.currentSong.thumbnail"
  266. onerror="this.src='/assets/notes-transparent.png'"
  267. />
  268. </figure>
  269. </div>
  270. <div class="card-content">
  271. <div class="media">
  272. <div class="media-left displayName">
  273. <i
  274. v-if="loggedIn && !station.isFavorited"
  275. @click.prevent="favoriteStation(station)"
  276. class="favorite material-icons"
  277. >star_border</i
  278. >
  279. <i
  280. v-if="loggedIn && station.isFavorited"
  281. @click.prevent="unfavoriteStation(station)"
  282. class="favorite material-icons"
  283. >star</i
  284. >
  285. <h5>{{ station.displayName }}</h5>
  286. <i
  287. v-if="station.type === 'official'"
  288. class="material-icons verified-station"
  289. title="Verified station"
  290. >
  291. check_circle
  292. </i>
  293. </div>
  294. </div>
  295. <div class="content">
  296. {{ station.description }}
  297. </div>
  298. <div class="under-content">
  299. <p class="hostedBy">
  300. Hosted by
  301. <span class="host">
  302. <span
  303. v-if="station.type === 'official'"
  304. title="Musare"
  305. >Musare</span
  306. >
  307. <user-id-to-username
  308. v-else
  309. :user-id="station.owner"
  310. :link="true"
  311. />
  312. </span>
  313. </p>
  314. <div class="icons">
  315. <i
  316. v-if="
  317. station.type === 'community' &&
  318. isOwner(station)
  319. "
  320. class="homeIcon material-icons"
  321. title="This is your station."
  322. >home</i
  323. >
  324. <i
  325. v-if="station.privacy === 'private'"
  326. class="privateIcon material-icons"
  327. title="This station is not visible to other users."
  328. >lock</i
  329. >
  330. <i
  331. v-if="station.privacy === 'unlisted'"
  332. class="unlistedIcon material-icons"
  333. title="Unlisted Station"
  334. >link</i
  335. >
  336. </div>
  337. </div>
  338. </div>
  339. <div class="bottomBar">
  340. <i
  341. v-if="station.paused && station.currentSong.title"
  342. class="material-icons"
  343. title="Station Paused"
  344. >pause</i
  345. >
  346. <i
  347. v-else-if="station.currentSong.title"
  348. class="material-icons"
  349. >music_note</i
  350. >
  351. <i v-else class="material-icons">music_off</i>
  352. <span
  353. v-if="station.currentSong.title"
  354. class="songTitle"
  355. :title="
  356. station.currentSong.artists.length > 0
  357. ? 'Now Playing: ' +
  358. station.currentSong.title +
  359. ' by ' +
  360. station.currentSong.artists.join(',')
  361. : 'Now Playing: ' +
  362. station.currentSong.title
  363. "
  364. >{{ station.currentSong.title }}
  365. {{
  366. station.currentSong.artists.length > 0
  367. ? " by " +
  368. station.currentSong.artists.join(",")
  369. : ""
  370. }}</span
  371. >
  372. <span v-else class="songTitle">No Songs Playing</span>
  373. </div>
  374. </router-link>
  375. <h4 v-if="stations.length === 0">
  376. There are no stations to display
  377. </h4>
  378. </div>
  379. <main-footer />
  380. </div>
  381. <create-community-station v-if="modals.createCommunityStation" />
  382. </div>
  383. </template>
  384. <script>
  385. import { mapState, mapActions } from "vuex";
  386. import Toast from "toasters";
  387. import MainHeader from "../../components/layout/MainHeader.vue";
  388. import MainFooter from "../../components/layout/MainFooter.vue";
  389. import CreateCommunityStation from "./CreateCommunityStation.vue";
  390. import UserIdToUsername from "../../components/common/UserIdToUsername.vue";
  391. import io from "../../io";
  392. export default {
  393. components: {
  394. MainHeader,
  395. MainFooter,
  396. CreateCommunityStation,
  397. UserIdToUsername
  398. },
  399. data() {
  400. return {
  401. recaptcha: {
  402. key: ""
  403. },
  404. stations: [],
  405. searchQuery: "",
  406. siteName: "Musare"
  407. };
  408. },
  409. computed: {
  410. ...mapState({
  411. loggedIn: state => state.user.auth.loggedIn,
  412. userId: state => state.user.auth.userId,
  413. modals: state => state.modalVisibility.modals.home
  414. }),
  415. filteredStations() {
  416. const privacyOrder = ["public", "unlisted", "private"];
  417. return this.stations
  418. .filter(
  419. station =>
  420. JSON.stringify(Object.values(station)).indexOf(
  421. this.searchQuery
  422. ) !== -1
  423. )
  424. .sort(
  425. (a, b) =>
  426. this.isOwner(b) - this.isOwner(a) ||
  427. this.isPlaying(b) - this.isPlaying(a) ||
  428. a.paused - b.paused ||
  429. privacyOrder.indexOf(a.privacy) -
  430. privacyOrder.indexOf(b.privacy) ||
  431. b.userCount - a.userCount
  432. );
  433. },
  434. favoriteStations() {
  435. return this.filteredStations.filter(
  436. station => station.isFavorited === true
  437. );
  438. }
  439. },
  440. async mounted() {
  441. this.siteName = await lofig.get("siteSettings.siteName");
  442. io.getSocket(socket => {
  443. this.socket = socket;
  444. if (this.socket.connected) this.init();
  445. io.onConnect(() => this.init());
  446. this.socket.on("event:stations.created", res => {
  447. const station = res;
  448. if (
  449. this.stations.find(_station => _station._id === station._id)
  450. ) {
  451. this.stations.forEach(s => {
  452. const _station = s;
  453. if (_station._id === station._id) {
  454. _station.privacy = station.privacy;
  455. }
  456. });
  457. } else {
  458. if (!station.currentSong)
  459. station.currentSong = {
  460. thumbnail: "/assets/notes-transparent.png"
  461. };
  462. if (station.currentSong && !station.currentSong.thumbnail)
  463. station.currentSong.ytThumbnail = `https://img.youtube.com/vi/${station.currentSong.songId}/mqdefault.jpg`;
  464. this.stations.push(station);
  465. }
  466. });
  467. this.socket.on("event:station.removed", response => {
  468. const { stationId } = response;
  469. const station = this.stations.find(
  470. station => station._id === stationId
  471. );
  472. if (station) {
  473. const stationIndex = this.stations.indexOf(station);
  474. this.stations.splice(stationIndex, 1);
  475. }
  476. });
  477. this.socket.on(
  478. "event:userCount.updated",
  479. (stationId, userCount) => {
  480. this.stations.forEach(s => {
  481. const station = s;
  482. if (station._id === stationId) {
  483. station.userCount = userCount;
  484. }
  485. });
  486. }
  487. );
  488. this.socket.on("event:station.updatePrivacy", response => {
  489. const { stationId, privacy } = response;
  490. this.stations.forEach(s => {
  491. const station = s;
  492. if (station._id === stationId) {
  493. station.privacy = privacy;
  494. }
  495. });
  496. });
  497. this.socket.on("event:station.updateName", response => {
  498. const { stationId, name } = response;
  499. this.stations.forEach(s => {
  500. const station = s;
  501. if (station._id === stationId) {
  502. station.name = name;
  503. }
  504. });
  505. });
  506. this.socket.on("event:station.updateDisplayName", response => {
  507. const { stationId, displayName } = response;
  508. this.stations.forEach(s => {
  509. const station = s;
  510. if (station._id === stationId) {
  511. station.displayName = displayName;
  512. }
  513. });
  514. });
  515. this.socket.on("event:station.updateDescription", response => {
  516. const { stationId, description } = response;
  517. this.stations.forEach(s => {
  518. const station = s;
  519. if (station._id === stationId) {
  520. station.description = description;
  521. }
  522. });
  523. });
  524. this.socket.on("event:station.updateTheme", response => {
  525. const { stationId, theme } = response;
  526. this.stations.forEach(s => {
  527. const station = s;
  528. if (station._id === stationId) {
  529. station.theme = theme;
  530. if (theme === "blue") {
  531. station.themeCode = "rgb(2, 166, 242)";
  532. } else if (theme === "purple") {
  533. station.themeCode = "rgb(143, 40, 140)";
  534. } else if (theme === "teal") {
  535. station.themeCode = "rgb(0, 209, 178)";
  536. } else if (theme === "orange") {
  537. station.themeCode = "rgb(255, 94, 0)";
  538. } else {
  539. station.themeCode = "rgb(2, 166, 242)";
  540. }
  541. }
  542. });
  543. });
  544. this.socket.on("event:station.nextSong", (stationId, song) => {
  545. let newSong = song;
  546. this.stations.forEach(s => {
  547. const station = s;
  548. if (station._id === stationId) {
  549. if (!newSong)
  550. newSong = {
  551. thumbnail: "/assets/notes-transparent.png"
  552. };
  553. if (newSong && !newSong.thumbnail)
  554. newSong.ytThumbnail = `https://img.youtube.com/vi/${newSong.songId}/mqdefault.jpg`;
  555. station.currentSong = newSong;
  556. }
  557. });
  558. });
  559. this.socket.on("event:station.pause", response => {
  560. const { stationId } = response;
  561. this.stations.forEach(s => {
  562. const station = s;
  563. if (station._id === stationId) {
  564. station.paused = true;
  565. }
  566. });
  567. });
  568. this.socket.on("event:station.resume", response => {
  569. const { stationId } = response;
  570. this.stations.forEach(s => {
  571. const station = s;
  572. if (station._id === stationId) {
  573. station.paused = false;
  574. }
  575. });
  576. });
  577. this.socket.on("event:user.favoritedStation", stationId => {
  578. this.stations.forEach(s => {
  579. const station = s;
  580. if (station._id === stationId) {
  581. station.isFavorited = true;
  582. }
  583. });
  584. });
  585. this.socket.on("event:user.unfavoritedStation", stationId => {
  586. this.stations.forEach(s => {
  587. const station = s;
  588. if (station._id === stationId) {
  589. station.isFavorited = false;
  590. }
  591. });
  592. });
  593. });
  594. },
  595. methods: {
  596. init() {
  597. this.socket.emit("stations.index", data => {
  598. this.stations = [];
  599. if (data.status === "success")
  600. data.stations.forEach(station => {
  601. const modifiableStation = station;
  602. if (!modifiableStation.currentSong)
  603. modifiableStation.currentSong = {
  604. thumbnail: "/assets/notes-transparent.png"
  605. };
  606. if (
  607. modifiableStation.currentSong &&
  608. !modifiableStation.currentSong.thumbnail
  609. )
  610. modifiableStation.currentSong.ytThumbnail = `https://img.youtube.com/vi/${station.currentSong.songId}/mqdefault.jpg`;
  611. if (modifiableStation.theme === "blue") {
  612. modifiableStation.themeCode = "rgb(2, 166, 242)";
  613. } else if (modifiableStation.theme === "purple") {
  614. modifiableStation.themeCode = "rgb(143, 40, 140)";
  615. } else if (modifiableStation.theme === "teal") {
  616. modifiableStation.themeCode = "rgb(0, 209, 178)";
  617. } else if (modifiableStation.theme === "orange") {
  618. modifiableStation.themeCode = "rgb(255, 94, 0)";
  619. } else {
  620. modifiableStation.themeCode = "rgb(2, 166, 242)";
  621. }
  622. this.stations.push(modifiableStation);
  623. });
  624. });
  625. this.socket.emit("apis.joinRoom", "home", () => {});
  626. },
  627. isOwner(station) {
  628. return station.owner === this.userId;
  629. },
  630. isPlaying(station) {
  631. return typeof station.currentSong.title !== "undefined";
  632. },
  633. favoriteStation(station) {
  634. this.socket.emit("stations.favoriteStation", station._id, res => {
  635. if (res.status === "success") {
  636. new Toast({
  637. content: "Successfully favorited station.",
  638. timeout: 4000
  639. });
  640. } else new Toast({ content: res.message, timeout: 8000 });
  641. });
  642. },
  643. unfavoriteStation(station) {
  644. this.socket.emit("stations.unfavoriteStation", station._id, res => {
  645. if (res.status === "success") {
  646. new Toast({
  647. content: "Successfully unfavorited station.",
  648. timeout: 4000
  649. });
  650. } else new Toast({ content: res.message, timeout: 8000 });
  651. });
  652. },
  653. ...mapActions("modalVisibility", ["openModal"]),
  654. ...mapActions("station", ["updateIfStationIsFavorited"])
  655. }
  656. };
  657. </script>
  658. <style lang="scss">
  659. @import "../../styles/global.scss";
  660. * {
  661. box-sizing: border-box;
  662. }
  663. html {
  664. width: 100%;
  665. height: 100%;
  666. color: rgba(0, 0, 0, 0.87);
  667. body {
  668. width: 100%;
  669. height: 100%;
  670. margin: 0;
  671. padding: 0;
  672. }
  673. }
  674. .night-mode {
  675. .header .overlay {
  676. background: linear-gradient(
  677. 180deg,
  678. rgba(34, 34, 34, 0.8) 0%,
  679. rgba(34, 34, 34, 0.95) 31.25%,
  680. rgba(34, 34, 34, 0.9) 54.17%,
  681. rgba(34, 34, 34, 0.8) 100%
  682. );
  683. }
  684. .card,
  685. .card-content,
  686. .card-content div {
  687. background-color: $night-mode-bg-secondary;
  688. }
  689. .card-content .icons i,
  690. .group-title i {
  691. color: $night-mode-text;
  692. }
  693. .card-image .image {
  694. background-color: #333;
  695. }
  696. .card-content .under-content .hostedBy {
  697. color: $night-mode-text;
  698. }
  699. }
  700. @media only screen and (min-width: 1200px) {
  701. html {
  702. font-size: 15px;
  703. }
  704. }
  705. @media only screen and (min-width: 992px) {
  706. html {
  707. font-size: 14.5px;
  708. }
  709. }
  710. @media only screen and (min-width: 0) {
  711. html {
  712. font-size: 14px;
  713. }
  714. }
  715. .header {
  716. display: flex;
  717. height: 35vh;
  718. margin-top: -64px;
  719. border-radius: 0% 0% 33% 33% / 0% 0% 7% 7%;
  720. img.background {
  721. height: 35vh;
  722. width: 100%;
  723. object-fit: cover;
  724. object-position: center;
  725. filter: blur(1px);
  726. border-radius: 0% 0% 33% 33% / 0% 0% 7% 7%;
  727. overflow: hidden;
  728. }
  729. .overlay {
  730. background: linear-gradient(
  731. 180deg,
  732. rgba(3, 169, 244, 0.8) 0%,
  733. rgba(3, 169, 244, 0.95) 31.25%,
  734. rgba(3, 169, 244, 0.9) 54.17%,
  735. rgba(3, 169, 244, 0.8) 100%
  736. );
  737. position: absolute;
  738. height: 35vh;
  739. width: 100%;
  740. border-radius: 0% 0% 33% 33% / 0% 0% 7% 7%;
  741. overflow: hidden;
  742. }
  743. .content-container {
  744. position: absolute;
  745. left: 0;
  746. right: 0;
  747. margin-left: auto;
  748. margin-right: auto;
  749. text-align: center;
  750. height: 100%;
  751. height: 35vh;
  752. .content {
  753. position: absolute;
  754. top: 50%;
  755. left: 0;
  756. right: 0;
  757. transform: translateY(-50%);
  758. background-color: transparent !important;
  759. img.logo {
  760. max-height: 90px;
  761. font-size: 40px;
  762. color: white;
  763. font-family: Pacifico, cursive;
  764. }
  765. .buttons {
  766. display: flex;
  767. justify-content: center;
  768. margin-top: 20px;
  769. flex-wrap: wrap;
  770. .login,
  771. .register {
  772. margin: 5px 10px;
  773. padding: 10px 15px;
  774. border-radius: 5px;
  775. font-size: 18px;
  776. width: 100%;
  777. max-width: 250px;
  778. font-weight: 600;
  779. border: 0;
  780. height: inherit;
  781. }
  782. .login {
  783. background: white;
  784. color: $musare-blue;
  785. }
  786. .register {
  787. background: $purple;
  788. color: white;
  789. }
  790. }
  791. }
  792. }
  793. &.loggedIn {
  794. height: 20vh;
  795. .overlay,
  796. .content-container,
  797. img.background {
  798. height: 20vh;
  799. }
  800. }
  801. }
  802. @media only screen and (max-width: 550px) {
  803. .header {
  804. height: 45vh;
  805. .overlay,
  806. .content-container,
  807. img.background {
  808. height: 45vh;
  809. }
  810. }
  811. }
  812. .under-content {
  813. height: 20px;
  814. position: relative;
  815. line-height: 1;
  816. font-size: 24px;
  817. display: flex;
  818. align-items: center;
  819. text-align: left;
  820. margin-top: 10px;
  821. p {
  822. font-size: 15px;
  823. line-height: 15px;
  824. display: inline;
  825. }
  826. i {
  827. font-size: 20px;
  828. }
  829. * {
  830. z-index: 10;
  831. position: relative;
  832. }
  833. .icons {
  834. position: absolute;
  835. right: 0;
  836. .material-icons {
  837. font-size: 22px;
  838. }
  839. .material-icons:first-child {
  840. margin-left: 5px;
  841. }
  842. .unlistedIcon {
  843. color: $light-orange;
  844. }
  845. .privateIcon {
  846. color: $dark-pink;
  847. }
  848. .homeIcon {
  849. color: $light-purple;
  850. }
  851. }
  852. .hostedBy {
  853. font-weight: 400;
  854. font-size: 12px;
  855. color: $black;
  856. .host,
  857. .host a {
  858. font-weight: 400;
  859. color: var(--station-theme);
  860. &:hover,
  861. &:focus {
  862. filter: brightness(90%);
  863. }
  864. }
  865. }
  866. }
  867. .app {
  868. display: flex;
  869. flex-direction: column;
  870. }
  871. .users-count {
  872. font-size: 20px;
  873. position: relative;
  874. top: -4px;
  875. }
  876. .group {
  877. min-height: 64px;
  878. flex: 1 0 auto;
  879. }
  880. .station-card {
  881. display: inline-flex;
  882. flex-direction: row;
  883. overflow: hidden;
  884. margin: 10px;
  885. cursor: pointer;
  886. height: 150px;
  887. width: calc(100% - 30px);
  888. max-width: 400px;
  889. flex-wrap: wrap;
  890. border-radius: 5px;
  891. box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
  892. transition: all ease-in-out 0.2s;
  893. .card-content {
  894. padding: 10px 10px 10px 15px;
  895. display: flex;
  896. flex-direction: column;
  897. flex-grow: 1;
  898. -webkit-line-clamp: 2;
  899. .media {
  900. display: flex;
  901. align-items: center;
  902. margin-bottom: 0;
  903. .displayName {
  904. display: flex;
  905. align-items: center;
  906. width: 100%;
  907. overflow: hidden;
  908. text-overflow: ellipsis;
  909. display: flex;
  910. line-height: 30px;
  911. max-height: 30px;
  912. .favorite {
  913. position: absolute;
  914. color: $yellow;
  915. right: 10px;
  916. top: 10px;
  917. font-size: 28px;
  918. }
  919. h5 {
  920. font-size: 20px;
  921. font-weight: 400;
  922. margin: 0;
  923. display: inline;
  924. margin-right: 6px;
  925. line-height: 30px;
  926. text-overflow: ellipsis;
  927. overflow: hidden;
  928. white-space: nowrap;
  929. max-width: 200px;
  930. }
  931. i {
  932. font-size: 22px;
  933. }
  934. .verified-station {
  935. color: var(--station-theme);
  936. }
  937. }
  938. }
  939. .content {
  940. word-wrap: break-word;
  941. overflow: hidden;
  942. text-overflow: ellipsis;
  943. display: -webkit-box;
  944. -webkit-box-orient: vertical;
  945. -webkit-line-clamp: 3;
  946. line-height: 20px;
  947. flex-grow: 1;
  948. text-align: left;
  949. word-wrap: break-word;
  950. margin-bottom: 0;
  951. }
  952. }
  953. .card-image {
  954. width: 120px;
  955. .image {
  956. box-shadow: 1px 0 3px rgba(100, 100, 100, 0.3);
  957. .ytThumbnailBg {
  958. background: url("/assets/notes-transparent.png") no-repeat
  959. center center;
  960. background-size: cover;
  961. height: 100%;
  962. width: 100%;
  963. position: absolute;
  964. top: 0;
  965. filter: blur(1px);
  966. }
  967. img {
  968. height: auto;
  969. width: 120px;
  970. top: 0;
  971. margin-top: auto;
  972. margin-bottom: auto;
  973. z-index: 1;
  974. }
  975. }
  976. }
  977. .bottomBar {
  978. position: relative;
  979. display: flex;
  980. align-items: center;
  981. background: var(--station-theme);
  982. // box-shadow: inset 0px 2px 4px rgba(100, 100, 100, 0.3);
  983. width: 100%;
  984. height: 30px;
  985. line-height: 30px;
  986. color: $white;
  987. font-weight: 400;
  988. font-size: 12px;
  989. padding: 0 5px;
  990. flex-basis: 100%;
  991. i.material-icons {
  992. vertical-align: middle;
  993. margin-left: 5px;
  994. font-size: 18px;
  995. }
  996. .songTitle {
  997. text-align: left;
  998. vertical-align: middle;
  999. margin-left: 5px;
  1000. line-height: 30px;
  1001. flex: 2 1 0;
  1002. overflow: hidden;
  1003. text-overflow: ellipsis;
  1004. white-space: nowrap;
  1005. }
  1006. }
  1007. &.createStation {
  1008. .card-image .image.is-square .material-icons {
  1009. position: absolute;
  1010. top: 25px;
  1011. bottom: 25px;
  1012. left: 0;
  1013. right: 0;
  1014. text-align: center;
  1015. font-size: 70px;
  1016. color: var(--station-theme);
  1017. }
  1018. .card-content {
  1019. .media {
  1020. margin-top: auto;
  1021. .displayName h5 {
  1022. font-weight: 600;
  1023. }
  1024. }
  1025. .content {
  1026. flex-grow: unset;
  1027. margin-bottom: auto;
  1028. }
  1029. }
  1030. }
  1031. }
  1032. .station-card:hover {
  1033. box-shadow: 0 2px 3px rgba(10, 10, 10, 0.3), 0 0 10px rgba(10, 10, 10, 0.3);
  1034. transition: all ease-in-out 0.2s;
  1035. }
  1036. /*.isPrivate {
  1037. background-color: #F8BBD0;
  1038. }
  1039. .isMine {
  1040. background-color: #29B6F6;
  1041. }*/
  1042. .community-button {
  1043. cursor: pointer;
  1044. transition: 0.25s ease color;
  1045. font-size: 30px;
  1046. color: #4a4a4a;
  1047. }
  1048. .community-button:hover {
  1049. color: #03a9f4;
  1050. }
  1051. .station-privacy {
  1052. text-transform: capitalize;
  1053. }
  1054. .label {
  1055. display: flex;
  1056. }
  1057. .g-recaptcha {
  1058. display: flex;
  1059. justify-content: center;
  1060. margin-top: 20px;
  1061. }
  1062. .group {
  1063. text-align: center;
  1064. width: 100%;
  1065. margin: 10px 0;
  1066. .group-title {
  1067. display: flex;
  1068. align-items: center;
  1069. justify-content: center;
  1070. margin: 25px 0;
  1071. h1 {
  1072. display: inline-block;
  1073. font-size: 45px;
  1074. margin: 0;
  1075. }
  1076. h2 {
  1077. font-size: 35px;
  1078. margin: 0;
  1079. }
  1080. a {
  1081. display: flex;
  1082. margin-left: 8px;
  1083. }
  1084. }
  1085. &.bottom {
  1086. margin-bottom: 40px;
  1087. }
  1088. }
  1089. </style>