mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
added featured playlists feature
This commit is contained in:
parent
fa051b73b0
commit
518d2d498a
@ -3,11 +3,10 @@ import * as antd from "antd"
|
|||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
import { Translation } from "react-i18next"
|
import { Translation } from "react-i18next"
|
||||||
|
|
||||||
|
import Image from "components/Image"
|
||||||
import Searcher from "components/Searcher"
|
import Searcher from "components/Searcher"
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
|
|
||||||
import { WithPlayerContext } from "contexts/WithPlayerContext"
|
|
||||||
|
|
||||||
import FeedModel from "models/feed"
|
import FeedModel from "models/feed"
|
||||||
import MusicModel from "models/music"
|
import MusicModel from "models/music"
|
||||||
|
|
||||||
@ -16,6 +15,48 @@ import PlaylistItem from "components/Music/PlaylistItem"
|
|||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
|
const FeaturedPlaylist = (props) => {
|
||||||
|
const [featuredPlaylist, setFeaturedPlaylist] = React.useState(false)
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!featuredPlaylist) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.navigation.goToPlaylist(featuredPlaylist.playlist_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
MusicModel.getFeaturedPlaylists().then((data) => {
|
||||||
|
if (data[0]) {
|
||||||
|
console.log(`Loaded featured playlist >`, data[0])
|
||||||
|
setFeaturedPlaylist(data[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!featuredPlaylist) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="featured_playlist" onClick={onClick}>
|
||||||
|
<Image
|
||||||
|
src={featuredPlaylist.cover_url}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="featured_playlist_content">
|
||||||
|
<h1>{featuredPlaylist.title}</h1>
|
||||||
|
<p>{featuredPlaylist.description}</p>
|
||||||
|
|
||||||
|
{
|
||||||
|
featuredPlaylist.genre && <div className="featured_playlist_genre">
|
||||||
|
<span>{featuredPlaylist.genre}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
const MusicNavbar = (props) => {
|
const MusicNavbar = (props) => {
|
||||||
return <div className="music_navbar">
|
return <div className="music_navbar">
|
||||||
<Searcher
|
<Searcher
|
||||||
@ -206,39 +247,39 @@ const SearchResults = ({
|
|||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
groupsKeys.map((key, index) => {
|
groupsKeys.map((key, index) => {
|
||||||
const decorator = ResultGroupsDecorators[key] ?? {
|
const decorator = ResultGroupsDecorators[key] ?? {
|
||||||
icon: null,
|
icon: null,
|
||||||
label: key,
|
label: key,
|
||||||
renderItem: () => null
|
renderItem: () => null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="music-explorer_search_results_group" key={index}>
|
return <div className="music-explorer_search_results_group" key={index}>
|
||||||
<div className="music-explorer_search_results_group_header">
|
<div className="music-explorer_search_results_group_header">
|
||||||
<h1>
|
<h1>
|
||||||
{
|
|
||||||
createIconRender(decorator.icon)
|
|
||||||
}
|
|
||||||
<Translation>
|
|
||||||
{(t) => t(decorator.label)}
|
|
||||||
</Translation>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="music-explorer_search_results_group_list">
|
|
||||||
{
|
{
|
||||||
data[key].map((item, index) => {
|
createIconRender(decorator.icon)
|
||||||
return decorator.renderItem({
|
|
||||||
key: index,
|
|
||||||
item
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
</div>
|
<Translation>
|
||||||
|
{(t) => t(decorator.label)}
|
||||||
|
</Translation>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
})
|
|
||||||
}
|
<div className="music-explorer_search_results_group_list">
|
||||||
|
{
|
||||||
|
data[key].map((item, index) => {
|
||||||
|
return decorator.renderItem({
|
||||||
|
key: index,
|
||||||
|
item
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +328,8 @@ export default (props) => {
|
|||||||
|
|
||||||
{
|
{
|
||||||
!searchResults && <div className="feed_main">
|
!searchResults && <div className="feed_main">
|
||||||
|
<FeaturedPlaylist />
|
||||||
|
|
||||||
<PlaylistsList
|
<PlaylistsList
|
||||||
headerTitle="From your following artists"
|
headerTitle="From your following artists"
|
||||||
headerIcon={<Icons.MdPerson />}
|
headerIcon={<Icons.MdPerson />}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
html {
|
html {
|
||||||
&.mobile {
|
&.mobile {
|
||||||
.musicExplorer {
|
.musicExplorer {
|
||||||
|
|
||||||
.playlistExplorer_section_list {
|
.playlistExplorer_section_list {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
@ -16,6 +15,101 @@ html {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.featured_playlist {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.featured_playlist_content {
|
||||||
|
|
||||||
|
h1,
|
||||||
|
p {
|
||||||
|
-webkit-text-stroke-width: 1.6px;
|
||||||
|
-webkit-text-stroke-color: var(--border-color);
|
||||||
|
|
||||||
|
color: var(--background-color-contrast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lazy-load-image-background {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lazy-load-image-background {
|
||||||
|
z-index: 50;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
opacity: 0.3;
|
||||||
|
|
||||||
|
transition: all 300ms ease-in-out !important;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured_playlist_content {
|
||||||
|
z-index: 55;
|
||||||
|
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-weight: 900;
|
||||||
|
|
||||||
|
transition: all 300ms ease-in-out !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
|
||||||
|
transition: all 300ms ease-in-out !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured_playlist_genre {
|
||||||
|
z-index: 55;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
margin: 10px;
|
||||||
|
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.music_navbar {
|
.music_navbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -7,6 +7,21 @@ export default class MusicModel {
|
|||||||
return globalThis.__comty_shared_state.instances["music"]
|
return globalThis.__comty_shared_state.instances["music"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the official featured playlists.
|
||||||
|
*
|
||||||
|
* @return {Promise<Object>} The data containing the featured playlists.
|
||||||
|
*/
|
||||||
|
static async getFeaturedPlaylists() {
|
||||||
|
const response = await request({
|
||||||
|
instance: MusicModel.api_instance,
|
||||||
|
method: "GET",
|
||||||
|
url: "/featured/playlists",
|
||||||
|
})
|
||||||
|
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves track data for a given ID.
|
* Retrieves track data for a given ID.
|
||||||
*
|
*
|
||||||
|
21
packages/music_server/src/controllers/featured/index.js
Normal file
21
packages/music_server/src/controllers/featured/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import path from "path"
|
||||||
|
import createRoutesFromDirectory from "@utils/createRoutesFromDirectory"
|
||||||
|
import getMiddlewares from "@utils/getMiddlewares"
|
||||||
|
|
||||||
|
export default async (router) => {
|
||||||
|
// create a file based router
|
||||||
|
const routesPath = path.resolve(__dirname, "routes")
|
||||||
|
|
||||||
|
const middlewares = await getMiddlewares(["withOptionalAuth"])
|
||||||
|
|
||||||
|
for (const middleware of middlewares) {
|
||||||
|
router.use(middleware)
|
||||||
|
}
|
||||||
|
|
||||||
|
router = createRoutesFromDirectory("routes", routesPath, router)
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: "/featured",
|
||||||
|
router,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { FeaturedPlaylist } from "@shared-classes/DbModels"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const includeDisabled = req.query["include-disabled"] === "true"
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDisabled) {
|
||||||
|
query.enabled = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
let playlists = await FeaturedPlaylist.find(query).catch((error) => {
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.json(playlists)
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
global.FORCE_ENV = "prod"
|
||||||
|
|
||||||
import Boot from "linebridge/bootstrap"
|
import Boot from "linebridge/bootstrap"
|
||||||
import { Server } from "linebridge/dist/server"
|
import { Server } from "linebridge/dist/server"
|
||||||
|
|
||||||
|
12
shared/classes/DbModels/featuredPlaylist/index.js
Normal file
12
shared/classes/DbModels/featuredPlaylist/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
name: "FeaturedPlaylist",
|
||||||
|
collection: "featuredPlaylists",
|
||||||
|
schema: {
|
||||||
|
title: { type: String, required: true },
|
||||||
|
description: { type: String },
|
||||||
|
cover_url: { type: String },
|
||||||
|
enabled: { type: Boolean, default: true },
|
||||||
|
genre: { type: String },
|
||||||
|
playlist_id: { type: String, required: true },
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user