diff --git a/src/app/providers/KeycloakAuthProvider.tsx b/src/app/providers/KeycloakAuthProvider.tsx index 8c2e6a0..e2b2331 100644 --- a/src/app/providers/KeycloakAuthProvider.tsx +++ b/src/app/providers/KeycloakAuthProvider.tsx @@ -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(undefined) diff --git a/src/app/providers/RealTimeEventProvider.tsx b/src/app/providers/RealTimeEventProvider.tsx index 3d1bb56..0fdf572 100644 --- a/src/app/providers/RealTimeEventProvider.tsx +++ b/src/app/providers/RealTimeEventProvider.tsx @@ -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 { - 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 | undefined>(undefined) diff --git a/src/app/providers/RealTimeSocketProvider.tsx b/src/app/providers/RealTimeSocketProvider.tsx index 77940a1..863d90a 100644 --- a/src/app/providers/RealTimeSocketProvider.tsx +++ b/src/app/providers/RealTimeSocketProvider.tsx @@ -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) @@ -20,11 +21,6 @@ function buildSocketUrl(httpBase: string): string { return url.toString() } -export interface RealTimeSocketProviderProps { - children: React.ReactNode - httpBaseUrl?: string -} - const SocketContext = createContext(undefined) export function RealTimeSocketProvider({ children, httpBaseUrl }: RealTimeSocketProviderProps) { diff --git a/src/app/providers/RealTimeTopicProvider.tsx b/src/app/providers/RealTimeTopicProvider.tsx index 4f3b048..6a61447 100644 --- a/src/app/providers/RealTimeTopicProvider.tsx +++ b/src/app/providers/RealTimeTopicProvider.tsx @@ -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() diff --git a/src/app/providers/WebRTCProvider.tsx b/src/app/providers/WebRTCProvider.tsx index 6828588..4b427a8 100644 --- a/src/app/providers/WebRTCProvider.tsx +++ b/src/app/providers/WebRTCProvider.tsx @@ -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 - leave: () => Promise - startCam: () => Promise - stopCam: () => void - startMic: () => Promise - stopMic: () => void -} +import type { PresenceDesc, RemoteState, WebRTCState } from "./providers.types" const WebRTCContext = createContext(undefined) diff --git a/src/app/providers/providers.types.ts b/src/app/providers/providers.types.ts new file mode 100644 index 0000000..44c0857 --- /dev/null +++ b/src/app/providers/providers.types.ts @@ -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 { + 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 + leave: () => Promise + startCam: () => Promise + stopCam: () => void + startMic: () => Promise + stopMic: () => void +} + +export type { + RealTimeSocketProviderProps, + RealTimeEventProviderProps, + AuthState, + RealTimeTopicProviderProps, + PresenceDesc, + RemoteState, + WebRTCState, +}