From 54f995803a7f66ad08f8f4edee99c31d1f71c500 Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Fri, 19 May 2023 17:28:15 +0800 Subject: [PATCH] style: format ts file with prettier --- src/app/cache/index.ts | 4 +- src/app/cache/useRehydrate.ts | 13 +- .../handler.archive.msg.ts | 1 + .../listener.middleware/handler.channels.ts | 2 + src/app/listener.middleware/handler.dm.msg.ts | 1 + .../listener.middleware/handler.file.msg.ts | 1 + .../listener.middleware/handler.footprint.ts | 6 +- src/app/listener.middleware/handler.server.ts | 1 + src/app/listener.middleware/handler.users.ts | 5 +- src/app/listener.middleware/index.ts | 25 +- src/app/services/auth.ts | 9 +- src/app/services/base.query.ts | 20 +- src/app/services/channel.ts | 40 +- src/app/services/handlers.ts | 8 +- src/app/services/message.ts | 63 +- src/app/services/server.ts | 107 ++-- src/app/services/user.ts | 72 ++- src/app/slices/auth.data.ts | 19 +- src/app/slices/channels.ts | 13 +- src/app/slices/favorites.ts | 1 + src/app/slices/footprint.ts | 43 +- src/app/slices/message.archive.ts | 16 +- src/app/slices/message.channel.ts | 2 +- src/app/slices/message.ts | 6 +- src/app/slices/message.user.ts | 2 +- src/app/slices/server.ts | 18 +- src/app/slices/ui.ts | 2 +- src/app/slices/users.ts | 17 +- src/app/slices/voice.ts | 102 +-- src/app/store.ts | 33 +- src/components/Message/useInView.ts | 2 +- src/components/MixedInput/plugins.ts | 1 + src/components/Notification/useDeviceToken.ts | 6 +- src/components/Voice/useVoice.ts | 582 +++++++++--------- src/hooks/useAddLocalFileMessage.ts | 1 + src/hooks/useChatScroll.ts | 3 +- src/hooks/useConfig.ts | 28 +- src/hooks/useCopy.ts | 4 +- src/hooks/useFilteredChannels.ts | 3 +- src/hooks/useForwardMessage.ts | 3 +- src/hooks/useGoogleAuthConfig.ts | 3 +- src/hooks/useLeaveChannel.ts | 2 +- src/hooks/useLicense.ts | 17 +- src/hooks/useLogout.ts | 18 +- src/hooks/useNormalizeMessage.ts | 12 +- src/hooks/usePWAInstallPrompt.ts | 3 +- src/hooks/usePinMessage.ts | 3 +- src/hooks/usePreload.ts | 46 +- src/hooks/useSendMessage.ts | 11 +- src/hooks/useStreaming/chat.handler.ts | 24 +- src/hooks/useTabBroadcast.ts | 51 +- src/hooks/useUploadFile.ts | 10 +- src/hooks/useUserOperation.ts | 24 +- src/i18n.ts | 22 +- src/i18next.d.ts | 17 +- src/libs/DayjsSetting.ts | 4 +- src/react-app-env.d.ts | 2 +- src/routes/onboarding/steps.ts | 4 +- src/service-worker.ts | 8 +- src/serviceWorkerRegistration.ts | 12 +- src/types/auth.ts | 1 + src/types/channel.ts | 7 +- src/types/common.ts | 16 +- src/types/message.ts | 9 +- src/types/resource.ts | 4 +- src/types/server.ts | 43 +- src/types/sse.ts | 32 +- src/types/user.ts | 39 +- src/widget/useCache.ts | 22 +- src/widget/useSSE.ts | 47 +- 70 files changed, 1021 insertions(+), 777 deletions(-) diff --git a/src/app/cache/index.ts b/src/app/cache/index.ts index 9560641a..7fdfcadd 100644 --- a/src/app/cache/index.ts +++ b/src/app/cache/index.ts @@ -1,8 +1,8 @@ import * as localforage from "localforage"; -import { extendPrototype } from 'localforage-setitems'; +import { extendPrototype } from "localforage-setitems"; +import { CACHE_VERSION, KEY_UID } from "../config"; import useRehydrate from "./useRehydrate"; -import { KEY_UID, CACHE_VERSION } from "../config"; extendPrototype(localforage); const tables = [ diff --git a/src/app/cache/useRehydrate.ts b/src/app/cache/useRehydrate.ts index b9f93361..0b513e67 100644 --- a/src/app/cache/useRehydrate.ts +++ b/src/app/cache/useRehydrate.ts @@ -1,15 +1,16 @@ import { useState } from "react"; import { useDispatch } from "react-redux"; -import { fillReactionMessage } from "../slices/message.reaction"; -import { fillServer } from "../slices/server"; + +import { fillChannels } from "../slices/channels"; +import { fillFootprint } from "../slices/footprint"; import { fillMessage } from "../slices/message"; import { fillChannelMsg } from "../slices/message.channel"; -import { fillUserMsg } from "../slices/message.user"; -import { fillChannels } from "../slices/channels"; -import { fillUsers } from "../slices/users"; -import { fillFootprint } from "../slices/footprint"; import { fillFileMessage } from "../slices/message.file"; +import { fillReactionMessage } from "../slices/message.reaction"; +import { fillUserMsg } from "../slices/message.user"; +import { fillServer } from "../slices/server"; import { fillUI } from "../slices/ui"; +import { fillUsers } from "../slices/users"; const useRehydrate = () => { const [iterated, setIterated] = useState(false); diff --git a/src/app/listener.middleware/handler.archive.msg.ts b/src/app/listener.middleware/handler.archive.msg.ts index 591d99f2..ab43b3bb 100644 --- a/src/app/listener.middleware/handler.archive.msg.ts +++ b/src/app/listener.middleware/handler.archive.msg.ts @@ -1,4 +1,5 @@ import clearTable from "./clear.handler"; + interface Params { payload: any; operation: string; diff --git a/src/app/listener.middleware/handler.channels.ts b/src/app/listener.middleware/handler.channels.ts index d962b19c..412804bc 100644 --- a/src/app/listener.middleware/handler.channels.ts +++ b/src/app/listener.middleware/handler.channels.ts @@ -1,6 +1,8 @@ import localforage from "localforage"; + import { Channel } from "@/types/channel"; import clearTable from "./clear.handler"; + interface Params { data: any; payload: any; diff --git a/src/app/listener.middleware/handler.dm.msg.ts b/src/app/listener.middleware/handler.dm.msg.ts index 633a17c7..c23077bf 100644 --- a/src/app/listener.middleware/handler.dm.msg.ts +++ b/src/app/listener.middleware/handler.dm.msg.ts @@ -1,4 +1,5 @@ import clearTable from "./clear.handler"; + interface Params { data: any; payload: any; diff --git a/src/app/listener.middleware/handler.file.msg.ts b/src/app/listener.middleware/handler.file.msg.ts index d4953349..9f3a16b1 100644 --- a/src/app/listener.middleware/handler.file.msg.ts +++ b/src/app/listener.middleware/handler.file.msg.ts @@ -1,4 +1,5 @@ import clearTable from "./clear.handler"; + interface Params { data: any; operation: string; diff --git a/src/app/listener.middleware/handler.footprint.ts b/src/app/listener.middleware/handler.footprint.ts index c188c361..884cb997 100644 --- a/src/app/listener.middleware/handler.footprint.ts +++ b/src/app/listener.middleware/handler.footprint.ts @@ -1,5 +1,7 @@ import localforage from "localforage"; + import clearTable from "./clear.handler"; + interface Params { payload: any; data: any; @@ -7,7 +9,7 @@ interface Params { } const ignores = ["voice_fullscreen", "voice"]; export default async function handler({ operation, data = {}, payload }: Params) { - const table = window.CACHE["footprint"] as typeof localforage;; + const table = window.CACHE["footprint"] as typeof localforage; if (operation.startsWith("reset")) { clearTable("footprint"); return; @@ -34,7 +36,6 @@ export default async function handler({ operation, data = {}, payload }: Params) table?.setItem("afterMid", afterMid); } }); - } break; case "updateMute": @@ -48,7 +49,6 @@ export default async function handler({ operation, data = {}, payload }: Params) const { type } = payload; if (type == "channel") { await table?.setItem("historyChannels", data.historyChannels); - } else { await table?.setItem("historyUsers", data.historyUsers); } diff --git a/src/app/listener.middleware/handler.server.ts b/src/app/listener.middleware/handler.server.ts index 28ca6be6..43eaa9a0 100644 --- a/src/app/listener.middleware/handler.server.ts +++ b/src/app/listener.middleware/handler.server.ts @@ -1,4 +1,5 @@ import localforage from "localforage"; + import clearTable from "./clear.handler"; interface Params { diff --git a/src/app/listener.middleware/handler.users.ts b/src/app/listener.middleware/handler.users.ts index 61058030..89c3e9d8 100644 --- a/src/app/listener.middleware/handler.users.ts +++ b/src/app/listener.middleware/handler.users.ts @@ -1,4 +1,5 @@ import localforage from "localforage"; + import { ContactStatus, User } from "@/types/user"; import clearTable from "./clear.handler"; @@ -20,7 +21,9 @@ export default async function handler({ operation, data, payload }) { break; case "updateContactStatus": { - const tmp = payload as { uid: number, status: ContactStatus } | { uid: number, status: ContactStatus }[]; + const tmp = payload as + | { uid: number; status: ContactStatus } + | { uid: number; status: ContactStatus }[]; const arr = Array.isArray(tmp) ? tmp : [tmp]; const opts = arr.map(({ uid, status }) => { return { key: uid + "", value: { ...data.byId[uid], status } }; diff --git a/src/app/listener.middleware/index.ts b/src/app/listener.middleware/index.ts index eb04410f..44527318 100644 --- a/src/app/listener.middleware/index.ts +++ b/src/app/listener.middleware/index.ts @@ -1,17 +1,18 @@ import { createListenerMiddleware } from "@reduxjs/toolkit"; -import rtkqHandler from "./handler.rtkq"; -import channelsHandler from "./handler.channels"; -import usersHandler from "./handler.users"; -import channelMsgHandler from "./handler.channel.msg"; -import dmMsgHandler from "./handler.dm.msg"; -import serverHandler from "./handler.server"; -import messageHandler from "./handler.message"; -import fileMessageHandler from "./handler.file.msg"; -import archiveMessageHandler from "./handler.archive.msg"; -import reactionHandler from "./handler.reaction"; -import UIHandler from "./handler.ui"; -import footprintHandler from "./handler.footprint"; + import { RootState } from "../store"; +import archiveMessageHandler from "./handler.archive.msg"; +import channelMsgHandler from "./handler.channel.msg"; +import channelsHandler from "./handler.channels"; +import dmMsgHandler from "./handler.dm.msg"; +import fileMessageHandler from "./handler.file.msg"; +import footprintHandler from "./handler.footprint"; +import messageHandler from "./handler.message"; +import reactionHandler from "./handler.reaction"; +import rtkqHandler from "./handler.rtkq"; +import serverHandler from "./handler.server"; +import UIHandler from "./handler.ui"; +import usersHandler from "./handler.users"; const operations = [ "__rtkq", diff --git a/src/app/services/auth.ts b/src/app/services/auth.ts index 818b0bcd..5c75272e 100644 --- a/src/app/services/auth.ts +++ b/src/app/services/auth.ts @@ -1,8 +1,6 @@ -import { createApi } from "@reduxjs/toolkit/query/react"; import { nanoid } from "@reduxjs/toolkit"; -import baseQuery from "./base.query"; -import { setAuthData, updateToken, resetAuthData, updateInitialized } from "../slices/auth.data"; -import BASE_URL, { KEY_DEVICE_ID, KEY_DEVICE_TOKEN, KEY_LOCAL_MAGIC_TOKEN } from "../config"; +import { createApi } from "@reduxjs/toolkit/query/react"; + import { AuthData, CredentialResponse, @@ -11,6 +9,9 @@ import { RenewTokenResponse } from "@/types/auth"; import { UserRegDTO, UserRegResponse } from "@/types/user"; +import BASE_URL, { KEY_DEVICE_ID, KEY_DEVICE_TOKEN, KEY_LOCAL_MAGIC_TOKEN } from "../config"; +import { resetAuthData, setAuthData, updateInitialized, updateToken } from "../slices/auth.data"; +import baseQuery from "./base.query"; const getDeviceId = () => { let d = localStorage.getItem(KEY_DEVICE_ID); diff --git a/src/app/services/base.query.ts b/src/app/services/base.query.ts index 1a07c85f..79e61d76 100644 --- a/src/app/services/base.query.ts +++ b/src/app/services/base.query.ts @@ -1,10 +1,11 @@ -import { fetchBaseQuery } from "@reduxjs/toolkit/query"; import toast from "react-hot-toast"; +import { fetchBaseQuery } from "@reduxjs/toolkit/query"; import dayjs from "dayjs"; -import { updateToken, resetAuthData } from "../slices/auth.data"; -import BASE_URL, { tokenHeader } from "../config"; -import { RootState } from "../store"; + import { getLocalAuthData } from "@/utils"; +import BASE_URL, { tokenHeader } from "../config"; +import { resetAuthData, updateToken } from "../slices/auth.data"; +import { RootState } from "../store"; const whiteList = [ "guestLogin", @@ -102,12 +103,13 @@ const baseQueryWithTokenCheck = async (args: any, api: any, extraOptions: any) = } } break; - case 403: { - const whiteList403 = ["sendMsg"]; - if (!whiteList403.includes(api.endpoint)) { - toast.error("Request Not Allowed"); + case 403: + { + const whiteList403 = ["sendMsg"]; + if (!whiteList403.includes(api.endpoint)) { + toast.error("Request Not Allowed"); + } } - } break; case 404: diff --git a/src/app/services/channel.ts b/src/app/services/channel.ts index 1f36d844..02032cd4 100644 --- a/src/app/services/channel.ts +++ b/src/app/services/channel.ts @@ -1,16 +1,17 @@ import { createApi } from "@reduxjs/toolkit/query/react"; -// import toast from "react-hot-toast"; -import baseQuery from "./base.query"; + +import { Channel, ChannelDTO, CreateChannelDTO } from "@/types/channel"; +import { ContentTypeKey } from "@/types/message"; import BASE_URL, { ContentTypes } from "../config"; -import { updateChannel, removeChannel } from "../slices/channels"; -import { updateRememberedNavs } from "../slices/ui"; +import { removeChannel, updateChannel } from "../slices/channels"; import { removeMessage } from "../slices/message"; import { removeChannelSession } from "../slices/message.channel"; import { removeReactionMessage } from "../slices/message.reaction"; -import { onMessageSendStarted } from "./handlers"; -import { Channel, ChannelDTO, CreateChannelDTO } from "@/types/channel"; +import { updateRememberedNavs } from "../slices/ui"; import { RootState } from "../store"; -import { ContentTypeKey } from "@/types/message"; +// import toast from "react-hot-toast"; +import baseQuery from "./base.query"; +import { onMessageSendStarted } from "./handlers"; export const channelApi = createApi({ reducerPath: "channelApi", @@ -34,14 +35,19 @@ export const channelApi = createApi({ } } }), - createChannel: builder.mutation<{ gid: number, created_at: number } | number, CreateChannelDTO>({ - query: (data) => ({ - url: "group", - method: "POST", - body: data - }) - }), - changeChannelType: builder.mutation({ + createChannel: builder.mutation<{ gid: number; created_at: number } | number, CreateChannelDTO>( + { + query: (data) => ({ + url: "group", + method: "POST", + body: data + }) + } + ), + changeChannelType: builder.mutation< + number, + { is_public: boolean; id: number; members?: number[] } + >({ query: ({ id, is_public, members }) => ({ url: `/group/${id}/change_type`, method: "POST", @@ -101,9 +107,7 @@ export const channelApi = createApi({ method: "DELETE" }), async onQueryStarted(id, { dispatch, getState, queryFulfilled }) { - const { - channelMessage, - } = getState() as RootState; + const { channelMessage } = getState() as RootState; try { await queryFulfilled; // 删掉该channel下的所有消息&reaction diff --git a/src/app/services/handlers.ts b/src/app/services/handlers.ts index 860be90f..f16a4502 100644 --- a/src/app/services/handlers.ts +++ b/src/app/services/handlers.ts @@ -2,9 +2,9 @@ import toast from "react-hot-toast"; import { batch } from "react-redux"; import { ContentTypes } from "../config"; +import { addMessage, removeMessage, updateMessage } from "../slices/message"; import { addChannelMsg, removeChannelMsg } from "../slices/message.channel"; import { addUserMsg, removeUserMsg } from "../slices/message.user"; -import { addMessage, removeMessage, updateMessage } from "../slices/message"; export const onMessageSendStarted = async ( { @@ -23,7 +23,9 @@ export const onMessageSendStarted = async ( if (type == "archive") return; // id: who send to ,from_uid: who sent // console.log("handlers data", content, type, properties, ignoreLocal, id); - const isMedia = properties.content_type ? ["image", "video", "audio"].includes(properties.content_type.split('/')[0]) : false; + const isMedia = properties.content_type + ? ["image", "video", "audio"].includes(properties.content_type.split("/")[0]) + : false; // const isImage = properties.content_type?.startsWith("image"); const ts = properties.local_id || +new Date(); const tmpMsg = { @@ -63,7 +65,7 @@ export const onMessageSendStarted = async ( // toast.error(`Failed to send, blocked maybe.`,); dispatch(updateMessage({ mid: ts, failed: true })); } else { - toast.error(`Send Message Failed ${JSON.stringify(error)}`,); + toast.error(`Send Message Failed ${JSON.stringify(error)}`); dispatch(removeContextMessage({ id, mid: ts })); dispatch(removeMessage(ts)); } diff --git a/src/app/services/message.ts b/src/app/services/message.ts index e93801c4..52802892 100644 --- a/src/app/services/message.ts +++ b/src/app/services/message.ts @@ -1,16 +1,17 @@ import { createApi } from "@reduxjs/toolkit/query/react"; -import { ContentTypes } from "../config"; -import { updateReadChannels, updateReadUsers, upsertOG } from "../slices/footprint"; -import { fillFavorites, populateFavorite, addFavorite, deleteFavorite } from "../slices/favorites"; -import { onMessageSendStarted } from "./handlers"; -import { normalizeArchiveData } from "@/utils"; -import baseQuery from "./base.query"; -import { Archive, FavoriteArchive, OG } from "@/types/resource"; -import { ChatMessage, ContentTypeKey, UploadFileResponse } from "@/types/message"; + import { ChatContext } from "@/types/common"; +import { ChatMessage, ContentTypeKey, UploadFileResponse } from "@/types/message"; +import { Archive, FavoriteArchive, OG } from "@/types/resource"; import handleChatMessage from "@/hooks/useStreaming/chat.handler"; -import { RootState } from "../store"; +import { normalizeArchiveData } from "@/utils"; +import { ContentTypes } from "../config"; +import { addFavorite, deleteFavorite, fillFavorites, populateFavorite } from "../slices/favorites"; +import { updateReadChannels, updateReadUsers, upsertOG } from "../slices/footprint"; import { upsertArchiveMessage } from "../slices/message.archive"; +import { RootState } from "../store"; +import baseQuery from "./base.query"; +import { onMessageSendStarted } from "./handlers"; export const messageApi = createApi({ reducerPath: "messageApi", @@ -73,15 +74,18 @@ export const messageApi = createApi({ dispatch(upsertOG({ key: url, value: data })); } catch (err) { console.log("get og error", err); - dispatch(upsertOG({ - key: url, value: { - images: [], - audios: [], - videos: [], - title: "", - url, - } - })); + dispatch( + upsertOG({ + key: url, + value: { + images: [], + audios: [], + videos: [], + title: "", + url + } + }) + ); } } }), @@ -176,12 +180,15 @@ export const messageApi = createApi({ } } }), - loadMoreMessages: builder.query({ + loadMoreMessages: builder.query< + ChatMessage[], + { context?: ChatContext; id: number; mid?: number; limit?: number } + >({ query: ({ context = "channel", id, mid = "", limit = 100 }) => { - const url = context == "channel" ? - `/group/${id}/history?limit=${limit}${mid ? `&before=${mid}` : ""}` - : - `/user/${id}/history?limit=${limit}${mid ? `&before=${mid}` : ""}`; + const url = + context == "channel" + ? `/group/${id}/history?limit=${limit}${mid ? `&before=${mid}` : ""}` + : `/user/${id}/history?limit=${limit}${mid ? `&before=${mid}` : ""}`; return { url }; @@ -198,7 +205,13 @@ export const messageApi = createApi({ }), replyMessage: builder.mutation< number, - { from_uid: number, reply_mid: number; content: string; type: ContentTypeKey, properties?: {} } + { + from_uid: number; + reply_mid: number; + content: string; + type: ContentTypeKey; + properties?: {}; + } >({ query: ({ reply_mid, content, type = "text", properties }) => ({ headers: { @@ -245,7 +258,7 @@ export const messageApi = createApi({ method: "HEAD", responseHandler: "text" }) - }), + }) }) }); diff --git a/src/app/services/server.ts b/src/app/services/server.ts index f3ba57a9..047c9bb8 100644 --- a/src/app/services/server.ts +++ b/src/app/services/server.ts @@ -1,30 +1,31 @@ import { createApi } from "@reduxjs/toolkit/query/react"; -import BASE_URL, { ContentTypes, IS_OFFICIAL_DEMO, PAYMENT_URL_PREFIX } from "../config"; -import { updateInfo } from "../slices/server"; -import baseQuery from "./base.query"; -import { RootState } from "../store"; -import { User } from "@/types/user"; -import { - FirebaseConfig, - GoogleAuthConfig, - LoginConfig, - Server, - TestEmailDTO, - CreateAdminDTO, - SMTPConfig, - AgoraConfig, - GithubAuthConfig, - LicenseResponse, - RenewLicense, - RenewLicenseResponse, - AgoraTokenResponse, - AgoraVoicingListResponse, - SystemCommon, - AgoraChannelUsersResponse -} from "@/types/server"; + import { Channel } from "@/types/channel"; import { ContentTypeKey } from "@/types/message"; +import { + AgoraChannelUsersResponse, + AgoraConfig, + AgoraTokenResponse, + AgoraVoicingListResponse, + CreateAdminDTO, + FirebaseConfig, + GithubAuthConfig, + GoogleAuthConfig, + LicenseResponse, + LoginConfig, + RenewLicense, + RenewLicenseResponse, + Server, + SMTPConfig, + SystemCommon, + TestEmailDTO +} from "@/types/server"; +import { User } from "@/types/user"; +import BASE_URL, { ContentTypes, IS_OFFICIAL_DEMO, PAYMENT_URL_PREFIX } from "../config"; +import { updateInfo } from "../slices/server"; import { updateCallInfo, upsertVoiceList } from "../slices/voice"; +import { RootState } from "../store"; +import baseQuery from "./base.query"; const defaultExpireDuration = 2 * 24 * 60 * 60; @@ -69,9 +70,7 @@ export const serverApi = createApi({ async onQueryStarted(data, { dispatch, queryFulfilled }) { try { const resp = await queryFulfilled; - dispatch( - updateInfo({ version: resp.data }) - ); + dispatch(updateInfo({ version: resp.data })); } catch { console.error("get server version error"); } @@ -117,18 +116,26 @@ export const serverApi = createApi({ getAgoraConfig: builder.query({ query: () => ({ url: `/admin/agora/config` }) }), - getAgoraChannels: builder.query({ - query: (param = { page_no: 0, page_size: 100 }) => ({ url: `/admin/agora/channel/${param.page_no}/${param.page_size}` }), + getAgoraChannels: builder.query< + AgoraVoicingListResponse, + { page_no: number; page_size: number } + >({ + query: (param = { page_no: 0, page_size: 100 }) => ({ + url: `/admin/agora/channel/${param.page_no}/${param.page_size}` + }), async onQueryStarted(data, { dispatch, queryFulfilled, getState }) { try { - const { voice: { callingFrom }, authData } = getState() as RootState; + const { + voice: { callingFrom }, + authData + } = getState() as RootState; const { data: resp } = await queryFulfilled; const { success } = resp; if (success) { - const arr = resp.data.channels.map(data => { + const arr = resp.data.channels.map((data) => { const [type, id] = data.channel_name.split(":").slice(-2); const count = data.user_count; - const context = type === "group" ? "channel" as const : "dm" as const; + const context = type === "group" ? ("channel" as const) : ("dm" as const); return { id: +id, context, @@ -137,7 +144,9 @@ export const serverApi = createApi({ }; }); dispatch(upsertVoiceList(arr)); - const hasMyself = arr.some(data => data.context === "dm" && data.id == authData?.user?.uid); + const hasMyself = arr.some( + (data) => data.context === "dm" && data.id == authData?.user?.uid + ); const sendByMe = callingFrom && callingFrom === authData?.user?.uid; // reset dm call setting if (callingFrom && !sendByMe && !hasMyself) { @@ -187,9 +196,7 @@ export const serverApi = createApi({ async onQueryStarted(data, { dispatch, queryFulfilled }) { try { const resp = await queryFulfilled; - dispatch( - updateInfo(resp.data) - ); + dispatch(updateInfo(resp.data)); } catch { console.error("get server common error"); } @@ -204,9 +211,7 @@ export const serverApi = createApi({ async onQueryStarted(data, { dispatch, queryFulfilled }) { try { await queryFulfilled; - dispatch( - updateInfo(data) - ); + dispatch(updateInfo(data)); } catch { console.error("update server common error"); } @@ -309,7 +314,7 @@ export const serverApi = createApi({ url: `/admin/system/update_frontend_url`, method: "POST", headers: { - "content-type": 'text/plain', + "content-type": "text/plain" }, body: url }) @@ -324,16 +329,16 @@ export const serverApi = createApi({ const rootStore = getState() as RootState; const { upgraded: prevValue } = rootStore.server; try { - const { data: { user_limit } } = await queryFulfilled; + const { + data: { user_limit } + } = await queryFulfilled; const currValue = user_limit > 20; if (prevValue !== currValue) { dispatch(updateInfo({ upgraded: currValue })); } } catch { console.error("update license upgraded status failed "); - } - } }), @@ -363,15 +368,25 @@ export const serverApi = createApi({ body: { license } }) }), - getBotRelatedChannels: builder.query({ + getBotRelatedChannels: builder.query({ query: ({ api_key, public_only = false }) => ({ url: public_only ? `/bot?public_only=${public_only}` : `/bot`, headers: { "x-api-key": api_key - }, + } }) }), - sendMessageByBot: builder.mutation({ + sendMessageByBot: builder.mutation< + number, + { + uid?: number; + cid?: number; + api_key: string; + content: string; + type?: ContentTypeKey; + properties?: object; + } + >({ query: ({ uid, cid, api_key, type = "text", properties, content }) => ({ headers: { "x-api-key": api_key, @@ -384,7 +399,7 @@ export const serverApi = createApi({ method: "POST", body: content }) - }), + }) }) }); diff --git a/src/app/services/user.ts b/src/app/services/user.ts index 087109cd..fa2e46a0 100644 --- a/src/app/services/user.ts +++ b/src/app/services/user.ts @@ -1,13 +1,25 @@ import { createApi } from "@reduxjs/toolkit/query/react"; -// import toast from "react-hot-toast"; -import baseQuery from "./base.query"; + +import { ContentTypeKey, MuteDTO } from "@/types/message"; +import { + AutoDeleteMsgDTO, + BotAPIKey, + ContactAction, + ContactResponse, + ContactStatus, + User, + UserCreateDTO, + UserDTO, + UserForAdmin, + UserForAdminDTO +} from "@/types/user"; +import BASE_URL, { ContentTypes } from "../config"; import { updateAutoDeleteSetting, updateMute } from "../slices/footprint"; import { fillUsers, updateContactStatus as updateStatus } from "../slices/users"; -import BASE_URL, { ContentTypes } from "../config"; -import { onMessageSendStarted } from "./handlers"; -import { AutoDeleteMsgDTO, BotAPIKey, ContactAction, ContactResponse, ContactStatus, User, UserCreateDTO, UserDTO, UserForAdmin, UserForAdminDTO } from "@/types/user"; -import { ContentTypeKey, MuteDTO } from "@/types/message"; import { RootState } from "../store"; +// import toast from "react-hot-toast"; +import baseQuery from "./base.query"; +import { onMessageSendStarted } from "./handlers"; export const userApi = createApi({ reducerPath: "userApi", @@ -29,14 +41,20 @@ export const userApi = createApi({ async onQueryStarted(data, { dispatch, queryFulfilled, getState }) { try { const { data: users } = await queryFulfilled; - const { authData: { user: loginUser } } = getState() as RootState; - dispatch(fillUsers(users.map((u) => { - const status = loginUser?.uid == u.uid ? "added" : ""; - return { - ...u, - status - }; - }))); + const { + authData: { user: loginUser } + } = getState() as RootState; + dispatch( + fillUsers( + users.map((u) => { + const status = loginUser?.uid == u.uid ? "added" : ""; + return { + ...u, + status + }; + }) + ) + ); } catch { console.log("get user list error"); } @@ -71,7 +89,7 @@ export const userApi = createApi({ method: "POST" }) }), - searchUser: builder.mutation({ + searchUser: builder.mutation({ query: (input) => ({ url: `/user/search`, body: input, @@ -123,7 +141,7 @@ export const userApi = createApi({ } }), - updateContactStatus: builder.mutation({ + updateContactStatus: builder.mutation({ query: (payload) => ({ url: `/user/update_contact_status`, method: "POST", @@ -131,10 +149,10 @@ export const userApi = createApi({ }), async onQueryStarted(data, { dispatch, queryFulfilled }) { const map = { - "add": "added", - "block": "blocked", - "remove": "", - "unblock": "", + add: "added", + block: "blocked", + remove: "", + unblock: "" }; try { await queryFulfilled; @@ -170,7 +188,7 @@ export const userApi = createApi({ body: data }) }), - updateAvatarByAdmin: builder.mutation({ + updateAvatarByAdmin: builder.mutation({ query: ({ uid, file }) => ({ headers: { "content-type": "image/png" @@ -184,18 +202,17 @@ export const userApi = createApi({ query: (uid) => ({ url: `/admin/user/${uid}` }) }), // bot operations - createBotAPIKey: builder.mutation({ + createBotAPIKey: builder.mutation({ query: ({ uid, name }) => ({ url: `/admin/user/bot-api-key/${uid}`, method: "POST", - body: { name }, - }), - + body: { name } + }) }), getBotAPIKeys: builder.query({ query: (uid) => ({ url: `/admin/user/bot-api-key/${uid}` }) }), - deleteBotAPIKey: builder.query({ + deleteBotAPIKey: builder.query({ query: ({ uid, kid }) => ({ url: `/admin/user/bot-api-key/${uid}/${kid}`, method: "DELETE" }) }), // bot operations end @@ -225,8 +242,7 @@ export const userApi = createApi({ // @ts-ignore await onMessageSendStarted.call(this, param1, param2, "user"); } - }), - + }) }) }); diff --git a/src/app/slices/auth.data.ts b/src/app/slices/auth.data.ts index 7693bf26..ea27c550 100644 --- a/src/app/slices/auth.data.ts +++ b/src/app/slices/auth.data.ts @@ -1,15 +1,17 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +import { AuthData, RenewTokenResponse } from "@/types/auth"; // import { isNull, omitBy } from "lodash"; import { KEY_EXPIRE, - KEY_PWA_INSTALLED, KEY_LOGIN_USER, + KEY_PWA_INSTALLED, KEY_REFRESH_TOKEN, KEY_TOKEN, KEY_UID } from "../config"; -import { AuthData, RenewTokenResponse } from "@/types/auth"; import { StoredUser } from "./users"; + // import { updateUsersByLogs } from './users'; interface State { @@ -71,7 +73,7 @@ const authDataSlice = createSlice({ if (!state.user) return; const obj = { ...state.user, ...payload }; - Object.keys(obj).forEach(key => { + Object.keys(obj).forEach((key) => { // @ts-ignore if (obj[key] === undefined) { // @ts-ignore @@ -108,7 +110,7 @@ const authDataSlice = createSlice({ localStorage.setItem(KEY_TOKEN, token); localStorage.setItem(KEY_REFRESH_TOKEN, refresh_token); } - }, + } // extraReducers: (builder) => { // builder.addCase(updateUsersByLogs, (state, action) => { // const changeLogs = action.payload; @@ -131,5 +133,12 @@ const authDataSlice = createSlice({ // } }); -export const { updateInitialized, updateLoginUser, setAuthData, resetAuthData, updateToken, updateRoleChanged } = authDataSlice.actions; +export const { + updateInitialized, + updateLoginUser, + setAuthData, + resetAuthData, + updateToken, + updateRoleChanged +} = authDataSlice.actions; export default authDataSlice.reducer; diff --git a/src/app/slices/channels.ts b/src/app/slices/channels.ts index 49a41bf4..8e742837 100644 --- a/src/app/slices/channels.ts +++ b/src/app/slices/channels.ts @@ -1,7 +1,9 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { isNull, omitBy } from "lodash"; -import BASE_URL from "../config"; + import { Channel, UpdateChannelDTO, UpdatePinnedMessageDTO } from "@/types/channel"; +import BASE_URL from "../config"; + // import { updateVoicingInfo } from "./voice"; interface StoredChannel extends Channel { @@ -35,7 +37,7 @@ const channelsSlice = createSlice({ icon: c.avatar_updated_at == 0 ? "" - : `${BASE_URL}/resource/group_avatar?gid=${c.gid}&t=${c.avatar_updated_at}`, + : `${BASE_URL}/resource/group_avatar?gid=${c.gid}&t=${c.avatar_updated_at}` }; }); }, @@ -50,7 +52,7 @@ const channelsSlice = createSlice({ icon: avatar_updated_at == 0 ? "" - : `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}`, + : `${BASE_URL}/resource/group_avatar?gid=${gid}&t=${avatar_updated_at}` }; }, updateChannel(state, action: PayloadAction) { @@ -109,8 +111,7 @@ const channelsSlice = createSlice({ delete state.byId[gid]; } } - }, - + } }); export const { @@ -119,7 +120,7 @@ export const { fillChannels, addChannel, updateChannel, - removeChannel, + removeChannel } = channelsSlice.actions; export default channelsSlice.reducer; diff --git a/src/app/slices/favorites.ts b/src/app/slices/favorites.ts index af86ffe0..83353b60 100644 --- a/src/app/slices/favorites.ts +++ b/src/app/slices/favorites.ts @@ -1,4 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + // import BASE_URL from "../config"; export interface Favorite { diff --git a/src/app/slices/footprint.ts b/src/app/slices/footprint.ts index f0aa9cf3..acb7a9ed 100644 --- a/src/app/slices/footprint.ts +++ b/src/app/slices/footprint.ts @@ -1,14 +1,23 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +import { ChatContext } from "@/types/common"; import { MuteDTO } from "@/types/message"; import { OG } from "@/types/resource"; -import { AutoDeleteMessageSettingDTO, AutoDeleteMsgForGroup, AutoDeleteMsgForUser, AutoDeleteSettingForChannels, AutoDeleteSettingForUsers, PinChat, PinChatTarget } from "@/types/sse"; +import { + AutoDeleteMessageSettingDTO, + AutoDeleteMsgForGroup, + AutoDeleteMsgForUser, + AutoDeleteSettingForChannels, + AutoDeleteSettingForUsers, + PinChat, + PinChatTarget +} from "@/types/sse"; import { resetAuthData } from "./auth.data"; -import { ChatContext } from "@/types/common"; type ChannelAside = "members" | "voice" | "voice_fullscreen" | null; type DMAside = "voice" | "voice_fullscreen" | null; export interface State { - og: { [url: string]: OG } + og: { [url: string]: OG }; usersVersion: number; afterMid: number; historyUsers: { [uid: number]: string | "reached" }; @@ -24,7 +33,6 @@ export interface State { pinChats: PinChat[]; } - const initialState: State = { og: {}, usersVersion: 0, @@ -99,9 +107,9 @@ const footprintSlice = createSlice({ switch (key) { case "burn_after_reading_users": { const updates = (payload as AutoDeleteSettingForUsers).burn_after_reading_users; - updates.map(item => { + updates.map((item) => { const { uid } = item; - const idx = state.autoDeleteMsgUsers.findIndex(tmp => tmp.uid == uid); + const idx = state.autoDeleteMsgUsers.findIndex((tmp) => tmp.uid == uid); if (idx !== -1) { // update state.autoDeleteMsgUsers[idx] = item; @@ -114,9 +122,9 @@ const footprintSlice = createSlice({ } case "burn_after_reading_groups": { const updates = (payload as AutoDeleteSettingForChannels).burn_after_reading_groups; - updates.map(item => { + updates.map((item) => { const { gid } = item; - const idx = state.autoDeleteMsgChannels.findIndex(tmp => tmp.gid == gid); + const idx = state.autoDeleteMsgChannels.findIndex((tmp) => tmp.gid == gid); if (idx !== -1) { // update state.autoDeleteMsgChannels[idx] = item; @@ -169,7 +177,7 @@ const footprintSlice = createSlice({ } }); }, - upsertPinChats(state, action: PayloadAction<{ pins: PinChat[], override?: boolean }>) { + upsertPinChats(state, action: PayloadAction<{ pins: PinChat[]; override?: boolean }>) { const { pins, override = false } = action.payload; if (override) { state.pinChats = pins; @@ -179,17 +187,20 @@ const footprintSlice = createSlice({ }, removePinChats(state, action: PayloadAction) { const pins = action.payload; - state.pinChats = state.pinChats.filter(pin => { + state.pinChats = state.pinChats.filter((pin) => { const key = "uid" in pin.target ? "uid" : "gid"; // @ts-ignore - return !pins.some(p => p[key] == pin.target[key]); + return !pins.some((p) => p[key] == pin.target[key]); }); }, - upsertOG(state, action: PayloadAction<{ key: string, value: OG }>) { + upsertOG(state, action: PayloadAction<{ key: string; value: OG }>) { const { key, value } = action.payload; state.og[key] = value; }, - updateHistoryMark(state, action: PayloadAction<{ type: ChatContext, id: number, mid: string, }>) { + updateHistoryMark( + state, + action: PayloadAction<{ type: ChatContext; id: number; mid: string }> + ) { const { type, id, mid } = action.payload; if (type == "channel") { state.historyChannels[id] = mid; @@ -211,14 +222,14 @@ const footprintSlice = createSlice({ state.readChannels[gid] = mid; }); }, - updateChannelVisibleAside(state, action: PayloadAction<{ id: number, aside: ChannelAside }>) { + updateChannelVisibleAside(state, action: PayloadAction<{ id: number; aside: ChannelAside }>) { const { id, aside } = action.payload; state.channelAsides[id] = aside; }, - updateDMVisibleAside(state, action: PayloadAction<{ id: number, aside: DMAside }>) { + updateDMVisibleAside(state, action: PayloadAction<{ id: number; aside: DMAside }>) { const { id, aside } = action.payload; state.dmAsides[id] = aside; - }, + } }, extraReducers: (builder) => { builder.addCase(resetAuthData, (state) => { diff --git a/src/app/slices/message.archive.ts b/src/app/slices/message.archive.ts index 67898d74..ec02fb6e 100644 --- a/src/app/slices/message.archive.ts +++ b/src/app/slices/message.archive.ts @@ -1,13 +1,14 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + // import { ContentTypes } from "../config"; // import { normalizeFileMessage } from "../../common/utils"; // import { ContentType } from "@/types/message"; import { Archive } from "@/types/resource"; + export interface State { [key: string]: Archive; } -const initialState: State = { -}; +const initialState: State = {}; const messageArchiveSlice = createSlice({ name: "archiveMessage", @@ -19,17 +20,14 @@ const messageArchiveSlice = createSlice({ fillArchiveMessage(state, action) { return Object.assign({ ...initialState }, action.payload); }, - upsertArchiveMessage(state, action: PayloadAction<{ filePath: string, data: Archive }>) { + upsertArchiveMessage(state, action: PayloadAction<{ filePath: string; data: Archive }>) { const { filePath, data } = action.payload; state[filePath] = data; - }, + } } }); -export const { - resetArchiveMessage, - fillArchiveMessage, - upsertArchiveMessage -} = messageArchiveSlice.actions; +export const { resetArchiveMessage, fillArchiveMessage, upsertArchiveMessage } = + messageArchiveSlice.actions; export default messageArchiveSlice.reducer; diff --git a/src/app/slices/message.channel.ts b/src/app/slices/message.channel.ts index edcd2847..2f6b498c 100644 --- a/src/app/slices/message.channel.ts +++ b/src/app/slices/message.channel.ts @@ -27,7 +27,7 @@ const channelMsgSlice = createSlice({ const localMsgExisted = state[id]!.findIndex((id) => id == local_id) > -1; if (midExisted || localMsgExisted) return; // 每次入库,都排序 - const newArr = [...state[id] as number[], +mid].sort((a, b) => a - b); + const newArr = [...(state[id] as number[]), +mid].sort((a, b) => a - b); state[id] = newArr; } else { state[id] = [+mid]; diff --git a/src/app/slices/message.ts b/src/app/slices/message.ts index 74e4aad0..ca2a489e 100644 --- a/src/app/slices/message.ts +++ b/src/app/slices/message.ts @@ -1,7 +1,9 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +import { ContentType } from "@/types/message"; // import { ContentTypes } from "../config"; import { normalizeFileMessage } from "@/utils"; -import { ContentType } from "@/types/message"; + export interface MessagePayload { mid: number; from_uid?: number; @@ -45,7 +47,7 @@ const messageSlice = createSlice({ fillMessage(state, action) { return Object.assign({ ...initialState }, action.payload); }, - updateMessage(state, action: PayloadAction<{ mid: number;[key: string | number]: any }>) { + updateMessage(state, action: PayloadAction<{ mid: number; [key: string | number]: any }>) { const { mid, ...rest } = action.payload; state[mid] = { ...state[mid], ...rest }; }, diff --git a/src/app/slices/message.user.ts b/src/app/slices/message.user.ts index 0e11cf35..6c5d4ac5 100644 --- a/src/app/slices/message.user.ts +++ b/src/app/slices/message.user.ts @@ -40,7 +40,7 @@ const userMsgSlice = createSlice({ state.ids.push(+id); } }, - removeUserMsg(state, action: PayloadAction<{ id: number, mid: number }>) { + removeUserMsg(state, action: PayloadAction<{ id: number; mid: number }>) { const { id, mid } = action.payload; if (state.byId[id]) { const idx = state.byId[id].findIndex((i: number) => i == mid); diff --git a/src/app/slices/server.ts b/src/app/slices/server.ts index fa842624..c69a9141 100644 --- a/src/app/slices/server.ts +++ b/src/app/slices/server.ts @@ -1,9 +1,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + import { Server } from "@/types/server"; export interface StoredServer extends Server { - version: string, - upgraded: boolean, + version: string; + upgraded: boolean; logo: string; inviteLink?: { link: string; @@ -49,7 +50,18 @@ const serverSlice = createSlice({ contact_verification_enable = false, chat_layout_mode = "Left" } = action.payload || {}; - return { version, upgraded, name, logo, description, inviteLink, show_user_online_status, webclient_auto_update, contact_verification_enable, chat_layout_mode }; + return { + version, + upgraded, + name, + logo, + description, + inviteLink, + show_user_online_status, + webclient_auto_update, + contact_verification_enable, + chat_layout_mode + }; }, updateInfo(state, action: PayloadAction>) { const values = action.payload || {}; diff --git a/src/app/slices/ui.ts b/src/app/slices/ui.ts index e6f096d1..b218201a 100644 --- a/src/app/slices/ui.ts +++ b/src/app/slices/ui.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -export type ListView = "item" | "grid" +export type ListView = "item" | "grid"; export interface State { online: boolean; ready: boolean; diff --git a/src/app/slices/users.ts b/src/app/slices/users.ts index 94761a81..6578c462 100644 --- a/src/app/slices/users.ts +++ b/src/app/slices/users.ts @@ -1,15 +1,16 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { isNull, omitBy } from "lodash"; -import BASE_URL from "../config"; -import { Contact, ContactStatus } from "@/types/user"; + import { UserLog, UserState } from "@/types/sse"; +import { Contact, ContactStatus } from "@/types/user"; +import BASE_URL from "../config"; type DMAside = "voice" | null; export interface StoredUser extends Contact { online?: boolean; voice?: boolean; avatar?: string; - visibleAside?: DMAside + visibleAside?: DMAside; } export interface State { @@ -99,7 +100,12 @@ const usersSlice = createSlice({ } }); }, - updateContactStatus(state, action: PayloadAction<{ uid: number, status: ContactStatus } | { uid: number, status: ContactStatus }[]>) { + updateContactStatus( + state, + action: PayloadAction< + { uid: number; status: ContactStatus } | { uid: number; status: ContactStatus }[] + > + ) { const arr = Array.isArray(action.payload) ? action.payload : [action.payload]; arr.forEach((data) => { if (state.byId[data.uid]) { @@ -110,5 +116,6 @@ const usersSlice = createSlice({ } }); -export const { updateContactStatus, resetUsers, fillUsers, updateUsersByLogs, updateUsersStatus } = usersSlice.actions; +export const { updateContactStatus, resetUsers, fillUsers, updateUsersByLogs, updateUsersStatus } = + usersSlice.actions; export default usersSlice.reducer; diff --git a/src/app/slices/voice.ts b/src/app/slices/voice.ts index ef460c24..04138dc6 100644 --- a/src/app/slices/voice.ts +++ b/src/app/slices/voice.ts @@ -1,53 +1,55 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { KEY_UID } from "../config"; import { ConnectionState } from "agora-rtc-sdk-ng"; -import { resetAuthData } from "./auth.data"; + import { ChatContext } from "@/types/common"; +import { KEY_UID } from "../config"; +import { resetAuthData } from "./auth.data"; export type VoiceBasicInfo = { - context: ChatContext, - from?: number, - id: number,// means to in dm context -} + context: ChatContext; + from?: number; + id: number; // means to in dm context +}; export type VoicingInfo = { - downlinkNetworkQuality?: number, - joining?: boolean, - connectionState?: ConnectionState -} & VoiceBasicInfo & VoicingMemberInfo + downlinkNetworkQuality?: number; + joining?: boolean; + connectionState?: ConnectionState; +} & VoiceBasicInfo & + VoicingMemberInfo; export type VoicingMemberInfo = { - speakingVolume?: number, - muted?: boolean, - deafen?: boolean, - video?: boolean, - shareScreen?: boolean -} + speakingVolume?: number; + muted?: boolean; + deafen?: boolean; + video?: boolean; + shareScreen?: boolean; +}; export type VoicingMembers = { - ids: number[], + ids: number[]; byId: { - [key: number]: VoicingMemberInfo - }, - pin?: number -} + [key: number]: VoicingMemberInfo; + }; + pin?: number; +}; export type VoiceInfo = { - channelName: string, - memberCount: number -} & VoiceBasicInfo + channelName: string; + memberCount: number; +} & VoiceBasicInfo; export type DeviceInfo = Pick; -export type MediaDeviceKind = "audioinput" | "audiooutput" | "videoinput" +export type MediaDeviceKind = "audioinput" | "audiooutput" | "videoinput"; interface State { - callingFrom: number, - callingTo: number, - calling: boolean, - voicing: VoicingInfo | null, - voicingMembers: VoicingMembers, - list: VoiceInfo[], - devices: DeviceInfo[], - audioInputDeviceId: string, - audioOutputDeviceId: string, - videoInputDeviceId: string, + callingFrom: number; + callingTo: number; + calling: boolean; + voicing: VoicingInfo | null; + voicingMembers: VoicingMembers; + list: VoiceInfo[]; + devices: DeviceInfo[]; + audioInputDeviceId: string; + audioOutputDeviceId: string; + videoInputDeviceId: string; } const initialState: State = { calling: false, @@ -68,7 +70,10 @@ const voiceSlice = createSlice({ name: "voice", initialState, reducers: { - updateCallInfo(state, { payload }: PayloadAction<{ from: number, to?: number, calling?: boolean }>) { + updateCallInfo( + state, + { payload }: PayloadAction<{ from: number; to?: number; calling?: boolean }> + ) { const { from, to = 0, calling } = payload; state.callingFrom = from; state.callingTo = to; @@ -80,9 +85,9 @@ const voiceSlice = createSlice({ state.devices = payload; if (payload.length > 0) { // 默认选择第一个 - const audioInputDevice = payload.find(v => v.kind == "audioinput"); - const audioOutputDevice = payload.find(v => v.kind == "audiooutput"); - const videoInputDevice = payload.find(v => v.kind == "videoinput"); + const audioInputDevice = payload.find((v) => v.kind == "audioinput"); + const audioOutputDevice = payload.find((v) => v.kind == "audiooutput"); + const videoInputDevice = payload.find((v) => v.kind == "videoinput"); if (audioInputDevice) { state.audioInputDeviceId = audioInputDevice.deviceId; } @@ -97,7 +102,10 @@ const voiceSlice = createSlice({ updateCalling(state, { payload }: PayloadAction) { state.calling = payload; }, - updateSelectDeviceId(state, { payload }: PayloadAction<{ kind: MediaDeviceKind, value: string }>) { + updateSelectDeviceId( + state, + { payload }: PayloadAction<{ kind: MediaDeviceKind; value: string }> + ) { const { kind, value } = payload; switch (kind) { case "audioinput": @@ -152,7 +160,6 @@ const voiceSlice = createSlice({ } }); } - } else { // reset state.voicing = payload; @@ -177,7 +184,7 @@ const voiceSlice = createSlice({ state.list = payload; } else { const { id, context } = payload; - const idx = state.list.findIndex(v => v.id == id && v.context == context); + const idx = state.list.findIndex((v) => v.id == id && v.context == context); if (idx > -1) { state.list.splice(idx, 1, payload); } else { @@ -203,14 +210,17 @@ const voiceSlice = createSlice({ delete state.voicingMembers.byId[payload]; } }, - updateVoicingMember(state, { payload }: PayloadAction<{ uid: number, info: VoicingMemberInfo }>) { + updateVoicingMember( + state, + { payload }: PayloadAction<{ uid: number; info: VoicingMemberInfo }> + ) { const idx = state.voicingMembers.ids.findIndex((uid) => uid == payload.uid); if (idx > -1) { const { uid, info } = payload; state.voicingMembers.byId[uid] = { ...state.voicingMembers.byId[uid], ...info }; } }, - updatePin(state, { payload }: PayloadAction<{ uid: number, action: "pin" | "unpin" }>) { + updatePin(state, { payload }: PayloadAction<{ uid: number; action: "pin" | "unpin" }>) { const idx = state.voicingMembers.ids.findIndex((uid) => uid == payload.uid); if (idx > -1) { state.voicingMembers.pin = payload.action == "pin" ? payload.uid : undefined; @@ -223,13 +233,13 @@ const voiceSlice = createSlice({ if (window.VOICE_CLIENT) { window.VOICE_CLIENT.leave(); Object.entries(window.VOICE_TRACK_MAP).forEach(([uid, track]) => { - if (track && 'close' in track) { + if (track && "close" in track) { track.close(); } delete window.VOICE_TRACK_MAP[+uid]; }); Object.entries(window.VIDEO_TRACK_MAP).forEach(([uid, track]) => { - if (track && 'close' in track) { + if (track && "close" in track) { track?.close(); } delete window.VOICE_TRACK_MAP[+uid]; diff --git a/src/app/store.ts b/src/app/store.ts index 0f762d85..a32d2034 100644 --- a/src/app/store.ts +++ b/src/app/store.ts @@ -1,26 +1,27 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import { combineReducers, configureStore } from "@reduxjs/toolkit"; import { setupListeners } from "@reduxjs/toolkit/query"; -import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; + import listenerMiddleware from "./listener.middleware"; -import authDataReducer from "./slices/auth.data"; -import voiceReducer from "./slices/voice"; -import footprintReducer from "./slices/footprint"; -import serverReducer from "./slices/server"; -import uiReducer from "./slices/ui"; -import channelsReducer from "./slices/channels"; -import usersReducer from "./slices/users"; -import reactionMsgReducer from "./slices/message.reaction"; -import channelMsgReducer from "./slices/message.channel"; -import userMsgReducer from "./slices/message.user"; -import favoritesReducer from "./slices/favorites"; -import fileMsgReducer from "./slices/message.file"; -import archiveMsgReducer from "./slices/message.archive"; -import messageReducer from "./slices/message"; import { authApi } from "./services/auth"; -import { userApi } from "./services/user"; import { channelApi } from "./services/channel"; import { messageApi } from "./services/message"; import { serverApi } from "./services/server"; +import { userApi } from "./services/user"; +import authDataReducer from "./slices/auth.data"; +import channelsReducer from "./slices/channels"; +import favoritesReducer from "./slices/favorites"; +import footprintReducer from "./slices/footprint"; +import messageReducer from "./slices/message"; +import archiveMsgReducer from "./slices/message.archive"; +import channelMsgReducer from "./slices/message.channel"; +import fileMsgReducer from "./slices/message.file"; +import reactionMsgReducer from "./slices/message.reaction"; +import userMsgReducer from "./slices/message.user"; +import serverReducer from "./slices/server"; +import uiReducer from "./slices/ui"; +import usersReducer from "./slices/users"; +import voiceReducer from "./slices/voice"; const reducer = combineReducers({ authData: authDataReducer, diff --git a/src/components/Message/useInView.ts b/src/components/Message/useInView.ts index a8230b50..b16c915e 100644 --- a/src/components/Message/useInView.ts +++ b/src/components/Message/useInView.ts @@ -1,4 +1,4 @@ -import { useRef, useEffect } from "react"; +import { useEffect, useRef } from "react"; export default function useInView() { const ref = useRef(null); diff --git a/src/components/MixedInput/plugins.ts b/src/components/MixedInput/plugins.ts index 88cfac86..41164107 100644 --- a/src/components/MixedInput/plugins.ts +++ b/src/components/MixedInput/plugins.ts @@ -4,6 +4,7 @@ import { createParagraphPlugin, createSelectOnBackspacePlugin } from "@udecode/plate"; + import { CONFIG } from "./config"; const basicElements = [ diff --git a/src/components/Notification/useDeviceToken.ts b/src/components/Notification/useDeviceToken.ts index 893d51aa..0391922d 100644 --- a/src/components/Notification/useDeviceToken.ts +++ b/src/components/Notification/useDeviceToken.ts @@ -1,7 +1,9 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; import { initializeApp } from "firebase/app"; -import { getToken, getMessaging } from "firebase/messaging"; +import { getMessaging, getToken } from "firebase/messaging"; + import { firebaseConfig, KEY_DEVICE_TOKEN } from "@/app/config"; + let requesting = false; let error = false; const useDeviceToken = (vapidKey: string) => { diff --git a/src/components/Voice/useVoice.ts b/src/components/Voice/useVoice.ts index e0b7cc5d..45fc9744 100644 --- a/src/components/Voice/useVoice.ts +++ b/src/components/Voice/useVoice.ts @@ -1,287 +1,321 @@ -import AgoraRTC, { ICameraVideoTrack, IMicrophoneAudioTrack } from 'agora-rtc-sdk-ng'; -import { useDispatch } from 'react-redux'; - -import { ShareScreenTrack } from '../../types/global'; -import AudioJoin from '@/assets/join.wav'; - -import { useGenerateAgoraTokenMutation } from '../../app/services/server'; -import { updateChannelVisibleAside, updateDMVisibleAside } from '../../app/slices/footprint'; - -import { addVoiceMember, updatePin, updateVoicingInfo, upsertVoiceList } from '../../app/slices/voice'; -import { useAppSelector } from '../../app/store'; -import { playAgoraVideo } from '../../utils'; -import { ChatContext } from '../../types/common'; +import { useDispatch } from "react-redux"; +import AgoraRTC, { ICameraVideoTrack, IMicrophoneAudioTrack } from "agora-rtc-sdk-ng"; +import AudioJoin from "@/assets/join.wav"; +import { useGenerateAgoraTokenMutation } from "../../app/services/server"; +import { updateChannelVisibleAside, updateDMVisibleAside } from "../../app/slices/footprint"; +import { + addVoiceMember, + updatePin, + updateVoicingInfo, + upsertVoiceList +} from "../../app/slices/voice"; +import { useAppSelector } from "../../app/store"; +import { ChatContext } from "../../types/common"; +import { ShareScreenTrack } from "../../types/global"; +import { playAgoraVideo } from "../../utils"; type VoiceProps = { - id: number, - context?: ChatContext -} + id: number; + context?: ChatContext; +}; const audioJoin = new Audio(AudioJoin); const useVoice = ({ id, context = "channel" }: VoiceProps) => { - const dispatch = useDispatch(); - const { voicingInfo, loginUid, audioInputDevices, audioOutputDevices, videoInputDevices, audioInputDeviceId, audioOutputDeviceId, videoInputDeviceId } = useAppSelector(store => { - return { - loginUid: store.authData.user?.uid ?? 0, - voicingInfo: store.voice.voicing, - audioInputDevices: store.voice.devices.filter(d => d.kind == "audioinput") ?? [], - audioOutputDevices: store.voice.devices.filter(d => d.kind == "audiooutput") ?? [], - videoInputDevices: store.voice.devices.filter(d => d.kind == "videoinput") ?? [], - audioInputDeviceId: store.voice.audioInputDeviceId, - audioOutputDeviceId: store.voice.audioOutputDeviceId, - videoInputDeviceId: store.voice.videoInputDeviceId, - }; - }); - const [generateToken] = useGenerateAgoraTokenMutation(); - // const [joining, setJoining] = useState(false); - const joinVoice = async () => { - // setJoining(true); - dispatch(updateVoicingInfo({ - id, - context, - joining: true - })); - const resp = await generateToken(context == "channel" ? { gid: id } : { uid: id }); - if ('error' in resp) { - console.error("generate agora token error"); - dispatch(updateVoicingInfo({ - joining: false, - id, - context, - })); - } else { - console.table(resp.data); - const { channel_name, app_id, agora_token, uid } = resp.data; - if (window.VOICE_CLIENT) { - await window.VOICE_CLIENT.join(app_id, channel_name, agora_token, uid); - // Create a local audio track from the microphone audio. - const localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack({ microphoneId: audioInputDeviceId }); - // Publish the local audio track in the channel. - await window.VOICE_CLIENT.publish(localAudioTrack); - // play the join audio - try { - await audioJoin.play(); - } catch (error) { - console.warn("play join sound failed!", error); - } - console.log("Publish success!,joined the channel"); - dispatch(updateVoicingInfo({ - deafen: false, - muted: false, - joining: false, - id, - context, - })); - // 把自己加进去 - dispatch(addVoiceMember(uid)); - // 放到全局变量里 - window.VOICE_TRACK_MAP[loginUid] = localAudioTrack; - } - - } - // setJoining(false); + const dispatch = useDispatch(); + const { + voicingInfo, + loginUid, + audioInputDevices, + audioOutputDevices, + videoInputDevices, + audioInputDeviceId, + audioOutputDeviceId, + videoInputDeviceId + } = useAppSelector((store) => { + return { + loginUid: store.authData.user?.uid ?? 0, + voicingInfo: store.voice.voicing, + audioInputDevices: store.voice.devices.filter((d) => d.kind == "audioinput") ?? [], + audioOutputDevices: store.voice.devices.filter((d) => d.kind == "audiooutput") ?? [], + videoInputDevices: store.voice.devices.filter((d) => d.kind == "videoinput") ?? [], + audioInputDeviceId: store.voice.audioInputDeviceId, + audioOutputDeviceId: store.voice.audioOutputDeviceId, + videoInputDeviceId: store.voice.videoInputDeviceId }; - const openCamera = async () => { - const localVideoTrack = await AgoraRTC.createCameraVideoTrack({ cameraId: videoInputDeviceId }); - // 取消正在进行的桌面共享 - await stopShareScreen(); - await window.VOICE_CLIENT?.publish(localVideoTrack); - dispatch(updateVoicingInfo({ - video: true, - shareScreen: false, - id, - context, - })); - // 放到全局变量里 - window.VIDEO_TRACK_MAP[loginUid] = localVideoTrack; - playAgoraVideo(loginUid); - }; - const closeCamera = async () => { - const localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ICameraVideoTrack; - if (localVideoTrack) { - await window.VOICE_CLIENT?.unpublish(localVideoTrack); - localVideoTrack.close(); - window.VIDEO_TRACK_MAP[loginUid] = null; - // 关闭视频后,需要把视频的高度设置回去 - const playerEle = document.querySelector(`#CAMERA_${loginUid}`) as HTMLElement; - playerEle.classList.remove("h-[120px]"); - dispatch(updateVoicingInfo({ - video: false, - shareScreen: false, - id, - context, - })); - } - }; - const stopShareScreen = async () => { - let localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ShareScreenTrack | null; - if (!localVideoTrack) return; - await window.VOICE_CLIENT?.unpublish(localVideoTrack); - if ("close" in localVideoTrack) { - localVideoTrack.close(); - } else { - localVideoTrack[0].close(); - // localVideoTrack[0]=null - } - // localVideoTrack.close(); - window.VIDEO_TRACK_MAP[loginUid] = null; - // 关闭视频后,需要把视频的高度设置回去 - const playerEle = document.querySelector(`#CAMERA_${loginUid}`) as HTMLElement; - playerEle.classList.remove("h-[120px]"); - dispatch(updateVoicingInfo({ - video: false, - shareScreen: false, - id, - context, - })); - }; - const startShareScreen = async () => { + }); + const [generateToken] = useGenerateAgoraTokenMutation(); + // const [joining, setJoining] = useState(false); + const joinVoice = async () => { + // setJoining(true); + dispatch( + updateVoicingInfo({ + id, + context, + joining: true + }) + ); + const resp = await generateToken(context == "channel" ? { gid: id } : { uid: id }); + if ("error" in resp) { + console.error("generate agora token error"); + dispatch( + updateVoicingInfo({ + joining: false, + id, + context + }) + ); + } else { + console.table(resp.data); + const { channel_name, app_id, agora_token, uid } = resp.data; + if (window.VOICE_CLIENT) { + await window.VOICE_CLIENT.join(app_id, channel_name, agora_token, uid); + // Create a local audio track from the microphone audio. + const localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack({ + microphoneId: audioInputDeviceId + }); + // Publish the local audio track in the channel. + await window.VOICE_CLIENT.publish(localAudioTrack); + // play the join audio try { - const localVideoTrack = await AgoraRTC.createScreenVideoTrack({ - // electronScreenSourceId: "share_screen", - selfBrowserSurface: "exclude", - // 配置屏幕共享编码参数,详情请查看 API 文档。 - encoderConfig: "1080p_1", - // 设置视频传输优化策略为清晰优先或流畅优先。 - optimizationMode: "detail", - }); - - // 取消正在进行的视频 - await closeCamera(); - await window.VOICE_CLIENT?.publish(localVideoTrack); - dispatch(updateVoicingInfo({ - video: false, - shareScreen: true, - id, - context, - })); - // 放到全局变量里 - window.VIDEO_TRACK_MAP[loginUid] = localVideoTrack; - playAgoraVideo(loginUid); - // 进入全屏并Pin自己 - if (context == "channel") { - dispatch(updateChannelVisibleAside({ id, aside: "voice_fullscreen" })); - } else { - dispatch(updateDMVisibleAside({ id, aside: "voice_fullscreen" })); - } - dispatch(updatePin({ uid: loginUid, action: "pin" })); - // 监听屏幕共享结束事件 - if ("close" in localVideoTrack) { - localVideoTrack.getMediaStreamTrack().onended = () => { - stopShareScreen(); - }; - } + await audioJoin.play(); } catch (error) { - console.log("start share screen error", error); + console.warn("play join sound failed!", error); } - }; - - const leave = async () => { - const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; - const localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ICameraVideoTrack; - if (window.VOICE_CLIENT) { - await window.VOICE_CLIENT.leave(); - dispatch(updateVoicingInfo(null)); - if (localAudioTrack) { - localAudioTrack.close(); - window.VOICE_TRACK_MAP[loginUid] = null; - } - if (localVideoTrack) { - localVideoTrack.close(); - window.VIDEO_TRACK_MAP[loginUid] = null; - } - const updateAside = context == "channel" ? updateChannelVisibleAside : updateDMVisibleAside; - dispatch(updateAside({ id, aside: null })); - // 即时更新对应的活跃列表信息 - dispatch(upsertVoiceList({ - id, - context, - memberCount: 0, - // will fix it - channelName: `vocechat:${context}:${id}`, - })); - } - }; - const setMute = (mute: boolean) => { - const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; - if (!localAudioTrack) return; - localAudioTrack.setMuted(mute); - if (mute == false && voicingInfo?.deafen) { - // 远端音频,恢复原音 - Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { - audioTrack?.setVolume(100); - }); - dispatch(updateVoicingInfo({ - muted: false, - id, - context - })); - } - dispatch(updateVoicingInfo({ - muted: mute, + console.log("Publish success!,joined the channel"); + dispatch( + updateVoicingInfo({ + deafen: false, + muted: false, + joining: false, id, context - })); - }; - const setDeafen = (deafen: boolean) => { - const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; - if (!localAudioTrack) return; - if (deafen) { - localAudioTrack.setMuted(true); - // 远端音频,全部静音 - Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { - audioTrack?.setVolume(0); - }); - } else { - localAudioTrack.setMuted(false); - // 远端音频,恢复原音 - Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { - audioTrack?.setVolume(100); - }); - } - dispatch(updateVoicingInfo({ deafen, id, context })); - }; - const enterFullscreen = (uid?: number) => { - if (context == "channel") { - dispatch(updateChannelVisibleAside({ id, aside: "voice_fullscreen" })); - } else { - dispatch(updateDMVisibleAside({ id, aside: "voice_fullscreen" })); - } - if (uid) { - dispatch(updatePin({ uid, action: "pin" })); - } - }; - const exitFullscreen = () => { - if (context == "channel") { - dispatch(updateChannelVisibleAside({ id, aside: "voice" })); - } else { - dispatch(updateDMVisibleAside({ id, aside: null })); - } - }; - const joinedAtThisContext = voicingInfo ? (voicingInfo.id == id && voicingInfo.context == context) : false; - return { - setMute, - setDeafen, - leave, - // canVoice, - voicingInfo, - joining: voicingInfo ? voicingInfo.joining : undefined, - joinedAtThisContext, - joined: !!voicingInfo, - joinVoice, - openCamera, - closeCamera, - startShareScreen, - stopShareScreen, - enterFullscreen, - exitFullscreen, - audioInputDevices, - audioOutputDevices, - videoInputDevices, - audioInputDeviceId, - audioOutputDeviceId, - videoInputDeviceId - }; + }) + ); + // 把自己加进去 + dispatch(addVoiceMember(uid)); + // 放到全局变量里 + window.VOICE_TRACK_MAP[loginUid] = localAudioTrack; + } + } + // setJoining(false); + }; + const openCamera = async () => { + const localVideoTrack = await AgoraRTC.createCameraVideoTrack({ cameraId: videoInputDeviceId }); + // 取消正在进行的桌面共享 + await stopShareScreen(); + await window.VOICE_CLIENT?.publish(localVideoTrack); + dispatch( + updateVoicingInfo({ + video: true, + shareScreen: false, + id, + context + }) + ); + // 放到全局变量里 + window.VIDEO_TRACK_MAP[loginUid] = localVideoTrack; + playAgoraVideo(loginUid); + }; + const closeCamera = async () => { + const localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ICameraVideoTrack; + if (localVideoTrack) { + await window.VOICE_CLIENT?.unpublish(localVideoTrack); + localVideoTrack.close(); + window.VIDEO_TRACK_MAP[loginUid] = null; + // 关闭视频后,需要把视频的高度设置回去 + const playerEle = document.querySelector(`#CAMERA_${loginUid}`) as HTMLElement; + playerEle.classList.remove("h-[120px]"); + dispatch( + updateVoicingInfo({ + video: false, + shareScreen: false, + id, + context + }) + ); + } + }; + const stopShareScreen = async () => { + let localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ShareScreenTrack | null; + if (!localVideoTrack) return; + await window.VOICE_CLIENT?.unpublish(localVideoTrack); + if ("close" in localVideoTrack) { + localVideoTrack.close(); + } else { + localVideoTrack[0].close(); + // localVideoTrack[0]=null + } + // localVideoTrack.close(); + window.VIDEO_TRACK_MAP[loginUid] = null; + // 关闭视频后,需要把视频的高度设置回去 + const playerEle = document.querySelector(`#CAMERA_${loginUid}`) as HTMLElement; + playerEle.classList.remove("h-[120px]"); + dispatch( + updateVoicingInfo({ + video: false, + shareScreen: false, + id, + context + }) + ); + }; + const startShareScreen = async () => { + try { + const localVideoTrack = await AgoraRTC.createScreenVideoTrack({ + // electronScreenSourceId: "share_screen", + selfBrowserSurface: "exclude", + // 配置屏幕共享编码参数,详情请查看 API 文档。 + encoderConfig: "1080p_1", + // 设置视频传输优化策略为清晰优先或流畅优先。 + optimizationMode: "detail" + }); + + // 取消正在进行的视频 + await closeCamera(); + await window.VOICE_CLIENT?.publish(localVideoTrack); + dispatch( + updateVoicingInfo({ + video: false, + shareScreen: true, + id, + context + }) + ); + // 放到全局变量里 + window.VIDEO_TRACK_MAP[loginUid] = localVideoTrack; + playAgoraVideo(loginUid); + // 进入全屏并Pin自己 + if (context == "channel") { + dispatch(updateChannelVisibleAside({ id, aside: "voice_fullscreen" })); + } else { + dispatch(updateDMVisibleAside({ id, aside: "voice_fullscreen" })); + } + dispatch(updatePin({ uid: loginUid, action: "pin" })); + // 监听屏幕共享结束事件 + if ("close" in localVideoTrack) { + localVideoTrack.getMediaStreamTrack().onended = () => { + stopShareScreen(); + }; + } + } catch (error) { + console.log("start share screen error", error); + } + }; + + const leave = async () => { + const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; + const localVideoTrack = window.VIDEO_TRACK_MAP[loginUid] as ICameraVideoTrack; + if (window.VOICE_CLIENT) { + await window.VOICE_CLIENT.leave(); + dispatch(updateVoicingInfo(null)); + if (localAudioTrack) { + localAudioTrack.close(); + window.VOICE_TRACK_MAP[loginUid] = null; + } + if (localVideoTrack) { + localVideoTrack.close(); + window.VIDEO_TRACK_MAP[loginUid] = null; + } + const updateAside = context == "channel" ? updateChannelVisibleAside : updateDMVisibleAside; + dispatch(updateAside({ id, aside: null })); + // 即时更新对应的活跃列表信息 + dispatch( + upsertVoiceList({ + id, + context, + memberCount: 0, + // will fix it + channelName: `vocechat:${context}:${id}` + }) + ); + } + }; + const setMute = (mute: boolean) => { + const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; + if (!localAudioTrack) return; + localAudioTrack.setMuted(mute); + if (mute == false && voicingInfo?.deafen) { + // 远端音频,恢复原音 + Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { + audioTrack?.setVolume(100); + }); + dispatch( + updateVoicingInfo({ + muted: false, + id, + context + }) + ); + } + dispatch( + updateVoicingInfo({ + muted: mute, + id, + context + }) + ); + }; + const setDeafen = (deafen: boolean) => { + const localAudioTrack = window.VOICE_TRACK_MAP[loginUid] as IMicrophoneAudioTrack; + if (!localAudioTrack) return; + if (deafen) { + localAudioTrack.setMuted(true); + // 远端音频,全部静音 + Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { + audioTrack?.setVolume(0); + }); + } else { + localAudioTrack.setMuted(false); + // 远端音频,恢复原音 + Object.entries(window.VOICE_TRACK_MAP).forEach(([, audioTrack]) => { + audioTrack?.setVolume(100); + }); + } + dispatch(updateVoicingInfo({ deafen, id, context })); + }; + const enterFullscreen = (uid?: number) => { + if (context == "channel") { + dispatch(updateChannelVisibleAside({ id, aside: "voice_fullscreen" })); + } else { + dispatch(updateDMVisibleAside({ id, aside: "voice_fullscreen" })); + } + if (uid) { + dispatch(updatePin({ uid, action: "pin" })); + } + }; + const exitFullscreen = () => { + if (context == "channel") { + dispatch(updateChannelVisibleAside({ id, aside: "voice" })); + } else { + dispatch(updateDMVisibleAside({ id, aside: null })); + } + }; + const joinedAtThisContext = voicingInfo + ? voicingInfo.id == id && voicingInfo.context == context + : false; + return { + setMute, + setDeafen, + leave, + // canVoice, + voicingInfo, + joining: voicingInfo ? voicingInfo.joining : undefined, + joinedAtThisContext, + joined: !!voicingInfo, + joinVoice, + openCamera, + closeCamera, + startShareScreen, + stopShareScreen, + enterFullscreen, + exitFullscreen, + audioInputDevices, + audioOutputDevices, + videoInputDevices, + audioInputDeviceId, + audioOutputDeviceId, + videoInputDeviceId + }; }; -export default useVoice; \ No newline at end of file +export default useVoice; diff --git a/src/hooks/useAddLocalFileMessage.ts b/src/hooks/useAddLocalFileMessage.ts index 00a80f8f..ad62037a 100644 --- a/src/hooks/useAddLocalFileMessage.ts +++ b/src/hooks/useAddLocalFileMessage.ts @@ -1,4 +1,5 @@ import { useDispatch } from "react-redux"; + import { addMessage, MessagePayload } from "@/app/slices/message"; import { addChannelMsg } from "@/app/slices/message.channel"; import { addUserMsg } from "@/app/slices/message.user"; diff --git a/src/hooks/useChatScroll.ts b/src/hooks/useChatScroll.ts index a06bfcd1..2fdd3da4 100644 --- a/src/hooks/useChatScroll.ts +++ b/src/hooks/useChatScroll.ts @@ -1,4 +1,5 @@ -import { useRef, useEffect } from "react"; +import { useEffect, useRef } from "react"; + // import { useDebounce } from "rooks"; function useChatScroll() { diff --git a/src/hooks/useConfig.ts b/src/hooks/useConfig.ts index c8e3da64..4d80894b 100644 --- a/src/hooks/useConfig.ts +++ b/src/hooks/useConfig.ts @@ -1,18 +1,20 @@ import { useEffect, useState } from "react"; import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; import { isEqual } from "lodash"; + import { - useUpdateLoginConfigMutation, - useUpdateSMTPConfigMutation, + useLazyGetAgoraConfigQuery, + useLazyGetFirebaseConfigQuery, + useLazyGetLoginConfigQuery, + useLazyGetSMTPConfigQuery, useUpdateAgoraConfigMutation, useUpdateFirebaseConfigMutation, - useLazyGetFirebaseConfigQuery, - useLazyGetAgoraConfigQuery, - useLazyGetSMTPConfigQuery, - useLazyGetLoginConfigQuery + useUpdateLoginConfigMutation, + useUpdateSMTPConfigMutation } from "@/app/services/server"; import { AgoraConfig, FirebaseConfig, LoginConfig, SMTPConfig } from "@/types/server"; -import { useTranslation } from "react-i18next"; + // config: smtp agora login firebase type ConfigType = "smtp" | "agora" | "login" | "firebase"; type ConfigMap = Record; @@ -23,10 +25,14 @@ export default function useConfig(config: keyof ConfigMap = "smtp") { const { t: ct } = useTranslation(); const [changed, setChanged] = useState(false); const [values, setValues] = useState | undefined>(undefined); - const [updateLoginConfig, { isSuccess: LoginUpdated, isLoading: LoginUpdating }] = useUpdateLoginConfigMutation(); - const [updateSMTPConfig, { isSuccess: SMTPUpdated, isLoading: SMTPUpdating }] = useUpdateSMTPConfigMutation(); - const [updateAgoraConfig, { isSuccess: AgoraUpdated, isLoading: AgoraUpdating }] = useUpdateAgoraConfigMutation(); - const [updateFirebaseConfig, { isSuccess: FirebaseUpdated, isLoading: FirebaseUpdating }] = useUpdateFirebaseConfigMutation(); + const [updateLoginConfig, { isSuccess: LoginUpdated, isLoading: LoginUpdating }] = + useUpdateLoginConfigMutation(); + const [updateSMTPConfig, { isSuccess: SMTPUpdated, isLoading: SMTPUpdating }] = + useUpdateSMTPConfigMutation(); + const [updateAgoraConfig, { isSuccess: AgoraUpdated, isLoading: AgoraUpdating }] = + useUpdateAgoraConfigMutation(); + const [updateFirebaseConfig, { isSuccess: FirebaseUpdated, isLoading: FirebaseUpdating }] = + useUpdateFirebaseConfigMutation(); const [getAgoraConfig, { data: agoraConfig }] = useLazyGetAgoraConfigQuery(); const [getLoginConfig, { data: loginConfig }] = useLazyGetLoginConfigQuery(); const [getSMTPConfig, { data: smtpConfig }] = useLazyGetSMTPConfigQuery(); diff --git a/src/hooks/useCopy.ts b/src/hooks/useCopy.ts index fae65d29..074c770e 100644 --- a/src/hooks/useCopy.ts +++ b/src/hooks/useCopy.ts @@ -1,6 +1,6 @@ -import { useState, useEffect } from "react"; -import { copyImageToClipboard } from "copy-image-clipboard"; +import { useEffect, useState } from "react"; import toast from "react-hot-toast"; +import { copyImageToClipboard } from "copy-image-clipboard"; const useCopy = (config: { enableToast: boolean } | void) => { const { enableToast = true } = config || {}; diff --git a/src/hooks/useFilteredChannels.ts b/src/hooks/useFilteredChannels.ts index 0cf3fa8b..ad1f5e9f 100644 --- a/src/hooks/useFilteredChannels.ts +++ b/src/hooks/useFilteredChannels.ts @@ -1,4 +1,5 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; + import { useAppSelector } from "@/app/store"; import { Channel } from "@/types/channel"; diff --git a/src/hooks/useForwardMessage.ts b/src/hooks/useForwardMessage.ts index bcb1ed00..af1973cb 100644 --- a/src/hooks/useForwardMessage.ts +++ b/src/hooks/useForwardMessage.ts @@ -1,7 +1,8 @@ import { useState } from "react"; + import { useSendChannelMsgMutation } from "@/app/services/channel"; -import { useSendMsgMutation } from "@/app/services/user"; import { useCreateArchiveMutation } from "@/app/services/message"; +import { useSendMsgMutation } from "@/app/services/user"; export default function useForwardMessage() { const [forwarding, setForwarding] = useState(false); diff --git a/src/hooks/useGoogleAuthConfig.ts b/src/hooks/useGoogleAuthConfig.ts index 78897a49..1fcf0044 100644 --- a/src/hooks/useGoogleAuthConfig.ts +++ b/src/hooks/useGoogleAuthConfig.ts @@ -1,4 +1,5 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; + import { useGetGoogleAuthConfigQuery, useUpdateGoogleAuthConfigMutation diff --git a/src/hooks/useLeaveChannel.ts b/src/hooks/useLeaveChannel.ts index 69f841dc..189d1568 100644 --- a/src/hooks/useLeaveChannel.ts +++ b/src/hooks/useLeaveChannel.ts @@ -1,4 +1,4 @@ -import { useUpdateChannelMutation, useLazyLeaveChannelQuery } from "@/app/services/channel"; +import { useLazyLeaveChannelQuery, useUpdateChannelMutation } from "@/app/services/channel"; import { useAppSelector } from "@/app/store"; export default function useLeaveChannel(cid: number) { diff --git a/src/hooks/useLicense.ts b/src/hooks/useLicense.ts index 89dfdb99..e8b18581 100644 --- a/src/hooks/useLicense.ts +++ b/src/hooks/useLicense.ts @@ -1,23 +1,34 @@ import { useEffect } from "react"; + import { useCheckLicenseMutation, useGetLicenseQuery, useUpsertLicenseMutation } from "@/app/services/server"; import { useAppSelector } from "@/app/store"; + // type Props = { // refetchOnMountOrArgChange?: boolean // } | undefined const useLicense = (refetchOnMountOrArgChange = false) => { const { userCount, isGuest, upgraded } = useAppSelector((store) => { - return { userCount: store.users.ids.length, isGuest: store.authData.guest, upgraded: store.server.upgraded }; + return { + userCount: store.users.ids.length, + isGuest: store.authData.guest, + upgraded: store.server.upgraded + }; }); - const { data: license, refetch: refetchLicense, isLoading } = useGetLicenseQuery(undefined, { + const { + data: license, + refetch: refetchLicense, + isLoading + } = useGetLicenseQuery(undefined, { refetchOnMountOrArgChange, skip: isGuest }); const [check, { isLoading: isChecking, isSuccess: checked }] = useCheckLicenseMutation(); - const [upsert, { isSuccess: upserted, isLoading: upserting, reset: resetUpsert }] = useUpsertLicenseMutation(); + const [upsert, { isSuccess: upserted, isLoading: upserting, reset: resetUpsert }] = + useUpsertLicenseMutation(); const checkLicense = (l: string) => { check(l); }; diff --git a/src/hooks/useLogout.ts b/src/hooks/useLogout.ts index 5f377ead..af6be35a 100644 --- a/src/hooks/useLogout.ts +++ b/src/hooks/useLogout.ts @@ -1,13 +1,15 @@ import { useDispatch } from "react-redux"; -import { resetFootprint } from "@/app/slices/footprint"; -import { resetChannels } from "@/app/slices/channels"; -import { resetUsers } from "@/app/slices/users"; -import { resetChannelMsg } from "@/app/slices/message.channel"; -import { resetUserMsg } from "@/app/slices/message.user"; -import { resetReactionMessage } from "@/app/slices/message.reaction"; -import { resetFileMessage } from "@/app/slices/message.file"; -import { resetMessage } from "@/app/slices/message"; + import { useLazyLogoutQuery } from "@/app/services/auth"; +import { resetChannels } from "@/app/slices/channels"; +import { resetFootprint } from "@/app/slices/footprint"; +import { resetMessage } from "@/app/slices/message"; +import { resetChannelMsg } from "@/app/slices/message.channel"; +import { resetFileMessage } from "@/app/slices/message.file"; +import { resetReactionMessage } from "@/app/slices/message.reaction"; +import { resetUserMsg } from "@/app/slices/message.user"; +import { resetUsers } from "@/app/slices/users"; + export default function useLogout() { const dispatch = useDispatch(); const [logout, { isLoading, isSuccess }] = useLazyLogoutQuery(); diff --git a/src/hooks/useNormalizeMessage.ts b/src/hooks/useNormalizeMessage.ts index fdf0c0a5..11f32dce 100644 --- a/src/hooks/useNormalizeMessage.ts +++ b/src/hooks/useNormalizeMessage.ts @@ -1,14 +1,14 @@ -import { useState, useEffect } from "react"; -import { normalizeArchiveData } from "../utils"; +import { useEffect, useState } from "react"; + import { useLazyGetArchiveMessageQuery } from "@/app/services/message"; -import { ArchiveMessage } from "@/types/resource"; import { useAppSelector } from "@/app/store"; +import { ArchiveMessage } from "@/types/resource"; +import { normalizeArchiveData } from "../utils"; export default function useNormalizeMessage(filePath: string | null) { - const archiveData = useAppSelector(store => store.archiveMessage[filePath ?? ""]); + const archiveData = useAppSelector((store) => store.archiveMessage[filePath ?? ""]); const [normalizedMessages, setNormalizedMessages] = useState(null); - const [getArchiveMessage, { isError, isLoading, isSuccess }] = - useLazyGetArchiveMessageQuery(); + const [getArchiveMessage, { isError, isLoading, isSuccess }] = useLazyGetArchiveMessageQuery(); useEffect(() => { if (archiveData) { const msgs = normalizeArchiveData(archiveData, filePath); diff --git a/src/hooks/usePWAInstallPrompt.ts b/src/hooks/usePWAInstallPrompt.ts index 8adf1a8b..20fcdfcd 100644 --- a/src/hooks/usePWAInstallPrompt.ts +++ b/src/hooks/usePWAInstallPrompt.ts @@ -1,4 +1,5 @@ -import { useRef, useEffect } from "react"; +import { useEffect, useRef } from "react"; + import { KEY_PWA_INSTALLED } from "@/app/config"; import { BeforeInstallPromptEvent } from "@/types/global"; diff --git a/src/hooks/usePinMessage.ts b/src/hooks/usePinMessage.ts index 0c058bdd..5771d693 100644 --- a/src/hooks/usePinMessage.ts +++ b/src/hooks/usePinMessage.ts @@ -1,4 +1,5 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; + import { usePinMessageMutation, useUnpinMessageMutation } from "@/app/services/message"; import { useAppSelector } from "@/app/store"; import { PinnedMessage } from "@/types/channel"; diff --git a/src/hooks/usePreload.ts b/src/hooks/usePreload.ts index 6dcf8394..d6eebb37 100644 --- a/src/hooks/usePreload.ts +++ b/src/hooks/usePreload.ts @@ -1,14 +1,19 @@ import { useEffect } from "react"; import dayjs from "dayjs"; + import initCache, { useRehydrate } from "@/app/cache"; -import { useLazyGetFavoritesQuery } from "@/app/services/message"; +import { useLazyGetFavoritesQuery, useLazyLoadMoreMessagesQuery } from "@/app/services/message"; +import { + useLazyGetServerQuery, + useLazyGetServerVersionQuery, + useLazyGetSystemCommonQuery +} from "@/app/services/server"; import { useLazyGetContactsQuery, useLazyGetUsersQuery } from "@/app/services/user"; -import { useLazyGetSystemCommonQuery, useLazyGetServerQuery, useLazyGetServerVersionQuery } from "@/app/services/server"; -import useStreaming from "./useStreaming"; import { useAppSelector } from "@/app/store"; -import { useLazyLoadMoreMessagesQuery } from "@/app/services/message"; -import useLicense from "./useLicense"; import { compareVersion } from "../utils"; +import useLicense from "./useLicense"; +import useStreaming from "./useStreaming"; + // type Props={ // guest?:boolean // } @@ -44,16 +49,27 @@ export default function usePreload() { data: favorites } ] = useLazyGetFavoritesQuery(); - const [getUsers, { isLoading: usersLoading, isSuccess: usersSuccess, isError: usersError, data: users }] = useLazyGetUsersQuery(); + const [ + getUsers, + { isLoading: usersLoading, isSuccess: usersSuccess, isError: usersError, data: users } + ] = useLazyGetUsersQuery(); const [ getContacts, - { isLoading: contactsLoading, isSuccess: contactsSuccess, isError: contactsError, data: contacts } + { + isLoading: contactsLoading, + isSuccess: contactsSuccess, + isError: contactsError, + data: contacts + } ] = useLazyGetContactsQuery(); const [ getServer, { isLoading: serverLoading, isSuccess: serverSuccess, isError: serverError, data: server } ] = useLazyGetServerQuery(); - const [getServerVersion, { data: serverVersion, isSuccess: serverVersionSuccess, isLoading: loadingServerVersion }] = useLazyGetServerVersionQuery(); + const [ + getServerVersion, + { data: serverVersion, isSuccess: serverVersionSuccess, isLoading: loadingServerVersion } + ] = useLazyGetServerVersionQuery(); const [getSystemCommon] = useLazyGetSystemCommonQuery(); useEffect(() => { initCache(); @@ -79,14 +95,14 @@ export default function usePreload() { useEffect(() => { if (rehydrated && serverVersion) { getUsers().then(() => { - if (compareVersion(serverVersion, '0.3.7') >= 0 && !isGuest) { + if (compareVersion(serverVersion, "0.3.7") >= 0 && !isGuest) { // 根据版本号判断是否需要初始化contact getContacts(); } }); getServer(); getFavorites(); - if (compareVersion(serverVersion, '0.3.4') >= 0) { + if (compareVersion(serverVersion, "0.3.4") >= 0) { // 根据版本号判断是否需要调用system common api getSystemCommon(); } @@ -99,9 +115,15 @@ export default function usePreload() { useEffect(() => { setStreamingReady(canStreaming); }, [canStreaming]); - const enableContact = serverVersion ? compareVersion(serverVersion, '0.3.7') >= 0 : false; + const enableContact = serverVersion ? compareVersion(serverVersion, "0.3.7") >= 0 : false; return { - loading: usersLoading || serverLoading || favoritesLoading || !rehydrated || loadingLicense || loadingServerVersion, + loading: + usersLoading || + serverLoading || + favoritesLoading || + !rehydrated || + loadingLicense || + loadingServerVersion, error: usersError && serverError && favoritesError, success: usersSuccess && serverSuccess && favoritesSuccess && serverVersionSuccess, data: { diff --git a/src/hooks/useSendMessage.ts b/src/hooks/useSendMessage.ts index fa9f64f8..aba5747c 100644 --- a/src/hooks/useSendMessage.ts +++ b/src/hooks/useSendMessage.ts @@ -1,11 +1,12 @@ import toast from "react-hot-toast"; -import { removeReplyingMessage, addReplyingMessage, MessagePayload } from "@/app/slices/message"; + import { useSendChannelMsgMutation } from "@/app/services/channel"; -import { useSendMsgMutation } from "@/app/services/user"; import { useReplyMessageMutation } from "@/app/services/message"; +import { useSendMsgMutation } from "@/app/services/user"; +import { addReplyingMessage, MessagePayload, removeReplyingMessage } from "@/app/slices/message"; import { useAppDispatch, useAppSelector } from "@/app/store"; -import { ContentTypeKey } from "@/types/message"; import { ChatContext } from "@/types/common"; +import { ContentTypeKey } from "@/types/message"; interface Props { context: ChatContext; @@ -20,7 +21,9 @@ interface SendMessagesDTO { channels: number[]; } -type SendMessageDTO = { type: ContentTypeKey } & Partial & { ignoreLocal?: boolean } +type SendMessageDTO = { type: ContentTypeKey } & Partial & { + ignoreLocal?: boolean; + }; const useSendMessage = (props?: Props) => { const { context = "dm", from = 0, to = 0 } = props || {}; const dispatch = useAppDispatch(); diff --git a/src/hooks/useStreaming/chat.handler.ts b/src/hooks/useStreaming/chat.handler.ts index a7854f5b..bfa3f87f 100644 --- a/src/hooks/useStreaming/chat.handler.ts +++ b/src/hooks/useStreaming/chat.handler.ts @@ -1,16 +1,17 @@ import { batch } from "react-redux"; -import { addChannelMsg, removeChannelMsg } from "@/app/slices/message.channel"; -import { addMessage, removeMessage, updateMessage } from "@/app/slices/message"; -import { toggleReactionMessage } from "@/app/slices/message.reaction"; -import { addFileMessage, removeFileMessage } from "@/app/slices/message.file"; -import { addUserMsg, removeUserMsg } from "@/app/slices/message.user"; -import { updateAfterMid } from "@/app/slices/footprint"; + import { ContentTypes } from "@/app/config"; -import { ChatEvent } from "@/types/sse"; +import { updateAfterMid } from "@/app/slices/footprint"; +import { addMessage, removeMessage, updateMessage } from "@/app/slices/message"; +import { addChannelMsg, removeChannelMsg } from "@/app/slices/message.channel"; +import { addFileMessage, removeFileMessage } from "@/app/slices/message.file"; +import { toggleReactionMessage } from "@/app/slices/message.reaction"; +import { addUserMsg, removeUserMsg } from "@/app/slices/message.user"; import { AppDispatch } from "@/app/store"; +import { ChatEvent } from "@/types/sse"; type CurrentState = { - afterMid: number, + afterMid: number; ready: boolean; loginUid: number; readUsers: { @@ -20,7 +21,12 @@ type CurrentState = { [key: number]: number; }; }; -const handler = (data: ChatEvent, dispatch: AppDispatch, currState: CurrentState, fromHistory = false) => { +const handler = ( + data: ChatEvent, + dispatch: AppDispatch, + currState: CurrentState, + fromHistory = false +) => { const { mid, from_uid, diff --git a/src/hooks/useTabBroadcast.ts b/src/hooks/useTabBroadcast.ts index 0e811f3d..6fc8203c 100644 --- a/src/hooks/useTabBroadcast.ts +++ b/src/hooks/useTabBroadcast.ts @@ -1,32 +1,33 @@ -import { useState, useEffect } from 'react'; -import { BroadcastChannel } from 'broadcast-channel'; +import { useEffect, useState } from "react"; +import { BroadcastChannel } from "broadcast-channel"; + // import useStreaming from './useStreaming'; type ChannelData = { - type: "NEW_TAB", - message: string -} + type: "NEW_TAB"; + message: string; +}; const useTabBroadcast = () => { - const [tabActive, setTabActive] = useState(true); - useEffect(() => { - const channel = new BroadcastChannel('VOCECHAT_CHANNEL'); - channel.postMessage({ type: "NEW_TAB", message: "new tab opened" } as ChannelData); - const handler = (data: ChannelData) => { - console.log("channel data", data); - if (data.type == "NEW_TAB") { - setTabActive(false); - } - }; - channel.addEventListener('message', handler); - return () => { - channel.removeEventListener("message", handler); - channel.close(); - }; - }, []); - - return { - tabActive + const [tabActive, setTabActive] = useState(true); + useEffect(() => { + const channel = new BroadcastChannel("VOCECHAT_CHANNEL"); + channel.postMessage({ type: "NEW_TAB", message: "new tab opened" } as ChannelData); + const handler = (data: ChannelData) => { + console.log("channel data", data); + if (data.type == "NEW_TAB") { + setTabActive(false); + } }; + channel.addEventListener("message", handler); + return () => { + channel.removeEventListener("message", handler); + channel.close(); + }; + }, []); + + return { + tabActive + }; }; -export default useTabBroadcast; \ No newline at end of file +export default useTabBroadcast; diff --git a/src/hooks/useUploadFile.ts b/src/hooks/useUploadFile.ts index 8740dd53..e2cefbde 100644 --- a/src/hooks/useUploadFile.ts +++ b/src/hooks/useUploadFile.ts @@ -1,12 +1,13 @@ -import { useState, useRef } from "react"; +import { useRef, useState } from "react"; import toast from "react-hot-toast"; -import { updateUploadFiles } from "@/app/slices/ui"; + import BASE_URL, { FILE_SLICE_SIZE } from "@/app/config"; import { usePrepareUploadFileMutation, useUploadFileMutation } from "@/app/services/message"; +import { updateUploadFiles } from "@/app/slices/ui"; import { useAppDispatch, useAppSelector } from "@/app/store"; import { Message } from "@/types/channel"; -import { UploadFileResponse } from "@/types/message"; import { ChatContext } from "@/types/common"; +import { UploadFileResponse } from "@/types/message"; interface IProps { context: ChatContext; @@ -25,7 +26,8 @@ const useUploadFile = (props?: IProps) => { const canceledRef = useRef(false); const sliceUploadedCountRef = useRef(0); const totalSliceCountRef = useRef(1); - const [prepareUploadFile, { isLoading: isPreparing, isError: prepareFileError }] = usePrepareUploadFileMutation(); + const [prepareUploadFile, { isLoading: isPreparing, isError: prepareFileError }] = + usePrepareUploadFileMutation(); const [uploadFileFn, { isLoading: isUploading, isError: uploadFileError }] = useUploadFileMutation(); diff --git a/src/hooks/useUserOperation.ts b/src/hooks/useUserOperation.ts index 7d5dd34a..0f78a335 100644 --- a/src/hooks/useUserOperation.ts +++ b/src/hooks/useUserOperation.ts @@ -1,24 +1,30 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; import toast from "react-hot-toast"; -// import { ContentTypes } from "@/app/config"; -import { useNavigate, useMatch } from "react-router-dom"; -import { hideAll } from "tippy.js"; import { useTranslation } from "react-i18next"; +import { useDispatch } from "react-redux"; +// import { ContentTypes } from "@/app/config"; +import { useMatch, useNavigate } from "react-router-dom"; +import { hideAll } from "tippy.js"; import { useRemoveMembersMutation } from "@/app/services/channel"; import { useLazyDeleteUserQuery, useUpdateContactStatusMutation } from "@/app/services/user"; -import useCopy from "./useCopy"; +import { updateDMVisibleAside } from "@/app/slices/footprint"; +import { updateCallInfo } from "@/app/slices/voice"; import { useAppSelector } from "@/app/store"; import { useVoice } from "@/components/Voice"; -import { updateCallInfo } from "@/app/slices/voice"; -import { updateDMVisibleAside } from "@/app/slices/footprint"; -import { useDispatch } from "react-redux"; +import useCopy from "./useCopy"; + interface IProps { uid?: number; cid?: number; } const useUserOperation = ({ uid, cid }: IProps) => { - const { joinVoice, joined, joining = false, joinedAtThisContext } = useVoice({ id: uid ?? 0, context: "dm" }); + const { + joinVoice, + joined, + joining = false, + joinedAtThisContext + } = useVoice({ id: uid ?? 0, context: "dm" }); const dispatch = useDispatch(); const { t: ct } = useTranslation(); const [passedUid, setPassedUid] = useState(undefined); diff --git a/src/i18n.ts b/src/i18n.ts index ecd6696c..1357a60d 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,18 +1,20 @@ -import i18n from "i18next"; -import dayjs from "dayjs"; import { initReactI18next } from "react-i18next"; -import Backend, { HttpBackendOptions } from "i18next-http-backend"; +import dayjs from "dayjs"; +import i18n from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; -import common from "../public/locales/en/common.json"; +import Backend, { HttpBackendOptions } from "i18next-http-backend"; + +import pkg from "../package.json"; import auth from "../public/locales/en/auth.json"; -import member from "../public/locales/en/member.json"; import chat from "../public/locales/en/chat.json"; +import common from "../public/locales/en/common.json"; import fav from "../public/locales/en/fav.json"; -import widget from "../public/locales/en/widget.json"; -import welcome from "../public/locales/en/welcome.json"; -import setting from "../public/locales/en/setting.json"; import file from "../public/locales/en/file.json"; -import pkg from '../package.json'; +import member from "../public/locales/en/member.json"; +import setting from "../public/locales/en/setting.json"; +import welcome from "../public/locales/en/welcome.json"; +import widget from "../public/locales/en/widget.json"; + // don't want to use this? // have a look at the Quick start guide // for passing in lng and translations on init @@ -64,7 +66,7 @@ i18n returnNull: false, // for backend middleware backend: { - queryStringParams: { v: pkg.version }, + queryStringParams: { v: pkg.version } } }); diff --git a/src/i18next.d.ts b/src/i18next.d.ts index b4a21fa5..fea61e7e 100644 --- a/src/i18next.d.ts +++ b/src/i18next.d.ts @@ -1,9 +1,10 @@ -import { resources, defaultNS } from "./i18n"; +import { defaultNS, resources } from "./i18n"; + declare module "i18next" { - interface CustomTypeOptions { - allowObjectInHTMLChildren: true, - returnNull: false; - defaultNS: typeof defaultNS; - resources: typeof resources["en"]; - } -} \ No newline at end of file + interface CustomTypeOptions { + allowObjectInHTMLChildren: true; + returnNull: false; + defaultNS: typeof defaultNS; + resources: (typeof resources)["en"]; + } +} diff --git a/src/libs/DayjsSetting.ts b/src/libs/DayjsSetting.ts index ce2ddeed..d64a2bbc 100644 --- a/src/libs/DayjsSetting.ts +++ b/src/libs/DayjsSetting.ts @@ -1,10 +1,12 @@ import dayjs from "dayjs"; + import "dayjs/locale/zh-cn"; import "dayjs/locale/ja"; import duration from "dayjs/plugin/duration"; -import relativeTime from "dayjs/plugin/relativeTime"; import isToday from "dayjs/plugin/isToday"; import isYesterday from "dayjs/plugin/isYesterday"; +import relativeTime from "dayjs/plugin/relativeTime"; + // import i18n from '../i18n'; dayjs.extend(duration); diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 4ed598d5..d04af52f 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -66,7 +66,7 @@ declare module "*.module.css" { const classes: { readonly [key: string]: string }; export default classes; } -declare module '@emoji-mart/react'; +declare module "@emoji-mart/react"; interface Window { ethereum: any; diff --git a/src/routes/onboarding/steps.ts b/src/routes/onboarding/steps.ts index f8e6f8b9..6b84f9b9 100644 --- a/src/routes/onboarding/steps.ts +++ b/src/routes/onboarding/steps.ts @@ -1,5 +1,6 @@ // import { useState } from "react"; -import { t } from 'i18next'; +import { t } from "i18next"; + // `name` for in-code usage, `label` for display export interface Step { name: string; @@ -36,5 +37,4 @@ const steps: Step[] = [ } ]; - export default steps; diff --git a/src/service-worker.ts b/src/service-worker.ts index 616d2834..21db44a3 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -9,7 +9,7 @@ import { clientsClaim } from "workbox-core"; import { ExpirationPlugin } from "workbox-expiration"; -import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching"; +import { createHandlerBoundToURL, precacheAndRoute } from "workbox-precaching"; import { registerRoute } from "workbox-routing"; import { StaleWhileRevalidate } from "workbox-strategies"; @@ -34,7 +34,11 @@ registerRoute( } // If this is a URL that starts with /_, skip. const urlPath = url.pathname; // 忽略api开头的path 本地语言文件 挂件地址 - if (urlPath.startsWith("/api") || urlPath.startsWith("/locales/") || urlPath.startsWith("/widget")) { + if ( + urlPath.startsWith("/api") || + urlPath.startsWith("/locales/") || + urlPath.startsWith("/widget") + ) { return false; } if (urlPath.startsWith("/_")) { diff --git a/src/serviceWorkerRegistration.ts b/src/serviceWorkerRegistration.ts index 8095246f..022294b4 100644 --- a/src/serviceWorkerRegistration.ts +++ b/src/serviceWorkerRegistration.ts @@ -17,10 +17,10 @@ interface Config { const isLocalhost = Boolean( window.location.hostname === "localhost" || - // [::1] is the IPv6 localhost address. - window.location.hostname === "[::1]" || - // 127.0.0.0/8 are considered localhost for IPv4. - window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) + // [::1] is the IPv6 localhost address. + window.location.hostname === "[::1]" || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) ); export function register(config: Config) { @@ -46,7 +46,7 @@ export function register(config: Config) { navigator.serviceWorker.ready.then(() => { console.log( "This web app is being served cache-first by a service " + - "worker. To learn more, visit https://cra.link/PWA" + "worker. To learn more, visit https://cra.link/PWA" ); }); } else { @@ -75,7 +75,7 @@ function registerValidSW(swUrl: string, config: Config) { // content until all client tabs are closed. console.log( "New content is available and will be used when all " + - "tabs for this page are closed. See https://cra.link/PWA." + "tabs for this page are closed. See https://cra.link/PWA." ); // Execute callback diff --git a/src/types/auth.ts b/src/types/auth.ts index eb643858..36c59c39 100644 --- a/src/types/auth.ts +++ b/src/types/auth.ts @@ -1,4 +1,5 @@ import { User } from "./user"; + export interface AuthToken { // common server_id: string; diff --git a/src/types/channel.ts b/src/types/channel.ts index 58e6d09e..8779a94a 100644 --- a/src/types/channel.ts +++ b/src/types/channel.ts @@ -1,8 +1,9 @@ //call `group` in backend import { ContentType } from "./message"; -export interface ChannelMember { } -export interface Message { } +export interface ChannelMember {} + +export interface Message {} export interface PinnedMessage { mid: number; @@ -13,7 +14,7 @@ export interface PinnedMessage { properties: { local_id?: number; content_type?: string; - size?: number + size?: number; }; } diff --git a/src/types/common.ts b/src/types/common.ts index b09aba5c..f14fe0c6 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -7,11 +7,11 @@ export type AuthType = "register" | "login"; export type PriceType = "subscription" | "payment" | "booking"; export type PriceSubscriptionDuration = "month" | "quarter" | "year"; export type Price = { - title?: string, - price?: string, - limit?: number, - pid?: string, - desc?: string, - type: PriceType, - sub_dur?: PriceSubscriptionDuration -} + title?: string; + price?: string; + limit?: number; + pid?: string; + desc?: string; + type: PriceType; + sub_dur?: PriceSubscriptionDuration; +}; diff --git a/src/types/message.ts b/src/types/message.ts index e988f30b..ce5f32b0 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -1,6 +1,11 @@ import { ChatEvent } from "./sse"; -export type ContentType = "text/plain" | "text/markdown" | "vocechat/file" | "vocechat/audio" | "vocechat/archive"; +export type ContentType = + | "text/plain" + | "text/markdown" + | "vocechat/file" + | "vocechat/audio" + | "vocechat/archive"; export type ContentTypeKey = "text" | "markdown" | "file" | "audio" | "archive"; export interface MuteDTO { @@ -24,4 +29,4 @@ export interface UploadFileResponse { height: number; }; } -export interface ChatMessage extends Omit { } +export interface ChatMessage extends Omit {} diff --git a/src/types/resource.ts b/src/types/resource.ts index d8ab86d4..0ad7f3b5 100644 --- a/src/types/resource.ts +++ b/src/types/resource.ts @@ -56,9 +56,7 @@ export interface OG { secure_url?: string; width: number; height: number; - }[ - - ]; + }[]; favicon_url?: string; description?: string; locale?: string; diff --git a/src/types/server.ts b/src/types/server.ts index b420a798..1e27b2fa 100644 --- a/src/types/server.ts +++ b/src/types/server.ts @@ -6,14 +6,13 @@ export interface Server extends SystemCommon { name: string; description: string; } -export type ChatLayout = "SelfRight" | "Left" +export type ChatLayout = "SelfRight" | "Left"; export interface SystemCommon { - show_user_online_status?: boolean, - webclient_auto_update?: boolean, - contact_verification_enable?: boolean, - chat_layout_mode?: ChatLayout, - max_file_expiry_mode?: "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180" - + show_user_online_status?: boolean; + webclient_auto_update?: boolean; + contact_verification_enable?: boolean; + chat_layout_mode?: ChatLayout; + max_file_expiry_mode?: "Off" | "Day1" | "Day7" | "Day30" | "Day90" | "Day180"; } export interface GithubAuthConfig { client_id?: string; @@ -37,26 +36,26 @@ export interface AgoraConfig { customer_secret: string; } export interface AgoraVoicingListResponse { - success: boolean, + success: boolean; data: { - channels: { channel_name: string, user_count: number }[], - total_size: number - } + channels: { channel_name: string; user_count: number }[]; + total_size: number; + }; } export interface AgoraChannelUsersResponse { - success: boolean, + success: boolean; data: { - channel_exist: boolean, - mode?: number, - total?: number, - users?: number[] - } + channel_exist: boolean; + mode?: number; + total?: number; + users?: number[]; + }; } export interface AgoraTokenResponse { - agora_token: string, - uid: number, - channel_name: string, - expired_in: number, + agora_token: string; + uid: number; + channel_name: string; + expired_in: number; app_id: string; } export interface SMTPConfig { @@ -107,7 +106,7 @@ export interface LicenseMetadata { domain: string | string[]; } export interface RenewLicense { - type: PriceType, + type: PriceType; priceId: string; metadata: LicenseMetadata; cancel_url: string; diff --git a/src/types/sse.ts b/src/types/sse.ts index 5e175ad9..6cb885e4 100644 --- a/src/types/sse.ts +++ b/src/types/sse.ts @@ -1,6 +1,6 @@ -import { ContactInfo, User } from "./user"; import { Channel } from "./channel"; import { ContentType } from "./message"; +import { ContactInfo, User } from "./user"; export interface ReadyEvent { type: "ready"; @@ -47,11 +47,13 @@ export interface MuteChannel { expired_at: number; } export type AutoDeleteMsgForUser = { - uid: number; expires_in: number | null -} + uid: number; + expires_in: number | null; +}; export type AutoDeleteMsgForGroup = { - gid: number; expires_in: number | null -} + gid: number; + expires_in: number | null; +}; export type UserSettingsEvent = { type: "user_settings"; mute_users?: MuteUser[]; @@ -60,21 +62,21 @@ export type UserSettingsEvent = { read_index_groups?: { gid: number; mid: number }[]; burn_after_reading_users?: AutoDeleteMsgForUser[]; burn_after_reading_groups?: AutoDeleteMsgForGroup[]; -} +}; export type AutoDeleteSettingForUsers = { - burn_after_reading_users: AutoDeleteMsgForUser[] -} + burn_after_reading_users: AutoDeleteMsgForUser[]; +}; export type AutoDeleteSettingForChannels = { - burn_after_reading_groups: AutoDeleteMsgForGroup[] -} -export type AutoDeleteMessageSettingDTO = AutoDeleteSettingForUsers | AutoDeleteSettingForChannels + burn_after_reading_groups: AutoDeleteMsgForGroup[]; +}; +export type AutoDeleteMessageSettingDTO = AutoDeleteSettingForUsers | AutoDeleteSettingForChannels; export type UserSettingsChangedEvent = { type: "user_settings_changed"; from_device?: string; add_mute_users?: MuteUser[]; remove_mute_users?: number[]; add_mute_groups?: MuteChannel[]; - add_contacts?: { target_uid: number, info: ContactInfo }[]; + add_contacts?: { target_uid: number; info: ContactInfo }[]; remove_contacts?: number[]; remove_mute_groups?: number[]; add_pin_chats?: PinChat[]; @@ -83,7 +85,7 @@ export type UserSettingsChangedEvent = { read_index_groups?: { gid: number; mid: number }[]; burn_after_reading_users?: AutoDeleteMsgForUser[]; burn_after_reading_groups?: AutoDeleteMsgForGroup[]; -} +}; export interface RelatedGroupsEvent { type: "related_groups"; @@ -96,7 +98,7 @@ export type PinChatTargetChannel = { gid: number; }; export type PinChatTarget = PinChatTargetUser | PinChatTargetChannel; -export type PinChat = { target: PinChatTarget[], updated_at: number } +export type PinChat = { target: PinChatTarget[]; updated_at: number }; export interface PinnedChatsEvent { type: "pinned_chats"; chats: PinChat[]; @@ -214,7 +216,7 @@ interface GroupClearEvent { interface UserCallEvent { type: "user_calling"; target: number; - uid: number + uid: number; } export type ServerEvent = diff --git a/src/types/user.ts b/src/types/user.ts index d5f55f3b..759106ed 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -3,8 +3,8 @@ import { AuthToken } from "./auth"; export type Gender = 0 | 1; export interface AutoDeleteMsgDTO { - users?: { uid: number, expires_in: number }[], - groups?: { gid: number, expires_in: number }[], + users?: { uid: number; expires_in: number }[]; + groups?: { gid: number; expires_in: number }[]; } export interface User { uid: number; @@ -25,14 +25,14 @@ export type ContactInfo = { status: ContactStatus; created_at: number; updated_at: number; -} +}; export interface ContactResponse { target_uid: number; target_info: User; - contact_info: ContactInfo + contact_info: ContactInfo; } export interface Contact extends User { - status: ContactStatus + status: ContactStatus; } export type UserStatus = "normal" | "frozen"; export type UserDevice = { @@ -41,11 +41,11 @@ export type UserDevice = { is_online: boolean; }; export type BotAPIKey = { - id: number, - name: string, - key: string, - created_at: number, - last_used: number + id: number; + name: string; + key: string; + created_at: number; + last_used: number; }; export interface UserForAdmin extends User { @@ -58,16 +58,23 @@ export interface UserForAdmin extends User { export interface UserForAdminDTO extends Partial { id?: number; } -export interface UserDTO extends Partial> { - password?: string +export interface UserDTO + extends Partial> { + password?: string; } -export interface UserCreateDTO extends Pick { +export interface UserCreateDTO + extends Pick< + User, + "name" | "gender" | "language" | "email" | "webhook_url" | "is_bot" | "is_admin" + > { password: string; } -export interface UserRegDTO extends Partial>, Partial> { +export interface UserRegDTO + extends Partial>, + Partial> { password?: string; - magic_token?: string + magic_token?: string; } export interface UserRegResponse extends AuthToken { - user: User + user: User; } diff --git a/src/widget/useCache.ts b/src/widget/useCache.ts index fb38aea2..2cb7eb9c 100644 --- a/src/widget/useCache.ts +++ b/src/widget/useCache.ts @@ -1,13 +1,15 @@ import { useEffect } from "react"; -import initCache, { useRehydrate } from "../app/cache"; -export default function useCache() { - const { rehydrate, rehydrated } = useRehydrate(); - useEffect(() => { - initCache(); - rehydrate(); - }, []); - return { - rehydrated - }; +import initCache, { useRehydrate } from "../app/cache"; + +export default function useCache() { + const { rehydrate, rehydrated } = useRehydrate(); + useEffect(() => { + initCache(); + rehydrate(); + }, []); + + return { + rehydrated + }; } diff --git a/src/widget/useSSE.ts b/src/widget/useSSE.ts index 74fa2970..4518675f 100644 --- a/src/widget/useSSE.ts +++ b/src/widget/useSSE.ts @@ -1,32 +1,33 @@ import { useEffect } from "react"; import dayjs from "dayjs"; -import { useAppSelector } from "../app/store"; + import useStreaming from "@/hooks/useStreaming"; +import { useAppSelector } from "../app/store"; export default function useSSE() { - const { - loginUid, - token, - expireTime = +new Date(), - } = useAppSelector((store) => { - return { - loginUid: store.authData.user?.uid, - token: store.authData.token, - expireTime: store.authData.expireTime - }; - }); - const { setStreamingReady } = useStreaming(); + const { + loginUid, + token, + expireTime = +new Date() + } = useAppSelector((store) => { + return { + loginUid: store.authData.user?.uid, + token: store.authData.token, + expireTime: store.authData.expireTime + }; + }); + const { setStreamingReady } = useStreaming(); - const tokenAlmostExpire = dayjs().isAfter(new Date(expireTime - 20 * 1000)); - const canStreaming = !!loginUid && !!token && !tokenAlmostExpire; - // console.log("ttt", loginUid, rehydrated, token); + const tokenAlmostExpire = dayjs().isAfter(new Date(expireTime - 20 * 1000)); + const canStreaming = !!loginUid && !!token && !tokenAlmostExpire; + // console.log("ttt", loginUid, rehydrated, token); - useEffect(() => { - setStreamingReady(canStreaming); - return () => { - setStreamingReady(false); - }; - }, [canStreaming]); + useEffect(() => { + setStreamingReady(canStreaming); + return () => { + setStreamingReady(false); + }; + }, [canStreaming]); - return null; + return null; }