123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- import { BehaviorSubject, Subject } from "rxjs"
- import { logHoppRequestRunToAnalytics } from "../fb/analytics"
- import { SIOClientV2, SIOClientV3, SIOClientV4, SIOClient } from "./SIOClients"
- import { SIOClientVersion } from "~/newstore/SocketIOSession"
- export const SOCKET_CLIENTS = {
- v2: SIOClientV2,
- v3: SIOClientV3,
- v4: SIOClientV4,
- } as const
- type SIOAuth = { type: "None" } | { type: "Bearer"; token: string }
- export type ConnectionOption = {
- url: string
- path: string
- clientVersion: SIOClientVersion
- auth: SIOAuth | undefined
- }
- export type SIOMessage = {
- eventName: string
- value: unknown
- }
- type SIOErrorType = "CONNECTION" | "RECONNECT_ERROR" | "UNKNOWN"
- export type SIOError = {
- type: SIOErrorType
- value: unknown
- }
- export type SIOEvent = { time: number } & (
- | { type: "CONNECTING" }
- | { type: "CONNECTED" }
- | { type: "MESSAGE_SENT"; message: SIOMessage }
- | { type: "MESSAGE_RECEIVED"; message: SIOMessage }
- | { type: "DISCONNECTED"; manual: boolean }
- | { type: "ERROR"; error: SIOError }
- )
- export type ConnectionState = "CONNECTING" | "CONNECTED" | "DISCONNECTED"
- export class SIOConnection {
- connectionState$: BehaviorSubject<ConnectionState>
- event$: Subject<SIOEvent> = new Subject()
- socket: SIOClient | undefined
- constructor() {
- this.connectionState$ = new BehaviorSubject<ConnectionState>("DISCONNECTED")
- }
- private addEvent(event: SIOEvent) {
- this.event$.next(event)
- }
- connect({ url, path, clientVersion, auth }: ConnectionOption) {
- this.connectionState$.next("CONNECTING")
- this.addEvent({
- time: Date.now(),
- type: "CONNECTING",
- })
- try {
- this.socket = new SOCKET_CLIENTS[clientVersion]()
- if (auth?.type === "Bearer") {
- this.socket.connect(url, {
- path,
- auth: {
- token: auth.token,
- },
- })
- } else {
- this.socket.connect(url)
- }
- this.socket.on("connect", () => {
- this.connectionState$.next("CONNECTED")
- this.addEvent({
- type: "CONNECTED",
- time: Date.now(),
- })
- })
- this.socket.on("*", ({ data }: { data: string[] }) => {
- const [eventName, message] = data
- this.addEvent({
- message: { eventName, value: message },
- type: "MESSAGE_RECEIVED",
- time: Date.now(),
- })
- })
- this.socket.on("connect_error", (error: unknown) => {
- this.handleError(error, "CONNECTION")
- })
- this.socket.on("reconnect_error", (error: unknown) => {
- this.handleError(error, "RECONNECT_ERROR")
- })
- this.socket.on("error", (error: unknown) => {
- this.handleError(error, "UNKNOWN")
- })
- this.socket.on("disconnect", () => {
- this.connectionState$.next("DISCONNECTED")
- this.addEvent({
- type: "DISCONNECTED",
- time: Date.now(),
- manual: true,
- })
- })
- } catch (error) {
- this.handleError(error, "CONNECTION")
- }
- logHoppRequestRunToAnalytics({
- platform: "socketio",
- })
- }
- private handleError(error: unknown, type: SIOErrorType) {
- this.disconnect()
- this.addEvent({
- time: Date.now(),
- type: "ERROR",
- error: {
- type,
- value: error,
- },
- })
- }
- sendMessage(event: { message: string; eventName: string }) {
- if (this.connectionState$.value === "DISCONNECTED") return
- const { message, eventName } = event
- this.socket?.emit(eventName, message, (data) => {
- // receive response from server
- this.addEvent({
- time: Date.now(),
- type: "MESSAGE_RECEIVED",
- message: {
- eventName,
- value: data,
- },
- })
- })
- this.addEvent({
- time: Date.now(),
- type: "MESSAGE_SENT",
- message: {
- eventName,
- value: message,
- },
- })
- }
- disconnect() {
- this.socket?.close()
- this.connectionState$.next("DISCONNECTED")
- }
- }
|