Feat/site player (#642)
This commit is contained in:
parent
5a83b55b7a
commit
dc9788dff4
|
|
@ -67,7 +67,7 @@
|
|||
"@uiw/codemirror-extensions-events": "4.21.3",
|
||||
"@urql/core": "4.0.10",
|
||||
"ahooks": "3.7.7",
|
||||
"aplayer-react": "1.4.0",
|
||||
"aplayer": "^1.10.1",
|
||||
"async-lock": "1.4.0",
|
||||
"canvas": "2.11.2",
|
||||
"canvas-confetti": "1.6.0",
|
||||
|
|
|
|||
|
|
@ -116,9 +116,9 @@ dependencies:
|
|||
ahooks:
|
||||
specifier: 3.7.7
|
||||
version: 3.7.7(react@18.2.0)
|
||||
aplayer-react:
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0(react@18.2.0)
|
||||
aplayer:
|
||||
specifier: ^1.10.1
|
||||
version: 1.10.1
|
||||
async-lock:
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0
|
||||
|
|
@ -5658,15 +5658,12 @@ packages:
|
|||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
|
||||
/aplayer-react@1.4.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-gYEWk1ynBSdF7qjMCo1wl2itg595iVj5jdo0Ewep9RhX7aHxdCykwp22t/cwce+KSQ6BAXg4JhUPCsm1oopiLw==}
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
/aplayer@1.10.1:
|
||||
resolution: {integrity: sha512-HAfyxgCUTLAqtYlxzzK9Fyqg6y+kZ9CqT1WfeWE8FSzwspT6oBqWOZHANPHF3RGTtC33IsyEgrfthPDzU5r9kQ==}
|
||||
dependencies:
|
||||
clsx: 1.2.1
|
||||
fast-average-color: 9.3.0
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
balloon-css: 0.5.2
|
||||
promise-polyfill: 7.1.0
|
||||
smoothscroll: 0.4.0
|
||||
dev: false
|
||||
|
||||
/aproba@2.0.0:
|
||||
|
|
@ -5948,6 +5945,10 @@ packages:
|
|||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
/balloon-css@0.5.2:
|
||||
resolution: {integrity: sha512-zheJpzwyNrG4t39vusA67v3BYg1HTVXOF8cErPEHzWK88PEOFwgo6Ea9VHOgOWNMgeuOtFVtB73NE2NWl9uDyQ==}
|
||||
dev: false
|
||||
|
||||
/balloon-css@1.2.0:
|
||||
resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
|
||||
dev: false
|
||||
|
|
@ -11243,6 +11244,10 @@ packages:
|
|||
resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==}
|
||||
dev: false
|
||||
|
||||
/promise-polyfill@7.1.0:
|
||||
resolution: {integrity: sha512-P6NJ2wU/8fac44ENORsuqT8TiolKGB2u0fEClPtXezn7w5cmLIjM/7mhPlTebke2EPr6tmqZbXvnX0TxwykGrg==}
|
||||
dev: false
|
||||
|
||||
/promise-polyfill@8.3.0:
|
||||
resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==}
|
||||
dev: false
|
||||
|
|
@ -12330,6 +12335,10 @@ packages:
|
|||
is-fullwidth-code-point: 4.0.0
|
||||
dev: true
|
||||
|
||||
/smoothscroll@0.4.0:
|
||||
resolution: {integrity: sha512-sggQ3U2Un38b3+q/j1P4Y4fCboCtoUIaBYoge+Lb6Xg1H8RTIif/hugVr+ErMtIDpvBbhQfTjtiTeYAfbw1ZGQ==}
|
||||
dev: false
|
||||
|
||||
/sonic-boom@2.8.0:
|
||||
resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { notFound } from "next/navigation"
|
|||
import { Hydrate, dehydrate } from "@tanstack/react-query"
|
||||
|
||||
import { BlockchainInfo } from "~/components/common/BlockchainInfo"
|
||||
import { SitePlayerContainer } from "~/components/common/SitePlayer"
|
||||
import Style from "~/components/common/Style"
|
||||
import { BackToTopFAB } from "~/components/site/BackToTopFAB"
|
||||
import SiteFooter from "~/components/site/SiteFooter"
|
||||
|
|
@ -208,6 +209,8 @@ export default async function SiteLayout({
|
|||
<BackToTopFAB />
|
||||
</FABContainer>
|
||||
</div>
|
||||
|
||||
<SitePlayerContainer />
|
||||
</Hydrate>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
"use client"
|
||||
|
||||
// @ts-ignore
|
||||
import APlayer from "aplayer"
|
||||
import "aplayer/dist/APlayer.min.css"
|
||||
|
||||
let siteAPlayer: APlayer
|
||||
|
||||
const playerId = "site-aplayer"
|
||||
|
||||
// APlayer Container
|
||||
export const SitePlayerContainer = () => {
|
||||
return <div id={playerId} />
|
||||
}
|
||||
|
||||
export interface AudioInfo {
|
||||
name?: string
|
||||
artist?: string
|
||||
cover?: string
|
||||
lrc?: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export const sitePlayerAddToPlaylist = (audio: AudioInfo) => {
|
||||
// Check if player is initialized
|
||||
if (!siteAPlayer) {
|
||||
// Initialize
|
||||
siteAPlayer = new APlayer({
|
||||
container: document.getElementById(playerId),
|
||||
fixed: true,
|
||||
listFolded: false,
|
||||
})
|
||||
}
|
||||
// Add
|
||||
siteAPlayer.list.add(audio)
|
||||
}
|
||||
|
||||
export const sitePlayerPlayNow = (audio: AudioInfo) => {
|
||||
// Add to playlist
|
||||
sitePlayerAddToPlaylist(audio)
|
||||
// Play now
|
||||
siteAPlayer.list.switch(siteAPlayer.list.audios.length - 1)
|
||||
siteAPlayer.play()
|
||||
}
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
"use client"
|
||||
|
||||
import { APlayer as AplayerReact } from "aplayer-react"
|
||||
import "aplayer-react/dist/index.css"
|
||||
import Image from "next/image"
|
||||
import { memo } from "react"
|
||||
|
||||
import {
|
||||
sitePlayerAddToPlaylist,
|
||||
sitePlayerPlayNow,
|
||||
} from "~/components/common/SitePlayer"
|
||||
import { Tooltip } from "~/components/ui/Tooltip"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { toGateway } from "~/lib/ipfs-parser"
|
||||
|
||||
const APlayer = memo(function APlayer({
|
||||
|
|
@ -21,6 +26,8 @@ const APlayer = memo(function APlayer({
|
|||
cover?: string
|
||||
lrc?: string
|
||||
} & React.AudioHTMLAttributes<HTMLAudioElement>) {
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
if (!src) return null
|
||||
|
||||
src = toGateway(src)
|
||||
|
|
@ -31,19 +38,74 @@ const APlayer = memo(function APlayer({
|
|||
name = name.replace(/^user-content-/, "")
|
||||
}
|
||||
|
||||
const audioInfo = {
|
||||
name: name || "xLog audio",
|
||||
artist: artist || "",
|
||||
cover: cover || "/assets/logo.png",
|
||||
lrc,
|
||||
url: src,
|
||||
}
|
||||
|
||||
const addToList = () => {
|
||||
sitePlayerAddToPlaylist(audioInfo)
|
||||
}
|
||||
|
||||
const playNow = () => {
|
||||
sitePlayerPlayNow(audioInfo)
|
||||
}
|
||||
|
||||
return (
|
||||
<AplayerReact
|
||||
audio={{
|
||||
name: name || "xLog audio",
|
||||
artist: artist || "",
|
||||
cover: cover || "/assets/logo.png",
|
||||
lrc,
|
||||
url: src,
|
||||
}}
|
||||
volume={muted ? 0 : 1}
|
||||
autoPlay={autoPlay}
|
||||
initialLoop={loop ? "one" : "none"}
|
||||
/>
|
||||
<div className="border border-[var(--border-color)] rounded-lg outline-2 outline-transparent flex flex-row gap-4">
|
||||
{/*Info*/}
|
||||
<div className="flex flex-row flex-grow gap-4">
|
||||
{/*Cover*/}
|
||||
<div className="shrink-0">
|
||||
<Image
|
||||
width={72}
|
||||
height={72}
|
||||
alt={audioInfo.name}
|
||||
src={audioInfo.cover}
|
||||
className="rounded-l-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/*Name & Artist*/}
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
{/*Name*/}
|
||||
<div className="text-md font-semibold">{audioInfo.name}</div>
|
||||
|
||||
{/*Artist*/}
|
||||
<div className="text-sm">{audioInfo.artist}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/*Control buttons*/}
|
||||
<div className="flex flex-row items-center mr-4 gap-3">
|
||||
{/*Play Now*/}
|
||||
<div>
|
||||
<Tooltip label={t("Play")}>
|
||||
<button
|
||||
onClick={playNow}
|
||||
className="p-2 rounded-full bg-accent opacity-90 hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<i className="icon-[mingcute--play-fill] block text-4xl text-white" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{/*Add to playlist*/}
|
||||
<div>
|
||||
<Tooltip label={t("Add to playlist")}>
|
||||
<button
|
||||
onClick={addToList}
|
||||
className="p-2 rounded-full bg-gray-50 hover:bg-gray-100"
|
||||
>
|
||||
<i className="icon-[mingcute--calendar-add-line] block text-2xl" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -55,5 +55,7 @@
|
|||
"Showcase": "ショーケース",
|
||||
"AI-generated summary": "AIが生成した要約",
|
||||
"Generating": "生成中",
|
||||
"Show more": "もっと表示する"
|
||||
"Show more": "もっと表示する",
|
||||
"Play": "再生",
|
||||
"Add to playlist": "プレイリストに追加する"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,5 +83,7 @@
|
|||
"Refreshing": "重新載入中",
|
||||
"Search for your interest": "搜你所想",
|
||||
"results": "項結果",
|
||||
"My xLog": "你的 xLog"
|
||||
}
|
||||
"My xLog": "你的 xLog",
|
||||
"Play": "播放",
|
||||
"Add to playlist": "添加到播放列表"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,5 +88,7 @@
|
|||
"Comment Without Login": "免登录评论",
|
||||
"Name": "昵称",
|
||||
"Email": "电子邮箱",
|
||||
"You'll use an official public account for comments and give up blockchain ownership": "你将使用官方公共账号进行评论并放弃区块链所有权"
|
||||
"You'll use an official public account for comments and give up blockchain ownership": "你将使用官方公共账号进行评论并放弃区块链所有权",
|
||||
"Play": "播放",
|
||||
"Add to playlist": "添加到播放列表"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue