-
Notifications
You must be signed in to change notification settings - Fork 18
feat: userSettings for package #3235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ import React from 'react'; | |
| import {Xmark} from '@gravity-ui/icons'; | ||
| import {DrawerItem, Drawer as GravityDrawer} from '@gravity-ui/navigation'; | ||
| import {ActionTooltip, Button, Flex, Icon, Text} from '@gravity-ui/uikit'; | ||
| import {debounce} from 'lodash'; | ||
|
|
||
| import {cn} from '../../utils/cn'; | ||
| import {useSetting} from '../../utils/hooks/useSetting'; | ||
| import {isNumeric} from '../../utils/utils'; | ||
| import {CopyLinkButton} from '../CopyLinkButton/CopyLinkButton'; | ||
| import {Portal} from '../Portal/Portal'; | ||
|
|
@@ -16,6 +18,7 @@ import './Drawer.scss'; | |
| const DEFAULT_DRAWER_WIDTH_PERCENTS = 60; | ||
| const DEFAULT_DRAWER_WIDTH = 600; | ||
| const DRAWER_WIDTH_KEY = 'drawer-width'; | ||
| const SAVE_DEBOUNCE_MS = 200; | ||
| const b = cn('ydb-drawer'); | ||
|
|
||
| type DrawerEvent = MouseEvent & { | ||
|
|
@@ -49,9 +52,9 @@ const DrawerPaneContentWrapper = ({ | |
| isPercentageWidth, | ||
| hideVeil = true, | ||
| }: DrawerPaneContentWrapperProps) => { | ||
| const [drawerWidth, setDrawerWidth] = React.useState(() => { | ||
| const savedWidth = localStorage.getItem(storageKey); | ||
| return isNumeric(savedWidth) ? Number(savedWidth) : defaultWidth; | ||
| const [savedWidthString, setSavedWidthString] = useSetting<string | undefined>(storageKey); | ||
| const [drawerWidth, setDrawerWidth] = React.useState<number | undefined>(() => { | ||
| return isNumeric(savedWidthString) ? Number(savedWidthString) : defaultWidth; | ||
| }); | ||
|
Comment on lines
+55
to
58
|
||
|
|
||
| const drawerRef = React.useRef<HTMLDivElement>(null); | ||
|
|
@@ -91,14 +94,24 @@ const DrawerPaneContentWrapper = ({ | |
| }; | ||
| }, [isVisible, onClose, detectClickOutside]); | ||
|
|
||
| const saveWidthDebounced = React.useMemo(() => { | ||
| return debounce((value: string) => setSavedWidthString(value), SAVE_DEBOUNCE_MS); | ||
| }, [setSavedWidthString]); | ||
|
|
||
| React.useEffect(() => { | ||
| return () => { | ||
| saveWidthDebounced.cancel(); | ||
| }; | ||
| }, [saveWidthDebounced]); | ||
|
|
||
| const handleResizeDrawer = (width: number) => { | ||
| if (isPercentageWidth && containerWidth > 0) { | ||
| const percentageWidth = Math.round((width / containerWidth) * 100); | ||
| setDrawerWidth(percentageWidth); | ||
| localStorage.setItem(storageKey, percentageWidth.toString()); | ||
| saveWidthDebounced(percentageWidth.toString()); | ||
| } else { | ||
| setDrawerWidth(width); | ||
| localStorage.setItem(storageKey, width.toString()); | ||
| saveWidthDebounced(width.toString()); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import React from 'react'; | ||
|
|
||
| import {debounce} from 'lodash'; | ||
| import type {SplitProps} from 'react-split'; | ||
| import SplitPaneLib from 'react-split'; | ||
|
|
||
| import {cn} from '../../utils/cn'; | ||
| import {useSetting} from '../../utils/hooks/useSetting'; | ||
|
|
||
| import './SplitPane.scss'; | ||
|
|
||
|
|
@@ -25,22 +27,44 @@ interface SplitPaneProps { | |
|
|
||
| const minSizeDefaultInner = [0, 100]; | ||
| const sizesDefaultInner = [50, 50]; | ||
| const SAVE_DEBOUNCE_MS = 200; | ||
|
|
||
| function SplitPane(props: SplitPaneProps) { | ||
| const [innerSizes, setInnerSizes] = React.useState<number[]>(); | ||
| const { | ||
| collapsedSizes, | ||
| triggerCollapse, | ||
| triggerExpand, | ||
| defaultSizes: defaultSizesProp, | ||
| initialSizes, | ||
| } = props; | ||
| const [savedSizesString, setSavedSizesString] = useSetting<string | undefined>( | ||
| props.defaultSizePaneKey, | ||
| ); | ||
|
|
||
| const saveSizesStringDebounced = React.useMemo(() => { | ||
| return debounce((value: string) => setSavedSizesString(value), SAVE_DEBOUNCE_MS); | ||
| }, [setSavedSizesString]); | ||
|
|
||
| React.useEffect(() => { | ||
| return () => { | ||
| saveSizesStringDebounced.cancel(); | ||
| }; | ||
| }, [saveSizesStringDebounced]); | ||
|
|
||
| const getDefaultSizePane = () => { | ||
| const {defaultSizePaneKey, defaultSizes = sizesDefaultInner, initialSizes} = props; | ||
| const defaultSizePane = React.useMemo(() => { | ||
| if (initialSizes) { | ||
| return initialSizes; | ||
| } | ||
| const sizes = localStorage.getItem(defaultSizePaneKey)?.split(',').map(Number); | ||
| return sizes || defaultSizes; | ||
| }; | ||
| const setDefaultSizePane = (sizes: number[]) => { | ||
| const {defaultSizePaneKey} = props; | ||
| localStorage.setItem(defaultSizePaneKey, sizes.join(',')); | ||
| }; | ||
| const sizes = savedSizesString?.split(',').map(Number); | ||
| return sizes || defaultSizesProp || sizesDefaultInner; | ||
| }, [defaultSizesProp, initialSizes, savedSizesString]); | ||
|
Comment on lines
+55
to
+61
|
||
| const setDefaultSizePane = React.useCallback( | ||
| (sizes: number[]) => { | ||
| saveSizesStringDebounced(sizes.join(',')); | ||
| }, | ||
| [saveSizesStringDebounced], | ||
| ); | ||
| const onDragHandler = (sizes: number[]) => { | ||
| const {onSplitDragAdditional} = props; | ||
| if (onSplitDragAdditional) { | ||
|
|
@@ -58,27 +82,25 @@ function SplitPane(props: SplitPaneProps) { | |
| }; | ||
|
|
||
| React.useEffect(() => { | ||
| const {collapsedSizes, triggerCollapse} = props; | ||
| if (triggerCollapse) { | ||
| const newSizes = collapsedSizes || minSizeDefaultInner; | ||
| setDefaultSizePane(newSizes); | ||
| setInnerSizes(newSizes); | ||
| } | ||
| }, [props.triggerCollapse]); | ||
| }, [collapsedSizes, triggerCollapse, setDefaultSizePane]); | ||
|
|
||
| React.useEffect(() => { | ||
| const {triggerExpand, defaultSizes} = props; | ||
| const newSizes = defaultSizes || sizesDefaultInner; | ||
| const newSizes = defaultSizesProp || sizesDefaultInner; | ||
| if (triggerExpand) { | ||
| setDefaultSizePane(newSizes); | ||
| setInnerSizes(newSizes); | ||
| } | ||
| }, [props.triggerExpand]); | ||
| }, [defaultSizesProp, triggerExpand, setDefaultSizePane]); | ||
| return ( | ||
| <React.Fragment> | ||
| <SplitPaneLib | ||
| direction={props.direction || 'horizontal'} | ||
| sizes={innerSizes || getDefaultSizePane()} | ||
| sizes={innerSizes || defaultSizePane} | ||
| minSize={props.minSize || [0, 0]} | ||
| onDrag={onDragHandler} | ||
| className={b(null, props.direction || 'horizontal')} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import React from 'react'; | ||
|
|
||
| import {skipToken} from '@reduxjs/toolkit/query'; | ||
|
|
||
| import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper'; | ||
| import {selectMetaUser} from '../../store/reducers/authentication/authentication'; | ||
| import {settingsApi} from '../../store/reducers/settings/api'; | ||
| import {DEFAULT_USER_SETTINGS} from '../../store/reducers/settings/constants'; | ||
| import {uiFactory} from '../../uiFactory/uiFactory'; | ||
| import { | ||
| DEFAULT_CLUSTER_TAB_KEY, | ||
| DEFAULT_IS_QUERY_RESULT_COLLAPSED, | ||
| DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED, | ||
| DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, | ||
| DEFAULT_SIZE_RESULT_PANE_KEY, | ||
| DEFAULT_SIZE_TENANT_KEY, | ||
| DEFAULT_SIZE_TENANT_SUMMARY_KEY, | ||
| } from '../../utils/constants'; | ||
| import {useTypedSelector} from '../../utils/hooks/useTypedSelector'; | ||
|
|
||
| const DRAWER_WIDTH_KEY = 'drawer-width'; | ||
|
|
||
| /** | ||
| * Preloaded keys for "UI layout" state (pane sizes, collapsed flags, etc.) that are stored in the same | ||
| * settings backend but are not part of `DEFAULT_USER_SETTINGS`. | ||
| * | ||
| * If a new UI layout key should be preloaded to prevent UI "pop-in" in remote-settings mode, add it here. | ||
| */ | ||
| const PRELOADED_UI_LAYOUT_KEYS = [ | ||
| DEFAULT_CLUSTER_TAB_KEY, | ||
| DEFAULT_SIZE_RESULT_PANE_KEY, | ||
| DEFAULT_SIZE_TENANT_SUMMARY_KEY, | ||
| DEFAULT_SIZE_TENANT_KEY, | ||
| DEFAULT_IS_TENANT_SUMMARY_COLLAPSED, | ||
| DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED, | ||
| DEFAULT_IS_QUERY_RESULT_COLLAPSED, | ||
| DRAWER_WIDTH_KEY, | ||
| ] as const; | ||
|
|
||
| interface SettingsBootstrapProps { | ||
| children: React.ReactNode; | ||
| } | ||
|
|
||
| export function SettingsBootstrap({children}: SettingsBootstrapProps) { | ||
| const fallbackUser = useTypedSelector(selectMetaUser); | ||
| const userFromFactory = uiFactory.settingsBackend?.getUserId?.(); | ||
| const endpointFromFactory = uiFactory.settingsBackend?.getEndpoint?.(); | ||
| const remoteAvailable = Boolean( | ||
| (endpointFromFactory && userFromFactory && window.api?.settingsService) || | ||
| window.api?.metaSettings, | ||
| ); | ||
|
|
||
| const user = userFromFactory ?? fallbackUser; | ||
|
|
||
| const settingsKeysToPreload = React.useMemo(() => { | ||
| const keys = new Set<string>(Object.keys(DEFAULT_USER_SETTINGS)); | ||
|
|
||
| PRELOADED_UI_LAYOUT_KEYS.forEach((key) => keys.add(key)); | ||
|
|
||
| return Array.from(keys); | ||
| }, []); | ||
astandrik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const params = React.useMemo(() => { | ||
| if (user && remoteAvailable) { | ||
| return {user, name: settingsKeysToPreload}; | ||
| } | ||
| return skipToken; | ||
| }, [remoteAvailable, user, settingsKeysToPreload]); | ||
|
|
||
| const {isLoading} = settingsApi.useGetSettingsQuery(params); | ||
|
|
||
| if (!remoteAvailable) { | ||
| return children; | ||
| } | ||
|
|
||
| return ( | ||
| <LoaderWrapper loading={isLoading} size="l" delay={0}> | ||
| {children} | ||
| </LoaderWrapper> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.