refactor: more TS code
This commit is contained in:
@@ -58,7 +58,7 @@ export const channelApi = createApi({
|
||||
}
|
||||
}
|
||||
}),
|
||||
getHistoryMessages: builder.query<ChatMessage[], { id: number; mid?: number; limit: number }>({
|
||||
getHistoryMessages: builder.query<ChatMessage[], { id: number; mid?: number; limit?: number }>({
|
||||
query: ({ id, mid = null, limit = 100 }) => ({
|
||||
url: mid
|
||||
? `/group/${id}/history?before=${mid}&limit=${limit}`
|
||||
|
||||
@@ -166,7 +166,7 @@ export const messageApi = createApi({
|
||||
}),
|
||||
readMessage: builder.mutation<
|
||||
void,
|
||||
{ users?: [{ uid: number; mid: number }]; groups?: [{ gid: number; mid: number }] }
|
||||
{ users?: { uid: number; mid: number }[]; groups?: { gid: number; mid: number }[] }
|
||||
>({
|
||||
query: (data) => ({
|
||||
url: `/user/read-index`,
|
||||
|
||||
@@ -112,7 +112,7 @@ export const userApi = createApi({
|
||||
await onMessageSendStarted.call(this, param1, param2, "user");
|
||||
}
|
||||
}),
|
||||
getHistoryMessages: builder.query<ChatMessage[], { id: number; mid?: number; limit: number }>({
|
||||
getHistoryMessages: builder.query<ChatMessage[], { id: number; mid?: number; limit?: number }>({
|
||||
query: ({ id, mid = null, limit = 100 }) => ({
|
||||
url: mid
|
||||
? `/user/${id}/history?before=${mid}&limit=${limit}`
|
||||
|
||||
@@ -13,25 +13,25 @@ import { User } from "../../types/user";
|
||||
interface State {
|
||||
initialized: boolean;
|
||||
user: User | undefined;
|
||||
token: string | null;
|
||||
token: string;
|
||||
expireTime: number;
|
||||
refreshToken: string | null;
|
||||
refreshToken: string;
|
||||
}
|
||||
const loginUser = localStorage.getItem(KEY_LOGIN_USER) || "";
|
||||
const initialState: State = {
|
||||
initialized: true,
|
||||
user: loginUser ? JSON.parse(loginUser) : undefined,
|
||||
token: localStorage.getItem(KEY_TOKEN),
|
||||
token: localStorage.getItem(KEY_TOKEN) || "",
|
||||
expireTime: Number(localStorage.getItem(KEY_EXPIRE) || +new Date()),
|
||||
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN)
|
||||
refreshToken: localStorage.getItem(KEY_REFRESH_TOKEN) || ""
|
||||
};
|
||||
|
||||
const emptyState: State = {
|
||||
initialized: true,
|
||||
user: undefined,
|
||||
token: null,
|
||||
token: "",
|
||||
expireTime: +new Date(),
|
||||
refreshToken: null
|
||||
refreshToken: ""
|
||||
};
|
||||
|
||||
const authDataSlice = createSlice({
|
||||
|
||||
@@ -87,14 +87,14 @@ const footprintSlice = createSlice({
|
||||
}
|
||||
});
|
||||
},
|
||||
updateReadUsers(state, action: PayloadAction<{ uid: number; mid: number }[]>) {
|
||||
updateReadUsers(state, action: PayloadAction<{ uid: number; mid: number }[] | undefined>) {
|
||||
const reads = action.payload || [];
|
||||
if (reads.length == 0) return;
|
||||
reads.forEach(({ uid, mid }) => {
|
||||
state.readUsers[uid] = mid;
|
||||
});
|
||||
},
|
||||
updateReadChannels(state, action: PayloadAction<{ gid: number; mid: number }[]>) {
|
||||
updateReadChannels(state, action: PayloadAction<{ gid: number; mid: number }[] | undefined>) {
|
||||
const reads = action.payload || [];
|
||||
if (reads.length == 0) return;
|
||||
reads.forEach(({ gid, mid }) => {
|
||||
|
||||
+14
-17
@@ -8,7 +8,19 @@ export interface State {
|
||||
[key: string | number]: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MessagePayload {
|
||||
mid: number;
|
||||
sending: boolean;
|
||||
content_type: string;
|
||||
content: string;
|
||||
properties?: {
|
||||
content_type: string;
|
||||
size: number;
|
||||
};
|
||||
file_path?: string;
|
||||
download?: string;
|
||||
thumbnail?: string;
|
||||
}
|
||||
const initialState: State = {
|
||||
replying: {}
|
||||
};
|
||||
@@ -27,22 +39,7 @@ const messageSlice = createSlice({
|
||||
const { mid, ...rest } = action.payload;
|
||||
state[mid] = { ...state[mid], ...rest };
|
||||
},
|
||||
addMessage(
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
mid: number;
|
||||
sending: boolean;
|
||||
content_type: string;
|
||||
content: string;
|
||||
properties?: {
|
||||
content_type: string;
|
||||
size: number;
|
||||
};
|
||||
file_path?: string;
|
||||
download?: string;
|
||||
thumbnail?: string;
|
||||
}>
|
||||
) {
|
||||
addMessage(state, action: PayloadAction<MessagePayload>) {
|
||||
const data = action.payload;
|
||||
const { mid, sending, content_type, content, properties } = data;
|
||||
// 如果是正发送,并且已存在,则不覆盖
|
||||
|
||||
@@ -22,7 +22,7 @@ const userMsgSlice = createSlice({
|
||||
state.ids = Object.keys(action.payload).map((k) => +k);
|
||||
state.byId = action.payload;
|
||||
},
|
||||
addUserMsg(state, action: PayloadAction<{ id: number; mid: number; local_id: number }>) {
|
||||
addUserMsg(state, action: PayloadAction<{ id: number; mid: number; local_id?: number }>) {
|
||||
const { id, mid, local_id } = action.payload;
|
||||
if (state.byId[id]) {
|
||||
const midExisted = state.byId[id].findIndex((id: number) => id == mid) > -1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// import React from 'react'
|
||||
import { useDispatch } from "react-redux";
|
||||
import { addMessage } from "../../app/slices/message";
|
||||
import { addMessage, MessagePayload } from "../../app/slices/message";
|
||||
import { addChannelMsg } from "../../app/slices/message.channel";
|
||||
import { addUserMsg } from "../../app/slices/message.user";
|
||||
|
||||
@@ -8,11 +8,10 @@ interface IProps {
|
||||
context: "channel" | "user";
|
||||
to: number;
|
||||
}
|
||||
|
||||
export default function useAddLocalFileMessage({ context, to }: IProps) {
|
||||
const dispatch = useDispatch();
|
||||
const addContextMessage = context == "channel" ? addChannelMsg : addUserMsg;
|
||||
const addLocalFileMessage = (data) => {
|
||||
const addLocalFileMessage = (data: MessagePayload) => {
|
||||
dispatch(addMessage(data));
|
||||
dispatch(addContextMessage({ id: to, mid: data.mid }));
|
||||
};
|
||||
|
||||
@@ -3,18 +3,6 @@ import { useRef, useEffect } from "react";
|
||||
|
||||
function useChatScroll<T extends HTMLElement>() {
|
||||
const ref = useRef<T>(null);
|
||||
// useEffect(() => {
|
||||
// console.log("chat scroll", ref);
|
||||
// if (ref.current) {
|
||||
// // setTimeout(() => {
|
||||
// if (ref.current) {
|
||||
// setTimeout(() => {
|
||||
// ref.current.scrollTop = ref.current.scrollHeight;
|
||||
// }, 500);
|
||||
// }
|
||||
// // }, 20);
|
||||
// }
|
||||
// }, [...deps]);
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
console.log("chat scroll", ref);
|
||||
|
||||
@@ -38,13 +38,13 @@ const useCopy = (config: { enableToast: boolean } | void) => {
|
||||
if (!copied) {
|
||||
if (!isImage) {
|
||||
setCopied(copyToClipboard(text));
|
||||
inter = setTimeout(() => {
|
||||
inter = window.setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 500);
|
||||
} else {
|
||||
copyImageToClipboard(text).then(() => {
|
||||
setCopied(true);
|
||||
inter = setTimeout(() => {
|
||||
inter = window.setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 500);
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function useDeleteMessage() {
|
||||
remove
|
||||
// { isError, isLoading, isSuccess },
|
||||
] = useLazyDeleteMessageQuery();
|
||||
const deleteMessage = async (mids) => {
|
||||
const deleteMessage = async (mids: number[]) => {
|
||||
if (!mids) return;
|
||||
const _arr = Array.isArray(mids) ? mids : [mids];
|
||||
setDeleting(true);
|
||||
@@ -26,18 +26,12 @@ export default function useDeleteMessage() {
|
||||
const canDelete = (mids = []) => {
|
||||
if (!mids || mids.length == 0) return false;
|
||||
// 管理员
|
||||
if (loginUser.is_admin) return true;
|
||||
if (loginUser?.is_admin) return true;
|
||||
// 检查是否是自己的消息
|
||||
return mids.every((mid) => {
|
||||
return messageData[mid]?.from_uid == loginUser.uid;
|
||||
return messageData[mid]?.from_uid == loginUser?.uid;
|
||||
});
|
||||
};
|
||||
// useEffect(() => {
|
||||
// if (channel) {
|
||||
// setPins(channel.pinned_messages);
|
||||
// }
|
||||
// }, [channel]);
|
||||
|
||||
return {
|
||||
canDelete,
|
||||
isDeleting: deleting,
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function useDraft({ context = "", id = "" }) {
|
||||
|
||||
const getUpdateDraft = (type = "mixed") => {
|
||||
const update = type == "mixed" ? updateDraftMixedText : updateDraftMarkdown;
|
||||
return (value) => {
|
||||
return (value: string) => {
|
||||
dispatch(update({ key: _key, value }));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useLazyRemoveFavoriteQuery, useFavoriteMessageMutation } from "../../app/services/message";
|
||||
import { Favorite } from "../../app/slices/favorites";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
export default function useFavMessage({
|
||||
@@ -11,7 +12,7 @@ export default function useFavMessage({
|
||||
}) {
|
||||
const [removeFav] = useLazyRemoveFavoriteQuery();
|
||||
const [addFav] = useFavoriteMessageMutation();
|
||||
const [favorites, setFavorites] = useState([]);
|
||||
const [favorites, setFavorites] = useState<Favorite[]>([]);
|
||||
const { favs = [] } = useAppSelector((store) => {
|
||||
return { favs: store.favorites };
|
||||
});
|
||||
@@ -23,16 +24,16 @@ export default function useFavMessage({
|
||||
return !error;
|
||||
};
|
||||
|
||||
const removeFavorite = (id) => {
|
||||
const removeFavorite = (id: number) => {
|
||||
if (!id) return;
|
||||
removeFav(id);
|
||||
};
|
||||
|
||||
const isFavorited = (mid = null) => {
|
||||
if (!mid) return false;
|
||||
let mids = [];
|
||||
favorites.forEach((f) => {
|
||||
if (f.messages.length == 1) {
|
||||
let mids: number[] = [];
|
||||
favorites.forEach((f: Favorite) => {
|
||||
if (f?.messages?.length == 1) {
|
||||
const ids = f.messages.map((m) => m.from_mid);
|
||||
mids = [...mids, ...ids];
|
||||
}
|
||||
@@ -41,7 +42,7 @@ export default function useFavMessage({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let filtereds = [];
|
||||
let filtereds: Favorite[] = [];
|
||||
filtereds = cid
|
||||
? favs.filter((f) => {
|
||||
if (!f.messages) return false;
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function useMessageFeed({ context = "channel", id }: Props) {
|
||||
const [loadMoreChannelMsgs] = useLazyGetHistoryMessagesQuery();
|
||||
const [loadMoreDmMsgs] = useLazyGetDMHistoryMsg();
|
||||
const listRef = useRef<number[]>([]);
|
||||
const pageRef = useRef<object | null>(null);
|
||||
const pageRef = useRef<PageInfo | null>(null);
|
||||
const containerRef = useRef<HTMLElement | null>(null);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [appends, setAppends] = useState([]);
|
||||
@@ -141,12 +141,12 @@ export default function useMessageFeed({ context = "channel", id }: Props) {
|
||||
mid: firstMid,
|
||||
id
|
||||
});
|
||||
if (newList.length == 0) {
|
||||
if (newList?.length == 0) {
|
||||
setHasMore(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let pageInfo = null;
|
||||
let pageInfo: PageInfo;
|
||||
if (!currPageInfo) {
|
||||
// 初始化
|
||||
pageInfo = getFeedWithPagination({
|
||||
@@ -173,7 +173,7 @@ export default function useMessageFeed({ context = "channel", id }: Props) {
|
||||
oldScroll = container.scrollHeight - container.clientHeight;
|
||||
}
|
||||
},
|
||||
currPageInfo.isLast ? 10 : 800
|
||||
currPageInfo?.isLast ? 10 : 800
|
||||
);
|
||||
};
|
||||
const pullDown = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { normalizeArchiveData, ArchiveMessage } from "../utils";
|
||||
import { normalizeArchiveData } from "../utils";
|
||||
import { useLazyGetArchiveMessageQuery } from "../../app/services/message";
|
||||
import { ArchiveMessage } from "../../types/resource";
|
||||
|
||||
export default function useNormalizeMessage() {
|
||||
const [filePath, setFilePath] = useState<string | null>(null);
|
||||
|
||||
@@ -7,9 +7,18 @@ 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 { AppDispatch, RootState } from "../../../app/store";
|
||||
|
||||
const handler = (data: ChatEvent, dispatch: AppDispatch, currState: RootState) => {
|
||||
import { AppDispatch } from "../../../app/store";
|
||||
type CurrentState = {
|
||||
ready: boolean;
|
||||
loginUid: number;
|
||||
readUsers: {
|
||||
[key: number]: number;
|
||||
};
|
||||
readChannels: {
|
||||
[key: number]: number;
|
||||
};
|
||||
};
|
||||
const handler = (data: ChatEvent, dispatch: AppDispatch, currState: CurrentState) => {
|
||||
const {
|
||||
mid,
|
||||
from_uid,
|
||||
@@ -41,7 +50,7 @@ const handler = (data: ChatEvent, dispatch: AppDispatch, currState: RootState) =
|
||||
break;
|
||||
}
|
||||
const { ready, loginUid, readUsers = {}, readChannels = {} } = currState;
|
||||
const to = typeof target.gid !== "undefined" ? "channel" : "user";
|
||||
const to = "gid" in target ? "channel" : "user";
|
||||
const appendMessage = to == "user" ? addUserMsg : addChannelMsg;
|
||||
const self = from_uid == loginUid;
|
||||
// 此处有点绕
|
||||
|
||||
@@ -22,13 +22,13 @@ import { updateUsersByLogs, updateUsersStatus } from "../../../app/slices/users"
|
||||
import { resetAuthData } from "../../../app/slices/auth.data";
|
||||
import chatMessageHandler from "./chat.handler";
|
||||
import store, { useAppDispatch, useAppSelector } from "../../../app/store";
|
||||
import { ServerEvent } from "../../../types/sse";
|
||||
import { ServerEvent, UsersStateEvent } from "../../../types/sse";
|
||||
|
||||
class RetriableError extends Error {}
|
||||
|
||||
class FatalError extends Error {}
|
||||
|
||||
const getQueryString = (params = {}) => {
|
||||
const getQueryString = (params: { [key: string]: string }) => {
|
||||
const sp = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, val]) => {
|
||||
if (val) {
|
||||
@@ -37,11 +37,6 @@ const getQueryString = (params = {}) => {
|
||||
});
|
||||
return sp.toString();
|
||||
};
|
||||
// const StreamStatus = {
|
||||
// waiting: "waiting",
|
||||
// initialized: "initialized",
|
||||
// streaming: "streaming",
|
||||
// };
|
||||
let inter: number | null = null;
|
||||
|
||||
export default function useStreaming() {
|
||||
@@ -53,7 +48,7 @@ export default function useStreaming() {
|
||||
} = useAppSelector((store) => store);
|
||||
const [renewToken] = useRenewMutation();
|
||||
const dispatch = useAppDispatch();
|
||||
const loginUid = authData.user?.uid;
|
||||
const loginUid = authData.user?.uid || 0;
|
||||
let initialized = false;
|
||||
let initializing = false;
|
||||
let controller = new AbortController();
|
||||
@@ -63,21 +58,20 @@ export default function useStreaming() {
|
||||
if (initialized || initializing) return;
|
||||
// 如果token快要过期,先renew
|
||||
const {
|
||||
authData: { token, expireTime = +new Date(), refreshToken }
|
||||
authData: { token = "", expireTime = +new Date(), refreshToken }
|
||||
} = store.getState();
|
||||
let api_token = token;
|
||||
const tokenAlmostExpire = dayjs().isAfter(new Date(expireTime - 20 * 1000));
|
||||
console.log("check token expire time", tokenAlmostExpire);
|
||||
if (tokenAlmostExpire) {
|
||||
const {
|
||||
data: { token: newToken },
|
||||
isError
|
||||
} = await renewToken({
|
||||
const resp = await renewToken({
|
||||
token,
|
||||
refresh_token: refreshToken
|
||||
});
|
||||
if (isError) return;
|
||||
api_token = newToken;
|
||||
if ("error" in resp) return;
|
||||
if ("data" in resp) {
|
||||
api_token = resp.data.token;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始初始化
|
||||
@@ -85,8 +79,8 @@ export default function useStreaming() {
|
||||
await fetchEventSource(
|
||||
`${BASE_URL}/user/events?${getQueryString({
|
||||
"api-key": api_token,
|
||||
users_version: usersVersion,
|
||||
after_mid: afterMid
|
||||
users_version: `${usersVersion}`,
|
||||
after_mid: `${afterMid}`
|
||||
})}`,
|
||||
{
|
||||
openWhenHidden: true,
|
||||
@@ -186,7 +180,8 @@ export default function useStreaming() {
|
||||
case "users_state_changed":
|
||||
{
|
||||
let { type, ...rest } = data;
|
||||
const onlines = type == "users_state_changed" ? [rest] : rest.users;
|
||||
const onlines =
|
||||
type == "users_state_changed" ? [rest] : (rest as UsersStateEvent).users;
|
||||
dispatch(updateUsersStatus(onlines));
|
||||
}
|
||||
break;
|
||||
@@ -300,7 +295,7 @@ export default function useStreaming() {
|
||||
clearTimeout(inter);
|
||||
}
|
||||
// 重连
|
||||
inter = setTimeout(() => {
|
||||
inter = window.setTimeout(() => {
|
||||
initialized = false;
|
||||
startStreaming();
|
||||
}, 2000);
|
||||
@@ -323,7 +318,7 @@ export default function useStreaming() {
|
||||
}
|
||||
};
|
||||
|
||||
const setStreamingReady = (ready) => {
|
||||
const setStreamingReady = (ready: boolean) => {
|
||||
setReadyPullData(ready);
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,8 @@ export const getInitialsAvatar = ({
|
||||
canvas.style.width = `${width}px`;
|
||||
canvas.style.height = `${height}px`;
|
||||
|
||||
const context = canvas.getContext("2d");
|
||||
const context = canvas.getContext("2d") as CanvasRenderingContext2D;
|
||||
|
||||
context.scale(devicePixelRatio, devicePixelRatio);
|
||||
context.rect(0, 0, canvas.width, canvas.height);
|
||||
context.fillStyle = background;
|
||||
@@ -115,7 +116,7 @@ export const getInitialsAvatar = ({
|
||||
* @param {Number} - chunksAmount
|
||||
* @return {Array} - an array of Blobs
|
||||
**/
|
||||
export function sliceFile(file, chunksAmount) {
|
||||
export function sliceFile(file: File | null, chunksAmount: number) {
|
||||
if (!file) return null;
|
||||
let byteIndex = 0;
|
||||
let chunks = [];
|
||||
@@ -194,7 +195,7 @@ export const normalizeArchiveData = (
|
||||
};
|
||||
return messages.map(
|
||||
({ source, mid, content, file_id, thumbnail_id, content_type, properties, from_user }) => {
|
||||
let user = { ...(users[from_user] || {}) };
|
||||
let user = users[from_user] ?? {};
|
||||
const { transformedContent, thumbnail, download, avatarUrl } = getUrls(uid, {
|
||||
content,
|
||||
content_type,
|
||||
|
||||
Vendored
+4
@@ -1,6 +1,7 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="react" />
|
||||
/// <reference types="react-dom" />
|
||||
/// <reference types="react-scripts" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
@@ -61,3 +62,6 @@ declare module "*.module.css" {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
interface Window {
|
||||
ethereum: any;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,14 @@ const NavItem: FC<IProps> = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
handleContextMenuEvent,
|
||||
hideContextMenu
|
||||
} = useContextMenu();
|
||||
const { channel, mids, messageData, readIndex, muted, loginUid } = useAppSelector((store) => {
|
||||
const {
|
||||
channel,
|
||||
mids = [],
|
||||
messageData,
|
||||
readIndex,
|
||||
muted,
|
||||
loginUid = 0
|
||||
} = useAppSelector((store) => {
|
||||
return {
|
||||
channel: store.channels.byId[id],
|
||||
mids: store.channelMessage[id],
|
||||
@@ -76,7 +83,7 @@ const NavItem: FC<IProps> = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
updateReadIndex(param);
|
||||
}
|
||||
};
|
||||
const toggleInviteModalVisible = (evt) => {
|
||||
const toggleInviteModalVisible = (evt?: Event) => {
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -87,6 +94,7 @@ const NavItem: FC<IProps> = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
const data = muted ? { remove_groups: [id] } : { add_groups: [{ gid: id }] };
|
||||
muteChannel(data);
|
||||
};
|
||||
if (!channel) return null;
|
||||
const { is_public, name, owner } = channel;
|
||||
const { unreads = 0, mentions = [] } = getUnreadCount({
|
||||
mids,
|
||||
@@ -119,10 +127,6 @@ const NavItem: FC<IProps> = ({ id, setFiles, toggleRemoveConfirm }) => {
|
||||
title: muted ? "Unmute" : "Mute",
|
||||
handler: handleMute
|
||||
},
|
||||
// {
|
||||
// title: "Notification Settings",
|
||||
// underline: true,
|
||||
// },
|
||||
{
|
||||
title: "Invite People",
|
||||
handler: toggleInviteModalVisible
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRef, useEffect } from "react";
|
||||
import { useRef, useEffect, FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Waveform } from "@uiball/loaders";
|
||||
|
||||
@@ -10,8 +10,11 @@ const Styled = styled.div`
|
||||
width: 100%;
|
||||
padding: 30px 0;
|
||||
`;
|
||||
export default function LoadMore({ pullUp = null }) {
|
||||
const ref = useRef(undefined);
|
||||
type Props = {
|
||||
pullUp: () => void | null;
|
||||
};
|
||||
const LoadMore: FC<Props> = ({ pullUp = null }) => {
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
@@ -20,7 +23,7 @@ export default function LoadMore({ pullUp = null }) {
|
||||
// const currEle = entry.target;
|
||||
if (intersecting && pullUp) {
|
||||
// load more
|
||||
console.log("inview");
|
||||
// console.log("inview");
|
||||
pullUp();
|
||||
}
|
||||
});
|
||||
@@ -29,7 +32,7 @@ export default function LoadMore({ pullUp = null }) {
|
||||
);
|
||||
const currEle = ref?.current;
|
||||
if (currEle) {
|
||||
observer.observe(ref.current);
|
||||
observer.observe(currEle);
|
||||
}
|
||||
return () => {
|
||||
if (currEle) {
|
||||
@@ -39,7 +42,8 @@ export default function LoadMore({ pullUp = null }) {
|
||||
}, [ref, pullUp]);
|
||||
return (
|
||||
<Styled ref={ref}>
|
||||
<Waveform className="loading" size={24} lineWeight={5} speed={1} color="#ccc" />
|
||||
<Waveform size={24} lineWeight={5} speed={1} color="#ccc" />
|
||||
</Styled>
|
||||
);
|
||||
}
|
||||
};
|
||||
export default LoadMore;
|
||||
|
||||
@@ -29,7 +29,6 @@ const Styled = styled.ul`
|
||||
height: 40px;
|
||||
&.channel_default {
|
||||
padding: 5px;
|
||||
/* height: 35px; */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,17 @@ import { updateSelectMessages } from "../../app/slices/ui";
|
||||
import Mention from "../../common/component/Message/Mention";
|
||||
import { useAppSelector } from "../../app/store";
|
||||
|
||||
export function getUnreadCount({ mids = [], messageData = {}, loginUid = 0, readIndex = 0 }) {
|
||||
export function getUnreadCount({
|
||||
mids = [],
|
||||
messageData = {},
|
||||
loginUid = 0,
|
||||
readIndex = 0
|
||||
}: {
|
||||
mids?: number[];
|
||||
messageData: object;
|
||||
loginUid: number;
|
||||
readIndex: number;
|
||||
}) {
|
||||
// console.log({ mids, loginUid, readIndex });
|
||||
// 先过滤掉空信息和from自己的
|
||||
const others = mids.filter((mid) => {
|
||||
|
||||
@@ -100,13 +100,7 @@ const InvitePage: FC = () => {
|
||||
toast.error("Register Failed: invalid token or expired");
|
||||
break;
|
||||
case 409: {
|
||||
const tips = {
|
||||
email_conflict: "email conflict",
|
||||
name_conflict: "name conflict"
|
||||
};
|
||||
// todo
|
||||
// @ts-ignore
|
||||
toast.error(`Register Failed: ${tips[error.data?.reason]}`);
|
||||
toast.error(`Register Failed: ${error.data?.reason}`);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -6,7 +6,6 @@ const StyledWrapper = styled.div`
|
||||
height: 100vh;
|
||||
.form {
|
||||
padding: 36px 40px 32px 40px;
|
||||
/* border: 1px solid #eee; */
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
|
||||
@@ -24,7 +24,6 @@ const StyledWrapper = styled.div`
|
||||
height: 100vh;
|
||||
.form {
|
||||
padding: 36px 40px 32px 40px;
|
||||
/* border: 1px solid #eee; */
|
||||
box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
|
||||
border-radius: 12px;
|
||||
.tips {
|
||||
|
||||
@@ -6,11 +6,11 @@ export type ContentTypeKey = "text" | "markdown" | "file" | "archive";
|
||||
export interface MuteDTO {
|
||||
add_users?: {
|
||||
uid: number;
|
||||
expired_in: number;
|
||||
expired_in?: number;
|
||||
}[];
|
||||
add_groups?: {
|
||||
gid: number;
|
||||
expired_in: number;
|
||||
expired_in?: number;
|
||||
}[];
|
||||
remove_users?: number[];
|
||||
remove_groups?: number[];
|
||||
|
||||
Reference in New Issue
Block a user