添加signal

main
落雨楓 5 months ago
parent a966aa9378
commit 5d7f192b45

@ -10,12 +10,7 @@ var router *gin.Engine
func init() { func init() {
serverOpts := socket.DefaultServerOptions() serverOpts := socket.DefaultServerOptions()
serverOpts.SetConnectionStateRecovery(nil)
csrOpts := &socket.ConnectionStateRecovery{}
csrOpts.SetMaxDisconnectionDuration(2 * 60 * 1000) // 2 minutes
csrOpts.SetSkipMiddlewares(true)
serverOpts.SetConnectionStateRecovery(csrOpts)
server = socket.NewServer(nil, serverOpts) server = socket.NewServer(nil, serverOpts)

@ -0,0 +1,52 @@
import { useMemo, useRef, useState } from "react";
export interface SignalWrapper<T> {
value: T;
setValue: (value: T) => void;
update: () => void;
}
export function useSignal<T>(): SignalWrapper<T | undefined>;
export function useSignal<T>(initialValue: T): SignalWrapper<T>;
export function useSignal<T>(initialValue?: T): SignalWrapper<T | undefined> {
const $value = useRef<T | undefined>(initialValue);
const [_, updateState] = useState(0);
return useMemo(() => {
const setValue = (value: T | undefined) => {
$value.current = value;
updateState((prev) => prev + 1);
};
const update = () => {
updateState((prev) => prev + 1);
};
const proxy: SignalWrapper<T | undefined> = new Proxy({} as SignalWrapper<T | undefined>, {
get(_, prop) {
if (prop === 'value') {
return $value.current;
}
if (prop === 'setValue') {
return setValue;
}
if (prop === 'update') {
return update;
}
// 处理其他可能的属性访问
return undefined;
},
set(_, prop, value) {
if (prop === 'value') {
$value.current = value;
// 避免直接调用 update而是直接更新状态
updateState((prev) => prev + 1);
return true;
}
return false;
}
});
return proxy;
}, [updateState]);
}

@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { Label } from '@radix-ui/react-label' import { Label } from '@radix-ui/react-label'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
import Head from 'next/head'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useState } from 'react' import { useState } from 'react'
@ -25,6 +26,9 @@ export default function Home() {
<main <main
className={`${inter.className}`} className={`${inter.className}`}
> >
<Head>
<title></title>
</Head>
<div className='flex justify-center items-center h-screen'> <div className='flex justify-center items-center h-screen'>
<form className='flex flex-col gap-3' onSubmit={onSubmit}> <form className='flex flex-col gap-3' onSubmit={onSubmit}>
<Label className='font-bold text-center'></Label> <Label className='font-bold text-center'></Label>

@ -8,18 +8,20 @@ import { ClientMessage, RoomStateChangedMessage, ServerMessage, UserJoinLeaveMes
import { $playerState, $userInfo, $userStatus } from '@/store/player' import { $playerState, $userInfo, $userStatus } from '@/store/player'
import { useStore } from '@nanostores/react' import { useStore } from '@nanostores/react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import "@radix-ui/themes/components/tabs" import "@radix-ui/themes/components/tabs"
import { Label } from '@/components/ui/label' import { Label } from '@/components/ui/label'
import { useSignal } from '@/lib/signal'
import Head from 'next/head'
export default function Page() { export default function Page() {
const router = useRouter() const router = useRouter()
const userInfo = useStore($userInfo) const userInfo = useStore($userInfo)
const playerState = useStore($playerState)
const [joinRole, setJoinRole] = useState<string>('user') const joinRole = useSignal<string>('user')
const [urlInput, setUrlInput] = useState<string | undefined>() const urlInput = useSignal<string | undefined>()
const [isRoomEmpty, setIsRoomEmpty] = useState(false) const isRoomEmpty = useSignal(false)
const [isJoined, setIsJoined] = useState(false) const isJoined = useSignal(false)
const roomName = router.query.room as string const roomName = router.query.room as string
@ -34,12 +36,12 @@ export default function Page() {
function onRoomInfo(d: any) { function onRoomInfo(d: any) {
const msg = d as RoomStateChangedMessage const msg = d as RoomStateChangedMessage
if (!msg.userStatus || msg.userStatus.length === 0) { if (!msg.userStatus || msg.userStatus.length === 0) {
setIsRoomEmpty(true) isRoomEmpty.value = true
setJoinRole('admin') joinRole.value = 'admin'
return return
} else { } else {
setIsRoomEmpty(false) isRoomEmpty.value = false
setJoinRole('user') joinRole.value = 'user'
} }
$userStatus.set([ $userStatus.set([
@ -53,7 +55,7 @@ export default function Page() {
if (!msg.userInfo) { if (!msg.userInfo) {
return return
} }
setIsJoined(true) isJoined.value = true
$userInfo.set({ $userInfo.set({
...$userInfo.value, ...$userInfo.value,
isAdmin: msg.userInfo.isAdmin, isAdmin: msg.userInfo.isAdmin,
@ -62,6 +64,10 @@ export default function Page() {
console.log('$userInfo', $userInfo.value) console.log('$userInfo', $userInfo.value)
} }
function onSetUrl(d: any) {
}
socket.on('connect', onConnect) socket.on('connect', onConnect)
socket.on('roomInfo', onRoomInfo) socket.on('roomInfo', onRoomInfo)
socket.on('joined', onJoined) socket.on('joined', onJoined)
@ -80,35 +86,38 @@ export default function Page() {
const handleJoinClick = useCallback(() => { const handleJoinClick = useCallback(() => {
socket.connect() socket.connect()
socket.emit('join', { socket.emit('join', {
username: userInfo?.username, username: $userInfo.value?.username,
room: roomName, room: roomName,
password: userInfo?.password, password: $userInfo.value?.password,
} as ClientMessage) } as ClientMessage)
}, [userInfo, roomName]) }, [roomName])
const handleSetUrlClick = useCallback(() => { const handleSetUrlClick = useCallback(() => {
$playerState.set({ ...$playerState.value, url: urlInput }) $playerState.set({ ...$playerState.value, url: urlInput.value })
socket.emit('setUrl', { socket.emit('setUrl', {
room: roomName, room: roomName,
username: userInfo?.username, username: $userInfo.value?.username,
url: urlInput url: urlInput.value,
} as ClientMessage) } as ClientMessage)
}, [roomName, userInfo, urlInput]) }, [])
const isAllowedLogin = useCallback(() => { const isAllowedLogin = useCallback(() => {
if (isJoined) { if (isJoined.value) {
return false return false
} }
if (joinRole === 'admin' && !userInfo?.password) { if (joinRole.value === 'admin' && !$userInfo.value?.password) {
return false return false
} }
return userInfo?.username && userInfo.username.length > 0 return $userInfo.value?.username && $userInfo.value.username.length > 0
}, [joinRole, isJoined, userInfo]) }, [])
return ( return (
<div className='flex flex-col lg:flex-row m-2 justify-center'> <div className='flex flex-col lg:flex-row m-2 justify-center'>
{isJoined && <div className='w-full lg:mr-2'> <Head>
<title>{roomName} | </title>
</Head>
{isJoined.value && <div className='w-full lg:mr-2'>
<Player roomName={roomName} /> <Player roomName={roomName} />
</div>} </div>}
<div className='w-full lg:w-[450px] mb-1 border rounded'> <div className='w-full lg:w-[450px] mb-1 border rounded'>
@ -117,12 +126,12 @@ export default function Page() {
<div className='flex flex-row gap-4 items-center'> <div className='flex flex-row gap-4 items-center'>
<Badge className='!px-2 !py-1 mt-1'></Badge> <Badge className='!px-2 !py-1 mt-1'></Badge>
<Tabs.Root value={joinRole} onValueChange={setJoinRole}> <Tabs.Root value={joinRole.value} onValueChange={joinRole.setValue}>
<Tabs.List size="1"> <Tabs.List size="1">
<Tabs.Trigger <Tabs.Trigger
value="user" value="user"
disabled={isRoomEmpty} disabled={isRoomEmpty.value}
title={isRoomEmpty ? '创建房间必须使用房管身份' : undefined}> title={isRoomEmpty.value ? '创建房间必须使用房管身份' : undefined}>
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger value="admin"></Tabs.Trigger> <Tabs.Trigger value="admin"></Tabs.Trigger>
@ -132,51 +141,51 @@ export default function Page() {
</div> </div>
{roomName && <div className='m-2 flex flex-row gap-2'> {roomName && <div className='m-2 flex flex-row gap-2'>
<Input <Input
value={userInfo?.username ?? ""}
onChange={(e) => { onChange={(e) => {
$userInfo.set({ $userInfo.set({
...userInfo, ...$userInfo.value,
username: e.target.value username: e.target.value
}) })
}} }}
value={userInfo?.username ?? ""}
placeholder='你的用户名' placeholder='你的用户名'
name='username' name='username'
disabled={isJoined} disabled={isJoined.value}
/> />
{joinRole == 'admin' && ( {joinRole.value == 'admin' && (
<Input <Input
placeholder='管理密码' placeholder='管理密码'
value={userInfo?.password ?? ""}
onChange={(e) => { onChange={(e) => {
$userInfo.set({ $userInfo.set({
...userInfo, ...$userInfo.value,
password: e.target.value, password: e.target.value,
}) })
}} }}
value={userInfo?.password ?? ""}
type='password' type='password'
name='password' name='password'
disabled={isJoined} disabled={isJoined.value}
/> />
)} )}
<Button <Button
onClick={handleJoinClick} onClick={handleJoinClick}
disabled={!isAllowedLogin()} disabled={!isAllowedLogin()}
>{isRoomEmpty ? "创建房间" : "加入房间"}</Button> >{isRoomEmpty.value ? "创建房间" : "加入房间"}</Button>
</div>} </div>}
{isJoined && userInfo?.isAdmin && <div className='m-2 flex flex-row gap-2'> {isJoined.value && userInfo?.isAdmin && <div className='m-2 flex flex-row gap-2'>
<Input <Input
name='videoUrl' name='videoUrl'
type='url' type='url'
value={urlInput} value={urlInput.value}
autoComplete='url'
onChange={(e) => { onChange={(e) => {
setUrlInput(e.target.value) urlInput.value = e.target.value
}} }}
autoComplete='url'
placeholder='视频直链' placeholder='视频直链'
/> />
<Button onClick={handleSetUrlClick} ></Button> <Button onClick={handleSetUrlClick}></Button>
</div>} </div>}
{roomName && <UserList roomName={roomName} />} {roomName && <UserList roomName={roomName} />}
</div> </div>

@ -0,0 +1,3 @@
- [ ] 手机端播放器问题
- [ ] 自动重连后没有join的问题
- [ ] 播放结束后没有停止计时
Loading…
Cancel
Save