Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions src/app/providers/KeycloakAuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@ import type { User } from "@/shared/models/user"
import React, { createContext, useContext, useMemo } from "react"
import { AuthProvider as OidcAuthProvider, useAuth as useOidcAuth } from "react-oidc-context"
import { WebStorageStateStore, type User as OidcUser } from "oidc-client-ts"

export interface AuthState {
isAuthenticated: boolean
isLoading: boolean
user: User | null
accessToken: string | null
login: () => void
logout: () => void
error: Error | null
}
import type { AuthState } from "./providers.types"

const AuthContext = createContext<AuthState | undefined>(undefined)

Expand Down
23 changes: 5 additions & 18 deletions src/app/providers/RealTimeEventProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react"
import type { Channel } from "phoenix"
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react"
import { useAuth } from "@/app/providers/KeycloakAuthProvider"
import type { Channel } from "phoenix"

import { useRealTimeSocket } from "./RealTimeSocketProvider"
import type { EventContextValue, TopicName } from "@/shared/models/real-time.ts"

export interface RealTimeEventProviderProps<TPayload, TState> {
children: React.ReactNode
topic: TopicName
event: string
// Optional explicit id for nested access; defaults to `${resolvedTopic}:${event}`
id?: string
// Optional local state sink for this event
state?: {
initial: TState
reducer: (prev: TState, msg: TPayload, event: string) => TState
}
// Optional side-effect handler invoked for every event message
onEvent?: (payload: TPayload, meta: { topic: string; channel: Channel; event: string }) => void
}
import type { EventContextValue } from "@/shared/models/real-time.ts"
import type { RealTimeEventProviderProps } from "./providers.types"

const EventContext = createContext<EventContextValue<unknown> | undefined>(undefined)

Expand Down
6 changes: 1 addition & 5 deletions src/app/providers/RealTimeSocketProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { Channel, Socket } from "phoenix"
import { Socket as PhoenixSocket } from "phoenix"
import { useAuth } from "@/app/providers/KeycloakAuthProvider"
import type { ChannelParams, RealTimeSocketState } from "@/shared/models/real-time.ts"
import type { RealTimeSocketProviderProps } from "./providers.types"

function buildSocketUrl(httpBase: string): string {
const url = new URL(httpBase)
Expand All @@ -20,11 +21,6 @@ function buildSocketUrl(httpBase: string): string {
return url.toString()
}

export interface RealTimeSocketProviderProps {
children: React.ReactNode
httpBaseUrl?: string
}

const SocketContext = createContext<RealTimeSocketState | undefined>(undefined)

export function RealTimeSocketProvider({ children, httpBaseUrl }: RealTimeSocketProviderProps) {
Expand Down
9 changes: 2 additions & 7 deletions src/app/providers/RealTimeTopicProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import React, { useEffect } from "react"
import { useEffect } from "react"
import { useAuth } from "@/app/providers/KeycloakAuthProvider"
import { useRealTimeSocket } from "./RealTimeSocketProvider"
import type { TopicJoinSpec } from "@/shared/models/real-time.ts"

export interface RealTimeTopicProviderProps {
children: React.ReactNode
topics?: TopicJoinSpec[]
}
import type { RealTimeTopicProviderProps } from "./providers.types"

export function RealTimeTopicProvider({ children, topics }: RealTimeTopicProviderProps) {
const { isAuthenticated, user } = useAuth()
Expand Down
35 changes: 1 addition & 34 deletions src/app/providers/WebRTCProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,7 @@
import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"
import { useRealTimeSocket } from "@/app/providers/RealTimeSocketProvider"
import { Presence } from "phoenix"

export interface PresenceDesc {
id: number
user_id: string
video: boolean
audio: boolean
}

export interface RemoteState {
id: number
userId: string
tracks: { audio: MediaStream | null; video: MediaStream | null }
audio: boolean
video: boolean
}

export interface WebRTCState {
// UI/status
session: number | null
iceStatus: RTCIceConnectionState
channelStatus: string
joined: boolean
camEnabled: boolean
micEnabled: boolean
// Media
remoteTracks: RemoteState[]
// Actions
join: (session: number) => Promise<void>
leave: () => Promise<void>
startCam: () => Promise<void>
stopCam: () => void
startMic: () => Promise<void>
stopMic: () => void
}
import type { PresenceDesc, RemoteState, WebRTCState } from "./providers.types"

const WebRTCContext = createContext<WebRTCState | undefined>(undefined)

Expand Down
83 changes: 83 additions & 0 deletions src/app/providers/providers.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { TopicName } from "@/shared/models/real-time"
import type { Channel } from "phoenix"
import type { User } from "@/shared/models/user"
import type { TopicJoinSpec } from "@/shared/models/real-time.ts"

interface RealTimeSocketProviderProps {
children: React.ReactNode
httpBaseUrl?: string
}

interface RealTimeEventProviderProps<TPayload, TState> {
children: React.ReactNode
topic: TopicName
event: string
// Optional explicit id for nested access; defaults to `${resolvedTopic}:${event}`
id?: string
// Optional local state sink for this event
state?: {
initial: TState
reducer: (prev: TState, msg: TPayload, event: string) => TState
}
// Optional side-effect handler invoked for every event message
onEvent?: (payload: TPayload, meta: { topic: string; channel: Channel; event: string }) => void
}

interface AuthState {
isAuthenticated: boolean
isLoading: boolean
user: User | null
accessToken: string | null
login: () => void
logout: () => void
error: Error | null
}

interface RealTimeTopicProviderProps {
children: React.ReactNode
topics?: TopicJoinSpec[]
}

interface PresenceDesc {
id: number
user_id: string
video: boolean
audio: boolean
}

interface RemoteState {
id: number
userId: string
tracks: { audio: MediaStream | null; video: MediaStream | null }
audio: boolean
video: boolean
}

interface WebRTCState {
// UI/status
session: number | null
iceStatus: RTCIceConnectionState
channelStatus: string
joined: boolean
camEnabled: boolean
micEnabled: boolean
// Media
remoteTracks: RemoteState[]
// Actions
join: (session: number) => Promise<void>
leave: () => Promise<void>
startCam: () => Promise<void>
stopCam: () => void
startMic: () => Promise<void>
stopMic: () => void
}

export type {
RealTimeSocketProviderProps,
RealTimeEventProviderProps,
AuthState,
RealTimeTopicProviderProps,
PresenceDesc,
RemoteState,
WebRTCState,
}