fix(setup): setup page display timing is incorrect

This commit is contained in:
molvqingtai 2024-09-23 03:16:44 +08:00
parent cc3424d4d8
commit f6277bcc83
7 changed files with 362 additions and 90 deletions

View file

@ -9,6 +9,8 @@ import { stringToHex } from '@/utils'
import { Toaster } from '@/components/ui/Sonner'
import UserInfoDomain from '@/domain/UserInfo'
import Setup from '@/app/content/views/Setup'
import MessageListDomain from '@/domain/MessageList'
import { useEffect } from 'react'
const hostRoomId = stringToHex(document.location.host)
@ -16,15 +18,31 @@ export default function App() {
const send = useRemeshSend()
const roomDomain = useRemeshDomain(RoomDomain())
const userInfoDomain = useRemeshDomain(UserInfoDomain())
send(roomDomain.command.JoinRoomCommand(hostRoomId))
const isLogin = useRemeshQuery(userInfoDomain.query.IsLoginQuery())
const messageListDomain = useRemeshDomain(MessageListDomain())
const joinRoomFinished = useRemeshQuery(roomDomain.query.IsFinishedQuery())
const userInfoFinished = useRemeshQuery(userInfoDomain.query.IsFinishedQuery())
const userInfo = useRemeshQuery(userInfoDomain.query.UserInfoQuery())
const notUserInfo = userInfoFinished && !userInfo
useEffect(() => {
if (userInfoFinished) {
if (userInfo) {
!joinRoomFinished && send(roomDomain.command.JoinRoomCommand(hostRoomId))
} else {
send(messageListDomain.command.ClearListCommand())
}
}
}, [userInfoFinished, userInfo, joinRoomFinished])
return (
<>
<AppContainer>
<Header />
<Main />
<Footer />
{!isLogin && <Setup />}
{notUserInfo && <Setup />}
</AppContainer>
<AppButton></AppButton>
<Toaster richColors offset="104px" position="top-center"></Toaster>

View file

@ -18,7 +18,7 @@ export default defineContentScript({
async main(ctx) {
const store = Remesh.store({
externs: [IndexDBStorageImpl, BrowserSyncStorageImpl, PeerRoomImpl],
inspectors: [RemeshLogger()]
inspectors: __DEV__ ? [RemeshLogger()] : []
})
const ui = await createShadowRootUi(ctx, {

View file

@ -76,17 +76,18 @@ const Setup: FC = () => {
}
useEffect(() => {
send(messageListDomain.command.ClearListCommand())
const timer = new Timer(
async () => {
const userInfo = await refreshUserInfo()
await createMessage(userInfo)
await createMessage(await refreshUserInfo())
},
{ delay: 2000, immediate: true, limit: mockTextList.length }
)
timer.start()
return () => timer.stop()
return () => {
timer.stop()
send(messageListDomain.command.ClearListCommand())
}
}, [])
return (
<div className="absolute inset-0 z-50 flex rounded-xl bg-black/10 shadow-2xl backdrop-blur-sm">

View file

@ -1,11 +1,12 @@
import { Remesh } from 'remesh'
import { map, merge, tap } from 'rxjs'
import { map, merge, switchMap, tap, defer, of, EMPTY, mergeMap } from 'rxjs'
import { type MessageUser } from './MessageList'
import { PeerRoomExtern } from '@/domain/externs/PeerRoom'
import MessageListDomain from '@/domain/MessageList'
import UserInfoDomain from '@/domain/UserInfo'
import { callbackToObservable, desert } from '@/utils'
import { nanoid } from 'nanoid'
import StatusModule from './modules/Status'
export enum MessageType {
Like = 'like',
@ -40,6 +41,10 @@ const RoomDomain = Remesh.domain({
const MessageListQuery = messageListDomain.query.ListQuery
const RoomStatusState = StatusModule(domain, {
name: 'Room.RoomStatusModule'
})
const PeerListState = domain.state<string[]>({
name: 'Room.PeerListState',
default: [peerRoom.selfId]
@ -56,15 +61,15 @@ const RoomDomain = Remesh.domain({
name: 'RoomJoinRoomCommand',
impl: (_, roomId: string) => {
peerRoom.joinRoom(roomId)
return [JoinRoomEvent(peerRoom.selfId)]
return [JoinRoomEvent(roomId), RoomStatusState.command.SetFinishedCommand()]
}
})
const LeaveRoomCommand = domain.command({
name: 'RoomLeaveRoomCommand',
impl: (_) => {
impl: (_, roomId: string) => {
peerRoom.leaveRoom()
return [LeaveRoomEvent(peerRoom.selfId)]
return [LeaveRoomEvent(roomId), RoomStatusState.command.SetInitialCommand()]
}
})
@ -156,6 +161,18 @@ const RoomDomain = Remesh.domain({
name: 'RoomLeaveRoomEvent'
})
const OnMessageEvent = domain.event<RoomMessage>({
name: 'RoomOnMessageEvent'
})
const OnJoinRoomEvent = domain.event<string>({
name: 'RoomOnJoinRoomEvent'
})
const OnLeaveRoomEvent = domain.event<string>({
name: 'RoomOnLeaveRoomEvent'
})
const UpdatePeerListCommand = domain.command({
name: 'RoomUpdatePeerListCommand',
impl: ({ get }, action: { type: 'create' | 'delete'; peerId: string }) => {
@ -208,106 +225,111 @@ const RoomDomain = Remesh.domain({
domain.effect({
name: 'RoomOnMessageEffect',
impl: ({ get }) => {
const onMessage$ = callbackToObservable<RoomMessage>(peerRoom.onMessage.bind(peerRoom))
return onMessage$.pipe(
map((message) => {
switch (message.type) {
case 'text':
return messageListDomain.command.CreateItemCommand({
...message,
date: Date.now(),
likeUsers: [],
hateUsers: []
})
case 'like': {
if (!get(messageListDomain.query.HasItemQuery(message.id))) {
return null
}
const _message = get(messageListDomain.query.ItemQuery(message.id))
return messageListDomain.command.UpdateItemCommand({
..._message,
likeUsers: desert(
_message.likeUsers,
{
userId: message.userId,
username: message.username,
userAvatar: message.userAvatar
},
'userId'
impl: ({ fromEvent, get }) => {
const onMessage$ = fromEvent(JoinRoomEvent).pipe(
switchMap(() => callbackToObservable<RoomMessage>(peerRoom.onMessage.bind(peerRoom))),
mergeMap((message) => {
console.log('onMessage', message)
const messageEvent$ = of(OnMessageEvent(message))
const commandEvent$ = (() => {
switch (message.type) {
case 'text':
return of(
messageListDomain.command.CreateItemCommand({
...message,
date: Date.now(),
likeUsers: [],
hateUsers: []
})
)
})
}
case 'hate': {
if (!get(messageListDomain.query.HasItemQuery(message.id))) {
return null
}
const _message = get(messageListDomain.query.ItemQuery(message.id))
return messageListDomain.command.UpdateItemCommand({
..._message,
hateUsers: desert(
_message.hateUsers,
{
userId: message.userId,
username: message.username,
userAvatar: message.userAvatar
},
'userId'
case 'like':
case 'hate': {
if (!get(messageListDomain.query.HasItemQuery(message.id))) {
return EMPTY
}
const _message = get(messageListDomain.query.ItemQuery(message.id))
const users = message.type === 'like' ? 'likeUsers' : 'hateUsers'
return of(
messageListDomain.command.UpdateItemCommand({
..._message,
[users]: desert(
_message[users],
{
userId: message.userId,
username: message.username,
userAvatar: message.userAvatar
},
'userId'
)
})
)
})
}
default:
console.warn('未知消息类型', message)
return EMPTY
}
default:
console.warn('unknown message type', message)
return null
}
})()
return merge(messageEvent$, commandEvent$)
})
)
return onMessage$
}
})
domain.effect({
name: 'RoomOnJoinRoomEffect',
impl: () => {
const onJoinRoom$ = callbackToObservable<string>(peerRoom.onJoinRoom.bind(peerRoom))
return onJoinRoom$.pipe(
impl: ({ fromEvent }) => {
const onJoinRoom$ = fromEvent(JoinRoomEvent).pipe(
switchMap(() => callbackToObservable<string>(peerRoom.onJoinRoom.bind(peerRoom))),
map((peerId) => {
console.log('onJoinRoom', peerId)
return [UpdatePeerListCommand({ type: 'create', peerId }), JoinRoomEvent(peerId)]
return [UpdatePeerListCommand({ type: 'create', peerId }), OnJoinRoomEvent(peerId)]
})
)
return onJoinRoom$
}
})
domain.effect({
name: 'RoomOnLeaveRoomEffect',
impl: () => {
const onLeaveRoom$ = callbackToObservable<string>(peerRoom.onLeaveRoom.bind(peerRoom))
return onLeaveRoom$.pipe(
impl: ({ fromEvent }) => {
const onLeaveRoom$ = fromEvent(JoinRoomEvent).pipe(
switchMap(() => callbackToObservable<string>(peerRoom.onLeaveRoom.bind(peerRoom))),
map((peerId) => {
console.log('onLeaveRoom', peerId)
return [UpdatePeerListCommand({ type: 'delete', peerId }), LeaveRoomEvent(peerId)]
return [UpdatePeerListCommand({ type: 'delete', peerId }), OnLeaveRoomEvent(peerId)]
})
)
return onLeaveRoom$
}
})
return {
query: {
PeerListQuery,
MessageListQuery
MessageListQuery,
...RoomStatusState.query
},
event: {
SendTextMessageEvent,
SendLikeMessageEvent,
SendHateMessageEvent,
JoinRoomEvent,
LeaveRoomEvent
LeaveRoomEvent,
OnMessageEvent,
OnJoinRoomEvent,
OnLeaveRoomEvent,
...RoomStatusState.event
},
command: {
JoinRoomCommand,
LeaveRoomCommand,
SendTextMessageCommand,
SendLikeMessageCommand,
SendHateMessageCommand
SendHateMessageCommand,
...RoomStatusState.command
}
}
}

View file

@ -1,6 +1,7 @@
import { Remesh } from 'remesh'
import { BrowserSyncStorageExtern } from '@/domain/externs/Storage'
import StorageEffect from '@/domain/modules/StorageEffect'
import StatusModule from './modules/Status'
export interface UserInfo {
id: string
@ -26,6 +27,10 @@ const UserInfoDomain = Remesh.domain({
default: null
})
const UserInfoStatusModule = StatusModule(domain, {
name: 'UserInfo.StatusModule'
})
const UserInfoQuery = domain.query({
name: 'UserInfo.UserInfoQuery',
impl: ({ get }) => {
@ -33,17 +38,17 @@ const UserInfoDomain = Remesh.domain({
}
})
const IsLoginQuery = domain.query({
name: 'UserInfo.IsLoginQuery',
impl: ({ get }) => {
return !!get(UserInfoState())?.id
}
})
const UpdateUserInfoCommand = domain.command({
name: 'UserInfo.UpdateUserInfoCommand',
impl: (_, userInfo: UserInfo | null) => {
return [UserInfoState().new(userInfo), UpdateUserInfoEvent(), SyncToStorageEvent()]
return [
UserInfoState().new(userInfo),
UpdateUserInfoEvent(),
SyncToStorageEvent(),
userInfo
? UserInfoStatusModule.command.SetFinishedCommand()
: UserInfoStatusModule.command.SetInitialCommand()
]
}
})
@ -74,21 +79,25 @@ const UserInfoDomain = Remesh.domain({
storageEffect
.set(SyncToStorageEvent)
.get<UserInfo>((value) => SyncToStateCommand(value))
.watch<UserInfo>((value) => SyncToStateCommand(value!))
.get<UserInfo>((value) => {
return [SyncToStateCommand(value), UserInfoStatusModule.command.SetFinishedCommand()]
})
.watch<UserInfo>((value) => [SyncToStateCommand(value)])
return {
query: {
UserInfoQuery,
IsLoginQuery
...UserInfoStatusModule.query
},
command: {
UpdateUserInfoCommand
UpdateUserInfoCommand,
...UserInfoStatusModule.command
},
event: {
SyncToStateEvent,
SyncToStorageEvent,
UpdateUserInfoEvent
UpdateUserInfoEvent,
...UserInfoStatusModule.event
}
}
}

View file

@ -0,0 +1,102 @@
import { DomainConceptName, RemeshDomainContext } from 'remesh'
export enum Status {
Initial = 0b001, // 1
Loading = 0b010, // 2
Finished = 0b100 // 4
}
export interface StatusOptions {
name: DomainConceptName<'StatusModule'>
default?: Status
}
const StatusModule = (domain: RemeshDomainContext, options: StatusOptions) => {
const StatusState = domain.state({
name: `${options.name}.StatusState`,
default: options.default ?? Status.Initial
})
const StatusQuery = domain.query({
name: `${options.name}.StatusQuery`,
impl: ({ get }) => {
return get(StatusState())
}
})
const IsInitialQuery = domain.query({
name: `${options.name}.IsInitialQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Initial) !== 0
}
})
const IsLoadingQuery = domain.query({
name: `${options.name}.IsLoadingQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Loading) !== 0
}
})
const IsFinishedQuery = domain.query({
name: `${options.name}.IsFinishedQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Finished) !== 0
}
})
const UpdateStatusEvent = domain.event<Status>({
name: `${options.name}.UpdateStatusEvent`
})
const SetInitialCommand = domain.command({
name: `${options.name}.SetInitialCommand`,
impl: () => {
return [StatusState().new(Status.Initial), UpdateStatusEvent(Status.Initial)]
}
})
const SetLoadingCommand = domain.command({
name: `${options.name}.SetLoadingCommand`,
impl: () => {
return [StatusState().new(Status.Loading), UpdateStatusEvent(Status.Loading)]
}
})
const SetFinishedCommand = domain.command({
name: `${options.name}.SetFinishedCommand`,
impl: () => {
return [StatusState().new(Status.Finished), UpdateStatusEvent(Status.Finished)]
}
})
const UpdateStatusCommand = domain.command({
name: `${options.name}.UpdateStatusCommand`,
impl: (_, status: Status) => {
return [StatusState().new(status), UpdateStatusEvent(status)]
}
})
return {
query: {
StatusQuery,
IsInitialQuery,
IsLoadingQuery,
IsFinishedQuery
},
command: {
SetInitialCommand,
SetLoadingCommand,
SetFinishedCommand,
UpdateStatusCommand
},
event: {
UpdateStatusEvent
}
}
}
export default StatusModule

View file

@ -57,8 +57,83 @@ function createAvatarSvg() {
'rgb(165, 42, 42)', // Red
'rgb(145, 85, 61)', // Auburn
'rgb(128, 128, 128)', // Grey
'rgb(185, 55, 55)' // Fire
// ... 其他颜色
'rgb(185, 55, 55)', // Fire
'rgb(255, 192, 203)', // Pastel Pink
'rgb(255, 105, 180)', // Bright Pink
'rgb(230, 230, 250)', // Lavender
'rgb(64, 224, 208)', // Turquoise
'rgb(0, 191, 255)', // Bright Blue
'rgb(148, 0, 211)', // Deep Purple
'rgb(50, 205, 50)', // Lime Green
'rgb(255, 165, 0)', // Vivid Orange
'rgb(220, 20, 60)', // Crimson Red
'rgb(192, 192, 192)', // Silver
'rgb(255, 215, 0)', // Gold
'rgb(255, 255, 255)', // White
'rgb(124, 252, 0)', // Lawn Green
'rgb(127, 255, 0)', // Chartreuse
'rgb(0, 255, 127)', // Spring Green
'rgb(72, 209, 204)', // Medium Turquoise
'rgb(0, 255, 255)', // Cyan
'rgb(0, 206, 209)', // Dark Turquoise
'rgb(32, 178, 170)', // Light Sea Green
'rgb(95, 158, 160)', // Cadet Blue
'rgb(70, 130, 180)', // Steel Blue
'rgb(176, 196, 222)', // Light Steel Blue
'rgb(30, 144, 255)', // Dodger Blue
'rgb(135, 206, 235)', // Sky Blue
'rgb(0, 0, 139)', // Dark Blue
'rgb(138, 43, 226)', // Blue Violet
'rgb(75, 0, 130)', // Indigo
'rgb(139, 0, 139)', // Dark Magenta
'rgb(153, 50, 204)', // Dark Orchid
'rgb(186, 85, 211)', // Medium Orchid
'rgb(218, 112, 214)', // Orchid
'rgb(221, 160, 221)', // Plum
'rgb(238, 130, 238)', // Violet
'rgb(255, 0, 255)', // Magenta
'rgb(216, 191, 216)', // Thistle
'rgb(255, 20, 147)', // Deep Pink
'rgb(255, 69, 0)', // Orange Red
'rgb(255, 140, 0)', // Dark Orange
'rgb(255, 165, 0)', // Orange
'rgb(250, 128, 114)', // Salmon
'rgb(233, 150, 122)', // Dark Salmon
'rgb(240, 128, 128)', // Light Coral
'rgb(205, 92, 92)', // Indian Red
'rgb(255, 99, 71)', // Tomato
'rgb(255, 160, 122)', // Light Salmon
'rgb(220, 20, 60)', // Crimson
'rgb(139, 0, 0)', // Dark Red
'rgb(178, 34, 34)', // Fire Brick
'rgb(250, 235, 215)', // Antique White
'rgb(255, 239, 213)', // Papaya Whip
'rgb(255, 235, 205)', // Blanched Almond
'rgb(255, 222, 173)', // Navajo White
'rgb(245, 245, 220)', // Beige
'rgb(255, 228, 196)', // Bisque
'rgb(255, 218, 185)', // Peach Puff
'rgb(244, 164, 96)', // Sandy Brown
'rgb(210, 180, 140)', // Tan
'rgb(222, 184, 135)', // Burly Wood
'rgb(250, 250, 210)', // Light Goldenrod Yellow
'rgb(255, 250, 205)', // Lemon Chiffon
'rgb(255, 245, 238)', // Sea Shell
'rgb(253, 245, 230)', // Old Lace
'rgb(255, 228, 225)', // Misty Rose
'rgb(255, 240, 245)', // Lavender Blush
'rgb(250, 240, 230)', // Linen
'rgb(255, 228, 181)', // Moccasin
'rgb(255, 250, 250)', // Snow
'rgb(240, 255, 255)', // Azure
'rgb(240, 255, 240)', // Honeydew
'rgb(245, 245, 245)', // White Smoke
'rgb(245, 255, 250)', // Mint Cream
'rgb(240, 248, 255)', // Alice Blue
'rgb(240, 248, 255)', // Ghost White
'rgb(248, 248, 255)', // Ghost White
'rgb(255, 250, 240)', // Floral White
'rgb(253, 245, 230)' // Old Lace
],
hairColor: 'black',
dyeColorOffset: '50%',
@ -72,8 +147,54 @@ function createAvatarSvg() {
'rgb(188, 143, 143)', // Dusty Rose
'rgb(135, 206, 235)', // Sky Blue
'rgb(245, 255, 250)', // Mint Cream
'rgb(245, 222, 179)' // Wheat
// ... 其他颜色
'rgb(245, 222, 179)', // Wheat
'rgb(47, 79, 79)', // Dark Slate Gray
'rgb(72, 61, 139)', // Dark Slate Blue
'rgb(60, 20, 20)', // Dark Brown
'rgb(25, 25, 112)', // Midnight Blue
'rgb(139, 0, 0)', // Dark Red
'rgb(85, 107, 47)', // Olive Drab
'rgb(128, 0, 128)', // Purple
'rgb(0, 100, 0)', // Dark Green
'rgb(0, 0, 139)', // Dark Blue
'rgb(105, 105, 105)', // Dim Gray
'rgb(240, 128, 128)', // Light Coral
'rgb(255, 160, 122)', // Light Salmon
'rgb(255, 218, 185)', // Peach Puff
'rgb(255, 228, 196)', // Bisque
'rgb(255, 222, 173)', // Navajo White
'rgb(255, 250, 205)', // Lemon Chiffon
'rgb(250, 250, 210)', // Light Goldenrod Yellow
'rgb(255, 239, 213)', // Papaya Whip
'rgb(255, 245, 238)', // Sea Shell
'rgb(255, 248, 220)', // Cornsilk
'rgb(255, 255, 240)', // Ivory
'rgb(240, 255, 240)', // Honeydew
'rgb(240, 255, 255)', // Azure
'rgb(240, 248, 255)', // Alice Blue
'rgb(248, 248, 255)', // Ghost White
'rgb(255, 250, 250)', // Snow
'rgb(255, 240, 245)', // Lavender Blush
'rgb(255, 228, 225)', // Misty Rose
'rgb(230, 230, 250)', // Lavender
'rgb(216, 191, 216)', // Thistle
'rgb(221, 160, 221)', // Plum
'rgb(238, 130, 238)', // Violet
'rgb(218, 112, 214)', // Orchid
'rgb(186, 85, 211)', // Medium Orchid
'rgb(147, 112, 219)', // Medium Purple
'rgb(138, 43, 226)', // Blue Violet
'rgb(148, 0, 211)', // Dark Violet
'rgb(153, 50, 204)', // Dark Orchid
'rgb(139, 69, 19)', // Saddle Brown
'rgb(160, 82, 45)', // Sienna
'rgb(210, 105, 30)', // Chocolate
'rgb(205, 133, 63)', // Peru
'rgb(244, 164, 96)', // Sandy Brown
'rgb(222, 184, 135)', // Burly Wood
'rgb(255, 250, 240)', // Floral White
'rgb(253, 245, 230)', // Old Lace
'rgb(250, 240, 230)' // Linen
],
mouthPoints: []
}
@ -250,7 +371,6 @@ function createAvatarSvg() {
return createElement(svgTemplate)
}
// 导出函数
export default function generateUglyAvatar() {
const svgElement = createAvatarSvg()
const serializer = new XMLSerializer()