117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
import { Account } from '@/db'
|
|
import generateNotification from '@/utils/notification'
|
|
import { unreadCount } from '@/entities/marker'
|
|
import generator, { Entity, MegalodonInterface, WebSocketInterface } from 'megalodon'
|
|
import { createContext, useContext, useRef, useState } from 'react'
|
|
import { useUnreads } from './unreads'
|
|
import { useIntl } from 'react-intl'
|
|
|
|
type Context = {
|
|
addAccount: (account: Account) => void
|
|
removeAccount: (account: Account) => void
|
|
removeAll: () => void
|
|
}
|
|
|
|
const AccountsContext = createContext<Context>({
|
|
addAccount: (_: Account) => {},
|
|
removeAccount: (_: Account) => {},
|
|
removeAll: () => {}
|
|
})
|
|
|
|
AccountsContext.displayName = 'AccountsContext'
|
|
|
|
export const useAccounts = () => {
|
|
return useContext(AccountsContext)
|
|
}
|
|
|
|
type Props = {
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export const AccountsProvider: React.FC<Props> = ({ children }) => {
|
|
const [_accounts, setAccounts] = useState<Array<Account>>([])
|
|
const streamings = useRef<{ [key: string]: WebSocketInterface }>({})
|
|
const { setUnreads } = useUnreads()
|
|
const { formatMessage } = useIntl()
|
|
|
|
const addAccount = (account: Account) => {
|
|
setAccounts(current => [...current, account])
|
|
startStreaming(account)
|
|
}
|
|
|
|
const removeAccount = (account: Account) => {
|
|
setAccounts(current => current.filter(a => a.id !== account.id))
|
|
if (streamings.current[account.id]) {
|
|
streamings.current[account.id].removeAllListeners()
|
|
streamings.current[account.id].stop()
|
|
console.log(`close user streaming for ${account.domain}`)
|
|
}
|
|
}
|
|
|
|
const removeAll = () => {
|
|
setAccounts([])
|
|
Object.keys(streamings.current).map(key => {
|
|
streamings.current[key].removeAllListeners()
|
|
streamings.current[key].stop()
|
|
})
|
|
console.log('close all user streamings')
|
|
streamings.current = {}
|
|
}
|
|
|
|
const startStreaming = async (account: Account) => {
|
|
if (!account.id) return
|
|
// Start user streaming for notification
|
|
const client = generator(account.sns, account.url, account.access_token, 'Whalebird')
|
|
const notifications = (await client.getNotifications()).data
|
|
try {
|
|
const res = await client.getMarkers(['notifications'])
|
|
const marker = res.data as Entity.Marker
|
|
if (marker.notifications) {
|
|
const count = unreadCount(marker.notifications, notifications)
|
|
setUnreads(current =>
|
|
Object.assign({}, current, {
|
|
[account.id?.toString()]: count
|
|
})
|
|
)
|
|
}
|
|
} catch (err) {
|
|
console.error(err)
|
|
}
|
|
|
|
const socket = await client.userStreaming()
|
|
streamings.current = Object.assign({}, streamings.current, {
|
|
[account.id]: socket
|
|
})
|
|
socket.on('connect', () => {
|
|
console.log(`connect to user streaming for ${account.domain}`)
|
|
})
|
|
socket.on('notification', (notification: Entity.Notification) => {
|
|
const [title, body] = generateNotification(notification, formatMessage)
|
|
if (title.length > 0) {
|
|
new window.Notification(title, { body: body })
|
|
}
|
|
updateUnreads(account, client)
|
|
})
|
|
}
|
|
|
|
const updateUnreads = async (account: Account, client: MegalodonInterface) => {
|
|
try {
|
|
const res = await client.getMarkers(['notifications'])
|
|
const marker = res.data as Entity.Marker
|
|
if (marker.notifications) {
|
|
const notifications = (await client.getNotifications()).data
|
|
const count = unreadCount(marker.notifications, notifications)
|
|
setUnreads(current =>
|
|
Object.assign({}, current, {
|
|
[account.id?.toString()]: count
|
|
})
|
|
)
|
|
}
|
|
} catch (err) {
|
|
console.error(err)
|
|
}
|
|
}
|
|
|
|
return <AccountsContext.Provider value={{ addAccount, removeAccount, removeAll }}>{children}</AccountsContext.Provider>
|
|
}
|