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 { Translation } from "react-i18next"
|
||||
|
||||
import Image from "components/Image"
|
||||
import Searcher from "components/Searcher"
|
||||
import { Icons, createIconRender } from "components/Icons"
|
||||
|
||||
import { WithPlayerContext } from "contexts/WithPlayerContext"
|
||||
|
||||
import FeedModel from "models/feed"
|
||||
import MusicModel from "models/music"
|
||||
|
||||
@ -16,6 +15,48 @@ import PlaylistItem from "components/Music/PlaylistItem"
|
||||
|
||||
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) => {
|
||||
return <div className="music_navbar">
|
||||
<Searcher
|
||||
@ -206,39 +247,39 @@ const SearchResults = ({
|
||||
}
|
||||
)}
|
||||
>
|
||||
{
|
||||
groupsKeys.map((key, index) => {
|
||||
const decorator = ResultGroupsDecorators[key] ?? {
|
||||
icon: null,
|
||||
label: key,
|
||||
renderItem: () => null
|
||||
}
|
||||
{
|
||||
groupsKeys.map((key, index) => {
|
||||
const decorator = ResultGroupsDecorators[key] ?? {
|
||||
icon: null,
|
||||
label: key,
|
||||
renderItem: () => null
|
||||
}
|
||||
|
||||
return <div className="music-explorer_search_results_group" key={index}>
|
||||
<div className="music-explorer_search_results_group_header">
|
||||
<h1>
|
||||
{
|
||||
createIconRender(decorator.icon)
|
||||
}
|
||||
<Translation>
|
||||
{(t) => t(decorator.label)}
|
||||
</Translation>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="music-explorer_search_results_group_list">
|
||||
return <div className="music-explorer_search_results_group" key={index}>
|
||||
<div className="music-explorer_search_results_group_header">
|
||||
<h1>
|
||||
{
|
||||
data[key].map((item, index) => {
|
||||
return decorator.renderItem({
|
||||
key: index,
|
||||
item
|
||||
})
|
||||
})
|
||||
createIconRender(decorator.icon)
|
||||
}
|
||||
</div>
|
||||
<Translation>
|
||||
{(t) => t(decorator.label)}
|
||||
</Translation>
|
||||
</h1>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
<div className="music-explorer_search_results_group_list">
|
||||
{
|
||||
data[key].map((item, index) => {
|
||||
return decorator.renderItem({
|
||||
key: index,
|
||||
item
|
||||
})
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -287,6 +328,8 @@ export default (props) => {
|
||||
|
||||
{
|
||||
!searchResults && <div className="feed_main">
|
||||
<FeaturedPlaylist />
|
||||
|
||||
<PlaylistsList
|
||||
headerTitle="From your following artists"
|
||||
headerIcon={<Icons.MdPerson />}
|
||||
|
@ -1,7 +1,6 @@
|
||||
html {
|
||||
&.mobile {
|
||||
.musicExplorer {
|
||||
|
||||
.playlistExplorer_section_list {
|
||||
overflow: visible;
|
||||
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 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -7,6 +7,21 @@ export default class MusicModel {
|
||||
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.
|
||||
*
|
||||
|
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 { 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