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 {
|
.lastLine {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
|
|||||||
@ -5,18 +5,19 @@
|
|||||||
|
|
||||||
import styles from "./log-list.module.scss";
|
import styles from "./log-list.module.scss";
|
||||||
|
|
||||||
import throttle from "lodash/throttle";
|
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import type { LogTabViewModel } from './logs-view-model';
|
|
||||||
import { LogRow } from "./log-row";
|
|
||||||
import { cssNames } from "../../../utils";
|
import { cssNames } from "../../../utils";
|
||||||
import { v4 as getRandomId } from "uuid";
|
import { LogRow } from "./log-row";
|
||||||
import { useJumpToBottomButton } from "./use-scroll-to-bottom";
|
import type { LogTabViewModel } from './logs-view-model';
|
||||||
import { useInitialScrollToBottom } from "./use-initial-scroll-to-bottom";
|
|
||||||
import { ToBottom } from "./to-bottom";
|
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 {
|
export interface LogListProps {
|
||||||
model: LogTabViewModel;
|
model: LogTabViewModel;
|
||||||
@ -25,10 +26,9 @@ export interface LogListProps {
|
|||||||
export const LogList = observer(({ model }: LogListProps) => {
|
export const LogList = observer(({ model }: LogListProps) => {
|
||||||
const { visibleLogs } = model;
|
const { visibleLogs } = model;
|
||||||
const parentRef = useRef<HTMLDivElement>(null);
|
const parentRef = useRef<HTMLDivElement>(null);
|
||||||
const lastLineRef = useRef<HTMLDivElement>(null);
|
const topLineRef = useRef<HTMLDivElement>(null);
|
||||||
const [rowKeySuffix, setRowKeySuffix] = React.useState(getRandomId());
|
const bottomLineRef = useRef<HTMLDivElement>(null);
|
||||||
const [toBottomVisible, setButtonVisibility] = useJumpToBottomButton(parentRef.current);
|
const [toBottomVisible, setButtonVisibility] = useJumpToBottomButton(parentRef.current);
|
||||||
const entry = useIntersectionObserver(lastLineRef.current, {});
|
|
||||||
|
|
||||||
const rowVirtualizer = useVirtualizer({
|
const rowVirtualizer = useVirtualizer({
|
||||||
count: visibleLogs.get().length,
|
count: visibleLogs.get().length,
|
||||||
@ -45,50 +45,21 @@ export const LogList = observer(({ model }: LogListProps) => {
|
|||||||
scrollTo(visibleLogs.get().length - 1);
|
scrollTo(visibleLogs.get().length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onScroll = throttle(() => {
|
const onScroll = () => {
|
||||||
if (!parentRef.current) return;
|
if (!parentRef.current) return;
|
||||||
|
|
||||||
setButtonVisibility();
|
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);
|
useInitialScrollToBottom(model, scrollToBottom);
|
||||||
|
|
||||||
useEffect(() => {
|
const uniqRowKey = useRefreshListOnDataChange(model.logTabData.get());
|
||||||
// rowVirtualizer.scrollToIndex(visibleLogs.get().length - 1, { align: 'end', smoothScroll: false });
|
|
||||||
// Refresh list
|
|
||||||
setRowKeySuffix(getRandomId());
|
|
||||||
}, [model.logTabData.get()]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useScrollOnSearch(model.searchStore, scrollTo);
|
||||||
if (!model.searchStore.occurrences.length) return;
|
|
||||||
|
|
||||||
scrollTo(model.searchStore.occurrences[model.searchStore.activeOverlayIndex]);
|
useStickToBottomOnLogsLoad({ bottomLineRef, model, scrollToBottom });
|
||||||
}, [model.searchStore.searchQuery, model.searchStore.activeOverlayIndex])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useOnScrollTop({ topLineRef, model, scrollTo });
|
||||||
if (entry?.isIntersecting) {
|
|
||||||
scrollToBottom();
|
|
||||||
}
|
|
||||||
}, [model.visibleLogs.get().length]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -102,9 +73,10 @@ export const LogList = observer(({ model }: LogListProps) => {
|
|||||||
}}
|
}}
|
||||||
className={styles.virtualizer}
|
className={styles.virtualizer}
|
||||||
>
|
>
|
||||||
|
<div className={styles.firstLine} ref={topLineRef}></div>
|
||||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
|
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
|
||||||
<div
|
<div
|
||||||
key={virtualRow.index + rowKeySuffix}
|
key={virtualRow.index + uniqRowKey}
|
||||||
ref={virtualRow.measureElement}
|
ref={virtualRow.measureElement}
|
||||||
style={{
|
style={{
|
||||||
transform: `translateY(${virtualRow.start}px)`,
|
transform: `translateY(${virtualRow.start}px)`,
|
||||||
@ -116,7 +88,7 @@ export const LogList = observer(({ model }: LogListProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className={styles.lastLine} ref={lastLineRef}></div>
|
<div className={styles.lastLine} ref={bottomLineRef}></div>
|
||||||
</div>
|
</div>
|
||||||
{toBottomVisible && (
|
{toBottomVisible && (
|
||||||
<ToBottom onClick={scrollToBottom} />
|
<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