feat: 支持弹幕显示
This commit is contained in:
parent
5c6b5b9761
commit
105f23d8a2
@ -1,33 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* :root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
} */
|
@ -15,7 +15,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html lang="zh_CN" data-theme="light">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
|
96
app/music/danmu.tsx
Normal file
96
app/music/danmu.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
'use client';
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import useWebSocket, { ReadyState } from 'react-use-websocket';
|
||||
|
||||
const MOMO_IM_URL = 'wss://live-ws.immomo.com/ws/im'
|
||||
|
||||
export const Danmu: React.FC = () => {
|
||||
//Public API that will echo messages sent to it back to the client
|
||||
const [socketUrl, setSocketUrl] = useState(MOMO_IM_URL);
|
||||
const [messageHistory, setMessageHistory] = useState<MessageEvent<any>[]>([]);
|
||||
|
||||
// { "msg_id": 1, "client_time": 1712305590234, "type": "Sauth", "data": { "momoid": "404821828", "roomid": "14515460054", "role": 6, "isVisitor": false, "token": "97e721d0bd3bf2e63a9797f9daf50919", "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" } }
|
||||
const { sendMessage, lastMessage, readyState } = useWebSocket(socketUrl, {
|
||||
heartbeat:
|
||||
{
|
||||
message: () => {
|
||||
const pingMessage = { "msg_id": 2, "client_time": Date.now(), "type": "Ping", "data": {} }
|
||||
return JSON.stringify(pingMessage);
|
||||
},
|
||||
interval: 5000,
|
||||
}
|
||||
});
|
||||
|
||||
const momoId = "404821828"
|
||||
const roomId = "14515460054"
|
||||
const momoToken = "97e721d0bd3bf2e63a9797f9daf50919"
|
||||
|
||||
const clientTime = Date.now()
|
||||
const data = {
|
||||
"msg_id": 1,
|
||||
"client_time": clientTime,
|
||||
"type": "Sauth",
|
||||
"data": {
|
||||
"momoid": momoId, "roomid": roomId,
|
||||
"role": 6, "isVisitor": false,
|
||||
"token": momoToken
|
||||
},
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (ReadyState.OPEN === readyState) {
|
||||
sendMessage(JSON.stringify(data));
|
||||
}
|
||||
}, [readyState])
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (lastMessage !== null) {
|
||||
if (lastMessage.data) {
|
||||
const message = JSON.parse(lastMessage.data);
|
||||
if (message.type === 'pong') {
|
||||
return;
|
||||
}
|
||||
setMessageHistory((prev) => prev.concat(message));
|
||||
}
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
|
||||
const connectionStatus = {
|
||||
[ReadyState.CONNECTING]: 'Connecting',
|
||||
[ReadyState.OPEN]: 'Open',
|
||||
[ReadyState.CLOSING]: 'Closing',
|
||||
[ReadyState.CLOSED]: 'Closed',
|
||||
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
|
||||
}[readyState];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{messageHistory.map((message, idx) => (
|
||||
<MessageDispatcher key={idx} message={message} />
|
||||
))}
|
||||
<div className="chat chat-end">
|
||||
<div className="chat-bubble">You underestimate my power!</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO
|
||||
const MessageDispatcher: React.FC = ({ message }) => {
|
||||
|
||||
const messageType = message?.groups?.[0]?.units?.[0]?.type
|
||||
let nick = 'wait';
|
||||
let content = 'wait';
|
||||
|
||||
if (messageType === 'Message') {
|
||||
nick = message?.groups?.[0]?.nick;
|
||||
content = message?.groups?.[0]?.units?.[0]?.['Msg.Message.data']?.text
|
||||
}
|
||||
return (<div className="chat chat-start">
|
||||
<div className="chat-bubble">{nick}: {content}</div>
|
||||
</div>)
|
||||
|
||||
};
|
@ -1,3 +1,7 @@
|
||||
import { Danmu } from "./danmu";
|
||||
|
||||
export default function Page() {
|
||||
return <main>hello world</main>
|
||||
return <main>hello world
|
||||
<Danmu />
|
||||
</main>
|
||||
}
|
229
app/page.tsx
229
app/page.tsx
@ -3,115 +3,122 @@ import SignoutButton from "@/app/components/signout-button";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
<SignoutButton />
|
||||
<Link className="btn" href="/music">弹幕测试</Link>
|
||||
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
|
||||
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
||||
Get started by editing
|
||||
<code className="font-mono font-bold">app/page.tsx</code>
|
||||
</p>
|
||||
<div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
|
||||
<a
|
||||
className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{" "}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className="dark:invert"
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex place-items-center before:absolute before:h-[300px] before:w-full sm:before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full sm:after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
|
||||
<Image
|
||||
className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Docs{" "}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Find in-depth information about Next.js features and API.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Learn{" "}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Learn about Next.js in an interactive course with quizzes!
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Templates{" "}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
Explore starter templates for Next.js.
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
Deploy{" "}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className={`m-0 max-w-[30ch] text-sm opacity-50 text-balance`}>
|
||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
return <main>
|
||||
<SignoutButton />
|
||||
<Link className="btn" href="/music">弹幕测试</Link>
|
||||
</main>
|
||||
}
|
||||
|
||||
// export default function Home() {
|
||||
// return (
|
||||
// <main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
// <SignoutButton />
|
||||
// <Link className="btn" href="/music">弹幕测试</Link>
|
||||
// <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
|
||||
// <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
||||
// Get started by editing
|
||||
// <code className="font-mono font-bold">app/page.tsx</code>
|
||||
// </p>
|
||||
// <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
|
||||
// <a
|
||||
// className="pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0"
|
||||
// href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// By{" "}
|
||||
// <Image
|
||||
// src="/vercel.svg"
|
||||
// alt="Vercel Logo"
|
||||
// className="dark:invert"
|
||||
// width={100}
|
||||
// height={24}
|
||||
// priority
|
||||
// />
|
||||
// </a>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div className="relative flex place-items-center before:absolute before:h-[300px] before:w-full sm:before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-full sm:after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px] z-[-1]">
|
||||
// <Image
|
||||
// className="relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
||||
// src="/next.svg"
|
||||
// alt="Next.js Logo"
|
||||
// width={180}
|
||||
// height={37}
|
||||
// priority
|
||||
// />
|
||||
// </div>
|
||||
|
||||
// <div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
||||
// <a
|
||||
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Docs{" "}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Find in-depth information about Next.js features and API.
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
// <a
|
||||
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Learn{" "}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Learn about Next.js in an interactive course with quizzes!
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
// <a
|
||||
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Templates{" "}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
|
||||
// Explore starter templates for Next.js.
|
||||
// </p>
|
||||
// </a>
|
||||
|
||||
// <a
|
||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
// className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <h2 className={`mb-3 text-2xl font-semibold`}>
|
||||
// Deploy{" "}
|
||||
// <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
// ->
|
||||
// </span>
|
||||
// </h2>
|
||||
// <p className={`m-0 max-w-[30ch] text-sm opacity-50 text-balance`}>
|
||||
// Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
// </p>
|
||||
// </a>
|
||||
// </div>
|
||||
// </main>
|
||||
// );
|
||||
// }
|
||||
|
@ -12,7 +12,8 @@
|
||||
"axios": "^1.6.8",
|
||||
"next": "14.1.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react-dom": "^18",
|
||||
"react-use-websocket": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
|
@ -17,6 +17,9 @@ dependencies:
|
||||
react-dom:
|
||||
specifier: ^18
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-use-websocket:
|
||||
specifier: ^4.8.1
|
||||
version: 4.8.1(react-dom@18.2.0)(react@18.2.0)
|
||||
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
@ -2393,6 +2396,16 @@ packages:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
dev: true
|
||||
|
||||
/react-use-websocket@4.8.1(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-FTXuG5O+LFozmu1BRfrzl7UIQngECvGJmL7BHsK4TYXuVt+mCizVA8lT0hGSIF0Z0TedF7bOo1nRzOUdginhDw==}
|
||||
peerDependencies:
|
||||
react: '>= 18.0.0'
|
||||
react-dom: '>= 18.0.0'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react@18.2.0:
|
||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -17,5 +17,15 @@ const config: Config = {
|
||||
},
|
||||
},
|
||||
plugins: [daisyui],
|
||||
daisyui: {
|
||||
themes: false, // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
|
||||
darkTheme: "dark", // name of one of the included themes for dark mode
|
||||
base: true, // applies background color and foreground color for root element by default
|
||||
styled: true, // include daisyUI colors and design decisions for all components
|
||||
utils: true, // adds responsive and modifier utility classes
|
||||
prefix: "", // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors)
|
||||
logs: true, // Shows info about daisyUI version and used config in the console when building your CSS
|
||||
themeRoot: ":root", // The element that receives theme color CSS variables
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
|
Loading…
Reference in New Issue
Block a user