Feat/site player (#642)

This commit is contained in:
Nya Candy 2023-06-21 06:19:18 +08:00 committed by GitHub
parent 5a83b55b7a
commit dc9788dff4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 154 additions and 30 deletions

View File

@ -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",

View File

@ -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:

View File

@ -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>
)
}

View File

@ -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()
}

View File

@ -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>
)
})

View File

@ -55,5 +55,7 @@
"Showcase": "ショーケース",
"AI-generated summary": "AIが生成した要約",
"Generating": "生成中",
"Show more": "もっと表示する"
"Show more": "もっと表示する",
"Play": "再生",
"Add to playlist": "プレイリストに追加する"
}

View File

@ -83,5 +83,7 @@
"Refreshing": "重新載入中",
"Search for your interest": "搜你所想",
"results": "項結果",
"My xLog": "你的 xLog"
}
"My xLog": "你的 xLog",
"Play": "播放",
"Add to playlist": "添加到播放列表"
}

View File

@ -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": "添加到播放列表"
}