(feat): add custom lint rules
Some checks failed
/ build-web (push) Successful in 7m4s
/ build-desktop (linux) (push) Successful in 11m31s
/ build-mobile (push) Successful in 18m12s
/ release (push) Failing after 3m2s

(qol): update todo
(chore): update licenses
This commit is contained in:
Alois 2026-07-24 12:44:08 +02:00
commit 17db895203
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
28 changed files with 585 additions and 513 deletions

View file

@ -1,8 +1,7 @@
import * as React from "react";
import type { RawMessage } from "../values";
import Text from "@tensamin/markdown/text";
import { AlertTriangle, Check, Ellipse, RefreshCw } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { memo, useCallback, useEffect, useState } from "react";
import type { User } from "@tensamin/user/context";
import {
@ -384,7 +383,7 @@ function MessageComponent({
);
}
export default React.memo(MessageComponent, (prev, next) => {
export default memo(MessageComponent, (prev, next) => {
return (
prev.message.SendTime === next.message.SendTime &&
prev.message.Content === next.message.Content &&

View file

@ -1,4 +1,11 @@
import * as React from "react";
import {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from "react";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useVirtualizer } from "@tanstack/react-virtual";
@ -82,21 +89,21 @@ export default function Screen() {
error,
errorDescription,
} = useChat();
const scrollRef = React.useRef<HTMLDivElement | null>(null);
const topSentinelRef = React.useRef<HTMLDivElement | null>(null);
const didInitialScrollRef = React.useRef(false);
const userScrolledUpRef = React.useRef(false);
const isAtBottomRef = React.useRef(true);
const smoothScrollFrameRef = React.useRef<number | null>(null);
const smoothScrollTargetRef = React.useRef(0);
const hasNextPageRef = React.useRef(false);
const isFetchingNextPageRef = React.useRef(false);
const fetchNextPageRef = React.useRef<(() => void) | null>(null);
const scrollRef = useRef<HTMLDivElement | null>(null);
const topSentinelRef = useRef<HTMLDivElement | null>(null);
const didInitialScrollRef = useRef(false);
const userScrolledUpRef = useRef(false);
const isAtBottomRef = useRef(true);
const smoothScrollFrameRef = useRef<number | null>(null);
const smoothScrollTargetRef = useRef(0);
const hasNextPageRef = useRef(false);
const isFetchingNextPageRef = useRef(false);
const fetchNextPageRef = useRef<(() => void) | null>(null);
const [lastLiveMessageCount, setLastLiveMessageCount] = React.useState(0);
const [didInitialScroll, setDidInitialScroll] = React.useState(false);
const [viewportHeight, setViewportHeight] = React.useState(0);
const [value, setValue] = React.useState("");
const [lastLiveMessageCount, setLastLiveMessageCount] = useState(0);
const [didInitialScroll, setDidInitialScroll] = useState(false);
const [viewportHeight, setViewportHeight] = useState(0);
const [value, setValue] = useState("");
const hasValidChatUser = Number.isSafeInteger(userId) && userId > 0;
const hasChatSecret = chatSecret !== null;
@ -120,7 +127,7 @@ export default function Screen() {
isFetchingNextPage: isFetchingMessagesNextPage,
} = messagesQuery;
React.useEffect(() => {
useEffect(() => {
hasNextPageRef.current = hasMessagesNextPage;
isFetchingNextPageRef.current = isFetchingMessagesNextPage;
fetchNextPageRef.current = () => {
@ -128,7 +135,7 @@ export default function Screen() {
};
}, [fetchMessagesNextPage, hasMessagesNextPage, isFetchingMessagesNextPage]);
React.useEffect(() => {
useEffect(() => {
clearLiveMessages();
didInitialScrollRef.current = false;
userScrolledUpRef.current = false;
@ -137,7 +144,7 @@ export default function Screen() {
setLastLiveMessageCount(0);
}, [clearLiveMessages, userId]);
const historicalMessages = React.useMemo(() => {
const historicalMessages = useMemo(() => {
const pages = messagesQuery.data?.pages ?? [];
const seenSendTimes = new Set<number>();
const dedupedMessages: RawMessage[] = [];
@ -156,7 +163,7 @@ export default function Screen() {
const liveMessagesSnapshot = liveMessages();
const liveWithoutDuplicates = React.useMemo(() => {
const liveWithoutDuplicates = useMemo(() => {
const historicalSendTimes = new Set(
historicalMessages.map((message) => message.SendTime),
);
@ -166,15 +173,15 @@ export default function Screen() {
);
}, [historicalMessages, liveMessagesSnapshot]);
const messages = React.useMemo(() => {
const messages = useMemo(() => {
return [...historicalMessages, ...liveWithoutDuplicates];
}, [historicalMessages, liveWithoutDuplicates]);
const historicalMessageChunks = React.useMemo(() => {
const historicalMessageChunks = useMemo(() => {
return buildMessageChunks(historicalMessages, "history");
}, [historicalMessages]);
const liveMessageChunks = React.useMemo(() => {
const liveMessageChunks = useMemo(() => {
return buildMessageChunks(
liveWithoutDuplicates,
"live",
@ -182,18 +189,18 @@ export default function Screen() {
);
}, [historicalMessages.length, liveWithoutDuplicates]);
const messageChunks = React.useMemo(() => {
const messageChunks = useMemo(() => {
return [...liveMessageChunks, ...historicalMessageChunks];
}, [historicalMessageChunks, liveMessageChunks]);
const virtualRowCount = messageChunks.length;
const getItemKey = React.useCallback(
const getItemKey = useCallback(
(index: number) => messageChunks[index]?.key ?? index,
[messageChunks],
);
const estimateSize = React.useCallback(() => FALLBACK_MESSAGE_HEIGHT, []);
const estimateSize = useCallback(() => FALLBACK_MESSAGE_HEIGHT, []);
// eslint-disable-next-line react-hooks/incompatible-library
const virtualizer = useVirtualizer({
@ -207,7 +214,7 @@ export default function Screen() {
const contentHeight = Math.max(totalSize, viewportHeight);
const verticalOffset = Math.max(0, viewportHeight - totalSize);
React.useLayoutEffect(() => {
useLayoutEffect(() => {
const element = scrollRef.current;
if (!element || typeof ResizeObserver === "undefined") {
return;
@ -227,7 +234,7 @@ export default function Screen() {
};
}, []);
React.useLayoutEffect(() => {
useLayoutEffect(() => {
if (didInitialScrollRef.current || virtualRowCount === 0) {
return;
}
@ -244,7 +251,7 @@ export default function Screen() {
});
}, [virtualizer, virtualRowCount]);
React.useLayoutEffect(() => {
useLayoutEffect(() => {
const count = liveMessagesSnapshot.length;
if (count > lastLiveMessageCount && isAtBottomRef.current) {
@ -263,7 +270,7 @@ export default function Screen() {
virtualizer,
]);
React.useEffect(() => {
useEffect(() => {
if (
viewportHeight === 0 ||
totalSize > viewportHeight ||
@ -276,7 +283,7 @@ export default function Screen() {
void messagesQuery.fetchNextPage();
}, [didInitialScroll, messagesQuery, totalSize, viewportHeight]);
React.useLayoutEffect(() => {
useLayoutEffect(() => {
if (
!didInitialScroll ||
userScrolledUpRef.current ||
@ -292,7 +299,7 @@ export default function Screen() {
});
}, [didInitialScroll, virtualRowCount]);
React.useEffect(() => {
useEffect(() => {
const root = scrollRef.current;
const sentinel = topSentinelRef.current;
@ -324,7 +331,7 @@ export default function Screen() {
};
}, [didInitialScroll, messagesQuery, virtualRowCount]);
const handleContainerScroll = React.useCallback(() => {
const handleContainerScroll = useCallback(() => {
if (!scrollRef.current) {
return;
}
@ -336,7 +343,7 @@ export default function Screen() {
}
}, []);
React.useEffect(() => {
useEffect(() => {
const element = scrollRef.current;
if (!element) {
return;