Skip to content

Commit

Permalink
feat: support socket
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <tukon479@gmail.com>
  • Loading branch information
Innei committed Jun 13, 2023
1 parent af543ec commit fa8d704
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"tv\\(([^)]*)\\)",
"[\"'`]([^\"'`]*).*?[\"'`]"
]
]
],
}
2 changes: 2 additions & 0 deletions src/atoms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './online'
export * from './viewport'
13 changes: 13 additions & 0 deletions src/atoms/online.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { atom, useAtomValue } from 'jotai'

import { jotaiStore } from '~/lib/store'

const onlineCountAtom = atom(0)

export const setOnlineCount = (count: number) => {
jotaiStore.set(onlineCountAtom, count)
}

export const useOnlineCount = () => {
return useAtomValue(onlineCountAtom)
}
6 changes: 0 additions & 6 deletions src/components/layout/footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { ThemeSwitcher } from '~/components/ui/theme-switcher'

import { FooterInfo } from './FooterInfo'

export const Footer = () => {
return (
<footer className="relative z-[1] mt-32 border-t border-uk-separator-opaque-light bg-uk-grouped-primary-light py-6 dark:border-uk-separator-opaque-dark dark:bg-uk-grouped-secondary-dark">
<FooterInfo />

<div className="mt-6 block text-center md:absolute md:bottom-6 md:right-6 md:mt-0">
<ThemeSwitcher />
</div>
</footer>
)
}
71 changes: 41 additions & 30 deletions src/components/layout/footer/FooterInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Link from 'next/link'

import { ThemeSwitcher } from '~/components/ui/theme-switcher'
import { clsxm } from '~/utils/helper'

import { GatewayCount } from './GatewayCount'
Expand Down Expand Up @@ -74,7 +75,7 @@ export const linkSections: LinkSection[] = [
export const FooterInfo = () => {
return (
<div className="px-4 sm:px-8">
<div className="mx-auto max-w-7xl lg:px-8">
<div className="relative mx-auto max-w-7xl lg:px-8">
<div className="space-x-0 space-y-3 md:space-x-6 md:space-y-0">
{linkSections.map((section) => {
return (
Expand Down Expand Up @@ -104,35 +105,10 @@ export const FooterInfo = () => {

{/* */}

<div className="mt-12 space-y-3 text-center md:mt-6 md:text-left">
<p>
© 2020-2023 Innei.
<span>
<Divider />
RSS
<Divider />
站点地图
<Divider className="hidden md:inline" />
</span>
<span className="mt-3 block md:mt-0 md:inline">
Stay hungry. Stay foolish.
</span>
</p>
<p>
<PoweredBy className="my-3 block md:my-0 md:inline" />
<Divider className="hidden md:inline" />
<StyledLink
href="http://beian.miit.gov.cn/"
target="_blank"
rel="noreferrer"
>
浙 ICP 备 20028356 号
</StyledLink>
<Divider />
<span>
<GatewayCount /> 个小伙伴正在浏览
</span>
</p>
<FooterBottom />

<div className="mt-6 block text-center md:absolute md:bottom-0 md:right-0 md:mt-0">
<ThemeSwitcher />
</div>
</div>
</div>
Expand Down Expand Up @@ -180,3 +156,38 @@ const PoweredBy: Component = ({ className }) => {
</span>
)
}

const FooterBottom = () => {
return (
<div className="mt-12 space-y-3 text-center md:mt-6 md:text-left">
<p>
© 2020-2023 Innei.
<span>
<Divider />
RSS
<Divider />
站点地图
<Divider className="hidden md:inline" />
</span>
<span className="mt-3 block md:mt-0 md:inline">
Stay hungry. Stay foolish.
</span>
</p>
<p>
<PoweredBy className="my-3 block md:my-0 md:inline" />
<Divider className="hidden md:inline" />
<StyledLink
href="http://beian.miit.gov.cn/"
target="_blank"
rel="noreferrer"
>
浙 ICP 备 20028356 号
</StyledLink>
<Divider />
<span>
<GatewayCount /> 个小伙伴正在浏览
</span>
</p>
</div>
)
}
6 changes: 5 additions & 1 deletion src/components/layout/footer/GatewayCount.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
'use client'

import { useOnlineCount } from '~/atoms'

export const GatewayCount = () => {
return '1'
return useOnlineCount()
}
2 changes: 2 additions & 0 deletions src/providers/root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { PropsWithChildren } from 'react'
import { DebugProvider } from './debug-provider'
import { JotaiStoreProvider } from './jotai-provider'
import { ViewportProvider } from './viewport-provider'
import { SocketProvider } from './socket-provider'

const ProviderComposer: Component<{
contexts: JSX.Element[]
Expand All @@ -22,6 +23,7 @@ const contexts: JSX.Element[] = [
<ReactQueryProvider key="reactQueryProvider" />,
<JotaiStoreProvider key="jotaiStoreProvider" />,
<ViewportProvider key="viewportProvider" />,
<SocketProvider key="socketProvider" />,
<DebugProvider key="debugProvider" />,
]
export function Providers({ children }: PropsWithChildren) {
Expand Down
26 changes: 11 additions & 15 deletions src/providers/root/react-query-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { QueryClient } from '@tanstack/react-query'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { useState } from 'react'
import { del, get, set } from 'idb-keyval'
import type {
PersistedClient,
Expand All @@ -22,21 +21,18 @@ const persister = {
await del(idbValidKey)
},
} as Persister
export const ReactQueryProvider = ({ children }: PropsWithChildren) => {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 5, // 5 minutes
refetchInterval: 1000 * 60 * 5, // 5 minutes
refetchOnWindowFocus: false,
refetchIntervalInBackground: false,
},
},
}),
)

export const queryClient = new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 5, // 5 minutes
refetchInterval: 1000 * 60 * 5, // 5 minutes
refetchOnWindowFocus: false,
refetchIntervalInBackground: false,
},
},
})
export const ReactQueryProvider = ({ children }: PropsWithChildren) => {
return (
<PersistQueryClientProvider
client={queryClient}
Expand Down
14 changes: 14 additions & 0 deletions src/providers/root/socket-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client'

import { createContext, useEffect } from 'react'

import { socketClient } from '~/socket'

const Context = createContext<typeof socketClient>(null as any)
export const SocketProvider: Component = ({ children }) => {
useEffect(() => {
socketClient.initIO()
}, [])

return <Context.Provider value={socketClient}>{children}</Context.Provider>
}
20 changes: 20 additions & 0 deletions src/socket/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { setOnlineCount } from '~/atoms'
import { EventTypes } from '~/types/events'
import { isDev } from '~/utils/env'

export const eventHandler = (type: EventTypes, data: any) => {
switch (type) {
case EventTypes.VISITOR_ONLINE:
case EventTypes.VISITOR_OFFLINE: {
const { online } = data
setOnlineCount(online)
break
}

default: {
if (isDev) {
console.log(type, data)
}
}
}
}
1 change: 1 addition & 0 deletions src/socket/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { socketClient } from './socket-client'
67 changes: 67 additions & 0 deletions src/socket/socket-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { io } from 'socket.io-client'
import type { EventTypes } from '~/types/events'
import type { Socket } from 'socket.io-client'

import { simpleCamelcaseKeys as camelcaseKeys } from '@mx-space/api-client'

import { GATEWAY_URL } from '~/constants/env'
import { isDev } from '~/utils/env'

import { eventHandler } from './handler'

class SocketClient {
public socket!: Socket

constructor() {
this.socket = io(`${GATEWAY_URL}/web`, {
timeout: 10000,
reconnectionDelay: 3000,
autoConnect: false,
reconnectionAttempts: 3,
transports: ['websocket'],
})
}
initIO() {
if (!this.socket) {
return
}
this.socket.close()
this.socket.open()
this.socket.on(
'message',
(payload: string | Record<'type' | 'data', any>) => {
if (typeof payload !== 'string') {
return this.handleEvent(payload.type, camelcaseKeys(payload.data))
}
const { data, type } = JSON.parse(payload) as {
data: any
type: EventTypes
}
this.handleEvent(type, camelcaseKeys(data))
},
)
}
reconnect() {
this.socket.open()
}
handleEvent(type: EventTypes, data: any) {
if (isDev) {
console.log(data)
}

window.dispatchEvent(new CustomEvent(type, { detail: data }))

eventHandler(type, data)
}
emit(event: EventTypes, payload: any) {
return new Promise((resolve) => {
if (this.socket && this.socket.connected) {
this.socket.emit(event, payload, (payload: any) => {
resolve(payload)
})
}
})
}
}

export const socketClient = new SocketClient()
30 changes: 30 additions & 0 deletions src/types/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export enum EventTypes {
GATEWAY_CONNECT = 'GATEWAY_CONNECT',
GATEWAY_DISCONNECT = 'GATEWAY_DISCONNECT',

VISITOR_ONLINE = 'VISITOR_ONLINE',
VISITOR_OFFLINE = 'VISITOR_OFFLINE',

AUTH_FAILED = 'AUTH_FAILED',

COMMENT_CREATE = 'COMMENT_CREATE',

POST_CREATE = 'POST_CREATE',
POST_UPDATE = 'POST_UPDATE',
POST_DELETE = 'POST_DELETE',

NOTE_CREATE = 'NOTE_CREATE',
NOTE_UPDATE = 'NOTE_UPDATE',
NOTE_DELETE = 'NOTE_DELETE',

PAGE_UPDATED = 'PAGE_UPDATED',

SAY_CREATE = 'SAY_CREATE',
SAY_DELETE = 'SAY_DELETE',
SAY_UPDATE = 'SAY_UPDATE',

DANMAKU_CREATE = 'DANMAKU_CREATE',

RECENTLY_CREATE = 'RECENTLY_CREATE',
RECENTLY_DElETE = 'RECENTLY_DElETE',
}

1 comment on commit fa8d704

@vercel
Copy link

@vercel vercel bot commented on fa8d704 Jun 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

springtide – ./

springtide-innei.vercel.app
springtide.vercel.app
springtide-git-main-innei.vercel.app

Please sign in to comment.