mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Move all side effects to hooks
Signed-off-by: alexfront <alex.andreev.email@gmail.com>
This commit is contained in:
parent
ac8ed1478e
commit
d951f3a017
@ -29,6 +29,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.firstLine {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
top: 0;
|
||||
background-color: var(--logsBackground);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.lastLine {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
@ -5,18 +5,19 @@
|
||||
|
||||
import styles from "./log-list.module.scss";
|
||||
|
||||
import throttle from "lodash/throttle";
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { observer } from 'mobx-react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import type { LogTabViewModel } from './logs-view-model';
|
||||
import { LogRow } from "./log-row";
|
||||
import React, { useRef } from 'react';
|
||||
import { cssNames } from "../../../utils";
|
||||
import { v4 as getRandomId } from "uuid";
|
||||
import { useJumpToBottomButton } from "./use-scroll-to-bottom";
|
||||
import { useInitialScrollToBottom } from "./use-initial-scroll-to-bottom";
|
||||
import { LogRow } from "./log-row";
|
||||
import type { LogTabViewModel } from './logs-view-model';
|
||||
import { ToBottom } from "./to-bottom";
|
||||
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
|
||||
import { useInitialScrollToBottom } from "./use-initial-scroll-to-bottom";
|
||||
import { useOnScrollTop } from "./use-on-scroll-top";
|
||||
import { useRefreshListOnDataChange } from "./use-refresh-list-on-data-change";
|
||||
import { useScrollOnSearch } from "./use-scroll-on-search";
|
||||
import { useJumpToBottomButton } from "./use-scroll-to-bottom";
|
||||
import { useStickToBottomOnLogsLoad } from "./use-stick-to-bottom-on-logs-load";
|
||||
|
||||
export interface LogListProps {
|
||||
model: LogTabViewModel;
|
||||
@ -25,10 +26,9 @@ export interface LogListProps {
|
||||
export const LogList = observer(({ model }: LogListProps) => {
|
||||
const { visibleLogs } = model;
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
const lastLineRef = useRef<HTMLDivElement>(null);
|
||||
const [rowKeySuffix, setRowKeySuffix] = React.useState(getRandomId());
|
||||
const topLineRef = useRef<HTMLDivElement>(null);
|
||||
const bottomLineRef = useRef<HTMLDivElement>(null);
|
||||
const [toBottomVisible, setButtonVisibility] = useJumpToBottomButton(parentRef.current);
|
||||
const entry = useIntersectionObserver(lastLineRef.current, {});
|
||||
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: visibleLogs.get().length,
|
||||
@ -45,50 +45,21 @@ export const LogList = observer(({ model }: LogListProps) => {
|
||||
scrollTo(visibleLogs.get().length - 1);
|
||||
}
|
||||
|
||||
const onScroll = throttle(() => {
|
||||
const onScroll = () => {
|
||||
if (!parentRef.current) return;
|
||||
|
||||
setButtonVisibility();
|
||||
onScrollToTop();
|
||||
}, 1_000, { trailing: true, leading: true });
|
||||
|
||||
/**
|
||||
* Loads new logs if user scrolled to the top
|
||||
*/
|
||||
const onScrollToTop = async () => {
|
||||
const { scrollTop } = parentRef.current as HTMLDivElement;
|
||||
|
||||
if (scrollTop === 0) {
|
||||
const logs = model.logs.get();
|
||||
const firstLog = logs[0];
|
||||
|
||||
await model.loadLogs();
|
||||
|
||||
const scrollToIndex = model.logs.get().findIndex(log => log === firstLog);
|
||||
|
||||
scrollTo(scrollToIndex);
|
||||
}
|
||||
};
|
||||
|
||||
useInitialScrollToBottom(model, scrollToBottom);
|
||||
|
||||
useEffect(() => {
|
||||
// rowVirtualizer.scrollToIndex(visibleLogs.get().length - 1, { align: 'end', smoothScroll: false });
|
||||
// Refresh list
|
||||
setRowKeySuffix(getRandomId());
|
||||
}, [model.logTabData.get()]);
|
||||
const uniqRowKey = useRefreshListOnDataChange(model.logTabData.get());
|
||||
|
||||
useEffect(() => {
|
||||
if (!model.searchStore.occurrences.length) return;
|
||||
useScrollOnSearch(model.searchStore, scrollTo);
|
||||
|
||||
scrollTo(model.searchStore.occurrences[model.searchStore.activeOverlayIndex]);
|
||||
}, [model.searchStore.searchQuery, model.searchStore.activeOverlayIndex])
|
||||
useStickToBottomOnLogsLoad({ bottomLineRef, model, scrollToBottom });
|
||||
|
||||
useEffect(() => {
|
||||
if (entry?.isIntersecting) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [model.visibleLogs.get().length]);
|
||||
useOnScrollTop({ topLineRef, model, scrollTo });
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -102,9 +73,10 @@ export const LogList = observer(({ model }: LogListProps) => {
|
||||
}}
|
||||
className={styles.virtualizer}
|
||||
>
|
||||
<div className={styles.firstLine} ref={topLineRef}></div>
|
||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
|
||||
<div
|
||||
key={virtualRow.index + rowKeySuffix}
|
||||
key={virtualRow.index + uniqRowKey}
|
||||
ref={virtualRow.measureElement}
|
||||
style={{
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
@ -116,7 +88,7 @@ export const LogList = observer(({ model }: LogListProps) => {
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className={styles.lastLine} ref={lastLineRef}></div>
|
||||
<div className={styles.lastLine} ref={bottomLineRef}></div>
|
||||
</div>
|
||||
{toBottomVisible && (
|
||||
<ToBottom onClick={scrollToBottom} />
|
||||
|
||||
37
src/renderer/components/dock/logs/use-on-scroll-top.ts
Normal file
37
src/renderer/components/dock/logs/use-on-scroll-top.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { RefObject } from "react";
|
||||
import { useEffect } from "react";
|
||||
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
|
||||
import type { LogTabViewModel } from "./logs-view-model";
|
||||
|
||||
interface UseStickToBottomProps {
|
||||
topLineRef: RefObject<HTMLDivElement>;
|
||||
model: LogTabViewModel;
|
||||
scrollTo: (index: number) => void;
|
||||
}
|
||||
|
||||
export function useOnScrollTop({ topLineRef, model, scrollTo }: UseStickToBottomProps) {
|
||||
const topLineEntry = useIntersectionObserver(topLineRef.current, {});
|
||||
|
||||
function getPreviouslyFirstLogIndex(firstLog: string) {
|
||||
return model.logs.get().findIndex(log => log === firstLog);
|
||||
}
|
||||
|
||||
async function onScrolledTop() {
|
||||
const firstLog = model.logs.get()[0];
|
||||
const scrollIndex = () => getPreviouslyFirstLogIndex(firstLog);
|
||||
|
||||
await model.loadLogs();
|
||||
scrollTo(scrollIndex());
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (topLineEntry?.isIntersecting) {
|
||||
onScrolledTop();
|
||||
}
|
||||
}, [topLineEntry?.isIntersecting]);
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import type { LogTabData } from "./tab-store";
|
||||
import { v4 as getRandomId } from "uuid";
|
||||
|
||||
export function useRefreshListOnDataChange(data: LogTabData | undefined) {
|
||||
const [rowKeySuffix, setRowKeySuffix] = useState(getRandomId());
|
||||
|
||||
useEffect(() => {
|
||||
// Refresh virtualizer list rows by changing their keys
|
||||
setRowKeySuffix(getRandomId());
|
||||
}, [data]);
|
||||
|
||||
return rowKeySuffix;
|
||||
}
|
||||
17
src/renderer/components/dock/logs/use-scroll-on-search.ts
Normal file
17
src/renderer/components/dock/logs/use-scroll-on-search.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { useEffect } from "react";
|
||||
import type { SearchStore } from "../../../search-store/search-store";
|
||||
|
||||
export function useScrollOnSearch(store: SearchStore, scrollTo: (index: number) => void) {
|
||||
const { occurrences, searchQuery, activeOverlayIndex } = store;
|
||||
|
||||
useEffect(() => {
|
||||
if (!occurrences.length) return;
|
||||
|
||||
scrollTo(occurrences[activeOverlayIndex]);
|
||||
}, [searchQuery, activeOverlayIndex]);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { RefObject } from "react";
|
||||
import { useEffect } from "react";
|
||||
import useIntersectionObserver from "../../../hooks/useIntersectionObserver";
|
||||
import type { LogTabViewModel } from "./logs-view-model";
|
||||
|
||||
interface UseStickToBottomProps {
|
||||
bottomLineRef: RefObject<HTMLDivElement>;
|
||||
model: LogTabViewModel;
|
||||
scrollToBottom: () => void;
|
||||
}
|
||||
|
||||
export function useStickToBottomOnLogsLoad({ bottomLineRef, model, scrollToBottom }: UseStickToBottomProps) {
|
||||
const bottomLineEntry = useIntersectionObserver(bottomLineRef.current, {});
|
||||
|
||||
useEffect(() => {
|
||||
if (bottomLineEntry?.isIntersecting) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [model.visibleLogs.get().length]);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user