index.vue 25 KB

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