From f6f72e06719049568369771924c03cd3cc005f27 Mon Sep 17 00:00:00 2001 From: Nuutti Date: Fri, 25 Apr 2025 20:42:24 +0300 Subject: [PATCH 1/2] Experimenting with legend list # Conflicts: # bun.lock # package.json --- app/(app)/server/[id]/index.tsx | 13 ++++++++----- bun.lock | 3 +++ hooks/useAutoScroll.ts | 4 ++-- package.json | 1 + 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/(app)/server/[id]/index.tsx b/app/(app)/server/[id]/index.tsx index ebf3d9a..84be114 100644 --- a/app/(app)/server/[id]/index.tsx +++ b/app/(app)/server/[id]/index.tsx @@ -9,7 +9,7 @@ import Animated, { import { useKeyboardHandler } from "react-native-keyboard-controller"; import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; import { useTranslation } from "react-i18next"; -import { FlashList } from "@shopify/flash-list"; +import { LegendList } from "@legendapp/list"; import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons"; import ConsoleText from "@/components/server/console/ConsoleText"; import TextInput from "@/components/ui/TextInput"; @@ -77,7 +77,7 @@ export default function ConsoleScreen() { ); const { server } = useServer(); const [lines, setLines] = useState([]); - const { listRef, isAtBottom, listMounted, handleScroll, handleContentSizeChange, goToBottom } = useAutoScroll({ data: lines, inverted: true }); + const { listRef, isAtBottom, listMounted, handleScroll, handleContentSizeChange, goToBottom } = useAutoScroll({ data: lines, inverted: false }); const chevronVisible = useSharedValue(0); const [initialScroll, setInitialScroll] = useState(true); const [hasGotItems, setHasGotItems] = useState(false); @@ -155,7 +155,7 @@ export default function ConsoleScreen() { newLines.pop(); } - setLines((lines) => [...newLines.reverse(), ...lines]); + setLines((lines) => [...lines, ...newLines]); setHasGotItems(true); }; @@ -172,7 +172,7 @@ export default function ConsoleScreen() { {server?.hasScope("server.console") && ( <> - index.toString()} renderItem={({ item }) => } estimatedItemSize={30} - inverted={true} + recycleItems={true} + alignItemsAtEnd={true} + maintainScrollAtEnd={true} + maintainScrollAtEndThreshold={0.1} showsVerticalScrollIndicator={false} /> diff --git a/bun.lock b/bun.lock index aaf1cb8..8e48be4 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@formatjs/intl-locale": "^4.2.11", "@gorhom/bottom-sheet": "^5.1.5", "@hookform/resolvers": "^5.0.1", + "@legendapp/list": "^1.0.3", "@react-native-async-storage/async-storage": "2.1.2", "@react-native-community/slider": "4.5.6", "@react-native-menu/menu": "^1.2.3", @@ -530,6 +531,8 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="], + "@legendapp/list": ["@legendapp/list@1.0.3", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-a3RoILTfSr3/A76ESNavZYP4OhjYHC+PcXo5f8OxqDfkmrcGYZOz6/1aYW5dhSF1R4UH7evj0i0OAk5P++qS6w=="], + "@nicolo-ribaudo/chokidar-2": ["@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3", "", {}, "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ=="], "@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], diff --git a/hooks/useAutoScroll.ts b/hooks/useAutoScroll.ts index 25dc53f..9d88629 100644 --- a/hooks/useAutoScroll.ts +++ b/hooks/useAutoScroll.ts @@ -1,6 +1,6 @@ import { useRef, useState, useEffect } from "react"; import { NativeScrollEvent, NativeSyntheticEvent } from "react-native"; -import { FlashList } from "@shopify/flash-list"; +import { LegendListRef } from "@legendapp/list"; type AutoScrollHookProps = { data: T[]; @@ -8,7 +8,7 @@ type AutoScrollHookProps = { }; export default function useAutoScroll({ data, inverted }: AutoScrollHookProps) { - const listRef = useRef>(null); + const listRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState(true); const [listMounted, setListMounted] = useState(false); diff --git a/package.json b/package.json index aa2f43c..d87e095 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@formatjs/intl-locale": "^4.2.11", "@gorhom/bottom-sheet": "^5.1.5", "@hookform/resolvers": "^5.0.1", + "@legendapp/list": "^1.0.3", "@react-native-async-storage/async-storage": "2.1.2", "@react-native-community/slider": "4.5.6", "@react-native-menu/menu": "^1.2.3", From d6fcb5bc60e655b66c404578ce78a35f61c25384 Mon Sep 17 00:00:00 2001 From: Nuutti Date: Thu, 29 May 2025 23:40:38 +0300 Subject: [PATCH 2/2] Updates --- app/(app)/server/[id]/index.tsx | 1 + bun.lock | 10 ++++++---- hooks/useAutoScroll.ts | 12 ++---------- package.json | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/app/(app)/server/[id]/index.tsx b/app/(app)/server/[id]/index.tsx index 84be114..12be3e1 100644 --- a/app/(app)/server/[id]/index.tsx +++ b/app/(app)/server/[id]/index.tsx @@ -185,6 +185,7 @@ export default function ConsoleScreen() { maintainScrollAtEnd={true} maintainScrollAtEndThreshold={0.1} showsVerticalScrollIndicator={false} + waitForInitialLayout={true} /> diff --git a/bun.lock b/bun.lock index 8e48be4..c0ecf44 100644 --- a/bun.lock +++ b/bun.lock @@ -10,7 +10,7 @@ "@formatjs/intl-locale": "^4.2.11", "@gorhom/bottom-sheet": "^5.1.5", "@hookform/resolvers": "^5.0.1", - "@legendapp/list": "^1.0.3", + "@legendapp/list": "^1.0.14", "@react-native-async-storage/async-storage": "2.1.2", "@react-native-community/slider": "4.5.6", "@react-native-menu/menu": "^1.2.3", @@ -529,9 +529,9 @@ "@jsamr/react-native-li": ["@jsamr/react-native-li@2.3.1", "", { "peerDependencies": { "@jsamr/counter-style": "^1.0.0 || ^2.0.0", "react": "*", "react-native": "*" } }, "sha512-Qbo4NEj48SQ4k8FZJHFE2fgZDKTWaUGmVxcIQh3msg5JezLdTMMHuRRDYctfdHI6L0FZGObmEv3haWbIvmol8w=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="], + "@legendapp/list": ["@legendapp/list@1.0.14", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-/3OWGGFgZWZK8ZvM4bdL18HhkxFEkpC0wEK8hvlLI8IeVxk0z/anKXb5hraijiMwfpDEyD4nDzQQDqtlJ1UVLQ=="], - "@legendapp/list": ["@legendapp/list@1.0.3", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-a3RoILTfSr3/A76ESNavZYP4OhjYHC+PcXo5f8OxqDfkmrcGYZOz6/1aYW5dhSF1R4UH7evj0i0OAk5P++qS6w=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="], "@nicolo-ribaudo/chokidar-2": ["@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3", "", {}, "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ=="], @@ -2361,7 +2361,7 @@ "use-sidecar": ["use-sidecar@1.1.2", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw=="], - "use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="], + "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], @@ -2901,6 +2901,8 @@ "@react-navigation/core/use-latest-callback": ["use-latest-callback@0.2.1", "", { "peerDependencies": { "react": ">=16.8" } }, "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ=="], + "@react-navigation/core/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="], + "@react-navigation/native/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], "@react-navigation/native/use-latest-callback": ["use-latest-callback@0.2.1", "", { "peerDependencies": { "react": ">=16.8" } }, "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ=="], diff --git a/hooks/useAutoScroll.ts b/hooks/useAutoScroll.ts index 9d88629..9dbc02e 100644 --- a/hooks/useAutoScroll.ts +++ b/hooks/useAutoScroll.ts @@ -1,5 +1,4 @@ import { useRef, useState, useEffect } from "react"; -import { NativeScrollEvent, NativeSyntheticEvent } from "react-native"; import { LegendListRef } from "@legendapp/list"; type AutoScrollHookProps = { @@ -18,15 +17,8 @@ export default function useAutoScroll({ data, inverted }: AutoScrollHookProps } }, [data, listMounted, isAtBottom]); - const handleScroll = (event: NativeSyntheticEvent) => { - const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; - const paddingToBottom = 20; - - if (inverted) { - setIsAtBottom(contentOffset.y <= paddingToBottom); - } else { - setIsAtBottom(layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom); - } + const handleScroll = () => { + setIsAtBottom(listRef.current?.getState().isAtEnd || false); }; const handleContentSizeChange = () => { diff --git a/package.json b/package.json index d87e095..d015932 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@formatjs/intl-locale": "^4.2.11", "@gorhom/bottom-sheet": "^5.1.5", "@hookform/resolvers": "^5.0.1", - "@legendapp/list": "^1.0.3", + "@legendapp/list": "^1.0.14", "@react-native-async-storage/async-storage": "2.1.2", "@react-native-community/slider": "4.5.6", "@react-native-menu/menu": "^1.2.3",