Skip to content

Commit

Permalink
start tidying up the DO, add some types to messages
Browse files Browse the repository at this point in the history
  • Loading branch information
threepointone committed Jun 13, 2024
1 parent ad5c629 commit 0b2c096
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 66 deletions.
68 changes: 37 additions & 31 deletions app/durableObjects/ChatRoom.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { AppLoadContext } from '@remix-run/cloudflare'
import type { ClientMessage, ServerMessage, User } from '~/types/Messages'
import type { Env } from '~/types/Env'
import type {
ClientMessage,
MessageFromServer,
ServerMessage,
User,
} from '~/types/Messages'
import { assertError } from '~/utils/assertError'
import assertNever from '~/utils/assertNever'
import { assertNonNullable } from '~/utils/assertNonNullable'
Expand All @@ -23,32 +28,32 @@ type Session = {
// ChatRoom implements a Durable Object that coordinates an individual chat room. Participants
// connect to the room using WebSockets, and the room broadcasts messages from each participant
// to all others.
export class ChatRoom {
storage: DurableObjectStorage
env: AppLoadContext
sessions: Session[]
lastTimestamp: number
export class ChatRoom implements DurableObject {
/**
* WebSocket objects for each client, along with some metadata
*/
sessions: Session[] = []
/**
* We keep track of the last-seen message's timestamp just so that we can assign monotonically
increasing timestamps even if multiple messages arrive simultaneously (see below). There's
no need to store this to disk since we assume if the object is destroyed and recreated, much
more than a millisecond will have gone by.
*/
lastTimestamp: number = 0
stateSyncInterval: ReturnType<typeof setInterval> | null = null

constructor(state: DurableObjectState, env: AppLoadContext) {
this.storage = state.storage

// `env` is our environment bindings (discussed earlier).
// We'd like to use Cloudflare's fancy RPC Durable Objects
// but currently we can't figure out a way to mark cloudflare:workers
// as an external when remix compiles it. Until then, let's match the
// newer style member names .ctx and .env
ctx: DurableObjectState
env: Env
constructor(ctx: DurableObjectState, env: Env) {
this.ctx = ctx
this.env = env

// We will put the WebSocket objects for each client, along with some metadata, into
// `sessions`.
this.sessions = []

// We keep track of the last-seen message's timestamp just so that we can assign monotonically
// increasing timestamps even if multiple messages arrive simultaneously (see below). There's
// no need to store this to disk since we assume if the object is destroyed and recreated, much
// more than a millisecond will have gone by.
this.lastTimestamp = 0

// check for previous sessions.
state.blockConcurrencyWhile(async () => {
this.sessions = (await this.storage.get<Session[]>('sessions')) ?? []
this.ctx.blockConcurrencyWhile(async () => {
this.sessions = (await this.ctx.storage.get<Session[]>('sessions')) ?? []
this.sessions.forEach((s) => this.setupHeartbeatInterval(s))
})
}
Expand Down Expand Up @@ -102,7 +107,7 @@ export class ChatRoom {
from: 'server',
timestamp: this.lastTimestamp,
message,
})
} satisfies MessageFromServer)
)
session.messageQueue = session.messageQueue.filter((m) => m !== message)
} else {
Expand All @@ -111,8 +116,8 @@ export class ChatRoom {
await this.storeSessions()
}

storeSessions = async () =>
this.storage.put(
async storeSessions() {
await this.ctx.storage.put(
'sessions',
this.sessions.map(
({
Expand All @@ -122,10 +127,11 @@ export class ChatRoom {
}) => s
)
)
}

broadcastState = () => {
broadcastState() {
if (this.sessions.length > 0 && this.stateSyncInterval === null) {
this.stateSyncInterval = setInterval(this.broadcastState, 5000)
this.stateSyncInterval = setInterval(() => this.broadcastState(), 5000)
} else if (this.sessions.length === 0 && this.stateSyncInterval !== null) {
clearInterval(this.stateSyncInterval)
this.stateSyncInterval = null
Expand All @@ -140,7 +146,7 @@ export class ChatRoom {
})
}

setupHeartbeatInterval = (session: Session) => {
setupHeartbeatInterval(session: Session) {
const resetHeartBeatTimeout = () => {
if (session.heartbeatTimeout) clearTimeout(session.heartbeatTimeout)
session.heartbeatTimeout = setTimeout(() => {
Expand All @@ -151,7 +157,7 @@ export class ChatRoom {
return resetHeartBeatTimeout
}

handleUserLeft = (session: Session) => {
handleUserLeft(session: Session) {
session.quit = true
this.sessions = this.sessions.filter((member) => member !== session)
this.broadcastState()
Expand Down
4 changes: 2 additions & 2 deletions app/hooks/useBroadcastStatus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import { useUnmount } from 'react-use'
import type { User } from '~/types/Messages'
import type { ClientMessage, User } from '~/types/Messages'
import type Peer from '~/utils/Peer.client'
import type Signal from '~/utils/Signal'
import type { RoomContextType } from './useRoomContext'
Expand Down Expand Up @@ -80,7 +80,7 @@ export default function useBroadcastStatus({
transceiverSessionId: peer?.sessionId,
tracks: {},
},
})
} satisfies ClientMessage)
}
})
}
2 changes: 1 addition & 1 deletion app/types/Env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type Env = {
TURN_SERVICE_TOKEN?: string
TRACE_LINK?: string
API_EXTRA_PARAMS?: string
limiters: DurableObjectNamespace
// limiters: DurableObjectNamespace
rooms: DurableObjectNamespace
MAX_WEBCAM_FRAMERATE?: string
MAX_WEBCAM_BITRATE?: string
Expand Down
31 changes: 0 additions & 31 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"@radix-ui/react-tooltip": "^1.0.7",
"@radix-ui/react-visually-hidden": "^1.0.3",
"@remix-run/cloudflare": "2.9.2",
"@remix-run/cloudflare-workers": "2.9.2",
"@remix-run/react": "2.9.2",
"@tensorflow-models/body-segmentation": "^1.0.2",
"@tensorflow/tfjs-backend-webgl": "^4.15.0",
Expand Down

0 comments on commit 0b2c096

Please sign in to comment.