192 lines
6.0 KiB
TypeScript
192 lines
6.0 KiB
TypeScript
import { useEffect, useState, useRef } from "react";
|
|
import { useLazyGetHistoryMessagesQuery } from "../../app/services/channel";
|
|
import { useLazyGetHistoryMessagesQuery as useLazyGetDMHistoryMsg } from "../../app/services/user";
|
|
import { useAppSelector } from "../../app/store";
|
|
export interface PageInfo {
|
|
isFirst: boolean;
|
|
isLast: boolean;
|
|
pageCount: number;
|
|
pageSize: number;
|
|
pageNumber: number;
|
|
ids: number[];
|
|
}
|
|
interface Config extends Partial<PageInfo> {
|
|
mids: number[];
|
|
}
|
|
const getFeedWithPagination = (config: Config): PageInfo => {
|
|
const { pageNumber = 1, pageSize = 40, mids = [], isLast = false } = config || {};
|
|
const shadowMids = mids.slice(0);
|
|
|
|
if (shadowMids.length == 0)
|
|
return {
|
|
isFirst: true,
|
|
isLast: true,
|
|
pageCount: 0,
|
|
pageSize,
|
|
pageNumber: 1,
|
|
ids: []
|
|
};
|
|
shadowMids.sort((a, b) => {
|
|
return Number(a) - Number(b);
|
|
});
|
|
console.log("message pagination", shadowMids);
|
|
const pageCount = Math.ceil(shadowMids.length / pageSize);
|
|
const computedPageNumber = isLast ? pageCount : pageNumber;
|
|
const _start = -(pageCount - computedPageNumber + 1) * pageSize;
|
|
const _tmp = _start + pageSize;
|
|
const _end = _tmp == 0 ? undefined : _tmp;
|
|
const ids = shadowMids.slice(_start, _end);
|
|
console.log("start,end", _start, _end, ids);
|
|
return {
|
|
isFirst: computedPageNumber == 1,
|
|
isLast: computedPageNumber == pageCount,
|
|
pageCount,
|
|
pageSize,
|
|
pageNumber: computedPageNumber,
|
|
ids
|
|
};
|
|
};
|
|
let curScrollPos = 0;
|
|
let oldScroll = 0;
|
|
type Props = {
|
|
context?: "channel" | "user";
|
|
id: number;
|
|
};
|
|
export default function useMessageFeed({ context = "channel", id }: Props) {
|
|
const [loadMoreChannelMsgs] = useLazyGetHistoryMessagesQuery();
|
|
const [loadMoreDmMsgs] = useLazyGetDMHistoryMsg();
|
|
const listRef = useRef<number[]>([]);
|
|
const pageRef = useRef<PageInfo | null>(null);
|
|
const containerRef = useRef<HTMLElement | null>(null);
|
|
const [hasMore, setHasMore] = useState(true);
|
|
const [appends, setAppends] = useState([]);
|
|
const [items, setItems] = useState<number[]>([]);
|
|
const loadMoreMsgsFromServer = context == "channel" ? loadMoreChannelMsgs : loadMoreDmMsgs;
|
|
const { mids, messageData, loginUid } = useAppSelector((store) => {
|
|
return {
|
|
loginUid: store.authData.user?.uid,
|
|
mids:
|
|
context == "channel" ? store.channelMessage[id] || [] : store.userMessage.byId[id] || [],
|
|
messageData: store.message
|
|
};
|
|
});
|
|
useEffect(() => {
|
|
listRef.current = [];
|
|
pageRef.current = null;
|
|
setItems([]);
|
|
setHasMore(true);
|
|
setAppends([]);
|
|
}, [context, id]);
|
|
useEffect(() => {
|
|
if (items.length) {
|
|
containerRef.current = document.querySelector(`#VOCECHAT_FEED_${context}_${id}`);
|
|
if (containerRef.current) {
|
|
const newScroll = containerRef.current.scrollHeight - containerRef.current.clientHeight;
|
|
containerRef.current.scrollTop = curScrollPos + (newScroll - oldScroll);
|
|
}
|
|
}
|
|
}, [items, context, id]);
|
|
useEffect(() => {
|
|
const serverMids = mids.filter((id: number) => {
|
|
const ts = +new Date();
|
|
return Math.abs(ts - id) > 10 * 1000;
|
|
});
|
|
if (listRef.current.length == 0 && serverMids.length) {
|
|
// 初次
|
|
const pageInfo = getFeedWithPagination({
|
|
mids: serverMids,
|
|
isLast: true
|
|
});
|
|
console.log("pull up 2", pageInfo);
|
|
pageRef.current = pageInfo;
|
|
listRef.current = pageInfo.ids;
|
|
setItems(listRef.current);
|
|
console.log("message pageInfo", serverMids, pageInfo);
|
|
} else {
|
|
// 追加
|
|
const [lastMid] = listRef.current.slice(-1);
|
|
const sorteds = mids.slice(0).sort((a: number, b: number) => {
|
|
return Number(a) - Number(b);
|
|
});
|
|
const appends = sorteds.filter((s: number) => s > lastMid);
|
|
console.log("appends", appends, sorteds, lastMid, mids);
|
|
if (appends.length) {
|
|
setAppends(appends);
|
|
const [newestMsgId] = appends.slice(-1);
|
|
// 自己发的消息
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
const msgFromSelf = loginUid == messageData[newestMsgId]?.from_uid;
|
|
const scrollDistance =
|
|
container.scrollHeight - (container.offsetHeight + container.scrollTop);
|
|
console.log("scrollDistance", msgFromSelf, scrollDistance);
|
|
if (msgFromSelf) {
|
|
container.scrollTop = container.scrollHeight;
|
|
} else if (scrollDistance <= 100) {
|
|
setTimeout(() => {
|
|
container.scrollTop = container.scrollHeight;
|
|
}, 100);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, [mids, messageData, loginUid]);
|
|
const pullUp = async () => {
|
|
const currPageInfo = pageRef.current;
|
|
console.log("pull up", currPageInfo);
|
|
// 第一页
|
|
if (currPageInfo && currPageInfo.isFirst) {
|
|
const [firstMid] = currPageInfo.ids;
|
|
const { data: newList } = await loadMoreMsgsFromServer({
|
|
mid: firstMid,
|
|
id
|
|
});
|
|
if (newList?.length == 0) {
|
|
setHasMore(false);
|
|
return;
|
|
}
|
|
}
|
|
let pageInfo: PageInfo;
|
|
if (!currPageInfo) {
|
|
// 初始化
|
|
pageInfo = getFeedWithPagination({
|
|
mids,
|
|
isLast: true
|
|
});
|
|
} else {
|
|
const prevPageNumber = currPageInfo.pageNumber - 1;
|
|
pageInfo = getFeedWithPagination({
|
|
mids,
|
|
pageNumber: prevPageNumber
|
|
});
|
|
}
|
|
pageRef.current = pageInfo;
|
|
listRef.current = [...pageInfo.ids, ...listRef.current];
|
|
setTimeout(
|
|
() => {
|
|
setItems(listRef.current);
|
|
console.log("pull up", currPageInfo, listRef.current);
|
|
setHasMore(pageInfo.pageNumber !== 1);
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
curScrollPos = container.scrollTop;
|
|
oldScroll = container.scrollHeight - container.clientHeight;
|
|
}
|
|
},
|
|
currPageInfo?.isLast ? 10 : 800
|
|
);
|
|
};
|
|
const pullDown = () => {
|
|
// 向下加载
|
|
};
|
|
|
|
return {
|
|
mids,
|
|
appends,
|
|
hasMore,
|
|
pullUp,
|
|
pullDown,
|
|
list: items
|
|
};
|
|
}
|