From 1501d14ed21cc3eef187433d2ade7e4deae6a22d Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Mon, 1 May 2023 17:36:33 -0400 Subject: [PATCH 01/20] lazy load component logs --- .../component/changelog/changelog.section.tsx | 4 +- .../changelog/changelog.ui.runtime.tsx | 12 +- .../changelog/ui/change-log-page.tsx | 58 ++++++-- scopes/component/component/ui/index.ts | 1 + .../component/ui/use-component-query.ts | 127 ++++++++++++++++-- .../component/component/ui/use-component.tsx | 4 + .../ui/version-block/version-block.tsx | 17 ++- 7 files changed, 188 insertions(+), 35 deletions(-) diff --git a/scopes/component/changelog/changelog.section.tsx b/scopes/component/changelog/changelog.section.tsx index 8ee4fad47aee..966e935a86dc 100644 --- a/scopes/component/changelog/changelog.section.tsx +++ b/scopes/component/changelog/changelog.section.tsx @@ -4,9 +4,11 @@ import { MenuWidgetIcon } from '@teambit/ui-foundation.ui.menu-widget-icon'; import { ChangeLogPage } from './ui/change-log-page'; export class ChangelogSection implements Section { + constructor(private host: string) {} + route = { path: '~changelog', - element: , + element: , }; navigationLink = { href: '~changelog', diff --git a/scopes/component/changelog/changelog.ui.runtime.tsx b/scopes/component/changelog/changelog.ui.runtime.tsx index 548b2e69c91d..2b1b49ab30a2 100644 --- a/scopes/component/changelog/changelog.ui.runtime.tsx +++ b/scopes/component/changelog/changelog.ui.runtime.tsx @@ -1,5 +1,6 @@ import { ComponentAspect, ComponentUI } from '@teambit/component'; import { UIRuntime } from '@teambit/ui'; +import { Harmony } from '@teambit/harmony'; import React from 'react'; import { ChangelogAspect } from './changelog.aspect'; @@ -7,17 +8,20 @@ import { ChangelogSection } from './changelog.section'; import { ChangeLogPage } from './ui/change-log-page'; export class ChangeLogUI { + constructor(private host: string) {} ChangeLog = () => { - return ; + return ; }; static dependencies = [ComponentAspect]; static runtime = UIRuntime; - static async provider([component]: [ComponentUI]) { - const ui = new ChangeLogUI(); - const section = new ChangelogSection(); + static async provider([component]: [ComponentUI], _, __, harmony: Harmony) { + const { config } = harmony; + const host = String(config.get('teambit.harmony/bit')); + const ui = new ChangeLogUI(host); + const section = new ChangelogSection(host); component.registerRoute(section.route); component.registerWidget(section.navigationLink, section.order); diff --git a/scopes/component/changelog/ui/change-log-page.tsx b/scopes/component/changelog/ui/change-log-page.tsx index 9a42b02fa742..daa481badfa3 100644 --- a/scopes/component/changelog/ui/change-log-page.tsx +++ b/scopes/component/changelog/ui/change-log-page.tsx @@ -1,4 +1,5 @@ -import { ComponentContext } from '@teambit/component'; +import React, { HTMLAttributes } from 'react'; +import { ComponentContext, useComponent } from '@teambit/component'; import { H1 } from '@teambit/documenter.ui.heading'; import { Separator } from '@teambit/design.ui.separator'; import { VersionBlock } from '@teambit/component.ui.version-block'; @@ -6,19 +7,51 @@ import classNames from 'classnames'; import { MDXLayout } from '@teambit/mdx.ui.mdx-layout'; import { ExportingComponents } from '@teambit/component.instructions.exporting-components'; import { AlertCard } from '@teambit/design.ui.alert-card'; -import React, { HTMLAttributes, useContext } from 'react'; import styles from './change-log-page.module.scss'; -type ChangeLogPageProps = {} & HTMLAttributes; +type ChangeLogPageProps = { + host: string; +} & HTMLAttributes; -export function ChangeLogPage({ className }: ChangeLogPageProps) { - const component = useContext(ComponentContext); - const { logs } = component; +export function ChangeLogPage({ className, host }: ChangeLogPageProps) { + const componentContext = React.useContext(ComponentContext); + const { + componentLogs: logs = [], + component, + loading, + loadMoreLogs, + hasMoreLogs: hasMore, + } = useComponent(host, componentContext?.id.toString(), { + logFilters: { + log: { + logLimit: 15, + }, + }, + }); - if (!logs) return null; + const observer = React.useRef(); + const handleLoadMore = () => { + loadMoreLogs?.(); + }; - if (logs.length === 0) { + const lastLogRef = React.useCallback( + (node) => { + if (loading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && hasMore) { + handleLoadMore(); + } + }); + if (node) observer.current.observe(node); + }, + [loading, hasMore] + ); + + if (loading) return null; + + if (logs?.length === 0) { return (

History

@@ -42,16 +75,17 @@ export function ChangeLogPage({ className }: ChangeLogPageProps) {

History

- {logs.map((snap, index) => { - const isLatest = component.latest === snap.tag || component.latest === snap.hash; - const isCurrent = component.version === snap.tag || component.version === snap.hash; + {(logs || []).map((snap, index) => { + const isLatest = component?.latest === snap.tag || component?.latest === snap.hash; + const isCurrent = component?.version === snap.tag || component?.version === snap.hash; return ( ); })} diff --git a/scopes/component/component/ui/index.ts b/scopes/component/component/ui/index.ts index bcaf819bbbcc..5c8be5db4d79 100644 --- a/scopes/component/component/ui/index.ts +++ b/scopes/component/component/ui/index.ts @@ -6,4 +6,5 @@ export { ComponentContext, ComponentProvider } from './context'; export { useComponent } from './use-component'; export { TopBarNav } from './top-bar-nav'; export { componentIdFields, componentOverviewFields, componentFields } from './use-component-query'; +export type { ComponentQueryResult } from './use-component-query'; export { useIdFromLocation } from './use-component-from-location'; diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index 41fe7ed7e342..6d2b73f70cf2 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -1,8 +1,9 @@ -import { useMemo, useEffect, useRef } from 'react'; +import React, { useMemo, useEffect, useRef } from 'react'; import { gql } from '@apollo/client'; import { useDataQuery } from '@teambit/ui-foundation.ui.hooks.use-data-query'; import { ComponentID, ComponentIdObj } from '@teambit/component-id'; import { ComponentDescriptor } from '@teambit/component-descriptor'; +import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { ComponentModel } from './component-model'; import { ComponentError } from './component-error'; @@ -151,16 +152,96 @@ const SUB_COMPONENT_REMOVED = gql` export type Filters = { log?: { logType?: string; logOffset?: number; logLimit?: number; logHead?: string; logSort?: string }; }; + +export type ComponentQueryResult = { + component?: ComponentModel; + error?: ComponentError; + componentDescriptor?: ComponentDescriptor; + loading?: boolean; + loadMoreLogs?: () => void; + hasMoreLogs?: boolean; + componentLogs?: LegacyComponentLog[]; +}; + /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ -export function useComponentQuery(componentId: string, host: string, filters?: Filters, skip?: boolean) { +export function useComponentQuery( + componentId: string, + host: string, + filtersFromProps?: Filters, + skip?: boolean +): ComponentQueryResult { const idRef = useRef(componentId); idRef.current = componentId; - const { data, error, loading, subscribeToMore, ...rest } = useDataQuery(GET_COMPONENT, { - variables: { id: componentId, extensionId: host, ...(filters?.log || {}) }, + const filters = useMemo(() => filtersFromProps?.log || {}, [filtersFromProps]); + const { logOffset, logLimit } = filters; + + const logsRef = React.useRef([]); + const { data, error, loading, subscribeToMore, fetchMore, ...rest } = useDataQuery(GET_COMPONENT, { + variables: { id: componentId, extensionId: host, ...filters }, skip, errorPolicy: 'all', }); + const rawComponent = data?.getHost?.get; + + logsRef.current = useMemo(() => { + if (!logOffset) return rawComponent?.logs; + + if (logsRef.current.length !== (logOffset ?? 0) + (logLimit ?? 0)) { + const allLogs = logsRef.current || []; + const newLogs = rawComponent?.logs || []; + newLogs.forEach((log) => allLogs.push(log)); + return allLogs; + } + if (rawComponent?.logs?.length > 0 && logsRef.current?.length === 0) { + return rawComponent?.logs; + } + return logsRef.current; + }, [rawComponent?.logs]); + + const hasMoreLogs = React.useRef(undefined); + + hasMoreLogs.current = useMemo(() => { + if (!logLimit) return false; + if (rawComponent === undefined) return undefined; + if (hasMoreLogs.current === undefined) return rawComponent?.logs.length === logLimit; + return hasMoreLogs.current; + }, [rawComponent?.logs]); + + const loadMoreLogs = React.useCallback(async () => { + if (logLimit) { + await fetchMore({ + variables: { + logOffset: logsRef.current.length, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const fetchedComponent = fetchMoreResult.getHost.get; + + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedLogs = [...prevComponent.logs, ...fetchedComponent.logs]; + hasMoreLogs.current = fetchedComponent.logs.length === logLimit; + return { + ...prev, + hasMoreLogs: false, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + logs: updatedLogs, + }, + }, + }; + } + + return prev; + }, + }); + } + }, [logLimit, fetchMore]); + useEffect(() => { // @TODO @Kutner fix subscription for scope if (host !== 'teambit.workspace/workspace') { @@ -242,11 +323,28 @@ export function useComponentQuery(componentId: string, host: string, filters?: F unsubChanges(); unsubAddition(); unsubRemoval(); + logsRef.current = []; }; }, []); - const rawComponent = data?.getHost?.get; - return useMemo(() => { + const idDepKey = rawComponent?.id + ? `${rawComponent?.id?.scope}/${rawComponent?.id?.name}@${rawComponent?.id?.version}}` + : undefined; + + const id: ComponentID | undefined = useMemo( + () => (rawComponent ? ComponentID.fromObject(rawComponent.id) : undefined), + [idDepKey] + ); + + const componentError = + error && !data ? new ComponentError(500, error.message) : !rawComponent && !loading && new ComponentError(404); + + const component = useMemo( + () => (rawComponent ? ComponentModel.from({ ...rawComponent, host }) : undefined), + [id?.toString(), logsRef.current] + ); + + const componentDescriptor = useMemo(() => { const aspectList = { entries: rawComponent?.aspects.map((aspectObject) => { return { @@ -256,15 +354,20 @@ export function useComponentQuery(componentId: string, host: string, filters?: F }; }), }; - const id = rawComponent && ComponentID.fromObject(rawComponent.id); - const componentError = - error && !data ? new ComponentError(500, error.message) : !rawComponent && !loading && new ComponentError(404); + + return id ? ComponentDescriptor.fromObject({ id: id.toString(), aspectList }) : undefined; + }, [id?.toString()]); + + return useMemo(() => { return { - componentDescriptor: id ? ComponentDescriptor.fromObject({ id: id.toString(), aspectList }) : undefined, - component: rawComponent ? ComponentModel.from({ ...rawComponent, host }) : undefined, + componentDescriptor, + component, error: componentError || undefined, loading, + loadMoreLogs, + hasMoreLogs: hasMoreLogs.current, + componentLogs: logsRef.current, ...rest, }; - }, [rawComponent, host, error]); + }, [host, component, componentDescriptor, componentError, hasMoreLogs]); } diff --git a/scopes/component/component/ui/use-component.tsx b/scopes/component/component/ui/use-component.tsx index 9db3b6596a1f..73e9ffa077e8 100644 --- a/scopes/component/component/ui/use-component.tsx +++ b/scopes/component/component/ui/use-component.tsx @@ -1,5 +1,6 @@ import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; import { ComponentDescriptor } from '@teambit/component-descriptor'; +import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { ComponentModel } from './component-model'; import { ComponentError } from './component-error'; import { Filters, useComponentQuery } from './use-component-query'; @@ -9,6 +10,9 @@ export type Component = { error?: ComponentError; componentDescriptor?: ComponentDescriptor; loading?: boolean; + loadMoreLogs?: () => void; + hasMoreLogs?: boolean; + componentLogs?: LegacyComponentLog[]; }; export type UseComponentOptions = { version?: string; diff --git a/scopes/component/ui/version-block/version-block.tsx b/scopes/component/ui/version-block/version-block.tsx index 90572439b554..a99bf437cb6d 100644 --- a/scopes/component/ui/version-block/version-block.tsx +++ b/scopes/component/ui/version-block/version-block.tsx @@ -17,11 +17,11 @@ export type VersionBlockProps = { snap: LegacyComponentLog; isCurrent: boolean; } & HTMLAttributes; -/** - * change log section - * @name VersionBlock - */ -export function VersionBlock({ isLatest, className, snap, componentId, isCurrent, ...rest }: VersionBlockProps) { + +function _VersionBlock( + { isLatest, className, snap, componentId, isCurrent, ...rest }: VersionBlockProps, + ref: React.ForwardedRef +) { const { username, email, message, tag, hash, date } = snap; const { lanesModel } = useLanes(); const currentLaneUrl = lanesModel?.viewedLane @@ -37,7 +37,7 @@ export function VersionBlock({ isLatest, className, snap, componentId, isCurrent const timestamp = useMemo(() => (date ? new Date(parseInt(date)).toString() : new Date().toString()), [date]); return ( -
+
@@ -62,6 +62,11 @@ export function VersionBlock({ isLatest, className, snap, componentId, isCurrent
); } +/** + * change log section + * @name VersionBlock + */ +export const VersionBlock = React.memo(React.forwardRef(_VersionBlock)); function commitMessage(message: string) { if (!message || message === '') return
No commit message
; From c1aff40eba2c10ff7c7da6eef1bb01b3acb16e83 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 2 May 2023 10:02:46 -0400 Subject: [PATCH 02/20] simplify lazily fetching component logs --- .../changelog/ui/change-log-page.tsx | 3 +- .../component/ui/use-component-query.ts | 37 +++++++------------ .../component/component/ui/use-component.tsx | 2 - 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/scopes/component/changelog/ui/change-log-page.tsx b/scopes/component/changelog/ui/change-log-page.tsx index daa481badfa3..0563fc356d62 100644 --- a/scopes/component/changelog/ui/change-log-page.tsx +++ b/scopes/component/changelog/ui/change-log-page.tsx @@ -17,7 +17,6 @@ type ChangeLogPageProps = { export function ChangeLogPage({ className, host }: ChangeLogPageProps) { const componentContext = React.useContext(ComponentContext); const { - componentLogs: logs = [], component, loading, loadMoreLogs, @@ -30,6 +29,8 @@ export function ChangeLogPage({ className, host }: ChangeLogPageProps) { }, }); + const logs = component?.logs ?? []; + const observer = React.useRef(); const handleLoadMore = () => { loadMoreLogs?.(); diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index ee45c01acbc8..f7c21d0e27a2 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -3,8 +3,6 @@ import { gql } from '@apollo/client'; import { useDataQuery } from '@teambit/ui-foundation.ui.hooks.use-data-query'; import { ComponentID, ComponentIdObj } from '@teambit/component-id'; import { ComponentDescriptor } from '@teambit/component-descriptor'; -import { LegacyComponentLog } from '@teambit/legacy-component-log'; - import { ComponentModel } from './component-model'; import { ComponentError } from './component-error'; @@ -162,7 +160,6 @@ export type ComponentQueryResult = { loading?: boolean; loadMoreLogs?: () => void; hasMoreLogs?: boolean; - componentLogs?: LegacyComponentLog[]; }; /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ @@ -175,9 +172,8 @@ export function useComponentQuery( const idRef = useRef(componentId); idRef.current = componentId; const filters = useMemo(() => filtersFromProps?.log || {}, [filtersFromProps]); - const { logOffset, logLimit } = filters; - - const logsRef = React.useRef([]); + const { logLimit } = filters; + const offsetRef = useRef(filters.logOffset); const { data, error, loading, subscribeToMore, fetchMore, ...rest } = useDataQuery(GET_COMPONENT, { variables: { id: componentId, extensionId: host, ...filters }, skip, @@ -185,21 +181,16 @@ export function useComponentQuery( }); const rawComponent = data?.getHost?.get; + const rawCompLogs = rawComponent?.logs ?? []; - logsRef.current = useMemo(() => { - if (!logOffset) return rawComponent?.logs; - - if (logsRef.current.length !== (logOffset ?? 0) + (logLimit ?? 0)) { - const allLogs = logsRef.current || []; - const newLogs = rawComponent?.logs || []; - newLogs.forEach((log) => allLogs.push(log)); - return allLogs; + offsetRef.current = useMemo(() => { + const currentOffset = offsetRef.current; + if (!currentOffset) return rawCompLogs.length; + if (currentOffset < rawCompLogs?.length) { + return rawCompLogs?.length; } - if (rawComponent?.logs?.length > 0 && logsRef.current?.length === 0) { - return rawComponent?.logs; - } - return logsRef.current; - }, [rawComponent?.logs]); + return offsetRef.current; + }, [rawCompLogs]); const hasMoreLogs = React.useRef(undefined); @@ -214,7 +205,7 @@ export function useComponentQuery( if (logLimit) { await fetchMore({ variables: { - logOffset: logsRef.current.length, + logOffset: offsetRef.current, }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; @@ -225,6 +216,7 @@ export function useComponentQuery( if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { const updatedLogs = [...prevComponent.logs, ...fetchedComponent.logs]; hasMoreLogs.current = fetchedComponent.logs.length === logLimit; + return { ...prev, hasMoreLogs: false, @@ -325,7 +317,7 @@ export function useComponentQuery( unsubChanges(); unsubAddition(); unsubRemoval(); - logsRef.current = []; + offsetRef.current = undefined; }; }, []); @@ -343,7 +335,7 @@ export function useComponentQuery( const component = useMemo( () => (rawComponent ? ComponentModel.from({ ...rawComponent, host }) : undefined), - [id?.toString(), logsRef.current] + [id?.toString(), rawComponent?.logs.length] ); const componentDescriptor = useMemo(() => { @@ -368,7 +360,6 @@ export function useComponentQuery( loading, loadMoreLogs, hasMoreLogs: hasMoreLogs.current, - componentLogs: logsRef.current, ...rest, }; }, [host, component, componentDescriptor, componentError, hasMoreLogs]); diff --git a/scopes/component/component/ui/use-component.tsx b/scopes/component/component/ui/use-component.tsx index 73e9ffa077e8..5166cb816776 100644 --- a/scopes/component/component/ui/use-component.tsx +++ b/scopes/component/component/ui/use-component.tsx @@ -1,6 +1,5 @@ import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; import { ComponentDescriptor } from '@teambit/component-descriptor'; -import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { ComponentModel } from './component-model'; import { ComponentError } from './component-error'; import { Filters, useComponentQuery } from './use-component-query'; @@ -12,7 +11,6 @@ export type Component = { loading?: boolean; loadMoreLogs?: () => void; hasMoreLogs?: boolean; - componentLogs?: LegacyComponentLog[]; }; export type UseComponentOptions = { version?: string; From 28d6c8ff79e0e918478a43d092c739d89695b6b2 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 2 May 2023 17:57:53 -0400 Subject: [PATCH 03/20] lazy load version in version dropdown --- .../code/ui/code-tab-page/code-tab-page.tsx | 4 +- scopes/component/component/ui/component.tsx | 2 +- scopes/component/component/ui/menu/menu.tsx | 153 ++++++++-- .../ui/version-dropdown/version-dropdown.tsx | 263 +++++++++--------- .../version-info/version-info.tsx | 31 ++- 5 files changed, 287 insertions(+), 166 deletions(-) diff --git a/scopes/code/ui/code-tab-page/code-tab-page.tsx b/scopes/code/ui/code-tab-page/code-tab-page.tsx index 0d698d0b02b7..499e63e3eccb 100644 --- a/scopes/code/ui/code-tab-page/code-tab-page.tsx +++ b/scopes/code/ui/code-tab-page/code-tab-page.tsx @@ -32,9 +32,10 @@ import styles from './code-tab-page.module.scss'; export type CodePageProps = { fileIconSlot?: FileIconSlot; host: string; + codeViewClassName?: string; } & HTMLAttributes; -export function CodePage({ className, fileIconSlot, host }: CodePageProps) { +export function CodePage({ className, fileIconSlot, host, codeViewClassName }: CodePageProps) { const urlParams = useCodeParams(); const component = useContext(ComponentContext); const { mainFile, fileTree = [], dependencies, devFiles } = useCode(component.id); @@ -67,6 +68,7 @@ export function CodePage({ className, fileIconSlot, host }: CodePageProps) { groupBy(flatten(menuItemSlot.values()), 'category'), [menuItemSlot]); - if (!component) return ; + if (loadingTags || loadingSnaps) return ; + if (!componentSnaps || !componentTags) { + // eslint-disable-next-line no-console + console.error(`failed loading component tags/snaps for ${idFromLocation}`); + return null; + } const RightSide = (
{RightNode || ( <> - + )} @@ -123,42 +177,46 @@ export function ComponentMenu({ } export function VersionRelatedDropdowns({ - component, + componentTags, + componentSnaps, consumeMethods, + loadMoreSnaps, + loadMoreTags, + hasMoreSnaps, + hasMoreTags, + loadingSnaps, + loadingTags, className, host, }: { - component: ComponentModel; + componentTags: ComponentModel; + componentSnaps: ComponentModel; + loadMoreTags?: () => void; + loadMoreSnaps?: () => void; + loadingSnaps?: boolean; + loadingTags?: boolean; + hasMoreTags?: boolean; + hasMoreSnaps?: boolean; consumeMethods?: ConsumeMethodSlot; className?: string; host: string; }) { const location = useLocation(); + const loading = loadingSnaps || loadingTags; const { lanesModel } = useLanes(); + const component = componentTags || componentSnaps; const viewedLane = lanesModel?.viewedLane?.id && !lanesModel?.viewedLane?.id.isDefault() ? lanesModel.viewedLane : undefined; - - const { logs } = component; + // const { logs } = component; const isWorkspace = host === 'teambit.workspace/workspace'; const snaps = useMemo(() => { - return (logs || []).filter((log) => !log.tag).map((snap) => ({ ...snap, version: snap.hash })); - }, [logs]); + return (componentSnaps.logs || []).filter((log) => !log.tag).map((snap) => ({ ...snap, version: snap.hash })); + }, [componentSnaps.logs]); const tags = useMemo(() => { - const tagLookup = new Map(); - (logs || []) - .filter((log) => log.tag) - .forEach((tag) => { - tagLookup.set(tag?.tag as string, tag); - }); - return compact( - component.tags - ?.toArray() - .reverse() - .map((tag) => tagLookup.get(tag.version.version)) - ).map((tag) => ({ ...tag, version: tag.tag as string })); - }, [logs]); + return (componentTags.logs || []).map((tag) => ({ ...tag, version: tag.tag as string })); + }, [componentTags.logs]); const isNew = snaps.length === 0 && tags.length === 0; @@ -167,8 +225,52 @@ export function VersionRelatedDropdowns({ const currentVersion = isWorkspace && !isNew && !location?.search.includes('version') ? 'workspace' : component.version; + const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; + const tabs = VERSION_TAB_NAMES.map((name) => { + switch (name) { + case 'SNAP': + return { name, payload: snaps || [] }; + case 'LANE': + return { name, payload: lanes || [] }; + default: + return { name, payload: tags || [] }; + } + }).filter((tab) => tab.payload.length > 0); const methods = useConsumeMethods(component, consumeMethods, viewedLane); + + const getActiveTabIndex = () => { + if (viewedLane?.components.some((c) => c.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'LANE'); + if ((snaps || []).some((snap) => snap.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'SNAP'); + return 0; + }; + + const [activeTabIndex, setActiveTab] = React.useState(getActiveTabIndex()); + const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' = tabs[activeTabIndex]?.name || tabs[0].name; + const hasMore = activeTabOrSnap === 'SNAP' ? hasMoreSnaps : hasMoreTags; + const observer = React.useRef(); + + const handleLoadMore = React.useCallback(() => { + if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); + if (activeTabOrSnap === 'TAG') loadMoreTags?.(); + }, [activeTabIndex, tabs.length]); + + const lastLogRef = React.useCallback( + (node) => { + if (loading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && hasMore) { + handleLoadMore(); + } + }); + if (node) observer.current.observe(node); + }, + [loading, hasMoreSnaps, hasMoreTags, activeTabIndex] + ); + return ( <> {consumeMethods && tags.length > 0 && ( @@ -179,9 +281,12 @@ export function VersionRelatedDropdowns({ /> )} & { version: string }; +const VersionMenu = React.memo(React.forwardRef(_VersionMenu)); + +function _VersionMenu( + { + currentVersion, + localVersion, + latestVersion, + currentLane, + overrideVersionHref, + showVersionDetails, + activeTabIndex, + setActiveTab, + tabs, + ...rest + }: VersionMenuProps, + ref?: React.ForwardedRef +) { + const multipleTabs = tabs.length > 1; + const message = multipleTabs + ? 'Switch to view tags, snaps, or lanes' + : `Switch between ${tabs[0].name.toLocaleLowerCase()}s`; + + return ( +
+
+
{message}
+ {localVersion && ( + +
+ + {LOCAL_VERSION} +
+
+ )} +
+
+ {multipleTabs && + tabs.map(({ name }, index) => { + return ( + setActiveTab(index)} + > + {name} + + ); + })} +
+
+ {tabs[activeTabIndex].name === 'LANE' && + tabs[activeTabIndex].payload.map((payload) => ( + + ))} + {tabs[activeTabIndex].name !== 'LANE' && + tabs[activeTabIndex].payload.map((payload, index) => ( + + ))} +
+
+ ); +} + export type VersionDropdownProps = { tags: DropdownComponentVersion[]; snaps?: DropdownComponentVersion[]; - lanes?: LaneModel[]; localVersion?: boolean; currentVersion: string; currentLane?: LaneModel; @@ -33,35 +108,61 @@ export type VersionDropdownProps = { showVersionDetails?: boolean; disabled?: boolean; placeholderComponent?: ReactNode; + activeTabIndex?: number; + setActiveTabIndex: (index: number) => void; + tabs: Array<{ name: string; payload: Array }>; } & React.HTMLAttributes; -export function VersionDropdown({ - snaps, - tags, - lanes, - currentVersion, - latestVersion, - localVersion, - loading, - currentLane, - overrideVersionHref, - className, - placeholderClassName, - dropdownClassName, - menuClassName, - showVersionDetails, - disabled, - placeholderComponent = ( - - ), - ...rest -}: VersionDropdownProps) { +/** + * + * @param param0 + const getActiveTabIndex = () => { + if (currentLane?.components.some((c) => c.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'LANE'); + if ((snaps || []).some((snap) => snap.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'SNAP'); + return 0; + }; + + const [activeTabIndex, setActiveTab] = useState(getActiveTabIndex()); + + * @returns + */ + +export const VersionDropdown = React.memo(React.forwardRef(_VersionDropdown)); + +function _VersionDropdown( + { + snaps, + tags, + currentVersion, + latestVersion, + localVersion, + loading, + currentLane, + overrideVersionHref, + className, + placeholderClassName, + dropdownClassName, + menuClassName, + showVersionDetails, + disabled, + activeTabIndex = 0, + setActiveTabIndex, + tabs, + placeholderComponent = ( + + ), + ...rest + }: VersionDropdownProps, + ref?: React.ForwardedRef +) { const [key, setKey] = useState(0); const singleVersion = (snaps || []).concat(tags).length < 2 && !localVersion; @@ -88,11 +189,12 @@ export function VersionDropdown({ {loading && } {loading || ( string; showVersionDetails?: boolean; + activeTabIndex: number; + setActiveTab: (index: number) => void; + tabs: Array<{ name: string; payload: Array }>; } & React.HTMLAttributes; - -const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; - -function VersionMenu({ - tags, - snaps, - lanes, - currentVersion, - localVersion, - latestVersion, - currentLane, - overrideVersionHref, - showVersionDetails, - ...rest -}: VersionMenuProps) { - const tabs = VERSION_TAB_NAMES.map((name) => { - switch (name) { - case 'SNAP': - return { name, payload: snaps || [] }; - case 'LANE': - return { name, payload: lanes || [] }; - default: - return { name, payload: tags || [] }; - } - }).filter((tab) => tab.payload.length > 0); - - const getActiveTabIndex = () => { - if (currentLane?.components.some((c) => c.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'LANE'); - if ((snaps || []).some((snap) => snap.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'SNAP'); - return 0; - }; - - const [activeTabIndex, setActiveTab] = useState(getActiveTabIndex()); - - const multipleTabs = tabs.length > 1; - const message = multipleTabs - ? 'Switch to view tags, snaps, or lanes' - : `Switch between ${tabs[0].name.toLocaleLowerCase()}s`; - - return ( -
-
-
{message}
- {localVersion && ( - -
- - {LOCAL_VERSION} -
-
- )} -
-
- {multipleTabs && - tabs.map(({ name }, index) => { - return ( - setActiveTab(index)} - > - {name} - - ); - })} -
-
- {tabs[activeTabIndex].name === 'LANE' && - tabs[activeTabIndex].payload.map((payload) => ( - - ))} - {tabs[activeTabIndex].name !== 'LANE' && - tabs[activeTabIndex].payload.map((payload) => ( - - ))} -
-
- ); -} diff --git a/scopes/component/ui/version-dropdown/version-info/version-info.tsx b/scopes/component/ui/version-dropdown/version-info/version-info.tsx index 85bff8c4a549..10f340bc39ad 100644 --- a/scopes/component/ui/version-dropdown/version-info/version-info.tsx +++ b/scopes/component/ui/version-dropdown/version-info/version-info.tsx @@ -16,18 +16,22 @@ export type VersionInfoProps = DropdownComponentVersion & { showDetails?: boolean; }; -export function VersionInfo({ - version, - currentVersion, - latestVersion, - date, - username, - email, - overrideVersionHref, - showDetails, - message, - tag, -}: VersionInfoProps) { +export const VersionInfo = React.memo(React.forwardRef(_VersionInfo)); +function _VersionInfo( + { + version, + currentVersion, + latestVersion, + date, + username, + email, + overrideVersionHref, + showDetails, + message, + tag, + }: VersionInfoProps, + ref?: React.ForwardedRef +) { const isCurrent = version === currentVersion; const author = useMemo(() => { return { @@ -38,6 +42,7 @@ export function VersionInfo({ const timestamp = useMemo(() => (date ? new Date(parseInt(date)).toString() : new Date().toString()), [date]); const currentVersionRef = useRef(null); + useEffect(() => { if (isCurrent) { currentVersionRef.current?.scrollIntoView({ block: 'nearest' }); @@ -47,7 +52,7 @@ export function VersionInfo({ const href = overrideVersionHref ? overrideVersionHref(version) : `?version=${version}`; return ( -
+
From 3ae0466ff799141067aec3fbafa58abdbd78095c Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 2 May 2023 18:10:45 -0400 Subject: [PATCH 04/20] import code view --- .bitmap | 6 ++ components/ui/code-view/code-view.module.scss | 44 +++++++++++ components/ui/code-view/code-view.tsx | 76 +++++++++++++++++++ components/ui/code-view/index.ts | 2 + .../ui/version-dropdown/version-dropdown.tsx | 16 ---- workspace.jsonc | 1 - 6 files changed, 128 insertions(+), 17 deletions(-) create mode 100644 components/ui/code-view/code-view.module.scss create mode 100644 components/ui/code-view/code-view.tsx create mode 100644 components/ui/code-view/index.ts diff --git a/.bitmap b/.bitmap index 50e850705531..633fb1b1214a 100644 --- a/.bitmap +++ b/.bitmap @@ -1329,6 +1329,12 @@ "mainFile": "index.ts", "rootDir": "scopes/code/ui/code-tab-tree" }, + "ui/code-view": { + "scope": "teambit.code", + "version": "5e2b49420c500d4d88e1b05aa232061abc0fa0a7", + "mainFile": "index.ts", + "rootDir": "components/ui/code-view" + }, "ui/compare/lane-compare": { "scope": "teambit.lanes", "version": "0.0.77", diff --git a/components/ui/code-view/code-view.module.scss b/components/ui/code-view/code-view.module.scss new file mode 100644 index 000000000000..990a5fe021a0 --- /dev/null +++ b/components/ui/code-view/code-view.module.scss @@ -0,0 +1,44 @@ +.codeView { + padding: 24px 40px; + width: 100%; + overflow: hidden; + box-sizing: border-box; + + .codeSnippetWrapper { + width: 100%; + max-width: 100%; + .codeSnippet { + display: block; + overflow: auto; + height: 100vh; + > code { + > code { + // this is to design the line numbers culumn + border-right: 1px solid #323232; + padding-right: 16px !important; + margin-right: 16px; + } + } + } + // TODO - fix this in code snippet component. it breaks when the component renders in different places + > span { + top: 13px !important; + } + } +} + +.img { + width: 20px; + margin-right: 10px; +} + +.fileName { + display: flex; + align-items: baseline; +} + +.emptyCodeView { + margin: auto; + text-align: center; + font-size: 24px; +} diff --git a/components/ui/code-view/code-view.tsx b/components/ui/code-view/code-view.tsx new file mode 100644 index 000000000000..26dd854865f5 --- /dev/null +++ b/components/ui/code-view/code-view.tsx @@ -0,0 +1,76 @@ +import { H1 } from '@teambit/documenter.ui.heading'; +import classNames from 'classnames'; +import React, { HTMLAttributes, useMemo } from 'react'; +import { CodeSnippet } from '@teambit/documenter.ui.code-snippet'; +import { useFileContent } from '@teambit/code.ui.queries.get-file-content'; +import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/prism-light'; +import markDownSyntax from 'react-syntax-highlighter/dist/esm/languages/prism/markdown'; +import { staticStorageUrl } from '@teambit/base-ui.constants.storage'; +import { ComponentID } from '@teambit/component'; +import styles from './code-view.module.scss'; + +export type CodeViewProps = { + componentId: ComponentID; + currentFile?: string; + currentFileContent?: string; + icon?: string; + loading?: boolean; + codeSnippetClassName?: string; +} & HTMLAttributes; + +SyntaxHighlighter.registerLanguage('md', markDownSyntax); + +export function CodeView({ + className, + componentId, + currentFile, + icon, + currentFileContent, + codeSnippetClassName, + loading: loadingFromProps, +}: CodeViewProps) { + const { fileContent: downloadedFileContent, loading: loadingFileContent } = useFileContent( + componentId, + currentFile, + !!currentFileContent + ); + const loading = loadingFromProps || loadingFileContent; + const fileContent = currentFileContent || downloadedFileContent; + const title = useMemo(() => currentFile?.split('/').pop(), [currentFile]); + const lang = useMemo(() => { + const langFromFileEnding = currentFile?.split('.').pop(); + + // for some reason, SyntaxHighlighter doesnt support scss or sass highlighting, only css. I need to check how to fix this properly + if (langFromFileEnding === 'scss' || langFromFileEnding === 'sass') return 'css'; + if (langFromFileEnding === 'mdx') return 'md'; + return langFromFileEnding; + }, [fileContent]); + + if (!fileContent && !loading && currentFile) return ; + + return ( +
+

+ {currentFile && } + {title} +

+ + {fileContent || ''} + +
+ ); +} + +function EmptyCodeView() { + return ( +
+ +
Nothing to show
+
+ ); +} diff --git a/components/ui/code-view/index.ts b/components/ui/code-view/index.ts new file mode 100644 index 000000000000..3dd57f347c74 --- /dev/null +++ b/components/ui/code-view/index.ts @@ -0,0 +1,2 @@ +export { CodeView } from './code-view'; +export type { CodeViewProps } from './code-view'; diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index baa50028b101..135405a69e66 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -113,22 +113,6 @@ export type VersionDropdownProps = { tabs: Array<{ name: string; payload: Array }>; } & React.HTMLAttributes; -/** - * - * @param param0 - const getActiveTabIndex = () => { - if (currentLane?.components.some((c) => c.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'LANE'); - if ((snaps || []).some((snap) => snap.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'SNAP'); - return 0; - }; - - const [activeTabIndex, setActiveTab] = useState(getActiveTabIndex()); - - * @returns - */ - export const VersionDropdown = React.memo(React.forwardRef(_VersionDropdown)); function _VersionDropdown( diff --git a/workspace.jsonc b/workspace.jsonc index 4a7bc51afe8f..25eaf6c865ce 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -82,7 +82,6 @@ "@teambit/bvm.config": "0.2.2", "@teambit/capsule": "0.0.12", "@teambit/code.ui.code-compare-section": "^0.0.5", - "@teambit/code.ui.code-view": "^0.0.508", "@teambit/code.ui.dependency-tree": "^0.0.546", "@teambit/code.ui.hooks.use-code-params": "^0.0.496", "@teambit/code.ui.object-formatter": "0.0.1", From 7a0f3462b442bb881c9d08ad1c001d827276f428 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Wed, 3 May 2023 19:28:58 -0400 Subject: [PATCH 05/20] lazy load snaps and tags separately in a single query + optimize component query --- scopes/component/component/ui/component.tsx | 2 +- scopes/component/component/ui/menu/menu.tsx | 160 +++------ .../component/ui/use-component-query.ts | 322 +++++++++++++++--- .../component/component/ui/use-component.tsx | 17 +- .../ui/version-dropdown/version-dropdown.tsx | 80 ++++- 5 files changed, 398 insertions(+), 183 deletions(-) diff --git a/scopes/component/component/ui/component.tsx b/scopes/component/component/ui/component.tsx index 8c1763612736..99e03dfc9c96 100644 --- a/scopes/component/component/ui/component.tsx +++ b/scopes/component/component/ui/component.tsx @@ -54,7 +54,7 @@ export function Component({ const componentId = _componentIdStr ? ComponentID.fromString(_componentIdStr) : undefined; const resolvedComponentIdStr = path || idFromLocation; const useComponentOptions = { - logFilters: useComponentFilters?.() || { log: { logOffset: 0, logLimit: 2 } }, + logFilters: useComponentFilters?.() || { log: { logOffset: 0, logLimit: 10 } }, customUseComponent: useComponent, }; diff --git a/scopes/component/component/ui/menu/menu.tsx b/scopes/component/component/ui/menu/menu.tsx index 914becf5235f..37919172bea5 100644 --- a/scopes/component/component/ui/menu/menu.tsx +++ b/scopes/component/component/ui/menu/menu.tsx @@ -1,17 +1,15 @@ import { Routes, Route } from 'react-router-dom'; import { MainDropdown, MenuItemSlot } from '@teambit/ui-foundation.ui.main-dropdown'; import { VersionDropdown } from '@teambit/component.ui.version-dropdown'; -import { FullLoader } from '@teambit/ui-foundation.ui.full-loader'; import type { ConsumeMethod } from '@teambit/ui-foundation.ui.use-box.menu'; import { useLocation } from '@teambit/base-react.navigation.link'; -import { flatten, groupBy, compact, isFunction } from 'lodash'; +import { flatten, groupBy, isFunction } from 'lodash'; import classnames from 'classnames'; import React, { useMemo } from 'react'; import { UseBoxDropdown } from '@teambit/ui-foundation.ui.use-box.dropdown'; import { useLanes } from '@teambit/lanes.hooks.use-lanes'; import { LaneModel } from '@teambit/lanes.ui.models.lanes-model'; import { Menu as ConsumeMethodsMenu } from '@teambit/ui-foundation.ui.use-box.menu'; -import { LegacyComponentLog } from '@teambit/legacy-component-log'; import type { ComponentModel } from '../component-model'; import { useComponent as useComponentQuery, UseComponentType } from '../use-component'; import { CollapsibleMenuNav } from './menu-nav'; @@ -20,6 +18,7 @@ import { OrderedNavigationSlot, ConsumeMethodSlot } from './nav-plugin'; import { useIdFromLocation } from '../use-component-from-location'; import { ComponentID } from '../..'; import { Filters } from '../use-component-query'; +import { LegacyComponentLog } from '@teambit/legacy-component-log'; export type MenuProps = { className?: string; @@ -83,73 +82,42 @@ export function ComponentMenu({ const _componentIdStr = getComponentIdStr(componentIdStr); const componentId = _componentIdStr ? ComponentID.fromString(_componentIdStr) : undefined; const resolvedComponentIdStr = path || idFromLocation; - + const componentFiltersFromProps = useComponentFilters?.() || {}; const useComponentOptions = { - logFilters: useComponentFilters?.() || { - log: { - logLimit: 20, - }, - }, - customUseComponent: useComponent, - }; - - const snapLogOptions = { - ...useComponentOptions, logFilters: { - ...useComponentOptions.logFilters, - log: { - ...useComponentOptions.logFilters.log, - type: 'snap', + snapLog: { + logLimit: 10, }, - }, - }; - const tagLogOptions = { - ...useComponentOptions, - logFilters: { - ...useComponentOptions.logFilters, - log: { - ...useComponentOptions.logFilters.log, - type: 'tag', + tagLog: { + logLimit: 10, }, + fetchLogsByTypeSeparately: true, + ...componentFiltersFromProps, }, + customUseComponent: useComponent, }; - const { - component: componentTags, - loadMoreLogs: loadMoreTags, - hasMoreLogs: hasMoreTags, - loading: loadingTags, - } = useComponentQuery(host, componentId?.toString() || idFromLocation, tagLogOptions); - - const { - component: componentSnaps, - loadMoreLogs: loadMoreSnaps, - hasMoreLogs: hasMoreSnaps, - loading: loadingSnaps, - } = useComponentQuery(host, componentId?.toString() || idFromLocation, snapLogOptions); + const { component, loading, loadMoreTags, loadMoreSnaps, hasMoreTags, hasMoreSnaps, snaps, tags } = useComponentQuery( + host, + componentId?.toString() || idFromLocation, + useComponentOptions + ); const mainMenuItems = useMemo(() => groupBy(flatten(menuItemSlot.values()), 'category'), [menuItemSlot]); - if (loadingTags || loadingSnaps) return ; - if (!componentSnaps || !componentTags) { - // eslint-disable-next-line no-console - console.error(`failed loading component tags/snaps for ${idFromLocation}`); - return null; - } - const RightSide = (
{RightNode || ( <> @@ -177,103 +145,59 @@ export function ComponentMenu({ } export function VersionRelatedDropdowns({ - componentTags, - componentSnaps, + component, + snaps: snapsFromProps = [], + tags: tagsFromProps = [], consumeMethods, loadMoreSnaps, loadMoreTags, hasMoreSnaps, hasMoreTags, - loadingSnaps, - loadingTags, className, + loading, host, }: { - componentTags: ComponentModel; - componentSnaps: ComponentModel; + component?: ComponentModel; + tags?: LegacyComponentLog[]; + snaps?: LegacyComponentLog[]; loadMoreTags?: () => void; loadMoreSnaps?: () => void; - loadingSnaps?: boolean; - loadingTags?: boolean; hasMoreTags?: boolean; hasMoreSnaps?: boolean; + loading?: boolean; consumeMethods?: ConsumeMethodSlot; className?: string; host: string; }) { const location = useLocation(); - const loading = loadingSnaps || loadingTags; const { lanesModel } = useLanes(); - const component = componentTags || componentSnaps; const viewedLane = lanesModel?.viewedLane?.id && !lanesModel?.viewedLane?.id.isDefault() ? lanesModel.viewedLane : undefined; - // const { logs } = component; const isWorkspace = host === 'teambit.workspace/workspace'; const snaps = useMemo(() => { - return (componentSnaps.logs || []).filter((log) => !log.tag).map((snap) => ({ ...snap, version: snap.hash })); - }, [componentSnaps.logs]); + return (snapsFromProps || []).map((snap) => ({ ...snap, version: snap.hash })); + }, [snapsFromProps]); const tags = useMemo(() => { - return (componentTags.logs || []).map((tag) => ({ ...tag, version: tag.tag as string })); - }, [componentTags.logs]); + return (tagsFromProps || []).map((tag) => ({ ...tag, version: tag.tag as string })); + }, [tagsFromProps]); const isNew = snaps.length === 0 && tags.length === 0; - const lanes = lanesModel?.getLanesByComponentId(component.id)?.filter((lane) => !lane.id.isDefault()) || []; + const lanes = component?.id + ? lanesModel?.getLanesByComponentId(component.id)?.filter((lane) => !lane.id.isDefault()) || [] + : []; const localVersion = isWorkspace && !isNew && (!viewedLane || lanesModel?.isViewingCurrentLane()); const currentVersion = - isWorkspace && !isNew && !location?.search.includes('version') ? 'workspace' : component.version; - const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; - const tabs = VERSION_TAB_NAMES.map((name) => { - switch (name) { - case 'SNAP': - return { name, payload: snaps || [] }; - case 'LANE': - return { name, payload: lanes || [] }; - default: - return { name, payload: tags || [] }; - } - }).filter((tab) => tab.payload.length > 0); + isWorkspace && !isNew && !location?.search.includes('version') ? 'workspace' : component?.version ?? ''; const methods = useConsumeMethods(component, consumeMethods, viewedLane); - const getActiveTabIndex = () => { - if (viewedLane?.components.some((c) => c.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'LANE'); - if ((snaps || []).some((snap) => snap.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'SNAP'); - return 0; - }; - - const [activeTabIndex, setActiveTab] = React.useState(getActiveTabIndex()); - const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' = tabs[activeTabIndex]?.name || tabs[0].name; - const hasMore = activeTabOrSnap === 'SNAP' ? hasMoreSnaps : hasMoreTags; - const observer = React.useRef(); - - const handleLoadMore = React.useCallback(() => { - if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); - if (activeTabOrSnap === 'TAG') loadMoreTags?.(); - }, [activeTabIndex, tabs.length]); - - const lastLogRef = React.useCallback( - (node) => { - if (loading) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting && hasMore) { - handleLoadMore(); - } - }); - if (node) observer.current.observe(node); - }, - [loading, hasMoreSnaps, hasMoreTags, activeTabIndex] - ); - return ( <> - {consumeMethods && tags.length > 0 && ( + {consumeMethods && tags.length > 0 && component?.id && ( )} void; hasMoreLogs?: boolean; + hasMoreSnaps?: boolean; + hasMoreTags?: boolean; + loadMoreLogs?: () => void; + loadMoreTags?: () => void; + loadMoreSnaps?: () => void; + snaps?: LegacyComponentLog[]; + tags?: LegacyComponentLog[]; + loading?: boolean; + error?: ComponentError; }; /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ export function useComponentQuery( componentId: string, host: string, - filtersFromProps?: Filters, + filters?: Filters, skip?: boolean ): ComponentQueryResult { const idRef = useRef(componentId); idRef.current = componentId; - const filters = useMemo(() => filtersFromProps?.log || {}, [filtersFromProps]); - const { logLimit } = filters; - const offsetRef = useRef(filters.logOffset); + + const { fetchLogsByTypeSeparately = false, log, tagLog, snapLog } = filters || {}; + const { + logHead: tagLogHead, + logOffset: tagLogOffset, + logSort: tagLogSort, + logLimit: tagLogLimit, + takeHeadFromComponent: tagLogTakeHeadFromComponent, + } = tagLog || {}; + + const { logHead, logOffset, logSort, logLimit, takeHeadFromComponent, logType } = log || {}; + const { + logHead: snapLogHead, + logOffset: snapLogOffset, + logSort: snapLogSort, + logLimit: snapLogLimit, + takeHeadFromComponent: snapLogTakeHeadFromComponent, + } = snapLog || {}; + const variables = { + id: componentId, + extensionId: host, + fetchLogsByTypeSeparately, + snapLogOffset: snapLogOffset ?? snapLogLimit ? 0 : undefined, + tagLogOffset: tagLogOffset ?? tagLogLimit ? 0 : undefined, + logOffset: logOffset ?? logLimit ? 0 : undefined, + logLimit, + snapLogLimit, + tagLogLimit, + logType, + logHead, + snapLogHead, + tagLogHead, + logSort, + snapLogSort, + tagLogSort, + takeHeadFromComponent, + snapLogTakeHeadFromComponent, + tagLogTakeHeadFromComponent, + }; + const offsetRef = useRef(logOffset); + const tagOffsetRef = useRef(tagLogOffset); + const snapOffsetRef = useRef(snapLogOffset); + const hasMoreLogs = useRef(undefined); + const hasMoreTagLogs = useRef(undefined); + const hasMoreSnapLogs = useRef(undefined); const { data, error, loading, subscribeToMore, fetchMore, ...rest } = useDataQuery(GET_COMPONENT, { - variables: { id: componentId, extensionId: host, ...filters }, + variables, skip, errorPolicy: 'all', }); const rawComponent = data?.getHost?.get; - const rawCompLogs = rawComponent?.logs ?? []; - + const rawTags = rawComponent?.tagLogs ?? []; + const rawSnaps = rawComponent?.snapLogs ?? []; + const rawCompLogs = rawComponent?.logs ?? mergeLogs(rawTags, rawSnaps); offsetRef.current = useMemo(() => { const currentOffset = offsetRef.current; if (!currentOffset) return rawCompLogs.length; - if (currentOffset < rawCompLogs?.length) { - return rawCompLogs?.length; - } return offsetRef.current; }, [rawCompLogs]); - const hasMoreLogs = React.useRef(undefined); + tagOffsetRef.current = useMemo(() => { + if (!fetchLogsByTypeSeparately) return offsetRef.current; + const currentOffset = tagOffsetRef.current; + if (!currentOffset) return rawTags.length; + return tagOffsetRef.current; + }, [rawCompLogs]); + + snapOffsetRef.current = useMemo(() => { + if (!fetchLogsByTypeSeparately) return offsetRef.current; + const currentOffset = snapOffsetRef.current; + if (!currentOffset) return rawSnaps.length; + return snapOffsetRef.current; + }, [rawSnaps]); hasMoreLogs.current = useMemo(() => { if (!logLimit) return false; if (rawComponent === undefined) return undefined; - if (hasMoreLogs.current === undefined) return rawComponent?.logs.length === logLimit; + if (hasMoreLogs.current === undefined) return rawComponent?.logs.length >= logLimit; return hasMoreLogs.current; - }, [rawComponent?.logs]); + }, [rawCompLogs]); + + hasMoreTagLogs.current = useMemo(() => { + if (!tagLogLimit) return false; + if (rawComponent === undefined) return undefined; + if (hasMoreTagLogs.current === undefined) return rawComponent?.tagLogs.length >= tagLogLimit; + return hasMoreTagLogs.current; + }, [rawTags]); + + hasMoreSnapLogs.current = useMemo(() => { + if (!snapLogLimit) return false; + if (rawComponent === undefined) return undefined; + if (hasMoreSnapLogs.current === undefined) return rawComponent?.snapLogs.length === snapLogLimit; + return hasMoreSnapLogs.current; + }, [rawSnaps]); const loadMoreLogs = React.useCallback(async () => { if (logLimit) { @@ -212,14 +345,15 @@ export function useComponentQuery( const prevComponent = prev.getHost.get; const fetchedComponent = fetchMoreResult.getHost.get; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedLogs = [...prevComponent.logs, ...fetchedComponent.logs]; - hasMoreLogs.current = fetchedComponent.logs.length === logLimit; + const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); + hasMoreLogs.current = fetchedComponent.logs.length >= logLimit; + if (updatedLogs.length > prevComponent.logs.length) { + offsetRef.current = updatedLogs.length; + } return { ...prev, - hasMoreLogs: false, getHost: { ...prev.getHost, get: { @@ -236,6 +370,81 @@ export function useComponentQuery( } }, [logLimit, fetchMore]); + const loadMoreTags = React.useCallback(async () => { + if (tagLogLimit) { + await fetchMore({ + variables: { + tagLogOffset: tagOffsetRef.current, + tagLogLimit, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const fetchedComponent = fetchMoreResult.getHost.get; + const prevCompLogs = prevComponent.tagLogs; + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedTags = mergeLogs(prevCompLogs, fetchedComponent.tagLogs); + if (updatedTags.length > prevCompLogs.length) { + tagOffsetRef.current = updatedTags.length; + } + hasMoreTagLogs.current = fetchedComponent.tagLogs.length >= tagLogLimit; + return { + ...prev, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + tagLogs: updatedTags, + }, + }, + }; + } + + return prev; + }, + }); + } + }, [tagLogLimit, fetchMore]); + + const loadMoreSnaps = React.useCallback(async () => { + if (snapLogLimit) { + await fetchMore({ + variables: { + snapLogOffset: snapOffsetRef.current, + snapLogLimit, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const prevCompLogs = prevComponent.snapLogs ?? []; + + const fetchedComponent = fetchMoreResult.getHost.get; + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedSnaps = mergeLogs(prevCompLogs, fetchedComponent.snapLogs); + if (updatedSnaps.length > prevCompLogs.length) { + snapOffsetRef.current = updatedSnaps.length; + } + hasMoreSnapLogs.current = fetchedComponent.snapLogs.length >= snapLogLimit; + return { + ...prev, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + snapLogs: updatedSnaps, + }, + }, + }; + } + + return prev; + }, + }); + } + }, [snapLogLimit, fetchMore]); + useEffect(() => { // @TODO @Kutner fix subscription for scope if (host !== 'teambit.workspace/workspace') { @@ -244,6 +453,9 @@ export function useComponentQuery( const unsubAddition = subscribeToMore({ document: SUB_SUBSCRIPTION_ADDED, + variables: { + fetchLogsByTypeSeparately, + }, updateQuery: (prev, { subscriptionData }) => { const prevComponent = prev?.getHost?.get; const addedComponent = subscriptionData?.data?.componentAdded?.component; @@ -266,6 +478,9 @@ export function useComponentQuery( const unsubChanges = subscribeToMore({ document: SUB_COMPONENT_CHANGED, + variables: { + fetchLogsByTypeSeparately, + }, updateQuery: (prev, { subscriptionData }) => { if (!subscriptionData.data) return prev; @@ -290,6 +505,9 @@ export function useComponentQuery( const unsubRemoval = subscribeToMore({ document: SUB_COMPONENT_REMOVED, + variables: { + fetchLogsByTypeSeparately, + }, updateQuery: (prev, { subscriptionData }) => { if (!subscriptionData.data) return prev; @@ -334,8 +552,8 @@ export function useComponentQuery( error && !data ? new ComponentError(500, error.message) : !rawComponent && !loading && new ComponentError(404); const component = useMemo( - () => (rawComponent ? ComponentModel.from({ ...rawComponent, host }) : undefined), - [id?.toString(), rawComponent?.logs.length] + () => (rawComponent ? ComponentModel.from({ ...rawComponent, host, logs: rawCompLogs }) : undefined), + [id?.toString(), rawCompLogs] ); const componentDescriptor = useMemo(() => { @@ -352,6 +570,14 @@ export function useComponentQuery( return id ? ComponentDescriptor.fromObject({ id: id.toString(), aspectList }) : undefined; }, [id?.toString()]); + const snaps = useMemo(() => { + return rawComponent?.snapLogs; + }, [rawComponent?.snapLogs]); + + const tags = useMemo(() => { + return rawComponent?.tagLogs; + }, [rawComponent?.tagLogs]); + return useMemo(() => { return { componentDescriptor, @@ -359,8 +585,26 @@ export function useComponentQuery( error: componentError || undefined, loading, loadMoreLogs, + loadMoreSnaps, + loadMoreTags, + hasMoreSnaps: hasMoreSnapLogs.current, + hasMoreTags: hasMoreTagLogs.current, hasMoreLogs: hasMoreLogs.current, + snaps, + tags, ...rest, }; - }, [host, component, componentDescriptor, componentError, hasMoreLogs]); + }, [host, component, componentDescriptor, componentError, hasMoreLogs, hasMoreSnapLogs, hasMoreTagLogs, snaps, tags]); +} + +interface Log { + id: string; + date: string; +} + +function mergeLogs(logs1: Log[] = [], logs2: Log[] = []): Log[] { + const mergedLogs: Log[] = []; + logs1.forEach((log) => mergedLogs.push(log)); + logs2.forEach((log) => mergedLogs.push(log)); + return mergedLogs; } diff --git a/scopes/component/component/ui/use-component.tsx b/scopes/component/component/ui/use-component.tsx index 5166cb816776..cebbe47852fb 100644 --- a/scopes/component/component/ui/use-component.tsx +++ b/scopes/component/component/ui/use-component.tsx @@ -1,17 +1,6 @@ import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; -import { ComponentDescriptor } from '@teambit/component-descriptor'; -import { ComponentModel } from './component-model'; -import { ComponentError } from './component-error'; -import { Filters, useComponentQuery } from './use-component-query'; +import { ComponentQueryResult, Filters, useComponentQuery } from './use-component-query'; -export type Component = { - component?: ComponentModel; - error?: ComponentError; - componentDescriptor?: ComponentDescriptor; - loading?: boolean; - loadMoreLogs?: () => void; - hasMoreLogs?: boolean; -}; export type UseComponentOptions = { version?: string; logFilters?: Filters; @@ -19,9 +8,9 @@ export type UseComponentOptions = { skip?: boolean; }; -export type UseComponentType = (id: string, host: string, filters?: Filters, skip?: boolean) => Component; +export type UseComponentType = (id: string, host: string, filters?: Filters, skip?: boolean) => ComponentQueryResult; -export function useComponent(host: string, id?: string, options?: UseComponentOptions): Component { +export function useComponent(host: string, id?: string, options?: UseComponentOptions): ComponentQueryResult { const query = useQuery(); const { version, logFilters, customUseComponent, skip } = options || {}; const componentVersion = (version || query.get('version')) ?? undefined; diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 135405a69e66..68c26f53c4b7 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -72,14 +72,14 @@ function _VersionMenu( })}
- {tabs[activeTabIndex].name === 'LANE' && - tabs[activeTabIndex].payload.map((payload) => ( + {tabs[activeTabIndex]?.name === 'LANE' && + tabs[activeTabIndex]?.payload.map((payload) => ( ))} - {tabs[activeTabIndex].name !== 'LANE' && - tabs[activeTabIndex].payload.map((payload, index) => ( + {tabs[activeTabIndex]?.name !== 'LANE' && + tabs[activeTabIndex]?.payload.map((payload, index) => ( void; + loadMoreSnaps?: () => void; + hasMoreTags?: boolean; + hasMoreSnaps?: boolean; localVersion?: boolean; currentVersion: string; currentLane?: LaneModel; @@ -108,9 +113,6 @@ export type VersionDropdownProps = { showVersionDetails?: boolean; disabled?: boolean; placeholderComponent?: ReactNode; - activeTabIndex?: number; - setActiveTabIndex: (index: number) => void; - tabs: Array<{ name: string; payload: Array }>; } & React.HTMLAttributes; export const VersionDropdown = React.memo(React.forwardRef(_VersionDropdown)); @@ -119,6 +121,7 @@ function _VersionDropdown( { snaps, tags, + lanes, currentVersion, latestVersion, localVersion, @@ -131,9 +134,10 @@ function _VersionDropdown( menuClassName, showVersionDetails, disabled, - activeTabIndex = 0, - setActiveTabIndex, - tabs, + loadMoreSnaps, + loadMoreTags, + hasMoreSnaps, + hasMoreTags, placeholderComponent = ( ) { + const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; + + const tabs = VERSION_TAB_NAMES.map((name) => { + switch (name) { + case 'SNAP': + return { name, payload: snaps || [] }; + case 'LANE': + return { name, payload: lanes || [] }; + default: + return { name, payload: tags || [] }; + } + }).filter((tab) => tab.payload.length > 0); + + const getActiveTabIndex = () => { + if (currentLane?.components.some((c) => c.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'LANE'); + if ((snaps || []).some((snap) => snap.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'SNAP'); + return 0; + }; + + const [activeTabIndex, setActiveTabIndex] = React.useState(getActiveTabIndex()); + + const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = tabs[activeTabIndex]?.name; + const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; + const observer = React.useRef(); + + const handleLoadMore = React.useCallback(() => { + if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); + if (activeTabOrSnap === 'TAG') loadMoreTags?.(); + }, [activeTabOrSnap, tabs.length]); + + const lastLogRef = React.useCallback( + (node) => { + if (loading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasMore) { + handleLoadMore(); + } + }, + { + threshold: 0.1, + rootMargin: '100px', + } + ); + if (node) observer.current.observe(node); + }, + [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + ); + const [key, setKey] = useState(0); const singleVersion = (snaps || []).concat(tags).length < 2 && !localVersion; @@ -173,7 +229,7 @@ function _VersionDropdown( {loading && } {loading || ( Date: Thu, 4 May 2023 14:21:07 -0400 Subject: [PATCH 06/20] lazy load versions until dropdown is opened --- components/ui/code-view/code-view.module.scss | 2 +- .../changelog/ui/change-log-page.tsx | 9 +- scopes/component/component/ui/component.tsx | 7 +- scopes/component/component/ui/menu/menu.tsx | 133 +++++------ .../component/ui/use-component-query.ts | 39 +-- .../version-dropdown-placeholder.tsx | 95 ++++---- .../version-dropdown.composition.tsx | 15 +- .../ui/version-dropdown/version-dropdown.tsx | 226 ++++++++++-------- 8 files changed, 282 insertions(+), 244 deletions(-) diff --git a/components/ui/code-view/code-view.module.scss b/components/ui/code-view/code-view.module.scss index 990a5fe021a0..f5fe367a61fd 100644 --- a/components/ui/code-view/code-view.module.scss +++ b/components/ui/code-view/code-view.module.scss @@ -10,7 +10,7 @@ .codeSnippet { display: block; overflow: auto; - height: 100vh; + height: calc(100vh - 200px); > code { > code { // this is to design the line numbers culumn diff --git a/scopes/component/changelog/ui/change-log-page.tsx b/scopes/component/changelog/ui/change-log-page.tsx index 0563fc356d62..c518c24aa956 100644 --- a/scopes/component/changelog/ui/change-log-page.tsx +++ b/scopes/component/changelog/ui/change-log-page.tsx @@ -16,19 +16,14 @@ type ChangeLogPageProps = { export function ChangeLogPage({ className, host }: ChangeLogPageProps) { const componentContext = React.useContext(ComponentContext); - const { - component, - loading, - loadMoreLogs, - hasMoreLogs: hasMore, - } = useComponent(host, componentContext?.id.toString(), { + const { component, loading, componentLogs } = useComponent(host, componentContext?.id.toString(), { logFilters: { log: { logLimit: 15, }, }, }); - + const { loadMoreLogs, hasMoreLogs: hasMore } = componentLogs || {}; const logs = component?.logs ?? []; const observer = React.useRef(); diff --git a/scopes/component/component/ui/component.tsx b/scopes/component/component/ui/component.tsx index 99e03dfc9c96..ebfc75db008e 100644 --- a/scopes/component/component/ui/component.tsx +++ b/scopes/component/component/ui/component.tsx @@ -53,8 +53,13 @@ export function Component({ const _componentIdStr = getComponentIdStr(componentIdStr); const componentId = _componentIdStr ? ComponentID.fromString(_componentIdStr) : undefined; const resolvedComponentIdStr = path || idFromLocation; + const componentFiltersFromProps = useComponentFilters?.() || {}; + const useComponentOptions = { - logFilters: useComponentFilters?.() || { log: { logOffset: 0, logLimit: 10 } }, + logFilters: { + log: { logLimit: 1 }, + ...componentFiltersFromProps, + }, customUseComponent: useComponent, }; diff --git a/scopes/component/component/ui/menu/menu.tsx b/scopes/component/component/ui/menu/menu.tsx index 37919172bea5..babf8dd97b84 100644 --- a/scopes/component/component/ui/menu/menu.tsx +++ b/scopes/component/component/ui/menu/menu.tsx @@ -18,7 +18,6 @@ import { OrderedNavigationSlot, ConsumeMethodSlot } from './nav-plugin'; import { useIdFromLocation } from '../use-component-from-location'; import { ComponentID } from '../..'; import { Filters } from '../use-component-query'; -import { LegacyComponentLog } from '@teambit/legacy-component-log'; export type MenuProps = { className?: string; @@ -82,27 +81,6 @@ export function ComponentMenu({ const _componentIdStr = getComponentIdStr(componentIdStr); const componentId = _componentIdStr ? ComponentID.fromString(_componentIdStr) : undefined; const resolvedComponentIdStr = path || idFromLocation; - const componentFiltersFromProps = useComponentFilters?.() || {}; - const useComponentOptions = { - logFilters: { - snapLog: { - logLimit: 10, - }, - tagLog: { - logLimit: 10, - }, - fetchLogsByTypeSeparately: true, - ...componentFiltersFromProps, - }, - customUseComponent: useComponent, - }; - - const { component, loading, loadMoreTags, loadMoreSnaps, hasMoreTags, hasMoreSnaps, snaps, tags } = useComponentQuery( - host, - componentId?.toString() || idFromLocation, - useComponentOptions - ); - const mainMenuItems = useMemo(() => groupBy(flatten(menuItemSlot.values()), 'category'), [menuItemSlot]); const RightSide = ( @@ -110,16 +88,11 @@ export function ComponentMenu({ {RightNode || ( <> @@ -144,50 +117,73 @@ export function ComponentMenu({ ); } +export type VersionRelatedDropdownsProps = { + consumeMethods?: ConsumeMethodSlot; + className?: string; + host: string; + useComponentFilters?: () => Filters; + useComponent?: UseComponentType; + componentId?: string; +}; + export function VersionRelatedDropdowns({ - component, - snaps: snapsFromProps = [], - tags: tagsFromProps = [], + componentId, + useComponentFilters, + useComponent, consumeMethods, - loadMoreSnaps, - loadMoreTags, - hasMoreSnaps, - hasMoreTags, className, - loading, host, -}: { - component?: ComponentModel; - tags?: LegacyComponentLog[]; - snaps?: LegacyComponentLog[]; - loadMoreTags?: () => void; - loadMoreSnaps?: () => void; - hasMoreTags?: boolean; - hasMoreSnaps?: boolean; - loading?: boolean; - consumeMethods?: ConsumeMethodSlot; - className?: string; - host: string; -}) { - const location = useLocation(); - const { lanesModel } = useLanes(); - const viewedLane = - lanesModel?.viewedLane?.id && !lanesModel?.viewedLane?.id.isDefault() ? lanesModel.viewedLane : undefined; - const isWorkspace = host === 'teambit.workspace/workspace'; +}: VersionRelatedDropdownsProps) { + const componentFiltersFromProps = useComponentFilters?.() || {}; + const componentWithLogsOptions = { + logFilters: { + snapLog: { + logLimit: 10, + }, + tagLog: { + logLimit: 10, + }, + fetchLogsByTypeSeparately: true, + ...componentFiltersFromProps, + }, + customUseComponent: useComponent, + }; - const snaps = useMemo(() => { - return (snapsFromProps || []).map((snap) => ({ ...snap, version: snap.hash })); - }, [snapsFromProps]); + // initially fetch just the component data + const initialFetchOptions = { + logFilters: { + log: { + logLimit: 1, + }, + ...componentFiltersFromProps, + }, + customUseComponent: useComponent, + }; - const tags = useMemo(() => { - return (tagsFromProps || []).map((tag) => ({ ...tag, version: tag.tag as string })); - }, [tagsFromProps]); + const { component, loading } = useComponentQuery(host, componentId, initialFetchOptions); - const isNew = snaps.length === 0 && tags.length === 0; + const useVersions = () => { + const { componentLogs = {}, loading: loadingLogs } = useComponentQuery(host, componentId, componentWithLogsOptions); + return { + loading: loadingLogs, + ...componentLogs, + snaps: (componentLogs.snaps || []).map((snap) => ({ ...snap, version: snap.hash })), + tags: (componentLogs.tags || []).map((tag) => ({ ...tag, version: tag.tag as string })), + }; + }; + const location = useLocation(); + const { lanesModel } = useLanes(); const lanes = component?.id ? lanesModel?.getLanesByComponentId(component.id)?.filter((lane) => !lane.id.isDefault()) || [] : []; + const viewedLane = + lanesModel?.viewedLane?.id && !lanesModel?.viewedLane?.id.isDefault() ? lanesModel.viewedLane : undefined; + + const isWorkspace = host === 'teambit.workspace/workspace'; + + const isNew = component?.logs?.length === 0; + const localVersion = isWorkspace && !isNew && (!viewedLane || lanesModel?.isViewingCurrentLane()); const currentVersion = @@ -197,7 +193,7 @@ export function VersionRelatedDropdowns({ return ( <> - {consumeMethods && tags.length > 0 && component?.id && ( + {consumeMethods && (component?.tags?.size ?? 0) > 0 && component?.id && ( )} flatten(consumeMethods?.values()) diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index 2bb658977952..99cc38106175 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -217,14 +217,17 @@ export type Filters = { export type ComponentQueryResult = { component?: ComponentModel; componentDescriptor?: ComponentDescriptor; - hasMoreLogs?: boolean; - hasMoreSnaps?: boolean; - hasMoreTags?: boolean; - loadMoreLogs?: () => void; - loadMoreTags?: () => void; - loadMoreSnaps?: () => void; - snaps?: LegacyComponentLog[]; - tags?: LegacyComponentLog[]; + // @todo refactor to useComponentLogs + componentLogs?: { + hasMoreLogs?: boolean; + hasMoreSnaps?: boolean; + hasMoreTags?: boolean; + loadMoreLogs?: () => void; + loadMoreTags?: () => void; + loadMoreSnaps?: () => void; + snaps?: LegacyComponentLog[]; + tags?: LegacyComponentLog[]; + }; loading?: boolean; error?: ComponentError; }; @@ -238,7 +241,6 @@ export function useComponentQuery( ): ComponentQueryResult { const idRef = useRef(componentId); idRef.current = componentId; - const { fetchLogsByTypeSeparately = false, log, tagLog, snapLog } = filters || {}; const { logHead: tagLogHead, @@ -247,7 +249,6 @@ export function useComponentQuery( logLimit: tagLogLimit, takeHeadFromComponent: tagLogTakeHeadFromComponent, } = tagLog || {}; - const { logHead, logOffset, logSort, logLimit, takeHeadFromComponent, logType } = log || {}; const { logHead: snapLogHead, @@ -582,16 +583,18 @@ export function useComponentQuery( return { componentDescriptor, component, + componentLogs: { + loadMoreLogs, + loadMoreSnaps, + loadMoreTags, + hasMoreSnaps: hasMoreSnapLogs.current, + hasMoreTags: hasMoreTagLogs.current, + hasMoreLogs: hasMoreLogs.current, + snaps, + tags, + }, error: componentError || undefined, loading, - loadMoreLogs, - loadMoreSnaps, - loadMoreTags, - hasMoreSnaps: hasMoreSnapLogs.current, - hasMoreTags: hasMoreTagLogs.current, - hasMoreLogs: hasMoreLogs.current, - snaps, - tags, ...rest, }; }, [host, component, componentDescriptor, componentError, hasMoreLogs, hasMoreSnapLogs, hasMoreTagLogs, snaps, tags]); diff --git a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx index 38b4f2892a00..6296aaefdb19 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx @@ -1,80 +1,79 @@ -import React, { HTMLAttributes, useMemo } from 'react'; +import React, { HTMLAttributes } from 'react'; import { Ellipsis } from '@teambit/design.ui.styles.ellipsis'; import classNames from 'classnames'; +import * as semver from 'semver'; import { Icon } from '@teambit/evangelist.elements.icon'; import { TimeAgo } from '@teambit/design.ui.time-ago'; import { UserAvatar } from '@teambit/design.ui.avatar'; -import { DropdownComponentVersion } from './version-dropdown'; import styles from './version-dropdown-placeholder.module.scss'; export type VersionProps = { - tags: DropdownComponentVersion[]; - snaps?: DropdownComponentVersion[]; - currentVersion: string; + currentVersion?: string; + timestamp?: string | number; + author?: { + displayName?: string; + email?: string; + }; + message?: string; disabled?: boolean; + hasMoreVersions?: boolean; } & HTMLAttributes; -const getVersionDetailFromTags = (version, tags) => tags.find((tag) => tag.tag === version); -const getVersionDetailFromSnaps = (version, snaps) => (snaps || []).find((snap) => snap.hash === version); -const getVersionDetails = (version, tags, snaps) => { - if (version === 'workspace' || version === 'new') return { version }; - return getVersionDetailFromTags(version, tags) || getVersionDetailFromSnaps(version, snaps); -}; - -export function SimpleVersion({ currentVersion, className, disabled, tags, snaps }: VersionProps) { - const showArrowDown = useMemo(() => (snaps || []).concat(tags).length > 1, [tags, snaps]); - const versionDetails = useMemo(() => getVersionDetails(currentVersion, tags, snaps), [currentVersion, tags, snaps]); +export function SimpleVersion({ + currentVersion, + className, + disabled, + hasMoreVersions, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + author, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + message, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + timestamp, + ...rest +}: VersionProps) { + const isTag = semver.valid(currentVersion); return ( -
+
{currentVersion} - {showArrowDown && } + {hasMoreVersions && }
); } -export function DetailedVersion({ currentVersion, className, disabled, snaps, tags }: VersionProps) { - const showArrowDown = useMemo(() => (snaps || []).concat(tags).length > 1, [tags, snaps]); - const versionDetails = useMemo(() => getVersionDetails(currentVersion, tags, snaps), [currentVersion, tags, snaps]); - - const timestamp = useMemo( - () => (versionDetails?.date ? new Date(parseInt(versionDetails.date)).toString() : new Date().toString()), - [versionDetails?.date] - ); - - const author = useMemo(() => { - return { - displayName: versionDetails?.username, - email: versionDetails?.email, - }; - }, [versionDetails]); +export function DetailedVersion({ + currentVersion, + className, + disabled, + hasMoreVersions, + timestamp, + author, + message, + ...rest +}: VersionProps) { + const isTag = semver.valid(currentVersion); return ( -
- +
+ {currentVersion} - {commitMessage(versionDetails?.message)} - - + {commitMessage(message)} + + - {showArrowDown && } + {hasMoreVersions && }
); } diff --git a/scopes/component/ui/version-dropdown/version-dropdown.composition.tsx b/scopes/component/ui/version-dropdown/version-dropdown.composition.tsx index d3a34f1171c5..e6185e683322 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.composition.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.composition.tsx @@ -8,17 +8,28 @@ const style = { display: 'flex', justifyContent: 'center', alignContent: 'center export const VersionDropdownWithOneVersion = () => { return ( - + ({ + tags: [{ version: '0.1' }], + })} + currentVersion="0.1" + /> ); }; export const VersionDropdownWithMultipleVersions = () => { const versions = ['0.3', '0.2', '0.1'].map((version) => ({ version })); + return ( - + ({ + tags: [{ version: '0.1' }], + })} + currentVersion={versions[0].version} + /> ); diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 68c26f53c4b7..251d915d4da6 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -24,20 +24,81 @@ function _VersionMenu( currentVersion, localVersion, latestVersion, - currentLane, overrideVersionHref, showVersionDetails, - activeTabIndex, - setActiveTab, - tabs, + useVersions, + currentLane, + lanes, + loading: loadingFromProps, ...rest }: VersionMenuProps, ref?: React.ForwardedRef ) { + const { + snaps, + tags, + hasMoreSnaps, + hasMoreTags, + loadMoreSnaps, + loadMoreTags, + loading: loadingVersions, + } = useVersions?.() || {}; + const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; + const loading = loadingFromProps || loadingVersions; + + const tabs = VERSION_TAB_NAMES.map((name) => { + switch (name) { + case 'SNAP': + return { name, payload: snaps || [] }; + case 'LANE': + return { name, payload: lanes || [] }; + default: + return { name, payload: tags || [] }; + } + }).filter((tab) => tab.payload.length > 0); + + const getActiveTabIndex = () => { + if (currentLane?.components.some((c) => c.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'LANE'); + if ((snaps || []).some((snap) => snap.version === currentVersion)) + return tabs.findIndex((tab) => tab.name === 'SNAP'); + return 0; + }; + + const [activeTabIndex, setActiveTab] = React.useState(getActiveTabIndex()); + + const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = tabs[activeTabIndex]?.name; + const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; + const observer = React.useRef(); + + const handleLoadMore = React.useCallback(() => { + if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); + if (activeTabOrSnap === 'TAG') loadMoreTags?.(); + }, [activeTabOrSnap, tabs.length]); + + const lastLogRef = React.useCallback( + (node) => { + if (loading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasMore) { + handleLoadMore(); + } + }, + { + threshold: 0.1, + rootMargin: '100px', + } + ); + if (node) observer.current.observe(node); + }, + [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + ); const multipleTabs = tabs.length > 1; const message = multipleTabs ? 'Switch to view tags, snaps, or lanes' - : `Switch between ${tabs[0].name.toLocaleLowerCase()}s`; + : `Switch between ${tabs[0]?.name.toLocaleLowerCase()}s`; return (
@@ -79,7 +140,7 @@ function _VersionMenu( {tabs[activeTabIndex]?.name !== 'LANE' && tabs[activeTabIndex]?.payload.map((payload, index) => ( void; loadMoreSnaps?: () => void; hasMoreTags?: boolean; hasMoreSnaps?: boolean; + loading?: boolean; +}; + +export type UseComponentVersions = () => UseComponentVersionsResult; + +export type VersionDropdownProps = { localVersion?: boolean; - currentVersion: string; - currentLane?: LaneModel; latestVersion?: string; + currentVersion: string; + currentVersionLog?: { + timestamp?: string | number; + author?: { + displayName?: string; + email?: string; + }; + message?: string; + }; + hasMoreVersions?: boolean; + // currentLane?: LaneModel; loading?: boolean; + useComponentVersions?: UseComponentVersions; + currentLane?: LaneModel; + lanes?: LaneModel[]; overrideVersionHref?: (version: string) => string; placeholderClassName?: string; dropdownClassName?: string; @@ -119,14 +197,12 @@ export const VersionDropdown = React.memo(React.forwardRef - ), + placeholderComponent, + currentLane, + useComponentVersions, + lanes, ...rest }: VersionDropdownProps, ref?: React.ForwardedRef ) { - const VERSION_TAB_NAMES = ['TAG', 'SNAP', 'LANE'] as const; + const [key, setKey] = useState(0); + const singleVersion = hasMoreVersions && !localVersion; + const [open, setOpen] = useState(false); + const { author, message, timestamp } = currentVersionLog; + if (disabled || (singleVersion && !loading)) { + return
{placeholderComponent}
; + } - const tabs = VERSION_TAB_NAMES.map((name) => { - switch (name) { - case 'SNAP': - return { name, payload: snaps || [] }; - case 'LANE': - return { name, payload: lanes || [] }; - default: - return { name, payload: tags || [] }; + const handlePlaceholderClicked = (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + setOpen((o) => !o); } - }).filter((tab) => tab.payload.length > 0); - - const getActiveTabIndex = () => { - if (currentLane?.components.some((c) => c.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'LANE'); - if ((snaps || []).some((snap) => snap.version === currentVersion)) - return tabs.findIndex((tab) => tab.name === 'SNAP'); - return 0; }; - const [activeTabIndex, setActiveTabIndex] = React.useState(getActiveTabIndex()); - - const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = tabs[activeTabIndex]?.name; - const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; - const observer = React.useRef(); - - const handleLoadMore = React.useCallback(() => { - if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); - if (activeTabOrSnap === 'TAG') loadMoreTags?.(); - }, [activeTabOrSnap, tabs.length]); - - const lastLogRef = React.useCallback( - (node) => { - if (loading) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting && hasMore) { - handleLoadMore(); - } - }, - { - threshold: 0.1, - rootMargin: '100px', - } - ); - if (node) observer.current.observe(node); - }, - [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + const defaultPlaceholder = ( + ); - const [key, setKey] = useState(0); - - const singleVersion = (snaps || []).concat(tags).length < 2 && !localVersion; - - if (disabled || (singleVersion && !loading)) { - return
{placeholderComponent}
; - } - return (
open && setKey((x) => x + 1)} // to reset menu to initial state when toggling + open={open} + onClick={handlePlaceholderClicked} + onClickOutside={() => setOpen(false)} + onChange={(_e, _open) => _open && setKey((x) => x + 1)} // to reset menu to initial state when toggling PlaceholderComponent={({ children, ...other }) => ( -
+
{children}
)} - placeholder={placeholderComponent} + placeholder={placeholderComponent || defaultPlaceholder} > {loading && } - {loading || ( + {!loading && open && ( )} @@ -252,10 +286,10 @@ type VersionMenuProps = { localVersion?: boolean; currentVersion?: string; latestVersion?: string; + useVersions?: UseComponentVersions; currentLane?: LaneModel; + lanes?: LaneModel[]; overrideVersionHref?: (version: string) => string; showVersionDetails?: boolean; - activeTabIndex: number; - setActiveTab: (index: number) => void; - tabs: Array<{ name: string; payload: Array }>; + loading?: boolean; } & React.HTMLAttributes; From e61fac95ea413a85a60ea531a752fa2140af69fe Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Thu, 4 May 2023 15:27:32 -0400 Subject: [PATCH 07/20] fix component compare to lazy load --- .../component-compare/component-compare.tsx | 21 +++++- .../component-compare-version-picker.tsx | 75 ++++++++++++------- scopes/component/component/ui/component.tsx | 2 +- scopes/component/component/ui/menu/menu.tsx | 5 +- .../ui/version-dropdown/version-dropdown.tsx | 15 ++-- 5 files changed, 77 insertions(+), 41 deletions(-) diff --git a/components/ui/component-compare/component-compare/component-compare.tsx b/components/ui/component-compare/component-compare/component-compare.tsx index ea837623333b..94aa2b7703b8 100644 --- a/components/ui/component-compare/component-compare/component-compare.tsx +++ b/components/ui/component-compare/component-compare/component-compare.tsx @@ -79,7 +79,15 @@ export function ComponentCompare(props: ComponentCompareProps) { component: base, loading: loadingBase, componentDescriptor: baseComponentDescriptor, - } = useComponent(host, baseId.toString(), { customUseComponent }); + } = useComponent(host, baseId.toString(), { + customUseComponent, + logFilters: { + log: { + logLimit: 3, + }, + }, + }); + const { component: compareComponent, loading: loadingCompare, @@ -87,6 +95,11 @@ export function ComponentCompare(props: ComponentCompareProps) { } = useComponent(host, _compareId?.toString() || '', { skip: !_compareId, customUseComponent, + logFilters: { + log: { + logLimit: 3, + }, + }, }); const loading = loadingBase || loadingCompare; @@ -170,7 +183,11 @@ function RenderCompareScreen(props: ComponentCompareProps) { return ( <> {showVersionPicker && ( -
{state?.versionPicker?.element || }
+
+ {state?.versionPicker?.element || ( + + )} +
)}
diff --git a/components/ui/component-compare/version-picker/component-compare-version-picker.tsx b/components/ui/component-compare/version-picker/component-compare-version-picker.tsx index 96f8e60902ba..174940355de0 100644 --- a/components/ui/component-compare/version-picker/component-compare-version-picker.tsx +++ b/components/ui/component-compare/version-picker/component-compare-version-picker.tsx @@ -1,36 +1,57 @@ -import React, { HTMLAttributes, useMemo } from 'react'; -import { DropdownComponentVersion, VersionDropdown } from '@teambit/component.ui.version-dropdown'; +import React, { HTMLAttributes } from 'react'; +import { VersionDropdown } from '@teambit/component.ui.version-dropdown'; import { useUpdatedUrlFromQuery } from '@teambit/component.ui.component-compare.hooks.use-component-compare-url'; import { useComponentCompare } from '@teambit/component.ui.component-compare.context'; +import { UseComponentType, useComponent } from '@teambit/component'; import classNames from 'classnames'; import styles from './component-compare-version-picker.module.scss'; -export type ComponentCompareVersionPickerProps = {} & HTMLAttributes; +export type ComponentCompareVersionPickerProps = { + customUseComponent?: UseComponentType; + host: string; +} & HTMLAttributes; -export function ComponentCompareVersionPicker({ className }: ComponentCompareVersionPickerProps) { +export function ComponentCompareVersionPicker({ + className, + host, + customUseComponent, +}: ComponentCompareVersionPickerProps) { const componentCompare = useComponentCompare(); const compare = componentCompare?.compare?.model; - - const logs = - (compare?.logs || []).filter((log) => { - const version = log.tag || log.hash; - return componentCompare?.compare?.hasLocalChanges || version !== compare?.id.version; - }) || []; - - const [tags, snaps] = useMemo(() => { - return (logs || []).reduce( - ([_tags, _snaps], log) => { - if (!log.tag) { - _snaps.push({ ...log, version: log.hash }); - } else { - _tags.push({ ...log, version: log.tag as string }); - } - return [_tags, _snaps]; + const componentId = compare?.id.toString(); + const componentWithLogsOptions = { + logFilters: { + snapLog: { + logLimit: 10, }, - [new Array(), new Array()] - ); - }, [logs]); + tagLog: { + logLimit: 10, + }, + fetchLogsByTypeSeparately: true, + }, + customUseComponent, + }; + + const useVersions = () => { + const { componentLogs = {}, loading: loadingLogs } = useComponent(host, componentId, componentWithLogsOptions); + return { + loading: loadingLogs, + ...componentLogs, + snaps: (componentLogs.snaps || []) + .map((snap) => ({ ...snap, version: snap.hash })) + .filter((log) => { + const version = log.tag || log.hash; + return componentCompare?.compare?.hasLocalChanges || version !== compare?.id.version; + }), + tags: (componentLogs.tags || []) + .map((tag) => ({ ...tag, version: tag.tag as string })) + .filter((log) => { + const version = log.tag || log.hash; + return componentCompare?.compare?.hasLocalChanges || version !== compare?.id.version; + }), + }; + }; const compareVersion = componentCompare?.compare?.hasLocalChanges ? 'workspace' : compare?.version; @@ -47,15 +68,15 @@ export function ComponentCompareVersionPicker({ className }: ComponentCompareVer dropdownClassName={styles.componentCompareDropdown} placeholderClassName={styles.componentCompareVersionPlaceholder} menuClassName={classNames(styles.componentCompareVersionMenu, styles.showMenuOverNav)} - snaps={snaps} - tags={tags} currentVersion={baseVersion as string} loading={componentCompare?.loading} overrideVersionHref={(_baseVersion) => { return useUpdatedUrlFromQuery({ baseVersion: _baseVersion }); }} - disabled={snaps.concat(tags).length < 2} + disabled={(compare?.logs?.length ?? 0) < 2} + hasMoreVersions={(compare?.logs?.length ?? 0) > 1} showVersionDetails={true} + useComponentVersions={useVersions} />
with
Filters; + path?: string; }; function getComponentIdStr(componentIdStr?: string | (() => string | undefined)): string | undefined { if (isFunction(componentIdStr)) return componentIdStr(); @@ -153,7 +152,7 @@ export function VersionRelatedDropdowns({ const initialFetchOptions = { logFilters: { log: { - logLimit: 1, + logLimit: 3, }, ...componentFiltersFromProps, }, diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 251d915d4da6..69a42f48d784 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -179,7 +179,6 @@ export type VersionDropdownProps = { message?: string; }; hasMoreVersions?: boolean; - // currentLane?: LaneModel; loading?: boolean; useComponentVersions?: UseComponentVersions; currentLane?: LaneModel; @@ -219,13 +218,10 @@ function _VersionDropdown( ref?: React.ForwardedRef ) { const [key, setKey] = useState(0); - const singleVersion = hasMoreVersions && !localVersion; + const singleVersion = !hasMoreVersions; const [open, setOpen] = useState(false); - const { author, message, timestamp } = currentVersionLog; - if (disabled || (singleVersion && !loading)) { - return
{placeholderComponent}
; - } + const { author, message, timestamp } = currentVersionLog; const handlePlaceholderClicked = (e: React.MouseEvent) => { if (e.target === e.currentTarget) { setOpen((o) => !o); @@ -245,6 +241,11 @@ function _VersionDropdown( /> ); + const PlaceholderComponent = placeholderComponent || defaultPlaceholder; + if (disabled || (singleVersion && !loading)) { + return
{PlaceholderComponent}
; + } + return (
)} - placeholder={placeholderComponent || defaultPlaceholder} + placeholder={PlaceholderComponent} > {loading && } {!loading && open && ( From a1e492b7b456c50b540db659ce5fde617dbf6951 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Fri, 5 May 2023 15:08:09 -0400 Subject: [PATCH 08/20] fix tests --- .../version-dropdown.spec.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/scopes/component/ui/version-dropdown/version-dropdown.spec.tsx b/scopes/component/ui/version-dropdown/version-dropdown.spec.tsx index cc8f31778b21..4f67e26029ac 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.spec.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.spec.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { expect } from 'chai'; import { VersionDropdownWithOneVersion, VersionDropdownWithMultipleVersions } from './version-dropdown.composition'; @@ -16,13 +16,14 @@ describe('version dropdown tests', () => { const textVersion = getByText(/^0.1$/); expect(textVersion).to.exist; }); - it('should return multiple versions', () => { - const { getByText, getAllByText } = render(); - const textVersionOne = getByText(/^0.1$/); - const textVersionTwo = getByText(/^0.2$/); - const textVersionThree = getAllByText(/^0.3$/); - expect(textVersionOne).to.exist; - expect(textVersionTwo).to.exist; - expect(textVersionThree).to.exist; + it('should not return multiple versions when mounted (lazy loading)', () => { + render(); + const textVersionOne = screen.queryByText(/^0.1$/); + const textVersionTwo = screen.queryByText(/^0.2$/); + const textVersionThree = screen.getAllByText(/^0.3$/); + + expect(textVersionOne).to.be.null; + expect(textVersionTwo).to.be.null; + expect(textVersionThree).to.have.lengthOf.at.least(1); }); }); From 239adc2a34a0283799c103c8907780896660a440 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Mon, 8 May 2023 14:23:51 -0400 Subject: [PATCH 09/20] fix loader - when lazy loading versions --- .../version-dropdown-placeholder.module.scss | 7 +++++++ .../version-dropdown/version-dropdown-placeholder.tsx | 7 +++++++ .../ui/version-dropdown/version-dropdown.module.scss | 9 ++++++++- .../ui/version-dropdown/version-dropdown.tsx | 11 +++++++---- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.module.scss b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.module.scss index 7d33937585b7..36155c16fdf6 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.module.scss +++ b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.module.scss @@ -95,3 +95,10 @@ flex: none; } } + +.loader { + color: var(--bit-bg-dent, #f6f6f6); + > span { + padding: 4px; + } +} diff --git a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx index 6296aaefdb19..f1176151cd37 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown-placeholder.tsx @@ -5,6 +5,7 @@ import * as semver from 'semver'; import { Icon } from '@teambit/evangelist.elements.icon'; import { TimeAgo } from '@teambit/design.ui.time-ago'; import { UserAvatar } from '@teambit/design.ui.avatar'; +import { WordSkeleton } from '@teambit/base-ui.loaders.skeleton'; import styles from './version-dropdown-placeholder.module.scss'; @@ -18,6 +19,7 @@ export type VersionProps = { message?: string; disabled?: boolean; hasMoreVersions?: boolean; + loading?: boolean; } & HTMLAttributes; export function SimpleVersion({ @@ -31,8 +33,11 @@ export function SimpleVersion({ message, // eslint-disable-next-line @typescript-eslint/no-unused-vars timestamp, + loading, ...rest }: VersionProps) { + if (loading) return ; + const isTag = semver.valid(currentVersion); return ( @@ -56,8 +61,10 @@ export function DetailedVersion({ timestamp, author, message, + loading, ...rest }: VersionProps) { + if (loading) return ; const isTag = semver.valid(currentVersion); return ( diff --git a/scopes/component/ui/version-dropdown/version-dropdown.module.scss b/scopes/component/ui/version-dropdown/version-dropdown.module.scss index f5f46def1e0e..1a89c23168a1 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.module.scss +++ b/scopes/component/ui/version-dropdown/version-dropdown.module.scss @@ -103,5 +103,12 @@ } .loading { - color: var(--bit-bg-heavy, #f6f6f6); + color: var(--bit-bg-dent, #f6f6f6); +} + +.loader { + color: var(--bit-bg-dent, #f6f6f6); + > div { + padding: 8px 0px; + } } diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 69a42f48d784..62061a63b386 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -4,7 +4,7 @@ import { Dropdown } from '@teambit/evangelist.surfaces.dropdown'; import { Tab } from '@teambit/ui-foundation.ui.use-box.tab'; import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { UserAvatar } from '@teambit/design.ui.avatar'; -import { LineSkeleton } from '@teambit/base-ui.loaders.skeleton'; +import { LineSkeleton, WordSkeleton } from '@teambit/base-ui.loaders.skeleton'; import { LaneModel } from '@teambit/lanes.ui.models.lanes-model'; import classNames from 'classnames'; @@ -103,7 +103,8 @@ function _VersionMenu( return (
-
{message}
+ {loading && } + {!loading &&
{message}
} {localVersion && ( ) => { + if (loading) return; if (e.target === e.currentTarget) { setOpen((o) => !o); } @@ -238,10 +240,12 @@ function _VersionDropdown( currentVersion={currentVersion} onClick={handlePlaceholderClicked} hasMoreVersions={hasMoreVersions} + loading={loading} /> ); const PlaceholderComponent = placeholderComponent || defaultPlaceholder; + if (disabled || (singleVersion && !loading)) { return
{PlaceholderComponent}
; } @@ -262,8 +266,7 @@ function _VersionDropdown( )} placeholder={PlaceholderComponent} > - {loading && } - {!loading && open && ( + {open && ( Date: Tue, 9 May 2023 13:17:20 -0400 Subject: [PATCH 10/20] query from head when on a version + fix loading state --- scopes/component/component/ui/menu/menu.tsx | 38 ++++++++++++------- .../ui/version-dropdown/version-dropdown.tsx | 8 +++- .../workspace/workspace.ui.drawer.tsx | 2 +- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/scopes/component/component/ui/menu/menu.tsx b/scopes/component/component/ui/menu/menu.tsx index 6a1866a62bc7..ec6a6a2e8071 100644 --- a/scopes/component/component/ui/menu/menu.tsx +++ b/scopes/component/component/ui/menu/menu.tsx @@ -10,6 +10,8 @@ import { UseBoxDropdown } from '@teambit/ui-foundation.ui.use-box.dropdown'; import { useLanes } from '@teambit/lanes.hooks.use-lanes'; import { LaneModel } from '@teambit/lanes.ui.models.lanes-model'; import { Menu as ConsumeMethodsMenu } from '@teambit/ui-foundation.ui.use-box.menu'; +import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; +import * as semver from 'semver'; import type { ComponentModel } from '../component-model'; import { useComponent as useComponentQuery, UseComponentType } from '../use-component'; import { CollapsibleMenuNav } from './menu-nav'; @@ -134,19 +136,10 @@ export function VersionRelatedDropdowns({ host, }: VersionRelatedDropdownsProps) { const componentFiltersFromProps = useComponentFilters?.() || {}; - const componentWithLogsOptions = { - logFilters: { - snapLog: { - logLimit: 10, - }, - tagLog: { - logLimit: 10, - }, - fetchLogsByTypeSeparately: true, - ...componentFiltersFromProps, - }, - customUseComponent: useComponent, - }; + const query = useQuery(); + const componentVersion = query.get('version'); + const isTag = componentVersion ? semver.valid(componentVersion) : undefined; + const isSnap = componentVersion ? !isTag : undefined; // initially fetch just the component data const initialFetchOptions = { @@ -161,6 +154,25 @@ export function VersionRelatedDropdowns({ const { component, loading } = useComponentQuery(host, componentId, initialFetchOptions); + const componentWithLogsOptions = React.useMemo( + () => ({ + logFilters: { + snapLog: { + logLimit: 10, + logHead: isSnap ? componentVersion : undefined, + }, + tagLog: { + logLimit: 10, + logHead: isTag ? componentVersion : undefined, + }, + fetchLogsByTypeSeparately: true, + ...componentFiltersFromProps, + }, + customUseComponent: useComponent, + }), + [componentVersion, isTag, isSnap] + ); + const useVersions = () => { const { componentLogs = {}, loading: loadingLogs } = useComponentQuery(host, componentId, componentWithLogsOptions); return { diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 62061a63b386..86a4a725a22b 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -105,7 +105,7 @@ function _VersionMenu(
{loading && } {!loading &&
{message}
} - {localVersion && ( + {!loading && localVersion && ( { + if (loading && open) { + setOpen(false); + } + }, [loading]); + const { author, message, timestamp } = currentVersionLog; const handlePlaceholderClicked = (e: React.MouseEvent) => { if (loading) return; diff --git a/scopes/workspace/workspace/workspace.ui.drawer.tsx b/scopes/workspace/workspace/workspace.ui.drawer.tsx index a3d262acff67..3be109b5f723 100644 --- a/scopes/workspace/workspace/workspace.ui.drawer.tsx +++ b/scopes/workspace/workspace/workspace.ui.drawer.tsx @@ -66,7 +66,7 @@ export const workspaceDrawer = ({ !isViewingWorkspaceVersions ? viewedLaneId : undefined ); const { components: mainComponents = [], loading: mainCompsLoading } = useLaneComponents( - !isViewingDefaultLane ? defaultLane?.id : undefined + isViewingDefaultLane ? defaultLane?.id : undefined ); const workspace = useContext(WorkspaceContext); From 9e14c51b0af9fa8a7a3ced5e755ae7ebb23d7108 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Fri, 12 May 2023 14:09:01 -0400 Subject: [PATCH 11/20] refator getLogs to allow fetching versions with advanced filters --- .../inputs/lane-selector/lane-placeholder.tsx | 6 + .../lane-selector/lane-selector-list.tsx | 3 + .../ui/inputs/lane-selector/lane-selector.tsx | 9 +- .../lane-switcher/lane-switcher.module.scss | 9 + .../lane-switcher/lane-switcher.tsx | 57 ++-- pnpm-lock.yaml | 113 ++++---- .../component/component/component-factory.ts | 10 +- .../component/component/component.graphql.ts | 10 + scopes/component/component/component.ts | 27 +- scopes/component/component/index.ts | 2 +- scopes/component/component/ui/component.tsx | 24 +- scopes/component/component/ui/index.ts | 7 +- scopes/component/component/ui/menu/menu.tsx | 61 ++-- .../component/ui/use-component-query.ts | 270 +++++++++++------- .../ui/version-dropdown/version-dropdown.tsx | 62 ++-- .../lanes/hooks/use-lanes/lanes-provider.tsx | 40 ++- scopes/lanes/hooks/use-lanes/use-lanes.tsx | 2 +- scopes/lanes/lanes/lanes.ui.runtime.tsx | 70 +++-- scopes/scope/scope/scope.main.runtime.ts | 97 ++++++- scopes/workspace/workspace/workspace.ts | 12 +- src/scope/component-ops/traverse-versions.ts | 4 +- src/scope/models/model-component.ts | 4 +- src/scope/scope.ts | 13 +- 23 files changed, 598 insertions(+), 314 deletions(-) diff --git a/components/ui/inputs/lane-selector/lane-placeholder.tsx b/components/ui/inputs/lane-selector/lane-placeholder.tsx index b3220287eb35..9a896a204c56 100644 --- a/components/ui/inputs/lane-selector/lane-placeholder.tsx +++ b/components/ui/inputs/lane-selector/lane-placeholder.tsx @@ -11,6 +11,7 @@ export type LanePlaceholderProps = { selectedLaneId?: LaneId; disabled?: boolean; showScope?: boolean; + loading?: boolean; } & HTMLAttributes; export function LanePlaceholder({ @@ -18,12 +19,17 @@ export function LanePlaceholder({ disabled, className, showScope = true, + loading, ...rest }: LanePlaceholderProps) { const laneIdStr = selectedLaneId?.isDefault() ? selectedLaneId.name : (showScope && selectedLaneId?.toString()) || selectedLaneId?.name; + if (loading) { + return null; + } + return (
diff --git a/components/ui/inputs/lane-selector/lane-selector-list.tsx b/components/ui/inputs/lane-selector/lane-selector-list.tsx index c6d1e69bef31..87dd227ad493 100644 --- a/components/ui/inputs/lane-selector/lane-selector-list.tsx +++ b/components/ui/inputs/lane-selector/lane-selector-list.tsx @@ -39,6 +39,7 @@ export function LaneSelectorList({ listNavigator, // eslint-disable-next-line @typescript-eslint/no-unused-vars forceCloseOnEnter, + loading, ...rest }: LaneSelectorListProps) { const navigate = useNavigate(); @@ -156,6 +157,8 @@ export function LaneSelectorList({ } }, [selectedLaneId?.toString()]); + if (loading) return null; + return (
{groupByScope && diff --git a/components/ui/inputs/lane-selector/lane-selector.tsx b/components/ui/inputs/lane-selector/lane-selector.tsx index d3e9cca881f4..e12d723298d9 100644 --- a/components/ui/inputs/lane-selector/lane-selector.tsx +++ b/components/ui/inputs/lane-selector/lane-selector.tsx @@ -29,6 +29,7 @@ export type LaneSelectorProps = { sortOptions?: LaneSelectorSortBy[]; scopeIconLookup?: Map; forceCloseOnEnter?: boolean; + loading?: boolean; } & HTMLAttributes; export type GroupedLaneDropdownItem = [scope: string, lanes: LaneModel[]]; @@ -61,6 +62,7 @@ export function LaneSelector(props: LaneSelectorProps) { scopeIcon, scopeIconLookup, forceCloseOnEnter, + loading, ...rest } = props; @@ -235,7 +237,12 @@ export function LaneSelector(props: LaneSelectorProps) { }); }} placeholderContent={ - + } className={classnames(styles.dropdown, !multipleLanes && styles.disabled)} > diff --git a/components/ui/navigation/lane-switcher/lane-switcher.module.scss b/components/ui/navigation/lane-switcher/lane-switcher.module.scss index 08e4c1795efe..14ae9c1fa63e 100644 --- a/components/ui/navigation/lane-switcher/lane-switcher.module.scss +++ b/components/ui/navigation/lane-switcher/lane-switcher.module.scss @@ -35,3 +35,12 @@ flex: 0; width: 36px; } + +.loader { + color: var(--bit-bg-dent, #f6f6f6); + padding: 4px 0px; + + > span { + padding: 8px; + } +} diff --git a/components/ui/navigation/lane-switcher/lane-switcher.tsx b/components/ui/navigation/lane-switcher/lane-switcher.tsx index 14842a91de37..b44afc7addae 100644 --- a/components/ui/navigation/lane-switcher/lane-switcher.tsx +++ b/components/ui/navigation/lane-switcher/lane-switcher.tsx @@ -1,9 +1,10 @@ -import React, { HTMLAttributes, useEffect, useState, useRef } from 'react'; +import React, { HTMLAttributes, useRef } from 'react'; import { useLanes as defaultUseLanes } from '@teambit/lanes.hooks.use-lanes'; import { LaneSelector, LaneSelectorSortBy } from '@teambit/lanes.ui.inputs.lane-selector'; import { LanesModel } from '@teambit/lanes.ui.models.lanes-model'; import { MenuLinkItem } from '@teambit/design.ui.surfaces.menu.link-item'; import { LaneId } from '@teambit/lane-id'; +import { WordSkeleton } from '@teambit/base-ui.loaders.skeleton'; import classnames from 'classnames'; import styles from './lane-switcher.module.scss'; @@ -29,10 +30,8 @@ export function LaneSwitcher({ getHref = LanesModel.getLaneUrl, ...rest }: LaneSwitcherProps) { - const { lanesModel } = useLanes(); - const [viewedLane, setViewedLane] = useState(lanesModel?.viewedLane); const containerRef = useRef(null); - + const { lanesModel, loading } = useLanes(); const mainLane = lanesModel?.getDefaultLane(); const nonMainLanes = lanesModel?.getNonMainLanes() || []; @@ -45,37 +44,37 @@ export function LaneSwitcher({ : [] ); - useEffect(() => { - if (lanesModel?.viewedLane?.id.toString() !== viewedLane?.id.toString()) { - setViewedLane(lanesModel?.viewedLane); - } - }, [lanesModel?.viewedLane?.id.toString()]); - - const selectedLane = viewedLane || mainLane; + const selectedLane = lanesModel?.viewedLane || mainLane; const selectedLaneGalleryHref = selectedLane && getHref(selectedLane.id); return (
- -
-
- - - + {loading && } + { + + }
+ {!loading && ( +
+ + + +
+ )}
); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fab5202d027..28f4a49fdda9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39120,7 +39120,7 @@ packages: dev: false /@teambit/component.modules.component-url@0.0.128(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-E5szS9hGfk/lAwEma3/ViMkm32A=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/teambit-component.modules.component-url-0.0.128.tgz} + resolution: {integrity: sha1-E5szS9hGfk/lAwEma3/ViMkm32A=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/@teambit-component.modules.component-url-0.0.128.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: 17.0.2 @@ -39134,7 +39134,7 @@ packages: dev: false /@teambit/component.modules.component-url@0.0.140(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-cmcWwISlL+PSKta3Q4HkpLTARBc=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/teambit-component.modules.component-url-0.0.140.tgz} + resolution: {integrity: sha1-cmcWwISlL+PSKta3Q4HkpLTARBc=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/@teambit-component.modules.component-url-0.0.140.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: 17.0.2 @@ -39148,7 +39148,7 @@ packages: dev: false /@teambit/component.modules.component-url@0.0.151(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-/dj3W/oKQC/p0X74zRJ+Mov/bec=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/teambit-component.modules.component-url-0.0.151.tgz} + resolution: {integrity: sha1-/dj3W/oKQC/p0X74zRJ+Mov/bec=, tarball: https://node-registry.bit.cloud/@teambit/component.modules.component-url/-/@teambit-component.modules.component-url-0.0.151.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: 17.0.2 @@ -39175,7 +39175,7 @@ packages: dev: false /@teambit/component.ui.badges.component-count@0.0.10(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-/KG3gyg1xt3Gk+7z1Yf2Ww9qd50=, tarball: https://node-registry.bit.cloud/tarballs/teambit.component/ui/badges/component-count@0.0.10.tgz} + resolution: {integrity: sha1-/KG3gyg1xt3Gk+7z1Yf2Ww9qd50=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.badges.component-count/-/@teambit-component.ui.badges.component-count-0.0.10.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39207,7 +39207,7 @@ packages: dev: false /@teambit/component.ui.component-compare.hooks.use-component-compare-url@0.0.3(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-mNono316l0rxMgrR/xuTDv4Rp/E=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.hooks.use-component-compare-url/-/teambit-component.ui.component-compare.hooks.use-component-compare-url-0.0.3.tgz} + resolution: {integrity: sha1-mNono316l0rxMgrR/xuTDv4Rp/E=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.hooks.use-component-compare-url/-/@teambit-component.ui.component-compare.hooks.use-component-compare-url-0.0.3.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39237,7 +39237,7 @@ packages: dev: false /@teambit/component.ui.component-compare.models.component-compare-change-type@0.0.1(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-2Er9FIkcKdYqxW2aeyZ5Gd370Kc=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-change-type/-/teambit-component.ui.component-compare.models.component-compare-change-type-0.0.1.tgz} + resolution: {integrity: sha1-2Er9FIkcKdYqxW2aeyZ5Gd370Kc=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-change-type/-/@teambit-component.ui.component-compare.models.component-compare-change-type-0.0.1.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39249,7 +39249,7 @@ packages: dev: false /@teambit/component.ui.component-compare.models.component-compare-hooks@0.0.4(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-kVPksYnom+gsZNPMZVx6uJqAHlI=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-hooks/-/teambit-component.ui.component-compare.models.component-compare-hooks-0.0.4.tgz} + resolution: {integrity: sha1-kVPksYnom+gsZNPMZVx6uJqAHlI=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-hooks/-/@teambit-component.ui.component-compare.models.component-compare-hooks-0.0.4.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39328,7 +39328,7 @@ packages: dev: false /@teambit/component.ui.component-compare.models.component-compare-state@0.0.2(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-KOvqyfEiwlEHJBWwxZZtLxWBc74=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-state/-/teambit-component.ui.component-compare.models.component-compare-state-0.0.2.tgz} + resolution: {integrity: sha1-KOvqyfEiwlEHJBWwxZZtLxWBc74=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.models.component-compare-state/-/@teambit-component.ui.component-compare.models.component-compare-state-0.0.2.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39354,7 +39354,7 @@ packages: dev: false /@teambit/component.ui.component-compare.utils.lazy-loading@0.0.1(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-K4iYPn+ng7gCPPwwQ3C3vdAWzDE=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.utils.lazy-loading/-/teambit-component.ui.component-compare.utils.lazy-loading-0.0.1.tgz} + resolution: {integrity: sha1-K4iYPn+ng7gCPPwwQ3C3vdAWzDE=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.component-compare.utils.lazy-loading/-/@teambit-component.ui.component-compare.utils.lazy-loading-0.0.1.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39367,7 +39367,7 @@ packages: dev: false /@teambit/component.ui.deprecation-icon@0.0.494(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-h1I4/6SkzcVxwWznLLLEW4M8r6g=, tarball: https://node-registry.bit.cloud/tarballs/teambit.component/ui/deprecation-icon@0.0.494.tgz} + resolution: {integrity: sha1-h1I4/6SkzcVxwWznLLLEW4M8r6g=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.deprecation-icon/-/@teambit-component.ui.deprecation-icon-0.0.494.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -39384,7 +39384,7 @@ packages: dev: false /@teambit/component.ui.deprecation-icon@0.0.500(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-KGzcqsYSN1S/mTpBE7Ohs8qm414=, tarball: https://node-registry.bit.cloud/tarballs/teambit.component/ui/deprecation-icon@0.0.500.tgz} + resolution: {integrity: sha1-KGzcqsYSN1S/mTpBE7Ohs8qm414=, tarball: https://node-registry.bit.cloud/@teambit/component.ui.deprecation-icon/-/@teambit-component.ui.deprecation-icon-0.0.500.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -40368,7 +40368,7 @@ packages: dev: false /@teambit/design.ui.pill-label@0.0.350(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-gPPLmQYfh6MMno1X4Hnu3eG+Pqw=, tarball: https://node-registry.bit.cloud/tarballs/teambit.design/ui/pill-label@0.0.350.tgz} + resolution: {integrity: sha1-gPPLmQYfh6MMno1X4Hnu3eG+Pqw=, tarball: https://node-registry.bit.cloud/@teambit/design.ui.pill-label/-/@teambit-design.ui.pill-label-0.0.350.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -40999,7 +40999,7 @@ packages: dev: false /@teambit/documenter.ui.heading@4.1.6(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-nCoruiI/7n5xcXd4GEMdtuU5nIU=, tarball: https://node-registry.bit.cloud/tarballs/teambit.documenter/ui/heading@4.1.6.tgz} + resolution: {integrity: sha1-nCoruiI/7n5xcXd4GEMdtuU5nIU=, tarball: https://node-registry.bit.cloud/@teambit/documenter.ui.heading/-/@teambit-documenter.ui.heading-4.1.6.tgz} peerDependencies: react: ^16.8.0 || ^17.0.0 react-dom: ^16.8.0 || ^17.0.0 @@ -41062,7 +41062,7 @@ packages: dev: false /@teambit/documenter.ui.linked-heading@4.1.8(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-LGpmPIXZSWsXbYgkS/WR1VxJcCg=, tarball: https://node-registry.bit.cloud/tarballs/teambit.documenter/ui/linked-heading@4.1.8.tgz} + resolution: {integrity: sha1-LGpmPIXZSWsXbYgkS/WR1VxJcCg=, tarball: https://node-registry.bit.cloud/@teambit/documenter.ui.linked-heading/-/@teambit-documenter.ui.linked-heading-4.1.8.tgz} peerDependencies: react: ^16.8.0 || ^17.0.0 react-dom: ^16.8.0 || ^17.0.0 @@ -41276,7 +41276,7 @@ packages: dev: false /@teambit/envs.ui.env-icon@0.0.486(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-w7KPvgzoXLcmnBjlu8gNuRffmsA=, tarball: https://node-registry.bit.cloud/tarballs/teambit.envs/ui/env-icon@0.0.486.tgz} + resolution: {integrity: sha1-w7KPvgzoXLcmnBjlu8gNuRffmsA=, tarball: https://node-registry.bit.cloud/@teambit/envs.ui.env-icon/-/@teambit-envs.ui.env-icon-0.0.486.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -41288,7 +41288,7 @@ packages: dev: false /@teambit/envs.ui.env-icon@0.0.492(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-WSxQWxQY/fhy5bqXSjp3fz3XUDY=, tarball: https://node-registry.bit.cloud/tarballs/teambit.envs/ui/env-icon@0.0.492.tgz} + resolution: {integrity: sha1-WSxQWxQY/fhy5bqXSjp3fz3XUDY=, tarball: https://node-registry.bit.cloud/@teambit/envs.ui.env-icon/-/@teambit-envs.ui.env-icon-0.0.492.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -41619,7 +41619,7 @@ packages: react-dom: 17.0.2(react@17.0.2) /@teambit/explorer.ui.gallery.component-card@0.0.495(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-gkm3X+OtPpzJSabL3xj7V6z75XA=, tarball: https://node-registry.bit.cloud/tarballs/teambit.explorer/ui/gallery/component-card@0.0.495.tgz} + resolution: {integrity: sha1-gkm3X+OtPpzJSabL3xj7V6z75XA=, tarball: https://node-registry.bit.cloud/@teambit/explorer.ui.gallery.component-card/-/@teambit-explorer.ui.gallery.component-card-0.0.495.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -41649,7 +41649,7 @@ packages: dev: false /@teambit/explorer.ui.gallery.component-card@0.0.507(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-p3EePsVfQINlcsiNPimNv3dK1Po=, tarball: https://node-registry.bit.cloud/tarballs/teambit.explorer/ui/gallery/component-card@0.0.507.tgz} + resolution: {integrity: sha1-p3EePsVfQINlcsiNPimNv3dK1Po=, tarball: https://node-registry.bit.cloud/@teambit/explorer.ui.gallery.component-card/-/@teambit-explorer.ui.gallery.component-card-0.0.507.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -41829,7 +41829,7 @@ packages: user-home: 2.0.0 /@teambit/html.modules.create-element-from-string@0.0.104: - resolution: {integrity: sha1-yk2HNfv5z+0qh75v4SL0qkRNP9U=, tarball: https://node-registry.bit.cloud/tarballs/teambit.html/modules/create-element-from-string@0.0.104.tgz} + resolution: {integrity: sha1-yk2HNfv5z+0qh75v4SL0qkRNP9U=, tarball: https://node-registry.bit.cloud/@teambit/html.modules.create-element-from-string/-/@teambit-html.modules.create-element-from-string-0.0.104.tgz} engines: {node: '>=12.22.0'} dev: false @@ -41858,7 +41858,7 @@ packages: dev: false /@teambit/lanes.hooks.use-lane-components@0.0.149(@apollo/client@3.6.9)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-QnNvne623/kMU2CO4PdxzQk1JKo=, tarball: https://node-registry.bit.cloud/@teambit/lanes.hooks.use-lane-components/-/teambit-lanes.hooks.use-lane-components-0.0.149.tgz} + resolution: {integrity: sha1-QnNvne623/kMU2CO4PdxzQk1JKo=, tarball: https://node-registry.bit.cloud/@teambit/lanes.hooks.use-lane-components/-/@teambit-lanes.hooks.use-lane-components-0.0.149.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@apollo/client': ^3.6.0 @@ -42343,7 +42343,7 @@ packages: dev: false /@teambit/preview.ui.preview-placeholder@0.0.496(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-MgSJUaYFpLjSPyLWttgR63sM/Xg=, tarball: https://node-registry.bit.cloud/tarballs/teambit.preview/ui/preview-placeholder@0.0.496.tgz} + resolution: {integrity: sha1-MgSJUaYFpLjSPyLWttgR63sM/Xg=, tarball: https://node-registry.bit.cloud/@teambit/preview.ui.preview-placeholder/-/@teambit-preview.ui.preview-placeholder-0.0.496.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42368,7 +42368,6 @@ packages: react-dom: 17.0.2(react@17.0.2) transitivePeerDependencies: - '@teambit/legacy' - dev: true /@teambit/react.instructions.react-native.adding-tests@0.0.1(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha1-Ej1pIaYbWdwLNUbsHMjEQQ/utnQ=, tarball: https://node-registry.bit.cloud/tarballs/teambit.react/instructions/react-native/adding-tests@0.0.1.tgz} @@ -42522,7 +42521,7 @@ packages: dev: false /@teambit/scope.ui.hooks.use-scope@0.0.240(@apollo/client@3.6.9)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-ArQ+DdQNq/Aw2rjpUZcWpcveyck=, tarball: https://node-registry.bit.cloud/tarballs/teambit.scope/ui/hooks/use-scope@0.0.240.tgz} + resolution: {integrity: sha1-ArQ+DdQNq/Aw2rjpUZcWpcveyck=, tarball: https://node-registry.bit.cloud/@teambit/scope.ui.hooks.use-scope/-/@teambit-scope.ui.hooks.use-scope-0.0.240.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@apollo/client': ^3.6.0 @@ -42590,7 +42589,7 @@ packages: dev: false /@teambit/scope.ui.scope-title@0.0.508(@testing-library/react@12.1.5)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-2lOC51kJif9bWAma5M4yaPPo/U4=, tarball: https://node-registry.bit.cloud/@teambit/scope.ui.scope-title/-/teambit-scope.ui.scope-title-0.0.508.tgz} + resolution: {integrity: sha1-2lOC51kJif9bWAma5M4yaPPo/U4=, tarball: https://node-registry.bit.cloud/@teambit/scope.ui.scope-title/-/@teambit-scope.ui.scope-title-0.0.508.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42662,16 +42661,16 @@ packages: dev: false /@teambit/toolbox.string.ellipsis@0.0.172: - resolution: {integrity: sha1-nbLwbPi2d8Qeg6aCX2TYlARSRew=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/string/ellipsis@0.0.172.tgz} + resolution: {integrity: sha1-nbLwbPi2d8Qeg6aCX2TYlARSRew=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.string.ellipsis/-/@teambit-toolbox.string.ellipsis-0.0.172.tgz} engines: {node: '>=12.22.0'} dev: true /@teambit/toolbox.string.ellipsis@0.0.173: - resolution: {integrity: sha1-CY+uj2sRmPejtTCee4HDvLFeeI8=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/string/ellipsis@0.0.173.tgz} + resolution: {integrity: sha1-CY+uj2sRmPejtTCee4HDvLFeeI8=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.string.ellipsis/-/@teambit-toolbox.string.ellipsis-0.0.173.tgz} engines: {node: '>=12.22.0'} /@teambit/toolbox.string.ellipsis@0.0.181: - resolution: {integrity: sha1-85eglHHFlGiDBGQi9V5m4z9Tr+w=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/string/ellipsis@0.0.181.tgz} + resolution: {integrity: sha1-85eglHHFlGiDBGQi9V5m4z9Tr+w=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.string.ellipsis/-/@teambit-toolbox.string.ellipsis-0.0.181.tgz} engines: {node: '>=12.22.0'} dev: false @@ -42681,17 +42680,17 @@ packages: dev: false /@teambit/toolbox.string.get-initials@0.0.491: - resolution: {integrity: sha1-dMkF7hxhtmK2aXz7QCkAAajSrKI=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/string/get-initials@0.0.491.tgz} + resolution: {integrity: sha1-dMkF7hxhtmK2aXz7QCkAAajSrKI=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.string.get-initials/-/@teambit-toolbox.string.get-initials-0.0.491.tgz} engines: {node: '>=12.22.0'} dev: false /@teambit/toolbox.types.serializable@0.0.483: - resolution: {integrity: sha1-bUTnjhsNOvMScS8+fSEk80Bvq3E=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/types/serializable@0.0.483.tgz} + resolution: {integrity: sha1-bUTnjhsNOvMScS8+fSEk80Bvq3E=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.types.serializable/-/@teambit-toolbox.types.serializable-0.0.483.tgz} engines: {node: '>=12.22.0'} dev: true /@teambit/toolbox.types.serializable@0.0.491: - resolution: {integrity: sha1-VTrj6yH43qYR2efWo5lAnh4dizI=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/types/serializable@0.0.491.tgz} + resolution: {integrity: sha1-VTrj6yH43qYR2efWo5lAnh4dizI=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.types.serializable/-/@teambit-toolbox.types.serializable-0.0.491.tgz} engines: {node: '>=12.22.0'} dev: false @@ -42701,7 +42700,7 @@ packages: dev: false /@teambit/toolbox.url.add-avatar-query-params@0.0.492: - resolution: {integrity: sha1-dECMYTXTkMlAIUaK22cpmNlTsak=, tarball: https://node-registry.bit.cloud/tarballs/teambit.toolbox/url/add-avatar-query-params@0.0.492.tgz} + resolution: {integrity: sha1-dECMYTXTkMlAIUaK22cpmNlTsak=, tarball: https://node-registry.bit.cloud/@teambit/toolbox.url.add-avatar-query-params/-/@teambit-toolbox.url.add-avatar-query-params-0.0.492.tgz} engines: {node: '>=12.22.0'} dev: false @@ -42729,7 +42728,7 @@ packages: dev: false /@teambit/ui-foundation.ui.constants.z-indexes@0.0.498(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-q5rrOHpQsr+mATrJu4oRiyU2M2E=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/constants/z-indexes@0.0.498.tgz} + resolution: {integrity: sha1-q5rrOHpQsr+mATrJu4oRiyU2M2E=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.constants.z-indexes/-/@teambit-ui-foundation.ui.constants.z-indexes-0.0.498.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42760,7 +42759,7 @@ packages: dev: false /@teambit/ui-foundation.ui.empty-component-gallery@0.0.502(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-nbagGhpo63WfemtMvlC2tuGRsok=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/empty-component-gallery@0.0.502.tgz} + resolution: {integrity: sha1-nbagGhpo63WfemtMvlC2tuGRsok=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.empty-component-gallery/-/@teambit-ui-foundation.ui.empty-component-gallery-0.0.502.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42804,7 +42803,7 @@ packages: dev: false /@teambit/ui-foundation.ui.get-icon-from-file-name@0.0.495(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-KzxZCIVyVGbjJHC01V/8RI6cyhY=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/get-icon-from-file-name@0.0.495.tgz} + resolution: {integrity: sha1-KzxZCIVyVGbjJHC01V/8RI6cyhY=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.get-icon-from-file-name/-/@teambit-ui-foundation.ui.get-icon-from-file-name-0.0.495.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42817,7 +42816,7 @@ packages: vscode-icons-js: 11.0.0 /@teambit/ui-foundation.ui.global-loader@0.0.474(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-7X0x1qLPQV7ySEUh5Rs1iXt+AX4=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/global-loader@0.0.474.tgz} + resolution: {integrity: sha1-7X0x1qLPQV7ySEUh5Rs1iXt+AX4=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.global-loader/-/@teambit-ui-foundation.ui.global-loader-0.0.474.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@teambit/legacy': 1.0.183 @@ -42831,7 +42830,7 @@ packages: dev: true /@teambit/ui-foundation.ui.global-loader@0.0.487(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-t9IdwYuJBCHPUYK73ELRUHQHPVs=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/global-loader@0.0.487.tgz} + resolution: {integrity: sha1-t9IdwYuJBCHPUYK73ELRUHQHPVs=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.global-loader/-/@teambit-ui-foundation.ui.global-loader-0.0.487.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42844,7 +42843,7 @@ packages: dev: false /@teambit/ui-foundation.ui.global-loader@0.0.493(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-0DiiKiGSijunFyRRRSMD4R2iQMk=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/global-loader@0.0.493.tgz} + resolution: {integrity: sha1-0DiiKiGSijunFyRRRSMD4R2iQMk=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.global-loader/-/@teambit-ui-foundation.ui.global-loader-0.0.493.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42857,7 +42856,7 @@ packages: dev: false /@teambit/ui-foundation.ui.global-loader@0.0.497(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-5fU/29iOW7vvZBaoyaRiSUCyl2I=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/global-loader@0.0.497.tgz} + resolution: {integrity: sha1-5fU/29iOW7vvZBaoyaRiSUCyl2I=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.global-loader/-/@teambit-ui-foundation.ui.global-loader-0.0.497.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42886,7 +42885,7 @@ packages: dev: false /@teambit/ui-foundation.ui.hooks.use-data-query@0.0.496(@apollo/client@3.6.9)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-oL6ZvMzY0Y/1C9rivf+CdsmJFdI=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/hooks/use-data-query@0.0.496.tgz} + resolution: {integrity: sha1-oL6ZvMzY0Y/1C9rivf+CdsmJFdI=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.hooks.use-data-query/-/@teambit-ui-foundation.ui.hooks.use-data-query-0.0.496.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@apollo/client': ^3.6.0 @@ -42902,7 +42901,7 @@ packages: dev: false /@teambit/ui-foundation.ui.hooks.use-data-query@0.0.500(@apollo/client@3.6.9)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-mKHB9+tWg+S+JJ5s4peW17vUf3s=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/hooks/use-data-query@0.0.500.tgz} + resolution: {integrity: sha1-mKHB9+tWg+S+JJ5s4peW17vUf3s=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.hooks.use-data-query/-/@teambit-ui-foundation.ui.hooks.use-data-query-0.0.500.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@apollo/client': ^3.6.0 @@ -42918,7 +42917,7 @@ packages: dev: false /@teambit/ui-foundation.ui.is-browser@0.0.486(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-KCR60Lp5r8XU/qN5aU6JnKyCo0U=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/is-browser@0.0.486.tgz} + resolution: {integrity: sha1-KCR60Lp5r8XU/qN5aU6JnKyCo0U=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.is-browser/-/@teambit-ui-foundation.ui.is-browser-0.0.486.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42930,7 +42929,7 @@ packages: dev: false /@teambit/ui-foundation.ui.is-browser@0.0.492(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-K8F9fjln4vfhtryfNuzr8RulqLo=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/is-browser@0.0.492.tgz} + resolution: {integrity: sha1-K8F9fjln4vfhtryfNuzr8RulqLo=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.is-browser/-/@teambit-ui-foundation.ui.is-browser-0.0.492.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42942,7 +42941,7 @@ packages: dev: false /@teambit/ui-foundation.ui.keycap@0.0.486(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-8os8TC+l8rJUXtJe4ehMT7H6+98=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/keycap@0.0.486.tgz} + resolution: {integrity: sha1-8os8TC+l8rJUXtJe4ehMT7H6+98=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.keycap/-/@teambit-ui-foundation.ui.keycap-0.0.486.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -42956,7 +42955,7 @@ packages: dev: false /@teambit/ui-foundation.ui.keycap@0.0.492(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-/XlMitYklsPYhMXE0hPJZw+1JHM=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/keycap@0.0.492.tgz} + resolution: {integrity: sha1-/XlMitYklsPYhMXE0hPJZw+1JHM=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.keycap/-/@teambit-ui-foundation.ui.keycap-0.0.492.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43077,7 +43076,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.notification-context@0.0.487(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-WvEy3IkkGkdqrKLZvWO5ay+2LH4=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/notification-context@0.0.487.tgz} + resolution: {integrity: sha1-WvEy3IkkGkdqrKLZvWO5ay+2LH4=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.notification-context/-/@teambit-ui-foundation.ui.notifications.notification-context-0.0.487.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43090,7 +43089,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.notification-context@0.0.493(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-LqvYMR2i+ZTOqyqWpBg/tXCzdgU=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/notification-context@0.0.493.tgz} + resolution: {integrity: sha1-LqvYMR2i+ZTOqyqWpBg/tXCzdgU=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.notification-context/-/@teambit-ui-foundation.ui.notifications.notification-context-0.0.493.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43103,7 +43102,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.notification-context@0.0.496(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-x23R91AzUscSV2WS2yZC2LY8CHE=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/notification-context@0.0.496.tgz} + resolution: {integrity: sha1-x23R91AzUscSV2WS2yZC2LY8CHE=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.notification-context/-/@teambit-ui-foundation.ui.notifications.notification-context-0.0.496.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43116,7 +43115,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.store@0.0.486(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-ZQ4iOfpuzj+jE12XS3b51WELT7I=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/store@0.0.486.tgz} + resolution: {integrity: sha1-ZQ4iOfpuzj+jE12XS3b51WELT7I=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.store/-/@teambit-ui-foundation.ui.notifications.store-0.0.486.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43128,7 +43127,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.store@0.0.492(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-8IWuSf9Qt6QHdudIbjqQ7IHiMFg=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/store@0.0.492.tgz} + resolution: {integrity: sha1-8IWuSf9Qt6QHdudIbjqQ7IHiMFg=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.store/-/@teambit-ui-foundation.ui.notifications.store-0.0.492.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43140,7 +43139,7 @@ packages: dev: false /@teambit/ui-foundation.ui.notifications.store@0.0.495(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-SciOZ58oyKDy8q/0nvQ4OpvuPz8=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/notifications/store@0.0.495.tgz} + resolution: {integrity: sha1-SciOZ58oyKDy8q/0nvQ4OpvuPz8=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.notifications.store/-/@teambit-ui-foundation.ui.notifications.store-0.0.495.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43172,7 +43171,7 @@ packages: dev: false /@teambit/ui-foundation.ui.react-router.slot-router@0.0.501(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react-router-dom@6.3.0)(react@17.0.2): - resolution: {integrity: sha1-uUc0/zKzRjSFXhWhBUDlDnIJuO0=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/react-router/slot-router@0.0.501.tgz} + resolution: {integrity: sha1-uUc0/zKzRjSFXhWhBUDlDnIJuO0=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.react-router.slot-router/-/@teambit-ui-foundation.ui.react-router.slot-router-0.0.501.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43192,7 +43191,7 @@ packages: dev: false /@teambit/ui-foundation.ui.react-router.use-query@0.0.496(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-rCJcyZss+uCe7COvKqfs0UQ0TME=, tarball: https://node-registry.bit.cloud/tarballs/teambit.ui-foundation/ui/react-router/use-query@0.0.496.tgz} + resolution: {integrity: sha1-rCJcyZss+uCe7COvKqfs0UQ0TME=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.react-router.use-query/-/@teambit-ui-foundation.ui.react-router.use-query-0.0.496.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43312,7 +43311,7 @@ packages: dev: false /@teambit/ui-foundation.ui.tree.drawer@0.0.512(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-bU5bUChvqZ6FgR2iDflWJU+hU8U=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.tree.drawer/-/teambit-ui-foundation.ui.tree.drawer-0.0.512.tgz} + resolution: {integrity: sha1-bU5bUChvqZ6FgR2iDflWJU+hU8U=, tarball: https://node-registry.bit.cloud/@teambit/ui-foundation.ui.tree.drawer/-/@teambit-ui-foundation.ui.tree.drawer-0.0.512.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43445,7 +43444,7 @@ packages: - '@teambit/legacy' /@teambit/workspace.ui.load-preview@0.0.489(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-e9OonGao9UxvvbCfsJKdGTPbXIg=, tarball: https://node-registry.bit.cloud/tarballs/teambit.workspace/ui/load-preview@0.0.489.tgz} + resolution: {integrity: sha1-e9OonGao9UxvvbCfsJKdGTPbXIg=, tarball: https://node-registry.bit.cloud/@teambit/workspace.ui.load-preview/-/@teambit-workspace.ui.load-preview-0.0.489.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -43459,7 +43458,7 @@ packages: dev: false /@teambit/workspace.ui.load-preview@0.0.499(@testing-library/react@12.1.5)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-lvfO7zmO9ZnlI+NPWAFOW3ViZwo=, tarball: https://node-registry.bit.cloud/tarballs/teambit.workspace/ui/load-preview@0.0.499.tgz} + resolution: {integrity: sha1-lvfO7zmO9ZnlI+NPWAFOW3ViZwo=, tarball: https://node-registry.bit.cloud/@teambit/workspace.ui.load-preview/-/@teambit-workspace.ui.load-preview-0.0.499.tgz} engines: {node: '>=12.22.0'} peerDependencies: '@testing-library/react': ^12.1.5 @@ -43493,7 +43492,7 @@ packages: dev: false /@teambit/workspace.ui.workspace-component-card@0.0.510(@testing-library/react@12.1.5)(@types/react@17.0.8)(react-dom@17.0.2)(react@17.0.2): - resolution: {integrity: sha1-bO15xcj1TSQ5iEfw8QBu6GxTigM=, tarball: https://node-registry.bit.cloud/tarballs/teambit.workspace/ui/workspace-component-card@0.0.510.tgz} + resolution: {integrity: sha1-bO15xcj1TSQ5iEfw8QBu6GxTigM=, tarball: https://node-registry.bit.cloud/@teambit/workspace.ui.workspace-component-card/-/@teambit-workspace.ui.workspace-component-card-0.0.510.tgz} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -69482,6 +69481,7 @@ packages: '@teambit/design.ui.input.option-button': 1.0.0(react-dom@17.0.2)(react@17.0.2) '@teambit/design.ui.tooltip': 0.0.361(react-dom@17.0.2)(react@17.0.2) '@teambit/harmony': 0.4.6 + '@teambit/react.content.react-overview': 1.95.0(react-dom@17.0.2)(react@17.0.2) '@teambit/react.instructions.react.adding-compositions': registry.npmjs.org/@teambit/react.instructions.react.adding-compositions@0.0.6(react-dom@17.0.2)(react@17.0.2) '@teambit/react.instructions.react.adding-tests': registry.npmjs.org/@teambit/react.instructions.react.adding-tests@0.0.6(react-dom@17.0.2)(react@17.0.2) '@teambit/react.rendering.ssr': 0.0.3(react-dom@17.0.2)(react@17.0.2) @@ -71471,6 +71471,7 @@ packages: version: 0.0.10 dependencies: expose-loader: 3.1.0(webpack@5.82.1) + json-formatter-js: 2.3.4 transitivePeerDependencies: - webpack dev: false diff --git a/scopes/component/component/component-factory.ts b/scopes/component/component/component-factory.ts index 7b2437dffa3b..4bbd20a5392b 100644 --- a/scopes/component/component/component-factory.ts +++ b/scopes/component/component/component-factory.ts @@ -115,7 +115,15 @@ export interface ComponentFactory { */ getGraphIds(ids?: ComponentID[], shouldThrowOnMissingDep?: boolean): Promise; - getLogs(id: ComponentID, shortHash?: boolean, startsFrom?: string): Promise; + getLogs( + id: ComponentID, + shortHash?: boolean, + head?: string, + startFrom?: string, + stopAt?: string, + startFromOffset?: number, + stopAtOffset?: number + ): Promise; /** * returns a specific state of a component by hash or semver. diff --git a/scopes/component/component/component.graphql.ts b/scopes/component/component/component.graphql.ts index 1dc97ec2a591..4177e7895fea 100644 --- a/scopes/component/component/component.graphql.ts +++ b/scopes/component/component/component.graphql.ts @@ -115,6 +115,14 @@ export function componentSchema(componentExtension: ComponentMain) { start traversing logs from the fetched component's head """ takeHeadFromComponent: Boolean + """ + start slicing logs from this version + """ + startFrom: String + """ + end slicing logs until this version + """ + until: String ): [LogEntry]! aspects(include: [String]): [Aspect] @@ -200,6 +208,8 @@ export function componentSchema(componentExtension: ComponentMain) { head?: string; sort?: string; takeHeadFromComponent: boolean; + startFrom?: string; + until?: string; } ) => { let head = filter?.head; diff --git a/scopes/component/component/component.ts b/scopes/component/component/component.ts index 77063bf01935..c1b489f9cd36 100644 --- a/scopes/component/component/component.ts +++ b/scopes/component/component/component.ts @@ -114,26 +114,31 @@ export class Component implements IComponent { return this.state.aspects.get(id)?.serialize(); } - async getLogs(filter?: { type?: string; offset?: number; limit?: number; head?: string; sort?: string }) { - const allLogs = await this.factory.getLogs(this.id, false, filter?.head); - - if (!filter) return allLogs; - - const { type, limit, offset, sort } = filter; - + async getLogs(filter?: { + type?: string; + offset?: number; + limit?: number; + head?: string; + sort?: string; + startFrom?: string; + until?: string; + }) { + const { type, limit, offset, sort, head, startFrom, until } = filter || {}; const typeFilter = (snap) => { if (type === 'tag') return snap.tag; if (type === 'snap') return !snap.tag; return true; }; - let filteredLogs = (type && allLogs.filter(typeFilter)) || allLogs; - if (sort !== 'asc') filteredLogs = filteredLogs.reverse(); + const allLogs = await this.factory.getLogs(this.id, false, head, startFrom, until, offset, limit); - if (limit) { - filteredLogs = slice(filteredLogs, offset, limit + (offset || 0)); + if (!filter) { + return allLogs; } + let filteredLogs = (type && allLogs.filter(typeFilter)) || allLogs; + if (sort !== 'asc') filteredLogs = filteredLogs.reverse(); + return filteredLogs; } diff --git a/scopes/component/component/index.ts b/scopes/component/component/index.ts index e7a01c62cab0..9aebd0b6ddb7 100644 --- a/scopes/component/component/index.ts +++ b/scopes/component/component/index.ts @@ -33,7 +33,7 @@ export { Section } from './section'; export { ComponentContext, ComponentDescriptorContext, useComponentDescriptor } from './ui/context/component-context'; export type { ComponentProviderProps, ComponentDescriptorProviderProps } from './ui/context'; export { ComponentProvider, ComponentDescriptorProvider } from './ui/context'; -export { componentFields, componentIdFields, componentOverviewFields } from './ui'; +export { componentFields, componentIdFields, componentOverviewFields, COMPONENT_QUERY_FIELDS } from './ui'; export { NavPlugin, ConsumePlugin, diff --git a/scopes/component/component/ui/component.tsx b/scopes/component/component/ui/component.tsx index 1fb97abedd6b..47c83781d88c 100644 --- a/scopes/component/component/ui/component.tsx +++ b/scopes/component/component/ui/component.tsx @@ -55,19 +55,27 @@ export function Component({ const resolvedComponentIdStr = path || idFromLocation; const componentFiltersFromProps = useComponentFilters?.() || {}; - const useComponentOptions = { - logFilters: { - log: { logLimit: 3 }, - ...componentFiltersFromProps, - }, - customUseComponent: useComponent, - }; + const useComponentOptions = componentFiltersFromProps.loading + ? { + logFilters: { + ...componentFiltersFromProps, + }, + customUseComponent: useComponent, + } + : { + logFilters: { + ...componentFiltersFromProps, + log: { logLimit: 3, ...componentFiltersFromProps.log }, + }, + customUseComponent: useComponent, + }; const { component, componentDescriptor, error } = useComponentQuery( host, componentId?.toString() || idFromLocation, useComponentOptions ); + // trigger onComponentChange when component changes useEffect(() => onComponentChange?.(component), [component]); // cleanup when unmounting component @@ -77,7 +85,7 @@ export function Component({ const before = useMemo(() => pageItems.filter((x) => x.type === 'before').map((x) => x.content), [pageItems]); const after = useMemo(() => pageItems.filter((x) => x.type === 'after').map((x) => x.content), [pageItems]); - if (error) return error.renderError(); + if (error) return error?.renderError(); if (!component) return
; return ( diff --git a/scopes/component/component/ui/index.ts b/scopes/component/component/ui/index.ts index 5c8be5db4d79..c64fa8d42593 100644 --- a/scopes/component/component/ui/index.ts +++ b/scopes/component/component/ui/index.ts @@ -5,6 +5,11 @@ export { ComponentModel, ComponentModelProps } from './component-model'; export { ComponentContext, ComponentProvider } from './context'; export { useComponent } from './use-component'; export { TopBarNav } from './top-bar-nav'; -export { componentIdFields, componentOverviewFields, componentFields } from './use-component-query'; +export { + componentIdFields, + componentOverviewFields, + componentFields, + COMPONENT_QUERY_FIELDS, +} from './use-component-query'; export type { ComponentQueryResult } from './use-component-query'; export { useIdFromLocation } from './use-component-from-location'; diff --git a/scopes/component/component/ui/menu/menu.tsx b/scopes/component/component/ui/menu/menu.tsx index ec6a6a2e8071..8056b027b1fb 100644 --- a/scopes/component/component/ui/menu/menu.tsx +++ b/scopes/component/component/ui/menu/menu.tsx @@ -55,6 +55,7 @@ export type MenuProps = { useComponent?: UseComponentType; useComponentFilters?: () => Filters; + path?: string; }; function getComponentIdStr(componentIdStr?: string | (() => string | undefined)): string | undefined { @@ -83,6 +84,7 @@ export function ComponentMenu({ const componentId = _componentIdStr ? ComponentID.fromString(_componentIdStr) : undefined; const resolvedComponentIdStr = path || idFromLocation; const mainMenuItems = useMemo(() => groupBy(flatten(menuItemSlot.values()), 'category'), [menuItemSlot]); + const { loading, ...componentFiltersFromProps } = useComponentFilters?.() || {}; const RightSide = (
@@ -93,7 +95,8 @@ export function ComponentMenu({ host={host} componentId={componentId?.toString() || idFromLocation} useComponent={useComponent} - useComponentFilters={useComponentFilters} + componentFilters={componentFiltersFromProps} + loading={loading} /> @@ -122,58 +125,68 @@ export type VersionRelatedDropdownsProps = { consumeMethods?: ConsumeMethodSlot; className?: string; host: string; - useComponentFilters?: () => Filters; + componentFilters?: Filters; + loading?: boolean; useComponent?: UseComponentType; componentId?: string; }; export function VersionRelatedDropdowns({ componentId, - useComponentFilters, + componentFilters: componentFiltersFromProps = {}, useComponent, consumeMethods, className, + loading: loadingFromProps, host, }: VersionRelatedDropdownsProps) { - const componentFiltersFromProps = useComponentFilters?.() || {}; const query = useQuery(); const componentVersion = query.get('version'); const isTag = componentVersion ? semver.valid(componentVersion) : undefined; const isSnap = componentVersion ? !isTag : undefined; // initially fetch just the component data - const initialFetchOptions = { - logFilters: { - log: { - logLimit: 3, + const initialFetchOptions = React.useMemo( + () => ({ + logFilters: { + ...componentFiltersFromProps, + log: { + logLimit: 3, + ...componentFiltersFromProps.log, + }, }, - ...componentFiltersFromProps, - }, - customUseComponent: useComponent, - }; + skip: loadingFromProps, + customUseComponent: useComponent, + }), + [loadingFromProps, componentFiltersFromProps, componentVersion] + ); - const { component, loading } = useComponentQuery(host, componentId, initialFetchOptions); + const { component, loading: loadingComponent } = useComponentQuery(host, componentId, initialFetchOptions); - const componentWithLogsOptions = React.useMemo( - () => ({ + const loading = React.useMemo(() => loadingComponent || loadingFromProps, [loadingComponent, loadingFromProps]); + + const useVersions = React.useCallback(() => { + const componentWithLogsOptions = { logFilters: { + fetchLogsByTypeSeparately: true, + ...componentFiltersFromProps, snapLog: { logLimit: 10, - logHead: isSnap ? componentVersion : undefined, + logStartFrom: isSnap ? componentVersion ?? undefined : undefined, + logOffset: isSnap ? -3 : undefined, + ...componentFiltersFromProps.snapLog, }, tagLog: { logLimit: 10, - logHead: isTag ? componentVersion : undefined, + logStartFrom: isTag ? componentVersion ?? undefined : undefined, + logOffset: isTag ? -3 : undefined, + ...componentFiltersFromProps.tagLog, }, - fetchLogsByTypeSeparately: true, - ...componentFiltersFromProps, }, + skip: loadingFromProps, customUseComponent: useComponent, - }), - [componentVersion, isTag, isSnap] - ); + }; - const useVersions = () => { const { componentLogs = {}, loading: loadingLogs } = useComponentQuery(host, componentId, componentWithLogsOptions); return { loading: loadingLogs, @@ -181,7 +194,7 @@ export function VersionRelatedDropdowns({ snaps: (componentLogs.snaps || []).map((snap) => ({ ...snap, version: snap.hash })), tags: (componentLogs.tags || []).map((tag) => ({ ...tag, version: tag.tag as string })), }; - }; + }, [componentVersion, isTag, isSnap, componentFiltersFromProps, loadingFromProps]); const location = useLocation(); const { lanesModel } = useLanes(); diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index 99cc38106175..aea5653c451f 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -85,6 +85,8 @@ export const componentFields = gql` sort: $logSort takeHeadFromComponent: $takeHeadFromComponent head: $logHead + startFrom: $logStartFrom + until: $logUntil ) @skip(if: $fetchLogsByTypeSeparately) { id message @@ -101,6 +103,8 @@ export const componentFields = gql` sort: $tagLogSort takeHeadFromComponent: $tagTakeHeadFromComponent head: $tagLogHead + startFrom: $tagStartFrom + until: $tagUntil ) @include(if: $fetchLogsByTypeSeparately) { id message @@ -117,6 +121,8 @@ export const componentFields = gql` sort: $snapLogSort takeHeadFromComponent: $snapTakeHeadFromComponent head: $snapLogHead + startFrom: $snapStartFrom + until: $snapUntil ) @include(if: $fetchLogsByTypeSeparately) { id message @@ -131,20 +137,26 @@ export const componentFields = gql` ${componentOverviewFields} `; -const COMPONENT_QUERY_FIELDS = ` +export const COMPONENT_QUERY_FIELDS = ` $logOffset: Int $logLimit: Int $logType: String $logHead: String $logSort: String + $logStartFrom: String + $logUntil: String $tagLogOffset: Int $tagLogLimit: Int $tagLogHead: String $tagLogSort: String + $tagStartFrom: String + $tagUntil: String $snapLogOffset: Int $snapLogLimit: Int $snapLogHead: String $snapLogSort: String + $snapStartFrom: String + $snapUntil: String $takeHeadFromComponent: Boolean $tagTakeHeadFromComponent: Boolean $snapTakeHeadFromComponent: Boolean @@ -203,6 +215,8 @@ export type LogFilter = { logOffset?: number; logLimit?: number; logHead?: string; + logStartFrom?: string; + logUntil?: string; logSort?: string; takeHeadFromComponent?: boolean; }; @@ -212,6 +226,7 @@ export type Filters = { tagLog?: LogFilter; snapLog?: LogFilter; fetchLogsByTypeSeparately?: boolean; + loading?: boolean; }; export type ComponentQueryResult = { @@ -222,16 +237,24 @@ export type ComponentQueryResult = { hasMoreLogs?: boolean; hasMoreSnaps?: boolean; hasMoreTags?: boolean; - loadMoreLogs?: () => void; - loadMoreTags?: () => void; - loadMoreSnaps?: () => void; + loadMoreLogs?: (backwards?: boolean) => void; + loadMoreTags?: (backwards?: boolean) => void; + loadMoreSnaps?: (backwards?: boolean) => void; snaps?: LegacyComponentLog[]; tags?: LegacyComponentLog[]; }; loading?: boolean; error?: ComponentError; }; - +function getOffsetValue(offset, limit) { + if (offset !== undefined) { + return offset; + } + if (limit !== undefined) { + return 0; + } + return undefined; +} /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ export function useComponentQuery( componentId: string, @@ -248,21 +271,25 @@ export function useComponentQuery( logSort: tagLogSort, logLimit: tagLogLimit, takeHeadFromComponent: tagLogTakeHeadFromComponent, + logStartFrom: tagStartFrom, + logUntil: tagUntil, } = tagLog || {}; - const { logHead, logOffset, logSort, logLimit, takeHeadFromComponent, logType } = log || {}; + const { logHead, logOffset, logSort, logLimit, takeHeadFromComponent, logType, logStartFrom, logUntil } = log || {}; const { logHead: snapLogHead, logOffset: snapLogOffset, logSort: snapLogSort, logLimit: snapLogLimit, takeHeadFromComponent: snapLogTakeHeadFromComponent, + logStartFrom: snapStartFrom, + logUntil: snapUntil, } = snapLog || {}; const variables = { id: componentId, extensionId: host, fetchLogsByTypeSeparately, - snapLogOffset: snapLogOffset ?? snapLogLimit ? 0 : undefined, - tagLogOffset: tagLogOffset ?? tagLogLimit ? 0 : undefined, + snapLogOffset: getOffsetValue(snapLogOffset, snapLogLimit), + tagLogOffset: getOffsetValue(tagLogOffset, tagLogLimit), logOffset: logOffset ?? logLimit ? 0 : undefined, logLimit, snapLogLimit, @@ -271,6 +298,12 @@ export function useComponentQuery( logHead, snapLogHead, tagLogHead, + logStartFrom, + snapStartFrom, + tagStartFrom, + logUntil, + snapUntil, + tagUntil, logSort, snapLogSort, tagLogSort, @@ -335,116 +368,131 @@ export function useComponentQuery( return hasMoreSnapLogs.current; }, [rawSnaps]); - const loadMoreLogs = React.useCallback(async () => { - if (logLimit) { - await fetchMore({ - variables: { - logOffset: offsetRef.current, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const fetchedComponent = fetchMoreResult.getHost.get; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); - hasMoreLogs.current = fetchedComponent.logs.length >= logLimit; - if (updatedLogs.length > prevComponent.logs.length) { - offsetRef.current = updatedLogs.length; + const loadMoreLogs = React.useCallback( + async (backwards = false) => { + const offset = offsetRef.current ? (backwards && -offsetRef.current) || offsetRef.current : undefined; + + if (logLimit) { + await fetchMore({ + variables: { + logOffset: offset, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const fetchedComponent = fetchMoreResult.getHost.get; + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); + hasMoreLogs.current = fetchedComponent.logs.length >= logLimit; + if (updatedLogs.length > prevComponent.logs.length) { + offsetRef.current = updatedLogs.length; + } + + return { + ...prev, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + logs: updatedLogs, + }, + }, + }; } - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - logs: updatedLogs, - }, - }, - }; - } + return prev; + }, + }); + } + }, + [logLimit, fetchMore] + ); - return prev; - }, - }); - } - }, [logLimit, fetchMore]); - - const loadMoreTags = React.useCallback(async () => { - if (tagLogLimit) { - await fetchMore({ - variables: { - tagLogOffset: tagOffsetRef.current, - tagLogLimit, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const fetchedComponent = fetchMoreResult.getHost.get; - const prevCompLogs = prevComponent.tagLogs; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedTags = mergeLogs(prevCompLogs, fetchedComponent.tagLogs); - if (updatedTags.length > prevCompLogs.length) { - tagOffsetRef.current = updatedTags.length; - } - hasMoreTagLogs.current = fetchedComponent.tagLogs.length >= tagLogLimit; - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - tagLogs: updatedTags, + const loadMoreTags = React.useCallback( + async (backwards = false) => { + const offset = tagOffsetRef.current ? (backwards && -tagOffsetRef.current) || tagOffsetRef.current : undefined; + + if (tagLogLimit) { + await fetchMore({ + variables: { + tagLogOffset: offset, + tagLogLimit, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const fetchedComponent = fetchMoreResult.getHost.get; + const prevCompLogs = prevComponent.tagLogs; + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedTags = mergeLogs(prevCompLogs, fetchedComponent.tagLogs); + if (updatedTags.length > prevCompLogs.length) { + tagOffsetRef.current = updatedTags.length; + } + hasMoreTagLogs.current = fetchedComponent.tagLogs.length >= tagLogLimit; + return { + ...prev, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + tagLogs: updatedTags, + }, }, - }, - }; - } - - return prev; - }, - }); - } - }, [tagLogLimit, fetchMore]); - - const loadMoreSnaps = React.useCallback(async () => { - if (snapLogLimit) { - await fetchMore({ - variables: { - snapLogOffset: snapOffsetRef.current, - snapLogLimit, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const prevCompLogs = prevComponent.snapLogs ?? []; - - const fetchedComponent = fetchMoreResult.getHost.get; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedSnaps = mergeLogs(prevCompLogs, fetchedComponent.snapLogs); - if (updatedSnaps.length > prevCompLogs.length) { - snapOffsetRef.current = updatedSnaps.length; + }; } - hasMoreSnapLogs.current = fetchedComponent.snapLogs.length >= snapLogLimit; - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - snapLogs: updatedSnaps, + + return prev; + }, + }); + } + }, + [tagLogLimit, fetchMore] + ); + + const loadMoreSnaps = React.useCallback( + async (backwards = false) => { + const offset = snapOffsetRef.current ? (backwards && -snapOffsetRef.current) || snapOffsetRef.current : undefined; + + if (snapLogLimit) { + await fetchMore({ + variables: { + snapLogOffset: offset, + snapLogLimit, + }, + updateQuery: (prev, { fetchMoreResult }) => { + if (!fetchMoreResult) return prev; + + const prevComponent = prev.getHost.get; + const prevCompLogs = prevComponent.snapLogs ?? []; + + const fetchedComponent = fetchMoreResult.getHost.get; + if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { + const updatedSnaps = mergeLogs(prevCompLogs, fetchedComponent.snapLogs); + if (updatedSnaps.length > prevCompLogs.length) { + snapOffsetRef.current = updatedSnaps.length; + } + hasMoreSnapLogs.current = fetchedComponent.snapLogs.length >= snapLogLimit; + return { + ...prev, + getHost: { + ...prev.getHost, + get: { + ...prevComponent, + snapLogs: updatedSnaps, + }, }, - }, - }; - } + }; + } - return prev; - }, - }); - } - }, [snapLogLimit, fetchMore]); + return prev; + }, + }); + } + }, + [snapLogLimit, fetchMore] + ); useEffect(() => { // @TODO @Kutner fix subscription for scope diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 86a4a725a22b..19698a8d7b00 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -71,10 +71,13 @@ function _VersionMenu( const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; const observer = React.useRef(); - const handleLoadMore = React.useCallback(() => { - if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(); - if (activeTabOrSnap === 'TAG') loadMoreTags?.(); - }, [activeTabOrSnap, tabs.length]); + const handleLoadMore = React.useCallback( + (backwards?: boolean) => { + if (activeTabOrSnap === 'SNAP') loadMoreSnaps?.(backwards); + if (activeTabOrSnap === 'TAG') loadMoreTags?.(backwards); + }, + [activeTabOrSnap, tabs.length] + ); const lastLogRef = React.useCallback( (node) => { @@ -95,6 +98,27 @@ function _VersionMenu( }, [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] ); + + const firstLogRef = React.useCallback( + (node) => { + if (loading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasMore) { + handleLoadMore(true); + } + }, + { + threshold: 0.1, + rootMargin: '100px', + } + ); + if (node) observer.current.observe(node); + }, + [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + ); + const multipleTabs = tabs.length > 1; const message = multipleTabs ? 'Switch to view tags, snaps, or lanes' @@ -139,17 +163,21 @@ function _VersionMenu( ))} {tabs[activeTabIndex]?.name !== 'LANE' && - tabs[activeTabIndex]?.payload.map((payload, index) => ( - - ))} + tabs[activeTabIndex]?.payload.map((payload, index) => { + const _ref = + index === 0 ? firstLogRef : (index === tabs[activeTabIndex]?.payload.length - 1 && lastLogRef) || ref; + return ( + + ); + })}
); @@ -158,8 +186,8 @@ function _VersionMenu( export type UseComponentVersionsResult = { tags?: DropdownComponentVersion[]; snaps?: DropdownComponentVersion[]; - loadMoreTags?: () => void; - loadMoreSnaps?: () => void; + loadMoreTags?: (backwards?: boolean) => void; + loadMoreSnaps?: (backwards?: boolean) => void; hasMoreTags?: boolean; hasMoreSnaps?: boolean; loading?: boolean; diff --git a/scopes/lanes/hooks/use-lanes/lanes-provider.tsx b/scopes/lanes/hooks/use-lanes/lanes-provider.tsx index 5c395fc5b592..182f6a8df9f7 100644 --- a/scopes/lanes/hooks/use-lanes/lanes-provider.tsx +++ b/scopes/lanes/hooks/use-lanes/lanes-provider.tsx @@ -27,6 +27,16 @@ export function LanesProvider({ const [lanesState, setLanesState] = useState(lanesModel); const [viewedLaneId, setViewedLaneId] = useState(viewedIdFromProps); + const updateViewedLane = useCallback( + (lane?: LaneId) => { + setViewedLaneId(lane); + setLanesState((state) => { + state?.setViewedOrDefaultLane(lane); + return state; + }); + }, + [lanesModel] + ); const location = useLocation(); const query = useQuery(); @@ -37,7 +47,7 @@ export function LanesProvider({ }, []); useEffect(() => { - if (viewedIdFromProps) setViewedLaneId(viewedIdFromProps); + if (viewedIdFromProps) updateViewedLane(viewedIdFromProps); }, [viewedIdFromProps?.toString()]); useEffect(() => { @@ -50,26 +60,28 @@ export function LanesProvider({ const viewedLaneIdToSet = viewedLaneIdFromUrl || lanesModel?.currentLane?.id || lanesModel?.lanes.find((lane) => lane.id.isDefault())?.id; - setViewedLaneId(viewedLaneIdToSet); + updateViewedLane(viewedLaneIdToSet); }, [location?.pathname]); useEffect(() => { - if (viewedLaneId === undefined && lanesModel?.currentLane?.id) { - setViewedLaneId(lanesModel.currentLane.id); - lanesModel?.setViewedOrDefaultLane(lanesModel.currentLane.id); - setLanesState(lanesModel); - return; - } - lanesModel?.setViewedOrDefaultLane(viewedLaneId); - setLanesState(lanesModel); - }, [loading, lanesModel?.lanes.length]); - - lanesState?.setViewedOrDefaultLane(viewedLaneId); + setLanesState((existing) => { + if (!loading && lanesModel?.lanes.length) { + const state = new LanesModel({ ...lanesModel }); + if (viewedLaneId === undefined && lanesModel?.currentLane?.id) { + state.setViewedOrDefaultLane(lanesModel?.currentLane?.id); + } else { + state.setViewedOrDefaultLane(viewedLaneId); + } + return state; + } + return existing; + }); + }, [loading, lanesModel?.lanes.length, viewedLaneId]); const lanesContextModel: LanesContextModel = { lanesModel: lanesState, updateLanesModel: setLanesState, - updateViewedLane: setViewedLaneId, + updateViewedLane, }; return {children}; diff --git a/scopes/lanes/hooks/use-lanes/use-lanes.tsx b/scopes/lanes/hooks/use-lanes/use-lanes.tsx index 4f30976048eb..4a4b2d6348c3 100644 --- a/scopes/lanes/hooks/use-lanes/use-lanes.tsx +++ b/scopes/lanes/hooks/use-lanes/use-lanes.tsx @@ -50,7 +50,7 @@ export function useLanes( skip?: boolean ): LanesContextModel & Omit, 'data'> { const lanesContext = useLanesContext(); - const shouldSkip = skip || !!targetLanes || !!lanesContext; + const shouldSkip = skip || !!targetLanes || !!lanesContext?.lanesModel; const { data, loading, ...rest } = useDataQuery(GET_LANES, { skip: shouldSkip, diff --git a/scopes/lanes/lanes/lanes.ui.runtime.tsx b/scopes/lanes/lanes/lanes.ui.runtime.tsx index 95e55d1c79a5..0d0ca89bc775 100644 --- a/scopes/lanes/lanes/lanes.ui.runtime.tsx +++ b/scopes/lanes/lanes/lanes.ui.runtime.tsx @@ -30,6 +30,41 @@ import styles from './lanes.ui.module.scss'; export type LaneCompareProps = Partial; export type LaneProviderIgnoreSlot = SlotRegistry; +export function useComponentFilters() { + const idFromLocation = useIdFromLocation(); + const { lanesModel, loading } = useLanes(); + const laneFromUrl = useViewedLaneFromUrl(); + const laneComponentId = + idFromLocation && !laneFromUrl?.isDefault() + ? lanesModel?.resolveComponentFromUrl(idFromLocation, laneFromUrl) ?? null + : null; + + if (laneComponentId === null || loading) { + return { + loading: true, + }; + } + + return { + loading: false, + log: { + logHead: laneComponentId.version, + }, + }; +} +export function useLaneComponentIdFromUrl() { + const idFromLocation = useIdFromLocation(); + const { lanesModel, loading } = useLanes(); + const laneFromUrl = useViewedLaneFromUrl(); + const laneComponentId = + idFromLocation && !laneFromUrl?.isDefault() + ? lanesModel?.resolveComponentFromUrl(idFromLocation, laneFromUrl) ?? null + : null; + return loading ? undefined : laneComponentId; +} +export function useComponentId() { + return useLaneComponentIdFromUrl()?.toString(); +} export class LanesUI { static dependencies = [UIAspect, ComponentAspect, WorkspaceAspect, ScopeAspect, ComponentCompareAspect]; @@ -112,42 +147,17 @@ export class LanesUI { // return ; // } - getLaneComponentIdFromUrl = () => { - const idFromLocation = useIdFromLocation(); - const { lanesModel } = useLanes(); - const laneFromUrl = useViewedLaneFromUrl(); - const laneComponentId = - idFromLocation && !laneFromUrl?.isDefault() - ? lanesModel?.resolveComponentFromUrl(idFromLocation, laneFromUrl) - : undefined; - return laneComponentId; - }; - - useComponentId = () => { - return this.getLaneComponentIdFromUrl()?.toString(); - }; - - useComponentFilters = () => { - const laneComponentId = this.getLaneComponentIdFromUrl(); - - return { - log: laneComponentId && { - logHead: laneComponentId.version, - }, - }; - }; - getLaneComponent() { return this.componentUI.getComponentUI(this.host, { - componentId: this.useComponentId, - useComponentFilters: this.useComponentFilters, + componentId: useComponentId, + useComponentFilters, }); } getLaneComponentMenu() { return this.componentUI.getMenu(this.host, { - componentId: this.useComponentId, - useComponentFilters: this.useComponentFilters, + componentId: useComponentId, + useComponentFilters, }); } @@ -252,7 +262,7 @@ export class LanesUI { ); diff --git a/scopes/scope/scope/scope.main.runtime.ts b/scopes/scope/scope/scope.main.runtime.ts index 0d8b3bc19144..5f00b95a3a68 100644 --- a/scopes/scope/scope/scope.main.runtime.ts +++ b/scopes/scope/scope/scope.main.runtime.ts @@ -681,11 +681,104 @@ export class ScopeMain implements ComponentFactory { return this.componentLoader.getSnap(id, ref.toString()); } + /** + * Performs a Depth-First Search (DFS) to find a node in a graph by offset. + * + * This function starts from a given node and moves either forward or backward in the graph, depending on the provided getNodes function. + * The goal is to reach a node that is at a given offset from the starting node. The offset is decremented each time a new node is visited. + * The function will return the node that corresponds to the exact offset if it exists. + * + * If the graph cannot go any deeper (i.e., there are no more successors or predecessors), or the offset becomes zero, the function will return the last visited node. + * This ensures that the function always returns a node, either the exact offset node (if it exists) or the closest node by offset. + * + * @param _node The node from where to start the search. + * @param _versionGraph The graph in which to perform the search. + * @param _offset The offset from the starting node to find. It is always an absolute value. + * @param _getNodesFunc A function that, given a node id, returns the successors (if moving forward) or predecessors (if moving backward) of that node in the graph. + * + * @returns The node that corresponds to the exact offset if it exists, or the closest node by offset otherwise. + */ + private findNodeByOffset(_node, _versionGraph, _offset, _getNodesFunc) { + if (_offset === 0 || !_node) return _node; + + const nextNodes = _getNodesFunc(_node.id, { edgeFilter: (edge) => edge.attr === 'parent' }); + + if (!nextNodes || nextNodes.length === 0) { + return _node; + } + + for (const nextNode of nextNodes) { + const foundNode = this.findNodeByOffset(nextNode, _versionGraph, _offset - 1, _getNodesFunc); + if (foundNode) { + return foundNode; + } + } + + return _node; + } + /** * get component log sorted by the timestamp in ascending order (from the earliest to the latest) */ - async getLogs(id: ComponentID, shortHash = false, startsFrom?: string): Promise { - return this.legacyScope.loadComponentLogs(id._legacy, shortHash, startsFrom); + async getLogs( + id: ComponentID, + shortHash = false, + head?: string, + startFrom?: string, + stopAt?: string, + startFromOffset?: number, + stopAtOffset?: number + ): Promise { + const componentModel = await this.legacyScope.getModelComponentIfExist(id._legacy); + + if (!componentModel) return []; + + const headRef = head ? componentModel.getRef(head) : componentModel.head; + + if (!headRef) return []; + + if (!startFromOffset && !stopAtOffset) { + return this.legacyScope.loadComponentLogs(id._legacy, shortHash, startFrom, stopAt ? [stopAt] : undefined); + } + const versionHistory = await componentModel.getAndPopulateVersionHistory(this.legacyScope.objects, headRef); + const versionGraph = versionHistory.getGraph(); + const startFromRef = startFrom ? componentModel.getRef(startFrom) : undefined; + let startNode = versionGraph.node(startFromRef?.hash ?? headRef.hash); + + if (!startNode) { + this.logger.error(`Node with id ${headRef.hash} not found`); + return []; + } + + const startOffset = startFromOffset || 0; + + if (startOffset !== 0) { + startNode = this.findNodeByOffset( + startNode, + versionGraph, + Math.abs(startOffset), + startOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph) + ); + } + + const stopOffset = stopAtOffset || 0; + const stopRef = stopAt ? componentModel.getRef(stopAt) : (stopOffset !== 0 && { hash: startNode?.id }) || undefined; + let stopNode = stopRef?.hash ? versionGraph.node(stopRef.hash) : undefined; + if (stopOffset !== 0) { + stopNode = this.findNodeByOffset( + stopNode, + versionGraph, + Math.abs(stopOffset), + stopOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph) + ); + } + + return this.legacyScope.loadComponentLogs( + id._legacy, + shortHash, + startNode?.id, + stopNode ? [stopNode.id] : undefined + ); } async getStagedConfig() { diff --git a/scopes/workspace/workspace/workspace.ts b/scopes/workspace/workspace/workspace.ts index 861f34f98775..553385e2b5a0 100644 --- a/scopes/workspace/workspace/workspace.ts +++ b/scopes/workspace/workspace/workspace.ts @@ -426,8 +426,16 @@ export class Workspace implements ComponentFactory { return this.getMany(ids); } - async getLogs(id: ComponentID, shortHash = false, startsFrom?: string): Promise { - return this.scope.getLogs(id, shortHash, startsFrom); + async getLogs( + id: ComponentID, + shortHash = false, + head?: string, + startFrom?: string, + stopAt?: string, + startFromOffset?: number, + stopAtOffset?: number + ): Promise { + return this.scope.getLogs(id, shortHash, head, startFrom, stopAt, startFromOffset, stopAtOffset); } async getGraph(ids?: ComponentID[], shouldThrowOnMissingDep = true): Promise> { diff --git a/src/scope/component-ops/traverse-versions.ts b/src/scope/component-ops/traverse-versions.ts index 5265cbcdfcb9..6fb540ade05c 100644 --- a/src/scope/component-ops/traverse-versions.ts +++ b/src/scope/component-ops/traverse-versions.ts @@ -48,8 +48,10 @@ export async function getAllVersionsInfo({ repo?: Repository; throws?: boolean; // in case objects are missing versionObjects?: Version[]; - startFrom?: Ref | null; // by default, start from the head + startFrom?: Ref | null; // by default, start from the head // + startFromOffset?: number; // -5 stopAt?: Ref[] | null; // by default, stop when the parents is empty + stopAtOffset?: number; // 5 }): Promise { const results: VersionInfo[] = []; const isAlreadyProcessed = (ref: Ref): boolean => { diff --git a/src/scope/models/model-component.ts b/src/scope/models/model-component.ts index ffcfa9b66b59..88d2268228ac 100644 --- a/src/scope/models/model-component.ts +++ b/src/scope/models/model-component.ts @@ -480,9 +480,9 @@ export default class Component extends BitObject { /** * get component log and sort by the timestamp in ascending order (from the earliest to the latest) */ - async collectLogs(scope: Scope, shortHash = false, startFrom?: Ref): Promise { + async collectLogs(scope: Scope, shortHash = false, startFrom?: Ref, stopAt?: Ref[]): Promise { const repo = scope.objects; - let versionsInfo = await getAllVersionsInfo({ modelComponent: this, repo, throws: false, startFrom }); + let versionsInfo = await getAllVersionsInfo({ modelComponent: this, repo, throws: false, startFrom, stopAt }); // due to recent changes of getting version-history object rather than fetching the entire history, some version // objects might be missing. import the component from the remote diff --git a/src/scope/scope.ts b/src/scope/scope.ts index 1676687e5033..8627d140da7d 100644 --- a/src/scope/scope.ts +++ b/src/scope/scope.ts @@ -2,6 +2,7 @@ import fs from 'fs-extra'; import * as pathLib from 'path'; import R from 'ramda'; import { LaneId } from '@teambit/lane-id'; +import { compact } from 'lodash'; import semver from 'semver'; import { isTag } from '@teambit/component-version'; import { Analytics } from '../analytics/analytics'; @@ -535,11 +536,19 @@ once done, to continue working, please run "bit cc"` return removeNils(components); } - async loadComponentLogs(id: BitId, shortHash = false, startFrom?: string): Promise { + async loadComponentLogs( + id: BitId, + shortHash = false, + startFrom?: string, + stopsAt?: string[] + ): Promise { const componentModel = await this.getModelComponentIfExist(id); if (!componentModel) return []; + const startFromRef = startFrom ? componentModel.getRef(startFrom) ?? undefined : undefined; - const logs = await componentModel.collectLogs(this, shortHash, startFromRef); + const stopsAtRef = stopsAt ? compact(stopsAt.map((s) => componentModel.getRef(s) ?? undefined)) : undefined; + + const logs = await componentModel.collectLogs(this, shortHash, startFromRef, stopsAtRef); return logs; } From 1fa4b10188766b3673d718dc43e98b9c8b21fb08 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Fri, 12 May 2023 14:38:17 -0400 Subject: [PATCH 12/20] clean up --- .../ui/component-tooltip/Untitled-1.yml | 789 ++++++++++++++++++ scopes/scope/scope/scope.main.runtime.ts | 59 +- src/scope/component-ops/traverse-versions.ts | 4 +- 3 files changed, 833 insertions(+), 19 deletions(-) create mode 100644 scopes/component/ui/component-tooltip/Untitled-1.yml diff --git a/scopes/component/ui/component-tooltip/Untitled-1.yml b/scopes/component/ui/component-tooltip/Untitled-1.yml new file mode 100644 index 000000000000..5f2bc6150471 --- /dev/null +++ b/scopes/component/ui/component-tooltip/Untitled-1.yml @@ -0,0 +1,789 @@ +[ + { + id: "060c458da72872729a87d8051ef08e190a25d751", + attr: { hash: "060c458da72872729a87d8051ef08e190a25d751" }, + _inEdges: + [ + "ff44ae21a26570f6395bc316417f0b6fb1bca930->060c458da72872729a87d8051ef08e190a25d751", + ], + _outEdges: + [ + "060c458da72872729a87d8051ef08e190a25d751->484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", + ], + }, + + { + id: "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", + attr: { hash: "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482" }, + _inEdges: + [ + "060c458da72872729a87d8051ef08e190a25d751->484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", + ], + _outEdges: + [ + "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482->7803e482a4943a6e31deb9b38c80d735f75916ae", + ], + }, + + { + id: "7803e482a4943a6e31deb9b38c80d735f75916ae", + attr: { hash: "7803e482a4943a6e31deb9b38c80d735f75916ae" }, + _inEdges: + [ + "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482->7803e482a4943a6e31deb9b38c80d735f75916ae", + ], + _outEdges: + [ + "7803e482a4943a6e31deb9b38c80d735f75916ae->7451e0eb12563c8c63b53dc9ddb141de5918b1d7", + ], + }, + + { + id: "7451e0eb12563c8c63b53dc9ddb141de5918b1d7", + attr: { hash: "7451e0eb12563c8c63b53dc9ddb141de5918b1d7" }, + _inEdges: + [ + "7803e482a4943a6e31deb9b38c80d735f75916ae->7451e0eb12563c8c63b53dc9ddb141de5918b1d7", + ], + _outEdges: + [ + "7451e0eb12563c8c63b53dc9ddb141de5918b1d7->8293c41d17d451d758725c7531c98138b9370b09", + ], + }, + + { + id: "8293c41d17d451d758725c7531c98138b9370b09", + attr: { hash: "8293c41d17d451d758725c7531c98138b9370b09" }, + _inEdges: + [ + "7451e0eb12563c8c63b53dc9ddb141de5918b1d7->8293c41d17d451d758725c7531c98138b9370b09", + ], + _outEdges: + [ + "8293c41d17d451d758725c7531c98138b9370b09->de5b08e7da761f619d2499eaa0720ad29fc77eb2", + ], + }, + + { + id: "de5b08e7da761f619d2499eaa0720ad29fc77eb2", + attr: { hash: "de5b08e7da761f619d2499eaa0720ad29fc77eb2" }, + _inEdges: + [ + "8293c41d17d451d758725c7531c98138b9370b09->de5b08e7da761f619d2499eaa0720ad29fc77eb2", + ], + _outEdges: + [ + "de5b08e7da761f619d2499eaa0720ad29fc77eb2->f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", + ], + }, + + { + id: "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", + attr: { hash: "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c" }, + _inEdges: + [ + "de5b08e7da761f619d2499eaa0720ad29fc77eb2->f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", + ], + _outEdges: + [ + "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->6e1e136d27542e751f0390d2c33b722f34d89a10", + "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->edbb65f181cceb87d9dda7c3c37d08ea601ec651", + ], + }, + + { + id: "6e1e136d27542e751f0390d2c33b722f34d89a10", + attr: { hash: "6e1e136d27542e751f0390d2c33b722f34d89a10" }, + _inEdges: + [ + "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->6e1e136d27542e751f0390d2c33b722f34d89a10", + ], + _outEdges: + [ + "6e1e136d27542e751f0390d2c33b722f34d89a10->e8f760d67664a04c4fde116183a4ca6bd7c9bfce", + ], + }, + + { + id: "e8f760d67664a04c4fde116183a4ca6bd7c9bfce", + attr: { hash: "e8f760d67664a04c4fde116183a4ca6bd7c9bfce" }, + _inEdges: + [ + "6e1e136d27542e751f0390d2c33b722f34d89a10->e8f760d67664a04c4fde116183a4ca6bd7c9bfce", + ], + _outEdges: + [ + "e8f760d67664a04c4fde116183a4ca6bd7c9bfce->d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", + ], + }, + + { + id: "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", + attr: { hash: "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6" }, + _inEdges: + [ + "e8f760d67664a04c4fde116183a4ca6bd7c9bfce->d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", + ], + _outEdges: + [ + "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->136aab756693cfa6283f45f02b2b6aedb71e3199", + "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->16be08d1f9207f58a3b13b643a5f6df83b00851c", + ], + }, + + { + id: "136aab756693cfa6283f45f02b2b6aedb71e3199", + attr: { hash: "136aab756693cfa6283f45f02b2b6aedb71e3199" }, + _inEdges: + [ + "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->136aab756693cfa6283f45f02b2b6aedb71e3199", + ], + _outEdges: + [ + "136aab756693cfa6283f45f02b2b6aedb71e3199->d18b9728f2cd80571ea7f070ec2a62e40461b51e", + ], + }, + + { + id: "d18b9728f2cd80571ea7f070ec2a62e40461b51e", + attr: { hash: "d18b9728f2cd80571ea7f070ec2a62e40461b51e" }, + _inEdges: + [ + "136aab756693cfa6283f45f02b2b6aedb71e3199->d18b9728f2cd80571ea7f070ec2a62e40461b51e", + ], + _outEdges: + [ + "d18b9728f2cd80571ea7f070ec2a62e40461b51e->cea5d5c39bb9a33b3af8e4f45df5722d58acae36", + "d18b9728f2cd80571ea7f070ec2a62e40461b51e->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", + ], + }, + + { + id: "cea5d5c39bb9a33b3af8e4f45df5722d58acae36", + attr: { hash: "cea5d5c39bb9a33b3af8e4f45df5722d58acae36" }, + _inEdges: + [ + "d18b9728f2cd80571ea7f070ec2a62e40461b51e->cea5d5c39bb9a33b3af8e4f45df5722d58acae36", + ], + _outEdges: + [ + "cea5d5c39bb9a33b3af8e4f45df5722d58acae36->c2a1ea45e7e2906d888355ea069303de347ef968", + ], + }, + + { + id: "c2a1ea45e7e2906d888355ea069303de347ef968", + attr: { hash: "c2a1ea45e7e2906d888355ea069303de347ef968" }, + _inEdges: + [ + "edbb65f181cceb87d9dda7c3c37d08ea601ec651->c2a1ea45e7e2906d888355ea069303de347ef968", + "cea5d5c39bb9a33b3af8e4f45df5722d58acae36->c2a1ea45e7e2906d888355ea069303de347ef968", + ], + _outEdges: + [ + "c2a1ea45e7e2906d888355ea069303de347ef968->0b6993ef2933137d9f009e1a55ac1603072c0065", + ], + }, + + { + id: "0b6993ef2933137d9f009e1a55ac1603072c0065", + attr: { hash: "0b6993ef2933137d9f009e1a55ac1603072c0065" }, + _inEdges: + [ + "c2a1ea45e7e2906d888355ea069303de347ef968->0b6993ef2933137d9f009e1a55ac1603072c0065", + ], + _outEdges: + [ + "0b6993ef2933137d9f009e1a55ac1603072c0065->33d5a8b8bf37f022b906740d2cd22d505073a34a", + ], + }, + + { + id: "33d5a8b8bf37f022b906740d2cd22d505073a34a", + attr: { hash: "33d5a8b8bf37f022b906740d2cd22d505073a34a" }, + _inEdges: + [ + "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->33d5a8b8bf37f022b906740d2cd22d505073a34a", + "0b6993ef2933137d9f009e1a55ac1603072c0065->33d5a8b8bf37f022b906740d2cd22d505073a34a", + "5f61a84fd0303994d1a31830b829c3d0f9ddeffa->33d5a8b8bf37f022b906740d2cd22d505073a34a", + ], + _outEdges: + [ + "33d5a8b8bf37f022b906740d2cd22d505073a34a->8c18ba98ff5fa4becf72e22602d108a061d39c96", + ], + }, + + { + id: "8c18ba98ff5fa4becf72e22602d108a061d39c96", + attr: { hash: "8c18ba98ff5fa4becf72e22602d108a061d39c96" }, + _inEdges: + [ + "33d5a8b8bf37f022b906740d2cd22d505073a34a->8c18ba98ff5fa4becf72e22602d108a061d39c96", + "10c7a8a81f5c7c86def9e2af55f3172d44a778cc->8c18ba98ff5fa4becf72e22602d108a061d39c96", + ], + _outEdges: + [ + "8c18ba98ff5fa4becf72e22602d108a061d39c96->30f947c6b332ea104cb643b2a635952d9a4b1774", + ], + }, + + { + id: "30f947c6b332ea104cb643b2a635952d9a4b1774", + attr: { hash: "30f947c6b332ea104cb643b2a635952d9a4b1774" }, + _inEdges: + [ + "8c18ba98ff5fa4becf72e22602d108a061d39c96->30f947c6b332ea104cb643b2a635952d9a4b1774", + ], + _outEdges: + [ + "30f947c6b332ea104cb643b2a635952d9a4b1774->3c14f556f1d32bc459963b8ea7464e6b896c3531", + ], + }, + + { + id: "3c14f556f1d32bc459963b8ea7464e6b896c3531", + attr: { hash: "3c14f556f1d32bc459963b8ea7464e6b896c3531" }, + _inEdges: + [ + "30f947c6b332ea104cb643b2a635952d9a4b1774->3c14f556f1d32bc459963b8ea7464e6b896c3531", + ], + _outEdges: + [ + "3c14f556f1d32bc459963b8ea7464e6b896c3531->97354a80b5aa0a430f89827f6810afd2ab4900ca", + ], + }, + + { + id: "97354a80b5aa0a430f89827f6810afd2ab4900ca", + attr: { hash: "97354a80b5aa0a430f89827f6810afd2ab4900ca" }, + _inEdges: + [ + "3c14f556f1d32bc459963b8ea7464e6b896c3531->97354a80b5aa0a430f89827f6810afd2ab4900ca", + ], + _outEdges: + [ + "97354a80b5aa0a430f89827f6810afd2ab4900ca->4ea6caed5281a2bd2b1d249708b08430b6c1a845", + ], + }, + + { + id: "4ea6caed5281a2bd2b1d249708b08430b6c1a845", + attr: { hash: "4ea6caed5281a2bd2b1d249708b08430b6c1a845" }, + _inEdges: + [ + "97354a80b5aa0a430f89827f6810afd2ab4900ca->4ea6caed5281a2bd2b1d249708b08430b6c1a845", + ], + _outEdges: + [ + "4ea6caed5281a2bd2b1d249708b08430b6c1a845->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", + ], + }, + + { + id: "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", + attr: { hash: "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9" }, + _inEdges: + [ + "4ea6caed5281a2bd2b1d249708b08430b6c1a845->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", + "8d97eff47eb7809463564fc3d861ac85be87dcc1->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", + ], + _outEdges: + [ + "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9->e75cd8c849caf103897a68aa8f6ec3ae7717a384", + ], + }, + + { + id: "e75cd8c849caf103897a68aa8f6ec3ae7717a384", + attr: { hash: "e75cd8c849caf103897a68aa8f6ec3ae7717a384" }, + _inEdges: + [ + "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9->e75cd8c849caf103897a68aa8f6ec3ae7717a384", + ], + _outEdges: + [ + "e75cd8c849caf103897a68aa8f6ec3ae7717a384->d8d7ba85f8ae204a28ec370774c856ea98ea9a52", + ], + }, + + { + id: "d8d7ba85f8ae204a28ec370774c856ea98ea9a52", + attr: { hash: "d8d7ba85f8ae204a28ec370774c856ea98ea9a52" }, + _inEdges: + [ + "e75cd8c849caf103897a68aa8f6ec3ae7717a384->d8d7ba85f8ae204a28ec370774c856ea98ea9a52", + ], + _outEdges: + [ + "d8d7ba85f8ae204a28ec370774c856ea98ea9a52->b9f744da953bdab1724fd48b259a42b3476d7a7d", + ], + }, + + { + id: "b9f744da953bdab1724fd48b259a42b3476d7a7d", + attr: { hash: "b9f744da953bdab1724fd48b259a42b3476d7a7d" }, + _inEdges: + [ + "d8d7ba85f8ae204a28ec370774c856ea98ea9a52->b9f744da953bdab1724fd48b259a42b3476d7a7d", + ], + _outEdges: + [ + "b9f744da953bdab1724fd48b259a42b3476d7a7d->6fa6d42b8580f301b9910a49c70d7b11d2fc357e", + ], + }, + + { + id: "6fa6d42b8580f301b9910a49c70d7b11d2fc357e", + attr: { hash: "6fa6d42b8580f301b9910a49c70d7b11d2fc357e" }, + _inEdges: + [ + "b9f744da953bdab1724fd48b259a42b3476d7a7d->6fa6d42b8580f301b9910a49c70d7b11d2fc357e", + ], + _outEdges: + [ + "6fa6d42b8580f301b9910a49c70d7b11d2fc357e->17eae8caf411633e4553ff7ee6ceab4a0dac835c", + ], + }, + + { + id: "17eae8caf411633e4553ff7ee6ceab4a0dac835c", + attr: { hash: "17eae8caf411633e4553ff7ee6ceab4a0dac835c" }, + _inEdges: + [ + "6fa6d42b8580f301b9910a49c70d7b11d2fc357e->17eae8caf411633e4553ff7ee6ceab4a0dac835c", + ], + _outEdges: + [ + "17eae8caf411633e4553ff7ee6ceab4a0dac835c->e72c0d0b1d31ed52999def993ca425bc8b6eec8d", + ], + }, + + { + id: "e72c0d0b1d31ed52999def993ca425bc8b6eec8d", + attr: { hash: "e72c0d0b1d31ed52999def993ca425bc8b6eec8d" }, + _inEdges: + [ + "17eae8caf411633e4553ff7ee6ceab4a0dac835c->e72c0d0b1d31ed52999def993ca425bc8b6eec8d", + ], + _outEdges: + [ + "e72c0d0b1d31ed52999def993ca425bc8b6eec8d->d2ab69219d828c8822444f5c8a7b92763f4bf9b8", + ], + }, + + { + id: "d2ab69219d828c8822444f5c8a7b92763f4bf9b8", + attr: { hash: "d2ab69219d828c8822444f5c8a7b92763f4bf9b8" }, + _inEdges: + [ + "e72c0d0b1d31ed52999def993ca425bc8b6eec8d->d2ab69219d828c8822444f5c8a7b92763f4bf9b8", + ], + _outEdges: + [ + "d2ab69219d828c8822444f5c8a7b92763f4bf9b8->f1cff5c303f39c5047c6bab98baab1156c48c1b6", + ], + }, + + { + id: "f1cff5c303f39c5047c6bab98baab1156c48c1b6", + attr: { hash: "f1cff5c303f39c5047c6bab98baab1156c48c1b6" }, + _inEdges: + [ + "d2ab69219d828c8822444f5c8a7b92763f4bf9b8->f1cff5c303f39c5047c6bab98baab1156c48c1b6", + ], + _outEdges: + [ + "f1cff5c303f39c5047c6bab98baab1156c48c1b6->2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", + ], + }, + + { + id: "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", + attr: { hash: "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01" }, + _inEdges: + [ + "f1cff5c303f39c5047c6bab98baab1156c48c1b6->2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", + ], + _outEdges: + [ + "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01->241832c97051733499b87d742286a811711dfcfb", + ], + }, + + { + id: "241832c97051733499b87d742286a811711dfcfb", + attr: { hash: "241832c97051733499b87d742286a811711dfcfb" }, + _inEdges: + [ + "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01->241832c97051733499b87d742286a811711dfcfb", + ], + _outEdges: + [ + "241832c97051733499b87d742286a811711dfcfb->efca1e0bfe4a8347e46048ee9dae0cc726461cb8", + ], + }, + + { + id: "efca1e0bfe4a8347e46048ee9dae0cc726461cb8", + attr: { hash: "efca1e0bfe4a8347e46048ee9dae0cc726461cb8" }, + _inEdges: + [ + "241832c97051733499b87d742286a811711dfcfb->efca1e0bfe4a8347e46048ee9dae0cc726461cb8", + ], + _outEdges: + [ + "efca1e0bfe4a8347e46048ee9dae0cc726461cb8->f9f52eb71d5d020f53a67ebf2c98049969553772", + ], + }, + + { + id: "f9f52eb71d5d020f53a67ebf2c98049969553772", + attr: { hash: "f9f52eb71d5d020f53a67ebf2c98049969553772" }, + _inEdges: + [ + "efca1e0bfe4a8347e46048ee9dae0cc726461cb8->f9f52eb71d5d020f53a67ebf2c98049969553772", + ], + _outEdges: + [ + "f9f52eb71d5d020f53a67ebf2c98049969553772->8eb3a70cf0752b54fa2f9651ccde79563112bad3", + ], + }, + + { + id: "8eb3a70cf0752b54fa2f9651ccde79563112bad3", + attr: { hash: "8eb3a70cf0752b54fa2f9651ccde79563112bad3" }, + _inEdges: + [ + "f9f52eb71d5d020f53a67ebf2c98049969553772->8eb3a70cf0752b54fa2f9651ccde79563112bad3", + ], + _outEdges: + [ + "8eb3a70cf0752b54fa2f9651ccde79563112bad3->6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", + ], + }, + + { + id: "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", + attr: { hash: "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f" }, + _inEdges: + [ + "8eb3a70cf0752b54fa2f9651ccde79563112bad3->6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", + ], + _outEdges: + [ + "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f->73d23a8fa2214261cacd174afdcf12d5cca86acb", + ], + }, + + { + id: "73d23a8fa2214261cacd174afdcf12d5cca86acb", + attr: { hash: "73d23a8fa2214261cacd174afdcf12d5cca86acb" }, + _inEdges: + [ + "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f->73d23a8fa2214261cacd174afdcf12d5cca86acb", + ], + _outEdges: [], + }, + + { + id: "7fa90951d4ec0646f3368ccdbbbe89fe3de09212", + attr: { hash: "7fa90951d4ec0646f3368ccdbbbe89fe3de09212" }, + _inEdges: + [ + "d18b9728f2cd80571ea7f070ec2a62e40461b51e->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", + "d2b7f87cb7f217f31076cd23ad903d73aeabb85d->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", + ], + _outEdges: + [ + "7fa90951d4ec0646f3368ccdbbbe89fe3de09212->0e42b353336a3b03c6c63765b7cf3258b725c559", + ], + }, + + { + id: "0e42b353336a3b03c6c63765b7cf3258b725c559", + attr: { hash: "0e42b353336a3b03c6c63765b7cf3258b725c559" }, + _inEdges: + [ + "7fa90951d4ec0646f3368ccdbbbe89fe3de09212->0e42b353336a3b03c6c63765b7cf3258b725c559", + ], + _outEdges: + [ + "0e42b353336a3b03c6c63765b7cf3258b725c559->5f61a84fd0303994d1a31830b829c3d0f9ddeffa", + ], + }, + + { + id: "5f61a84fd0303994d1a31830b829c3d0f9ddeffa", + attr: { hash: "5f61a84fd0303994d1a31830b829c3d0f9ddeffa" }, + _inEdges: + [ + "0e42b353336a3b03c6c63765b7cf3258b725c559->5f61a84fd0303994d1a31830b829c3d0f9ddeffa", + ], + _outEdges: + [ + "5f61a84fd0303994d1a31830b829c3d0f9ddeffa->33d5a8b8bf37f022b906740d2cd22d505073a34a", + ], + }, + + { + id: "16be08d1f9207f58a3b13b643a5f6df83b00851c", + attr: { hash: "16be08d1f9207f58a3b13b643a5f6df83b00851c" }, + _inEdges: + [ + "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->16be08d1f9207f58a3b13b643a5f6df83b00851c", + ], + _outEdges: + [ + "16be08d1f9207f58a3b13b643a5f6df83b00851c->b45b33d7c424984790725b545e9f8826a4a26e90", + ], + }, + + { + id: "b45b33d7c424984790725b545e9f8826a4a26e90", + attr: { hash: "b45b33d7c424984790725b545e9f8826a4a26e90" }, + _inEdges: + [ + "16be08d1f9207f58a3b13b643a5f6df83b00851c->b45b33d7c424984790725b545e9f8826a4a26e90", + ], + _outEdges: + [ + "b45b33d7c424984790725b545e9f8826a4a26e90->2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", + ], + }, + + { + id: "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", + attr: { hash: "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8" }, + _inEdges: + [ + "b45b33d7c424984790725b545e9f8826a4a26e90->2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", + ], + _outEdges: + [ + "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8->d1f10da8f64d3023c23428e7d65be0e633ce8f04", + ], + }, + + { + id: "d1f10da8f64d3023c23428e7d65be0e633ce8f04", + attr: { hash: "d1f10da8f64d3023c23428e7d65be0e633ce8f04" }, + _inEdges: + [ + "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8->d1f10da8f64d3023c23428e7d65be0e633ce8f04", + ], + _outEdges: + [ + "d1f10da8f64d3023c23428e7d65be0e633ce8f04->dd7a7047f2c1c0f899f396e8769d3380073b4243", + ], + }, + + { + id: "dd7a7047f2c1c0f899f396e8769d3380073b4243", + attr: { hash: "dd7a7047f2c1c0f899f396e8769d3380073b4243" }, + _inEdges: + [ + "d1f10da8f64d3023c23428e7d65be0e633ce8f04->dd7a7047f2c1c0f899f396e8769d3380073b4243", + ], + _outEdges: + [ + "dd7a7047f2c1c0f899f396e8769d3380073b4243->b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", + ], + }, + + { + id: "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", + attr: { hash: "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3" }, + _inEdges: + [ + "dd7a7047f2c1c0f899f396e8769d3380073b4243->b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", + ], + _outEdges: + [ + "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3->8c89c7ff1dc8da4795ab4b173549d074dd564aa7", + ], + }, + + { + id: "8c89c7ff1dc8da4795ab4b173549d074dd564aa7", + attr: { hash: "8c89c7ff1dc8da4795ab4b173549d074dd564aa7" }, + _inEdges: + [ + "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3->8c89c7ff1dc8da4795ab4b173549d074dd564aa7", + ], + _outEdges: + [ + "8c89c7ff1dc8da4795ab4b173549d074dd564aa7->596c3f35749b39d4ec449e0f382521da5747f081", + ], + }, + + { + id: "596c3f35749b39d4ec449e0f382521da5747f081", + attr: { hash: "596c3f35749b39d4ec449e0f382521da5747f081" }, + _inEdges: + [ + "8c89c7ff1dc8da4795ab4b173549d074dd564aa7->596c3f35749b39d4ec449e0f382521da5747f081", + ], + _outEdges: + [ + "596c3f35749b39d4ec449e0f382521da5747f081->f33f4b872f92bee46b104d94a7aad3474c91c53d", + ], + }, + + { + id: "f33f4b872f92bee46b104d94a7aad3474c91c53d", + attr: { hash: "f33f4b872f92bee46b104d94a7aad3474c91c53d" }, + _inEdges: + [ + "596c3f35749b39d4ec449e0f382521da5747f081->f33f4b872f92bee46b104d94a7aad3474c91c53d", + ], + _outEdges: + [ + "f33f4b872f92bee46b104d94a7aad3474c91c53d->49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", + ], + }, + + { + id: "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", + attr: { hash: "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e" }, + _inEdges: + [ + "f33f4b872f92bee46b104d94a7aad3474c91c53d->49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", + ], + _outEdges: + [ + "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e->ec1e05ae1f7b0f835b890b359459228d89b3788e", + ], + }, + + { + id: "ec1e05ae1f7b0f835b890b359459228d89b3788e", + attr: { hash: "ec1e05ae1f7b0f835b890b359459228d89b3788e" }, + _inEdges: + [ + "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e->ec1e05ae1f7b0f835b890b359459228d89b3788e", + ], + _outEdges: + [ + "ec1e05ae1f7b0f835b890b359459228d89b3788e->7c49a291b011c7d9c67a3c44f09cb018021b1d81", + ], + }, + + { + id: "7c49a291b011c7d9c67a3c44f09cb018021b1d81", + attr: { hash: "7c49a291b011c7d9c67a3c44f09cb018021b1d81" }, + _inEdges: + [ + "ec1e05ae1f7b0f835b890b359459228d89b3788e->7c49a291b011c7d9c67a3c44f09cb018021b1d81", + ], + _outEdges: + [ + "7c49a291b011c7d9c67a3c44f09cb018021b1d81->aa081b26db3039b4a39be9fde0d0595b2770625c", + ], + }, + + { + id: "aa081b26db3039b4a39be9fde0d0595b2770625c", + attr: { hash: "aa081b26db3039b4a39be9fde0d0595b2770625c" }, + _inEdges: + [ + "7c49a291b011c7d9c67a3c44f09cb018021b1d81->aa081b26db3039b4a39be9fde0d0595b2770625c", + ], + _outEdges: + [ + "aa081b26db3039b4a39be9fde0d0595b2770625c->7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", + ], + }, + + { + id: "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", + attr: { hash: "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1" }, + _inEdges: + [ + "aa081b26db3039b4a39be9fde0d0595b2770625c->7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", + ], + _outEdges: + [ + "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1->ad88d251fdef37f9e4b619b346099475c2a6a13d", + ], + }, + + { + id: "ad88d251fdef37f9e4b619b346099475c2a6a13d", + attr: { hash: "ad88d251fdef37f9e4b619b346099475c2a6a13d" }, + _inEdges: + [ + "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1->ad88d251fdef37f9e4b619b346099475c2a6a13d", + ], + _outEdges: + [ + "ad88d251fdef37f9e4b619b346099475c2a6a13d->336c861d90196d45d3df2b6c553e56810765fb4b", + ], + }, + + { + id: "336c861d90196d45d3df2b6c553e56810765fb4b", + attr: { hash: "336c861d90196d45d3df2b6c553e56810765fb4b" }, + _inEdges: + [ + "ad88d251fdef37f9e4b619b346099475c2a6a13d->336c861d90196d45d3df2b6c553e56810765fb4b", + ], + _outEdges: + [ + "336c861d90196d45d3df2b6c553e56810765fb4b->8d97eff47eb7809463564fc3d861ac85be87dcc1", + ], + }, + + { + id: "8d97eff47eb7809463564fc3d861ac85be87dcc1", + attr: { hash: "8d97eff47eb7809463564fc3d861ac85be87dcc1" }, + _inEdges: + [ + "336c861d90196d45d3df2b6c553e56810765fb4b->8d97eff47eb7809463564fc3d861ac85be87dcc1", + ], + _outEdges: + [ + "8d97eff47eb7809463564fc3d861ac85be87dcc1->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", + ], + }, + + { + id: "edbb65f181cceb87d9dda7c3c37d08ea601ec651", + attr: { hash: "edbb65f181cceb87d9dda7c3c37d08ea601ec651" }, + _inEdges: + [ + "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->edbb65f181cceb87d9dda7c3c37d08ea601ec651", + ], + _outEdges: + [ + "edbb65f181cceb87d9dda7c3c37d08ea601ec651->28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", + "edbb65f181cceb87d9dda7c3c37d08ea601ec651->c2a1ea45e7e2906d888355ea069303de347ef968", + ], + }, + + { + id: "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", + attr: { hash: "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3" }, + _inEdges: + [ + "edbb65f181cceb87d9dda7c3c37d08ea601ec651->28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", + ], + _outEdges: + [ + "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->10c7a8a81f5c7c86def9e2af55f3172d44a778cc", + "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->33d5a8b8bf37f022b906740d2cd22d505073a34a", + ], + }, + + { + id: "10c7a8a81f5c7c86def9e2af55f3172d44a778cc", + attr: { hash: "10c7a8a81f5c7c86def9e2af55f3172d44a778cc" }, + _inEdges: + [ + "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->10c7a8a81f5c7c86def9e2af55f3172d44a778cc", + ], + _outEdges: + [ + "10c7a8a81f5c7c86def9e2af55f3172d44a778cc->8c18ba98ff5fa4becf72e22602d108a061d39c96", + ], + }, +] diff --git a/scopes/scope/scope/scope.main.runtime.ts b/scopes/scope/scope/scope.main.runtime.ts index 5f00b95a3a68..f6e0df5db639 100644 --- a/scopes/scope/scope/scope.main.runtime.ts +++ b/scopes/scope/scope/scope.main.runtime.ts @@ -27,7 +27,7 @@ import { UIAspect } from '@teambit/ui'; import { BitId } from '@teambit/legacy-bit-id'; import { BitIds, BitIds as ComponentsIds } from '@teambit/legacy/dist/bit-id'; import { ModelComponent, Lane } from '@teambit/legacy/dist/scope/models'; -import { Repository } from '@teambit/legacy/dist/scope/objects'; +import { Ref, Repository } from '@teambit/legacy/dist/scope/objects'; import LegacyScope, { LegacyOnTagResult } from '@teambit/legacy/dist/scope/scope'; import { ComponentLog } from '@teambit/legacy/dist/scope/models/model-component'; import { loadScopeIfExist } from '@teambit/legacy/dist/scope/scope-loader'; @@ -691,34 +691,61 @@ export class ScopeMain implements ComponentFactory { * If the graph cannot go any deeper (i.e., there are no more successors or predecessors), or the offset becomes zero, the function will return the last visited node. * This ensures that the function always returns a node, either the exact offset node (if it exists) or the closest node by offset. * - * @param _node The node from where to start the search. - * @param _versionGraph The graph in which to perform the search. - * @param _offset The offset from the starting node to find. It is always an absolute value. - * @param _getNodesFunc A function that, given a node id, returns the successors (if moving forward) or predecessors (if moving backward) of that node in the graph. + * @param node The node from where to start the search. + * @param versionGraph The graph in which to perform the search. + * @param offset The offset from the starting node to find. It is always an absolute value. + * @param getNodesFunc A function that, given a node id, returns the successors (if moving forward) or predecessors (if moving backward) of that node in the graph. * * @returns The node that corresponds to the exact offset if it exists, or the closest node by offset otherwise. */ - private findNodeByOffset(_node, _versionGraph, _offset, _getNodesFunc) { - if (_offset === 0 || !_node) return _node; + private findNodeByOffset( + versionGraph: Graph, + offset: number, + getNodesFunc: ( + id: string, + { + nodeFilter, + edgeFilter, + }?: { + nodeFilter?: (node: Node) => boolean; + edgeFilter?: (edge: Edge) => boolean; + } + ) => Array>, + node?: Node + ) { + if (offset === 0 || !node) return node; - const nextNodes = _getNodesFunc(_node.id, { edgeFilter: (edge) => edge.attr === 'parent' }); + const nextNodes = getNodesFunc(node.id, { edgeFilter: (edge) => edge.attr === 'parent' }); if (!nextNodes || nextNodes.length === 0) { - return _node; + return node; } for (const nextNode of nextNodes) { - const foundNode = this.findNodeByOffset(nextNode, _versionGraph, _offset - 1, _getNodesFunc); + const foundNode = this.findNodeByOffset(versionGraph, offset - 1, getNodesFunc, nextNode); if (foundNode) { return foundNode; } } - return _node; + return node; } /** - * get component log sorted by the timestamp in ascending order (from the earliest to the latest) + * Fetches the logs for a given component. + * + * @param id - The ComponentID of the component for which to fetch logs. + * @param shortHash - If true, returns a shorter version of the hash. Defaults to false. + * @param head - The specific version to start fetching logs from. If not provided, starts from the head. + * @param startFrom - The specific version to start slicing logs from. + * @param stopAt - The specific version to stop fetching logs at. + * @param startFromOffset - Offset from the start version to fetch logs from. + * @param stopAtOffset - Offset from the stop version to fetch logs at. + * + * @returns A promise that resolves to an array of ComponentLog objects representing the filtered logs for the component. + * + * @throws Error - Throws an error if the node with given headRef hash is not found. + * */ async getLogs( id: ComponentID, @@ -754,10 +781,10 @@ export class ScopeMain implements ComponentFactory { if (startOffset !== 0) { startNode = this.findNodeByOffset( - startNode, versionGraph, Math.abs(startOffset), - startOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph) + startOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph), + startNode ); } @@ -766,10 +793,10 @@ export class ScopeMain implements ComponentFactory { let stopNode = stopRef?.hash ? versionGraph.node(stopRef.hash) : undefined; if (stopOffset !== 0) { stopNode = this.findNodeByOffset( - stopNode, versionGraph, Math.abs(stopOffset), - stopOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph) + stopOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph), + stopNode ); } diff --git a/src/scope/component-ops/traverse-versions.ts b/src/scope/component-ops/traverse-versions.ts index 6fb540ade05c..5265cbcdfcb9 100644 --- a/src/scope/component-ops/traverse-versions.ts +++ b/src/scope/component-ops/traverse-versions.ts @@ -48,10 +48,8 @@ export async function getAllVersionsInfo({ repo?: Repository; throws?: boolean; // in case objects are missing versionObjects?: Version[]; - startFrom?: Ref | null; // by default, start from the head // - startFromOffset?: number; // -5 + startFrom?: Ref | null; // by default, start from the head stopAt?: Ref[] | null; // by default, stop when the parents is empty - stopAtOffset?: number; // 5 }): Promise { const results: VersionInfo[] = []; const isAlreadyProcessed = (ref: Ref): boolean => { From 93c92d65c9d605459dd9ca7117c7ab40cec92358 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Fri, 12 May 2023 15:21:49 -0400 Subject: [PATCH 13/20] lint fix --- scopes/component/component/component.ts | 1 - scopes/component/ui/version-dropdown/version-dropdown.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scopes/component/component/component.ts b/scopes/component/component/component.ts index c1b489f9cd36..1dcab2d3b268 100644 --- a/scopes/component/component/component.ts +++ b/scopes/component/component/component.ts @@ -5,7 +5,6 @@ import { ComponentID } from '@teambit/component-id'; import { BitError } from '@teambit/bit-error'; import { BuildStatus } from '@teambit/legacy/dist/constants'; -import { slice } from 'lodash'; import { ComponentFactory } from './component-factory'; import ComponentFS from './component-fs'; // import { NothingToSnap } from './exceptions'; diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 19698a8d7b00..ca3441cce9a2 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -4,7 +4,7 @@ import { Dropdown } from '@teambit/evangelist.surfaces.dropdown'; import { Tab } from '@teambit/ui-foundation.ui.use-box.tab'; import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { UserAvatar } from '@teambit/design.ui.avatar'; -import { LineSkeleton, WordSkeleton } from '@teambit/base-ui.loaders.skeleton'; +import { LineSkeleton } from '@teambit/base-ui.loaders.skeleton'; import { LaneModel } from '@teambit/lanes.ui.models.lanes-model'; import classNames from 'classnames'; From ae77ed87d334c1991bc7a39eea9b84f71b662d65 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Fri, 12 May 2023 16:37:52 -0400 Subject: [PATCH 14/20] fix updating offset while lazy loading --- .../component/ui/use-component-query.ts | 145 +++++++++++------- 1 file changed, 90 insertions(+), 55 deletions(-) diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index aea5653c451f..123c5bae98e9 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -255,6 +255,51 @@ function getOffsetValue(offset, limit) { } return undefined; } +/** + * Calculates the new offset based on initial offset, current offset, and the number of logs. + * + * @param {boolean} [fetchLogsByTypeSeparately] A flag to determine if logs are fetched by type separately. + * @param {number} [initialOffset] The initial offset. + * @param {number} [currentOffset] The current offset. + * @param {any[]} [logs=[]] The array of logs. + * + * @returns {number | undefined} - new offset + */ +function calculateNewOffset( + fetchLogsByTypeSeparately?: boolean, + initialOffset = 0, + currentOffset = 0, + logs: any[] = [] +): number | undefined { + if (!fetchLogsByTypeSeparately) return currentOffset; + + const logCount = logs.length; + + if (initialOffset !== currentOffset && logCount + initialOffset >= currentOffset) return currentOffset; + return logCount + initialOffset; +} + +/** + * Calculate the availability of more logs. + * + * @param {number | undefined} logLimit - The limit for the logs. + * @param {any} rawComponent - The raw component object containing logs. + * @param {string} logType - Type of log ('logs', 'tagLogs', 'snapLogs'). + * @param {boolean | undefined} currentHasMoreLogs - Current state of having more logs. + * + * @returns {boolean | undefined} - Whether there are more logs available. + */ +function calculateHasMoreLogs( + logLimit?: number, + rawComponent?: any, + logType = 'logs', + currentHasMoreLogs?: boolean +): boolean | undefined { + if (!logLimit) return false; + if (rawComponent === undefined) return undefined; + if (currentHasMoreLogs === undefined) return rawComponent?.[logType]?.length >= logLimit; + return currentHasMoreLogs; +} /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ export function useComponentQuery( componentId: string, @@ -324,49 +369,38 @@ export function useComponentQuery( }); const rawComponent = data?.getHost?.get; - const rawTags = rawComponent?.tagLogs ?? []; - const rawSnaps = rawComponent?.snapLogs ?? []; - const rawCompLogs = rawComponent?.logs ?? mergeLogs(rawTags, rawSnaps); - offsetRef.current = useMemo(() => { - const currentOffset = offsetRef.current; - if (!currentOffset) return rawCompLogs.length; - return offsetRef.current; - }, [rawCompLogs]); - - tagOffsetRef.current = useMemo(() => { - if (!fetchLogsByTypeSeparately) return offsetRef.current; - const currentOffset = tagOffsetRef.current; - if (!currentOffset) return rawTags.length; - return tagOffsetRef.current; - }, [rawCompLogs]); - - snapOffsetRef.current = useMemo(() => { - if (!fetchLogsByTypeSeparately) return offsetRef.current; - const currentOffset = snapOffsetRef.current; - if (!currentOffset) return rawSnaps.length; - return snapOffsetRef.current; - }, [rawSnaps]); - - hasMoreLogs.current = useMemo(() => { - if (!logLimit) return false; - if (rawComponent === undefined) return undefined; - if (hasMoreLogs.current === undefined) return rawComponent?.logs.length >= logLimit; - return hasMoreLogs.current; - }, [rawCompLogs]); - - hasMoreTagLogs.current = useMemo(() => { - if (!tagLogLimit) return false; - if (rawComponent === undefined) return undefined; - if (hasMoreTagLogs.current === undefined) return rawComponent?.tagLogs.length >= tagLogLimit; - return hasMoreTagLogs.current; - }, [rawTags]); - - hasMoreSnapLogs.current = useMemo(() => { - if (!snapLogLimit) return false; - if (rawComponent === undefined) return undefined; - if (hasMoreSnapLogs.current === undefined) return rawComponent?.snapLogs.length === snapLogLimit; - return hasMoreSnapLogs.current; - }, [rawSnaps]); + const rawTags: Array = rawComponent?.tagLogs ?? []; + const rawSnaps: Array = rawComponent?.snapLogs ?? []; + const rawCompLogs: Array = rawComponent?.logs ?? mergeLogs(rawTags, rawSnaps); + offsetRef.current = useMemo( + () => calculateNewOffset(fetchLogsByTypeSeparately, logOffset, offsetRef.current, rawCompLogs), + [rawCompLogs, fetchLogsByTypeSeparately, logOffset] + ); + + tagOffsetRef.current = useMemo( + () => calculateNewOffset(fetchLogsByTypeSeparately, tagLogOffset, tagOffsetRef.current, rawTags), + [rawTags, fetchLogsByTypeSeparately, tagLogOffset] + ); + + snapOffsetRef.current = useMemo( + () => calculateNewOffset(fetchLogsByTypeSeparately, snapLogOffset, snapOffsetRef.current, rawSnaps), + [rawSnaps, fetchLogsByTypeSeparately, snapLogOffset] + ); + + hasMoreLogs.current = useMemo( + () => calculateHasMoreLogs(logLimit, rawComponent, 'logs', hasMoreLogs.current), + [rawCompLogs] + ); + + hasMoreTagLogs.current = useMemo( + () => calculateHasMoreLogs(tagLogLimit, rawComponent, 'tagLogs', hasMoreTagLogs.current), + [rawTags] + ); + + hasMoreSnapLogs.current = useMemo( + () => calculateHasMoreLogs(snapLogLimit, rawComponent, 'snapLogs', hasMoreSnapLogs.current), + [rawSnaps] + ); const loadMoreLogs = React.useCallback( async (backwards = false) => { @@ -386,7 +420,7 @@ export function useComponentQuery( const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); hasMoreLogs.current = fetchedComponent.logs.length >= logLimit; if (updatedLogs.length > prevComponent.logs.length) { - offsetRef.current = updatedLogs.length; + offsetRef.current = fetchedComponent.logs.length + offset; } return { @@ -424,13 +458,14 @@ export function useComponentQuery( const prevComponent = prev.getHost.get; const fetchedComponent = fetchMoreResult.getHost.get; - const prevCompLogs = prevComponent.tagLogs; + const prevTags = prevComponent.tagLogs; + const fetchedTags = fetchedComponent.tagLogs ?? []; if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedTags = mergeLogs(prevCompLogs, fetchedComponent.tagLogs); - if (updatedTags.length > prevCompLogs.length) { - tagOffsetRef.current = updatedTags.length; + const updatedTags = mergeLogs(prevTags, fetchedTags); + if (updatedTags.length > prevTags.length) { + tagOffsetRef.current = fetchedTags.length + offset; } - hasMoreTagLogs.current = fetchedComponent.tagLogs.length >= tagLogLimit; + hasMoreTagLogs.current = fetchedTags.length >= tagLogLimit; return { ...prev, getHost: { @@ -465,15 +500,15 @@ export function useComponentQuery( if (!fetchMoreResult) return prev; const prevComponent = prev.getHost.get; - const prevCompLogs = prevComponent.snapLogs ?? []; - + const prevSnaps = prevComponent.snapLogs ?? []; const fetchedComponent = fetchMoreResult.getHost.get; + const fetchedSnaps = fetchedComponent.snapLogs ?? []; if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedSnaps = mergeLogs(prevCompLogs, fetchedComponent.snapLogs); - if (updatedSnaps.length > prevCompLogs.length) { - snapOffsetRef.current = updatedSnaps.length; + const updatedSnaps = mergeLogs(prevSnaps, fetchedSnaps); + if (updatedSnaps.length > prevSnaps.length) { + snapOffsetRef.current = fetchedSnaps.length + offset; } - hasMoreSnapLogs.current = fetchedComponent.snapLogs.length >= snapLogLimit; + hasMoreSnapLogs.current = fetchedSnaps.length >= snapLogLimit; return { ...prev, getHost: { From 72b64f2a609af822ee084b28ecb25a40f1f92503 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Mon, 15 May 2023 17:35:10 -0400 Subject: [PATCH 15/20] allow lazy loading by type --- .../changelog/ui/change-log-page.tsx | 1 + .../component/component/component-factory.ts | 3 +- .../component/component/component.graphql.ts | 2 +- scopes/component/component/component.ts | 16 +--- .../component/ui/use-component-query.ts | 71 +++++++++----- .../ui/version-dropdown/version-dropdown.tsx | 17 ++-- scopes/lanes/lanes/lanes.ui.runtime.tsx | 13 ++- scopes/scope/scope/scope.main.runtime.ts | 95 ++++++++++++++----- scopes/workspace/workspace/workspace.ts | 5 +- 9 files changed, 149 insertions(+), 74 deletions(-) diff --git a/scopes/component/changelog/ui/change-log-page.tsx b/scopes/component/changelog/ui/change-log-page.tsx index c518c24aa956..a63ab88788ee 100644 --- a/scopes/component/changelog/ui/change-log-page.tsx +++ b/scopes/component/changelog/ui/change-log-page.tsx @@ -20,6 +20,7 @@ export function ChangeLogPage({ className, host }: ChangeLogPageProps) { logFilters: { log: { logLimit: 15, + logOffset: 0, }, }, }); diff --git a/scopes/component/component/component-factory.ts b/scopes/component/component/component-factory.ts index 4bbd20a5392b..2adb3d40d316 100644 --- a/scopes/component/component/component-factory.ts +++ b/scopes/component/component/component-factory.ts @@ -122,7 +122,8 @@ export interface ComponentFactory { startFrom?: string, stopAt?: string, startFromOffset?: number, - stopAtOffset?: number + stopAtOffset?: number, + type?: 'tag' | 'snap' ): Promise; /** diff --git a/scopes/component/component/component.graphql.ts b/scopes/component/component/component.graphql.ts index 4177e7895fea..1c3cc4e78875 100644 --- a/scopes/component/component/component.graphql.ts +++ b/scopes/component/component/component.graphql.ts @@ -202,7 +202,7 @@ export function componentSchema(componentExtension: ComponentMain) { logs: async ( component: Component, filter?: { - type?: string; + type?: 'tag' | 'snap'; offset?: number; limit?: number; head?: string; diff --git a/scopes/component/component/component.ts b/scopes/component/component/component.ts index 1dcab2d3b268..0505e8bced7f 100644 --- a/scopes/component/component/component.ts +++ b/scopes/component/component/component.ts @@ -114,7 +114,7 @@ export class Component implements IComponent { } async getLogs(filter?: { - type?: string; + type?: 'tag' | 'snap'; offset?: number; limit?: number; head?: string; @@ -123,20 +123,10 @@ export class Component implements IComponent { until?: string; }) { const { type, limit, offset, sort, head, startFrom, until } = filter || {}; - const typeFilter = (snap) => { - if (type === 'tag') return snap.tag; - if (type === 'snap') return !snap.tag; - return true; - }; - - const allLogs = await this.factory.getLogs(this.id, false, head, startFrom, until, offset, limit); - if (!filter) { - return allLogs; - } + const filteredLogs = await this.factory.getLogs(this.id, false, head, startFrom, until, offset, limit, type); - let filteredLogs = (type && allLogs.filter(typeFilter)) || allLogs; - if (sort !== 'asc') filteredLogs = filteredLogs.reverse(); + if (sort !== 'asc') filteredLogs.reverse(); return filteredLogs; } diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index 123c5bae98e9..945136ad5784 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -246,9 +246,9 @@ export type ComponentQueryResult = { loading?: boolean; error?: ComponentError; }; -function getOffsetValue(offset, limit) { +function getOffsetValue(offset, limit, backwards = false) { if (offset !== undefined) { - return offset; + return backwards ? -offset : offset; } if (limit !== undefined) { return 0; @@ -265,14 +265,7 @@ function getOffsetValue(offset, limit) { * * @returns {number | undefined} - new offset */ -function calculateNewOffset( - fetchLogsByTypeSeparately?: boolean, - initialOffset = 0, - currentOffset = 0, - logs: any[] = [] -): number | undefined { - if (!fetchLogsByTypeSeparately) return currentOffset; - +function calculateNewOffset(initialOffset = 0, currentOffset = 0, logs: any[] = []): number | undefined { const logCount = logs.length; if (initialOffset !== currentOffset && logCount + initialOffset >= currentOffset) return currentOffset; @@ -335,7 +328,7 @@ export function useComponentQuery( fetchLogsByTypeSeparately, snapLogOffset: getOffsetValue(snapLogOffset, snapLogLimit), tagLogOffset: getOffsetValue(tagLogOffset, tagLogLimit), - logOffset: logOffset ?? logLimit ? 0 : undefined, + logOffset: getOffsetValue(logOffset, logLimit), logLimit, snapLogLimit, tagLogLimit, @@ -373,17 +366,17 @@ export function useComponentQuery( const rawSnaps: Array = rawComponent?.snapLogs ?? []; const rawCompLogs: Array = rawComponent?.logs ?? mergeLogs(rawTags, rawSnaps); offsetRef.current = useMemo( - () => calculateNewOffset(fetchLogsByTypeSeparately, logOffset, offsetRef.current, rawCompLogs), + () => calculateNewOffset(logOffset, offsetRef.current, rawCompLogs), [rawCompLogs, fetchLogsByTypeSeparately, logOffset] ); tagOffsetRef.current = useMemo( - () => calculateNewOffset(fetchLogsByTypeSeparately, tagLogOffset, tagOffsetRef.current, rawTags), + () => calculateNewOffset(tagLogOffset, tagOffsetRef.current, rawTags), [rawTags, fetchLogsByTypeSeparately, tagLogOffset] ); snapOffsetRef.current = useMemo( - () => calculateNewOffset(fetchLogsByTypeSeparately, snapLogOffset, snapOffsetRef.current, rawSnaps), + () => calculateNewOffset(snapLogOffset, snapOffsetRef.current, rawSnaps), [rawSnaps, fetchLogsByTypeSeparately, snapLogOffset] ); @@ -404,7 +397,7 @@ export function useComponentQuery( const loadMoreLogs = React.useCallback( async (backwards = false) => { - const offset = offsetRef.current ? (backwards && -offsetRef.current) || offsetRef.current : undefined; + const offset = getOffsetValue(offsetRef.current, logLimit, backwards); if (logLimit) { await fetchMore({ @@ -445,7 +438,7 @@ export function useComponentQuery( const loadMoreTags = React.useCallback( async (backwards = false) => { - const offset = tagOffsetRef.current ? (backwards && -tagOffsetRef.current) || tagOffsetRef.current : undefined; + const offset = getOffsetValue(tagOffsetRef.current, tagLogLimit, backwards); if (tagLogLimit) { await fetchMore({ @@ -488,7 +481,7 @@ export function useComponentQuery( const loadMoreSnaps = React.useCallback( async (backwards = false) => { - const offset = snapOffsetRef.current ? (backwards && -snapOffsetRef.current) || snapOffsetRef.current : undefined; + const offset = getOffsetValue(snapOffsetRef.current, snapLogLimit, backwards); if (snapLogLimit) { await fetchMore({ @@ -687,10 +680,44 @@ interface Log { id: string; date: string; } - function mergeLogs(logs1: Log[] = [], logs2: Log[] = []): Log[] { - const mergedLogs: Log[] = []; - logs1.forEach((log) => mergedLogs.push(log)); - logs2.forEach((log) => mergedLogs.push(log)); - return mergedLogs; + const logMap = new Map(); + const result: Log[] = []; + + let index1 = 0; + let index2 = 0; + + while (index1 < logs1.length && index2 < logs2.length) { + if (Number(logs1[index1].date) >= Number(logs2[index2].date)) { + if (!logMap.has(logs1[index1].id)) { + logMap.set(logs1[index1].id, logs1[index1]); + result.push(logs1[index1]); + } + index1 += 1; + } else { + if (!logMap.has(logs2[index2].id)) { + logMap.set(logs2[index2].id, logs2[index2]); + result.push(logs2[index2]); + } + index2 += 1; + } + } + + while (index1 < logs1.length) { + if (!logMap.has(logs1[index1].id)) { + logMap.set(logs1[index1].id, logs1[index1]); + result.push(logs1[index1]); + } + index1 += 1; + } + + while (index2 < logs2.length) { + if (!logMap.has(logs2[index2].id)) { + logMap.set(logs2[index2].id, logs2[index2]); + result.push(logs2[index2]); + } + index2 += 1; + } + + return result; } diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index ca3441cce9a2..1ce97e72aae0 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -69,7 +69,8 @@ function _VersionMenu( const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = tabs[activeTabIndex]?.name; const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; - const observer = React.useRef(); + const firstObserver = React.useRef(); + const lastObserver = React.useRef(); const handleLoadMore = React.useCallback( (backwards?: boolean) => { @@ -82,8 +83,8 @@ function _VersionMenu( const lastLogRef = React.useCallback( (node) => { if (loading) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver( + if (lastObserver.current) lastObserver.current.disconnect(); + lastObserver.current = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && hasMore) { handleLoadMore(); @@ -94,7 +95,7 @@ function _VersionMenu( rootMargin: '100px', } ); - if (node) observer.current.observe(node); + if (node) lastObserver.current.observe(node); }, [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] ); @@ -102,8 +103,8 @@ function _VersionMenu( const firstLogRef = React.useCallback( (node) => { if (loading) return; - if (observer.current) observer.current.disconnect(); - observer.current = new IntersectionObserver( + if (firstObserver.current) firstObserver.current.disconnect(); + firstObserver.current = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && hasMore) { handleLoadMore(true); @@ -111,10 +112,10 @@ function _VersionMenu( }, { threshold: 0.1, - rootMargin: '100px', + rootMargin: '50px', } ); - if (node) observer.current.observe(node); + if (node) firstObserver.current.observe(node); }, [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] ); diff --git a/scopes/lanes/lanes/lanes.ui.runtime.tsx b/scopes/lanes/lanes/lanes.ui.runtime.tsx index 0d0ca89bc775..93402a945c31 100644 --- a/scopes/lanes/lanes/lanes.ui.runtime.tsx +++ b/scopes/lanes/lanes/lanes.ui.runtime.tsx @@ -16,6 +16,7 @@ import { LanesOrderedNavigationSlot, LanesOverviewMenu, } from '@teambit/lanes.ui.menus.lanes-overview-menu'; +import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; import { UseLaneMenu } from '@teambit/lanes.ui.menus.use-lanes-menu'; import { LanesHost, LanesModel } from '@teambit/lanes.ui.models.lanes-model'; import { LanesProvider, useLanes, IgnoreDerivingFromUrl } from '@teambit/lanes.hooks.use-lanes'; @@ -52,16 +53,26 @@ export function useComponentFilters() { }, }; } -export function useLaneComponentIdFromUrl() { +export function useLaneComponentIdFromUrl(): ComponentID | undefined | null { const idFromLocation = useIdFromLocation(); const { lanesModel, loading } = useLanes(); const laneFromUrl = useViewedLaneFromUrl(); + const query = useQuery(); + const componentVersion = query.get('version'); + + if (componentVersion && laneFromUrl) { + const componentId = ComponentID.fromString(`${idFromLocation}@${componentVersion}`); + console.log('🚀 ~ file: lanes.ui.runtime.tsx:66 ~ useLaneComponentIdFromUrl ~ componentId:', componentId); + return componentId; + } const laneComponentId = idFromLocation && !laneFromUrl?.isDefault() ? lanesModel?.resolveComponentFromUrl(idFromLocation, laneFromUrl) ?? null : null; + return loading ? undefined : laneComponentId; } + export function useComponentId() { return useLaneComponentIdFromUrl()?.toString(); } diff --git a/scopes/scope/scope/scope.main.runtime.ts b/scopes/scope/scope/scope.main.runtime.ts index f6e0df5db639..2efe6ced8a5a 100644 --- a/scopes/scope/scope/scope.main.runtime.ts +++ b/scopes/scope/scope/scope.main.runtime.ts @@ -51,6 +51,7 @@ import ConsumerComponent from '@teambit/legacy/dist/consumer/component'; import { resumeExport } from '@teambit/legacy/dist/scope/component-ops/export-scope-components'; import { ExtensionDataEntry, ExtensionDataList } from '@teambit/legacy/dist/consumer/config'; import EnvsAspect, { EnvsMain } from '@teambit/envs'; +import { isSnap, isTag } from '@teambit/component-version'; import { compact, slice, difference } from 'lodash'; import { ComponentNotFound } from './exceptions'; import { ScopeAspect } from './scope.aspect'; @@ -680,23 +681,24 @@ export class ScopeMain implements ComponentFactory { if (!ref) throw new Error(`ref was not found: ${id.toString()} with tag ${hash}`); return this.componentLoader.getSnap(id, ref.toString()); } - /** * Performs a Depth-First Search (DFS) to find a node in a graph by offset. * - * This function starts from a given node and moves either forward or backward in the graph, depending on the provided getNodes function. - * The goal is to reach a node that is at a given offset from the starting node. The offset is decremented each time a new node is visited. - * The function will return the node that corresponds to the exact offset if it exists. + * This function starts from a given node and moves either forward or backward in the graph, depending on the provided getNodesFunc function. + * The goal is to reach a node that is at a given offset from the starting node * * If the graph cannot go any deeper (i.e., there are no more successors or predecessors), or the offset becomes zero, the function will return the last visited node. * This ensures that the function always returns a node, either the exact offset node (if it exists) or the closest node by offset. * - * @param node The node from where to start the search. * @param versionGraph The graph in which to perform the search. * @param offset The offset from the starting node to find. It is always an absolute value. * @param getNodesFunc A function that, given a node id, returns the successors (if moving forward) or predecessors (if moving backward) of that node in the graph. + * @param node The node from where to start the search. If not provided, the function will return undefined. + * @param nodeFilter Optional. A function that, given a node, determines if it should be included in the search. If not provided, all nodes are included. + * @param edgeFilter Optional. A function that, given an edge, determines if it should be included in the search. If not provided, all edges are included. + * @param skipNode Optional. A function that, given a node, determines if it should be skipped (not decrease the offset). If not provided, no nodes are skipped. * - * @returns The node that corresponds to the exact offset if it exists, or the closest node by offset otherwise. + * @returns The node that corresponds to the exact offset if it exists, or the closest node by offset otherwise. If no starting node is provided, the function will return undefined. */ private findNodeByOffset( versionGraph: Graph, @@ -711,18 +713,33 @@ export class ScopeMain implements ComponentFactory { edgeFilter?: (edge: Edge) => boolean; } ) => Array>, - node?: Node + node?: Node, + nodeFilter?: (node: Node) => boolean, + edgeFilter?: (edge: Edge) => boolean, + skipNode?: (node: Node) => boolean ) { if (offset === 0 || !node) return node; - const nextNodes = getNodesFunc(node.id, { edgeFilter: (edge) => edge.attr === 'parent' }); + const nextNodes = getNodesFunc(node.id, { edgeFilter, nodeFilter }); if (!nextNodes || nextNodes.length === 0) { return node; } for (const nextNode of nextNodes) { - const foundNode = this.findNodeByOffset(versionGraph, offset - 1, getNodesFunc, nextNode); + const skip = skipNode && skipNode(nextNode); + const nextOffset = skip ? offset : offset - 1; + + const foundNode = this.findNodeByOffset( + versionGraph, + nextOffset, + getNodesFunc, + nextNode, + nodeFilter, + edgeFilter, + skipNode + ); + if (foundNode) { return foundNode; } @@ -734,19 +751,20 @@ export class ScopeMain implements ComponentFactory { /** * Fetches the logs for a given component. * - * @param id - The ComponentID of the component for which to fetch logs. - * @param shortHash - If true, returns a shorter version of the hash. Defaults to false. - * @param head - The specific version to start fetching logs from. If not provided, starts from the head. - * @param startFrom - The specific version to start slicing logs from. - * @param stopAt - The specific version to stop fetching logs at. - * @param startFromOffset - Offset from the start version to fetch logs from. - * @param stopAtOffset - Offset from the stop version to fetch logs at. + * @param id The ComponentID of the component for which to fetch logs. + * @param shortHash If true, returns a shorter version of the hash. Defaults to false. + * @param head The specific version to start fetching logs from. If not provided, starts from the head. + * @param startFrom The specific version to start slicing logs from. + * @param stopAt The specific version to stop fetching logs at. + * @param startFromOffset Offset from the start version to fetch logs from. + * @param stopAtOffset Offset from the stop version to fetch logs at. + * @param type Optional. The type of logs to fetch - 'snap' or 'tag' * * @returns A promise that resolves to an array of ComponentLog objects representing the filtered logs for the component. * * @throws Error - Throws an error if the node with given headRef hash is not found. - * */ + async getLogs( id: ComponentID, shortHash = false, @@ -754,7 +772,8 @@ export class ScopeMain implements ComponentFactory { startFrom?: string, stopAt?: string, startFromOffset?: number, - stopAtOffset?: number + stopAtOffset?: number, + type?: 'snap' | 'tag' ): Promise { const componentModel = await this.legacyScope.getModelComponentIfExist(id._legacy); @@ -779,12 +798,28 @@ export class ScopeMain implements ComponentFactory { const startOffset = startFromOffset || 0; + const componentVersionSet = new Set(componentModel.versionArray.map((v) => v.hash)); + + const skipNode = (node: Node) => { + if (type === 'snap') { + return componentVersionSet.has(node.id); + } + if (type === 'tag') { + return !componentVersionSet.has(node.id); + } + + return false; + }; + if (startOffset !== 0) { startNode = this.findNodeByOffset( versionGraph, Math.abs(startOffset), startOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph), - startNode + startNode, + undefined, + (edge) => edge.attr === 'parent', + skipNode ); } @@ -796,16 +831,24 @@ export class ScopeMain implements ComponentFactory { versionGraph, Math.abs(stopOffset), stopOffset > 0 ? versionGraph.successors.bind(versionGraph) : versionGraph.predecessors.bind(versionGraph), - stopNode + stopNode, + undefined, + (edge) => edge.attr === 'parent', + skipNode ); } - return this.legacyScope.loadComponentLogs( - id._legacy, - shortHash, - startNode?.id, - stopNode ? [stopNode.id] : undefined - ); + return this.legacyScope + .loadComponentLogs(id._legacy, shortHash, startNode?.id, stopNode ? [stopNode.id] : undefined) + .then((logs) => { + if (type === 'snap') { + return logs.filter((log) => !log.tag); + } + if (type === 'tag') { + return logs.filter((log) => log.tag); + } + return logs; + }); } async getStagedConfig() { diff --git a/scopes/workspace/workspace/workspace.ts b/scopes/workspace/workspace/workspace.ts index 553385e2b5a0..5f4ca3475dbb 100644 --- a/scopes/workspace/workspace/workspace.ts +++ b/scopes/workspace/workspace/workspace.ts @@ -433,9 +433,10 @@ export class Workspace implements ComponentFactory { startFrom?: string, stopAt?: string, startFromOffset?: number, - stopAtOffset?: number + stopAtOffset?: number, + type?: 'snap' | 'tag' ): Promise { - return this.scope.getLogs(id, shortHash, head, startFrom, stopAt, startFromOffset, stopAtOffset); + return this.scope.getLogs(id, shortHash, head, startFrom, stopAt, startFromOffset, stopAtOffset, type); } async getGraph(ids?: ComponentID[], shouldThrowOnMissingDep = true): Promise> { From 9027cd6ed036f3db8a1f85853c574873e3647dcb Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 16 May 2023 11:17:57 -0400 Subject: [PATCH 16/20] fix lazy loading backwards --- .../component/ui/use-component-query.ts | 20 +- .../version-dropdown.module.scss | 21 +++ .../ui/version-dropdown/version-dropdown.tsx | 178 ++++++++++++------ scopes/lanes/lanes/lanes.ui.runtime.tsx | 1 - scopes/scope/scope/scope.main.runtime.ts | 1 - 5 files changed, 153 insertions(+), 68 deletions(-) diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts index 945136ad5784..07aec5957045 100644 --- a/scopes/component/component/ui/use-component-query.ts +++ b/scopes/component/component/ui/use-component-query.ts @@ -237,9 +237,9 @@ export type ComponentQueryResult = { hasMoreLogs?: boolean; hasMoreSnaps?: boolean; hasMoreTags?: boolean; - loadMoreLogs?: (backwards?: boolean) => void; - loadMoreTags?: (backwards?: boolean) => void; - loadMoreSnaps?: (backwards?: boolean) => void; + loadMoreLogs?: (backwards?: boolean, offset?: number) => void; + loadMoreTags?: (backwards?: boolean, offset?: number) => void; + loadMoreSnaps?: (backwards?: boolean, offset?: number) => void; snaps?: LegacyComponentLog[]; tags?: LegacyComponentLog[]; }; @@ -283,6 +283,7 @@ function calculateNewOffset(initialOffset = 0, currentOffset = 0, logs: any[] = * @returns {boolean | undefined} - Whether there are more logs available. */ function calculateHasMoreLogs( + // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) logLimit?: number, rawComponent?: any, logType = 'logs', @@ -290,7 +291,7 @@ function calculateHasMoreLogs( ): boolean | undefined { if (!logLimit) return false; if (rawComponent === undefined) return undefined; - if (currentHasMoreLogs === undefined) return rawComponent?.[logType]?.length >= logLimit; + if (currentHasMoreLogs === undefined) return !!rawComponent?.[logType]?.length; return currentHasMoreLogs; } /** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ @@ -411,9 +412,10 @@ export function useComponentQuery( const fetchedComponent = fetchMoreResult.getHost.get; if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); - hasMoreLogs.current = fetchedComponent.logs.length >= logLimit; if (updatedLogs.length > prevComponent.logs.length) { offsetRef.current = fetchedComponent.logs.length + offset; + // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) + hasMoreLogs.current = true; } return { @@ -457,8 +459,10 @@ export function useComponentQuery( const updatedTags = mergeLogs(prevTags, fetchedTags); if (updatedTags.length > prevTags.length) { tagOffsetRef.current = fetchedTags.length + offset; + // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) + hasMoreTagLogs.current = true; } - hasMoreTagLogs.current = fetchedTags.length >= tagLogLimit; + return { ...prev, getHost: { @@ -500,8 +504,10 @@ export function useComponentQuery( const updatedSnaps = mergeLogs(prevSnaps, fetchedSnaps); if (updatedSnaps.length > prevSnaps.length) { snapOffsetRef.current = fetchedSnaps.length + offset; + // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) + hasMoreSnapLogs.current = true; } - hasMoreSnapLogs.current = fetchedSnaps.length >= snapLogLimit; + return { ...prev, getHost: { diff --git a/scopes/component/ui/version-dropdown/version-dropdown.module.scss b/scopes/component/ui/version-dropdown/version-dropdown.module.scss index 1a89c23168a1..d8e431adac2c 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.module.scss +++ b/scopes/component/ui/version-dropdown/version-dropdown.module.scss @@ -32,6 +32,7 @@ max-height: 240px; overflow-y: scroll; padding-bottom: 8px; + position: relative; } .versionRow { @@ -112,3 +113,23 @@ padding: 8px 0px; } } + +.pullDownIndicator { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + background-color: #f2f2f2; + font-size: 14px; + color: #666; + transform: translateY(-100%); + transition: transform 0.3s ease-in-out; + z-index: 1; + &.active { + transform: translateY(0); + } +} diff --git a/scopes/component/ui/version-dropdown/version-dropdown.tsx b/scopes/component/ui/version-dropdown/version-dropdown.tsx index 1ce97e72aae0..f38a0872bc2e 100644 --- a/scopes/component/ui/version-dropdown/version-dropdown.tsx +++ b/scopes/component/ui/version-dropdown/version-dropdown.tsx @@ -17,23 +17,20 @@ export const LOCAL_VERSION = 'workspace'; export type DropdownComponentVersion = Partial & { version: string }; -const VersionMenu = React.memo(React.forwardRef(_VersionMenu)); - -function _VersionMenu( - { - currentVersion, - localVersion, - latestVersion, - overrideVersionHref, - showVersionDetails, - useVersions, - currentLane, - lanes, - loading: loadingFromProps, - ...rest - }: VersionMenuProps, - ref?: React.ForwardedRef -) { +const VersionMenu = React.memo(_VersionMenu); + +function _VersionMenu({ + currentVersion, + localVersion, + latestVersion, + overrideVersionHref, + showVersionDetails, + useVersions, + currentLane, + lanes, + loading: loadingFromProps, + ...rest +}: VersionMenuProps) { const { snaps, tags, @@ -65,12 +62,52 @@ function _VersionMenu( return 0; }; - const [activeTabIndex, setActiveTab] = React.useState(getActiveTabIndex()); - - const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = tabs[activeTabIndex]?.name; - const hasMore = activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags; + const [activeTabIndex, setActiveTab] = React.useState(undefined); const firstObserver = React.useRef(); const lastObserver = React.useRef(); + const currentVersionRef = React.useRef(); + + React.useEffect(() => { + if (activeTabIndex !== undefined) return; + if (!currentLane) return; + if (tabs.length === 0) return; + const _activeTabIndex = getActiveTabIndex(); + if (_activeTabIndex !== activeTabIndex) setActiveTab(_activeTabIndex); + }, [currentLane, currentVersion, snaps?.length, tags?.length, tabs.length]); + + const activeTabOrSnap: 'SNAP' | 'TAG' | 'LANE' | undefined = React.useMemo( + () => (activeTabIndex !== undefined ? tabs[activeTabIndex]?.name : undefined), + [activeTabIndex, tabs.length] + ); + + const hasMore = React.useMemo( + () => (activeTabOrSnap === 'SNAP' ? !!hasMoreSnaps : activeTabOrSnap === 'TAG' && !!hasMoreTags), + [hasMoreSnaps, activeTabOrSnap, hasMoreTags] + ); + + const [isIndicatorActive, setIsIndicatorActive] = React.useState(false); + + const handleScroll = (event) => { + if (event.target.scrollTop === 0) { + setIsIndicatorActive(true); + } else { + setIsIndicatorActive(false); + } + }; + + React.useEffect(() => { + if (!currentVersion) return; + if (currentVersionRef.current) { + currentVersionRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + }); + } + }, [currentVersion]); + + React.useEffect(() => { + setIsIndicatorActive(false); + }, [activeTabIndex]); const handleLoadMore = React.useCallback( (backwards?: boolean) => { @@ -79,7 +116,6 @@ function _VersionMenu( }, [activeTabOrSnap, tabs.length] ); - const lastLogRef = React.useCallback( (node) => { if (loading) return; @@ -97,7 +133,7 @@ function _VersionMenu( ); if (node) lastObserver.current.observe(node); }, - [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + [loading, hasMore, handleLoadMore] ); const firstLogRef = React.useCallback( @@ -111,20 +147,35 @@ function _VersionMenu( } }, { - threshold: 0.1, - rootMargin: '50px', + threshold: 1, + rootMargin: '0px', } ); if (node) firstObserver.current.observe(node); }, - [loading, hasMoreSnaps, hasMoreTags, handleLoadMore] + [loading, hasMore, handleLoadMore] ); + const versionRef = (node, version, index, versions) => { + if (index === 0) { + firstLogRef(node); + return { current: firstObserver.current }; + } + if (index === versions.length - 1) { + lastLogRef(node); + return { current: lastObserver.current }; + } + if (version === currentVersion) return currentVersionRef; + return null; + }; + const multipleTabs = tabs.length > 1; const message = multipleTabs ? 'Switch to view tags, snaps, or lanes' : `Switch between ${tabs[0]?.name.toLocaleLowerCase()}s`; + const showTab = activeTabIndex !== undefined && tabs[activeTabIndex]?.payload.length > 0; + return (
@@ -158,18 +209,31 @@ function _VersionMenu( ); })}
-
- {tabs[activeTabIndex]?.name === 'LANE' && +
+ {showTab && tabs[activeTabIndex]?.name !== 'LANE' && ( +
+ versionRef(node, (tabs[activeTabIndex]?.payload?.[0] as any).version, 0, tabs[activeTabIndex]?.payload) + } + > + Pull down to load more +
+ )} + {showTab && + tabs[activeTabIndex]?.name === 'LANE' && tabs[activeTabIndex]?.payload.map((payload) => ( ))} - {tabs[activeTabIndex]?.name !== 'LANE' && - tabs[activeTabIndex]?.payload.map((payload, index) => { - const _ref = - index === 0 ? firstLogRef : (index === tabs[activeTabIndex]?.payload.length - 1 && lastLogRef) || ref; + {showTab && + tabs[activeTabIndex]?.name !== 'LANE' && + tabs[activeTabIndex]?.payload.map((payload, index, versions) => { + // const _ref = (index === tabs[activeTabIndex]?.payload.length - 1 && lastLogRef) || ref; return ( + versionRef(node, index === 0 ? undefined : payload.version, index === 0 ? undefined : index, versions) + } key={payload.version} currentVersion={currentVersion} latestVersion={latestVersion} @@ -222,31 +286,28 @@ export type VersionDropdownProps = { placeholderComponent?: ReactNode; } & React.HTMLAttributes; -export const VersionDropdown = React.memo(React.forwardRef(_VersionDropdown)); - -function _VersionDropdown( - { - currentVersion, - latestVersion, - localVersion, - currentVersionLog = {}, - hasMoreVersions, - loading, - overrideVersionHref, - className, - placeholderClassName, - dropdownClassName, - menuClassName, - showVersionDetails, - disabled, - placeholderComponent, - currentLane, - useComponentVersions, - lanes, - ...rest - }: VersionDropdownProps, - ref?: React.ForwardedRef -) { +export const VersionDropdown = React.memo(_VersionDropdown); + +function _VersionDropdown({ + currentVersion, + latestVersion, + localVersion, + currentVersionLog = {}, + hasMoreVersions, + loading, + overrideVersionHref, + className, + placeholderClassName, + dropdownClassName, + menuClassName, + showVersionDetails, + disabled, + placeholderComponent, + currentLane, + useComponentVersions, + lanes, + ...rest +}: VersionDropdownProps) { const [key, setKey] = useState(0); const singleVersion = !hasMoreVersions; const [open, setOpen] = useState(false); @@ -303,7 +364,6 @@ function _VersionDropdown( > {open && ( Date: Tue, 16 May 2023 18:46:09 -0400 Subject: [PATCH 17/20] clean up --- .../changelog/ui/change-log-page.tsx | 6 +- .../component/component/get-component-opts.ts | 3 +- scopes/component/component/index.ts | 2 +- scopes/component/component/ui/component.tsx | 9 +- scopes/component/component/ui/index.ts | 11 +- scopes/component/component/ui/menu/menu.tsx | 6 +- .../ui/use-component-from-location.tsx | 22 - .../component/ui/use-component-query.ts | 729 ------------------ .../component/component/ui/use-component.tsx | 28 - .../use-component/use-component.fragments.ts | 8 +- .../ui/use-component/use-component.model.ts | 3 +- 11 files changed, 25 insertions(+), 802 deletions(-) delete mode 100644 scopes/component/component/ui/use-component-from-location.tsx delete mode 100644 scopes/component/component/ui/use-component-query.ts delete mode 100644 scopes/component/component/ui/use-component.tsx diff --git a/scopes/component/changelog/ui/change-log-page.tsx b/scopes/component/changelog/ui/change-log-page.tsx index a63ab88788ee..68c904155842 100644 --- a/scopes/component/changelog/ui/change-log-page.tsx +++ b/scopes/component/changelog/ui/change-log-page.tsx @@ -28,8 +28,8 @@ export function ChangeLogPage({ className, host }: ChangeLogPageProps) { const logs = component?.logs ?? []; const observer = React.useRef(); - const handleLoadMore = () => { - loadMoreLogs?.(); + const handleLoadMore = async () => { + await loadMoreLogs?.(); }; const lastLogRef = React.useCallback( @@ -38,7 +38,7 @@ export function ChangeLogPage({ className, host }: ChangeLogPageProps) { if (observer.current) observer.current.disconnect(); observer.current = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && hasMore) { - handleLoadMore(); + handleLoadMore().catch(() => {}); } }); if (node) observer.current.observe(node); diff --git a/scopes/component/component/get-component-opts.ts b/scopes/component/component/get-component-opts.ts index 995c910e796c..ffbc6bcb1918 100644 --- a/scopes/component/component/get-component-opts.ts +++ b/scopes/component/component/get-component-opts.ts @@ -1,7 +1,6 @@ import React from 'react'; import { RouteProps } from 'react-router-dom'; -import type { UseComponentType } from './ui/use-component'; -import { Filters } from './ui/use-component-query'; +import type { Filters, UseComponentType } from './ui/use-component'; export type GetComponentsOptions = { useComponent?: UseComponentType; diff --git a/scopes/component/component/index.ts b/scopes/component/component/index.ts index 9aebd0b6ddb7..e7a01c62cab0 100644 --- a/scopes/component/component/index.ts +++ b/scopes/component/component/index.ts @@ -33,7 +33,7 @@ export { Section } from './section'; export { ComponentContext, ComponentDescriptorContext, useComponentDescriptor } from './ui/context/component-context'; export type { ComponentProviderProps, ComponentDescriptorProviderProps } from './ui/context'; export { ComponentProvider, ComponentDescriptorProvider } from './ui/context'; -export { componentFields, componentIdFields, componentOverviewFields, COMPONENT_QUERY_FIELDS } from './ui'; +export { componentFields, componentIdFields, componentOverviewFields } from './ui'; export { NavPlugin, ConsumePlugin, diff --git a/scopes/component/component/ui/component.tsx b/scopes/component/component/ui/component.tsx index 47c83781d88c..642515d35737 100644 --- a/scopes/component/component/ui/component.tsx +++ b/scopes/component/component/ui/component.tsx @@ -4,13 +4,12 @@ import flatten from 'lodash.flatten'; import { RouteSlot, SlotRouter } from '@teambit/ui-foundation.ui.react-router.slot-router'; import { SlotRegistry } from '@teambit/harmony'; import { isFunction } from 'lodash'; -import styles from './component.module.scss'; +import { ComponentID } from '@teambit/component-id'; import { ComponentProvider, ComponentDescriptorProvider } from './context'; -import { useComponent as useComponentQuery, UseComponentType } from './use-component'; +import { Filters, useComponent as useComponentQuery, UseComponentType, useIdFromLocation } from './use-component'; import { ComponentModel } from './component-model'; -import { useIdFromLocation } from './use-component-from-location'; -import { ComponentID } from '..'; -import { Filters } from './use-component-query'; + +import styles from './component.module.scss'; export type ComponentPageSlot = SlotRegistry; export type ComponentPageElement = { diff --git a/scopes/component/component/ui/index.ts b/scopes/component/component/ui/index.ts index c64fa8d42593..53573ae7cf2a 100644 --- a/scopes/component/component/ui/index.ts +++ b/scopes/component/component/ui/index.ts @@ -3,13 +3,14 @@ export { Component } from './component'; export { ConsumeMethodSlot, ComponentMenu, VersionRelatedDropdowns } from './menu'; export { ComponentModel, ComponentModelProps } from './component-model'; export { ComponentContext, ComponentProvider } from './context'; -export { useComponent } from './use-component'; export { TopBarNav } from './top-bar-nav'; export { componentIdFields, componentOverviewFields, componentFields, - COMPONENT_QUERY_FIELDS, -} from './use-component-query'; -export type { ComponentQueryResult } from './use-component-query'; -export { useIdFromLocation } from './use-component-from-location'; + useIdFromLocation, + useComponent, + useComponentLogs, + useComponentQuery, +} from './use-component'; +export type { ComponentQueryResult } from './use-component'; diff --git a/scopes/component/component/ui/menu/menu.tsx b/scopes/component/component/ui/menu/menu.tsx index 8056b027b1fb..cb9241a391d8 100644 --- a/scopes/component/component/ui/menu/menu.tsx +++ b/scopes/component/component/ui/menu/menu.tsx @@ -11,15 +11,13 @@ import { useLanes } from '@teambit/lanes.hooks.use-lanes'; import { LaneModel } from '@teambit/lanes.ui.models.lanes-model'; import { Menu as ConsumeMethodsMenu } from '@teambit/ui-foundation.ui.use-box.menu'; import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; +import { ComponentID } from '@teambit/component-id'; import * as semver from 'semver'; import type { ComponentModel } from '../component-model'; -import { useComponent as useComponentQuery, UseComponentType } from '../use-component'; +import { Filters, useComponent as useComponentQuery, UseComponentType, useIdFromLocation } from '../use-component'; import { CollapsibleMenuNav } from './menu-nav'; import styles from './menu.module.scss'; import { OrderedNavigationSlot, ConsumeMethodSlot } from './nav-plugin'; -import { useIdFromLocation } from '../use-component-from-location'; -import { ComponentID } from '../..'; -import { Filters } from '../use-component-query'; export type MenuProps = { className?: string; diff --git a/scopes/component/component/ui/use-component-from-location.tsx b/scopes/component/component/ui/use-component-from-location.tsx deleted file mode 100644 index f385489e3037..000000000000 --- a/scopes/component/component/ui/use-component-from-location.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useParams } from 'react-router-dom'; - -/** component url is comprised of letters, numbers, "_", "-", "/" but should not include trailing "/", and should not include "~" */ -const componentRegex = /^[\w/-]*[\w-]/; - -export function useIdFromLocation(url?: string): string | undefined { - const params = useParams(); - const splat = url || params['*']; - if (!splat) return undefined; - - const [maybeOrgWithScope, ...maybeFullName] = splat.split('/'); - const hasScope = maybeOrgWithScope.split('.').length > 1; - const fullNameFromUrl = hasScope ? maybeFullName.join('/') : splat; - let scope: string | undefined; - if (hasScope) { - scope = maybeOrgWithScope; - } - const match = componentRegex.exec(fullNameFromUrl); - if (!match?.[0]) return undefined; - if (scope) return `${scope}/${match[0]}`; - return match[0]; -} diff --git a/scopes/component/component/ui/use-component-query.ts b/scopes/component/component/ui/use-component-query.ts deleted file mode 100644 index 07aec5957045..000000000000 --- a/scopes/component/component/ui/use-component-query.ts +++ /dev/null @@ -1,729 +0,0 @@ -import React, { useMemo, useEffect, useRef } from 'react'; -import { gql } from '@apollo/client'; -import { useDataQuery } from '@teambit/ui-foundation.ui.hooks.use-data-query'; -import { ComponentID, ComponentIdObj } from '@teambit/component-id'; -import { ComponentDescriptor } from '@teambit/component-descriptor'; -import { LegacyComponentLog } from '@teambit/legacy-component-log'; -import { ComponentModel } from './component-model'; -import { ComponentError } from './component-error'; - -export const componentIdFields = gql` - fragment componentIdFields on ComponentID { - name - version - scope - } -`; - -export const componentOverviewFields = gql` - fragment componentOverviewFields on Component { - id { - ...componentIdFields - } - aspects(include: ["teambit.preview/preview", "teambit.envs/envs"]) { - # 'id' property in gql refers to a *global* identifier and used for caching. - # this makes aspect data cache under the same key, even when they are under different components. - # renaming the property fixes that. - id - data - } - elementsUrl - description - deprecation { - isDeprecate - newId - } - labels - displayName - server { - env - url - host - basePath - } - buildStatus - env { - id - icon - } - size { - compressedTotal - } - preview { - includesEnvTemplate - legacyHeader - isScaling - skipIncludes - } - compositions { - identifier - displayName - } - } - ${componentIdFields} -`; - -export const componentFields = gql` - fragment componentFields on Component { - id { - ...componentIdFields - } - ...componentOverviewFields - packageName - latest - compositions { - identifier - displayName - } - tags { - version - } - logs( - type: $logType - offset: $logOffset - limit: $logLimit - sort: $logSort - takeHeadFromComponent: $takeHeadFromComponent - head: $logHead - startFrom: $logStartFrom - until: $logUntil - ) @skip(if: $fetchLogsByTypeSeparately) { - id - message - username - email - date - hash - tag - } - tagLogs: logs( - type: "tag" - offset: $tagLogOffset - limit: $tagLogLimit - sort: $tagLogSort - takeHeadFromComponent: $tagTakeHeadFromComponent - head: $tagLogHead - startFrom: $tagStartFrom - until: $tagUntil - ) @include(if: $fetchLogsByTypeSeparately) { - id - message - username - email - date - hash - tag - } - snapLogs: logs( - type: "snap" - offset: $snapLogOffset - limit: $snapLogLimit - sort: $snapLogSort - takeHeadFromComponent: $snapTakeHeadFromComponent - head: $snapLogHead - startFrom: $snapStartFrom - until: $snapUntil - ) @include(if: $fetchLogsByTypeSeparately) { - id - message - username - email - date - hash - tag - } - } - ${componentIdFields} - ${componentOverviewFields} -`; - -export const COMPONENT_QUERY_FIELDS = ` - $logOffset: Int - $logLimit: Int - $logType: String - $logHead: String - $logSort: String - $logStartFrom: String - $logUntil: String - $tagLogOffset: Int - $tagLogLimit: Int - $tagLogHead: String - $tagLogSort: String - $tagStartFrom: String - $tagUntil: String - $snapLogOffset: Int - $snapLogLimit: Int - $snapLogHead: String - $snapLogSort: String - $snapStartFrom: String - $snapUntil: String - $takeHeadFromComponent: Boolean - $tagTakeHeadFromComponent: Boolean - $snapTakeHeadFromComponent: Boolean - $fetchLogsByTypeSeparately: Boolean!`; - -const GET_COMPONENT = gql` - query Component( - ${COMPONENT_QUERY_FIELDS} - $extensionId: String! - $id: String! - ) { - getHost(id: $extensionId) { - id # used for GQL caching - get(id: $id) { - ...componentFields - } - } - } - ${componentFields} -`; - -const SUB_SUBSCRIPTION_ADDED = gql` - subscription OnComponentAdded(${COMPONENT_QUERY_FIELDS}) { - componentAdded { - component { - ...componentFields - } - } - } - ${componentFields} -`; - -const SUB_COMPONENT_CHANGED = gql` - subscription OnComponentChanged(${COMPONENT_QUERY_FIELDS}) { - componentChanged { - component { - ...componentFields - } - } - } - ${componentFields} -`; - -const SUB_COMPONENT_REMOVED = gql` - subscription OnComponentRemoved { - componentRemoved { - componentIds { - ...componentIdFields - } - } - } - ${componentIdFields} -`; - -export type LogFilter = { - logOffset?: number; - logLimit?: number; - logHead?: string; - logStartFrom?: string; - logUntil?: string; - logSort?: string; - takeHeadFromComponent?: boolean; -}; - -export type Filters = { - log?: LogFilter & { logType?: string }; - tagLog?: LogFilter; - snapLog?: LogFilter; - fetchLogsByTypeSeparately?: boolean; - loading?: boolean; -}; - -export type ComponentQueryResult = { - component?: ComponentModel; - componentDescriptor?: ComponentDescriptor; - // @todo refactor to useComponentLogs - componentLogs?: { - hasMoreLogs?: boolean; - hasMoreSnaps?: boolean; - hasMoreTags?: boolean; - loadMoreLogs?: (backwards?: boolean, offset?: number) => void; - loadMoreTags?: (backwards?: boolean, offset?: number) => void; - loadMoreSnaps?: (backwards?: boolean, offset?: number) => void; - snaps?: LegacyComponentLog[]; - tags?: LegacyComponentLog[]; - }; - loading?: boolean; - error?: ComponentError; -}; -function getOffsetValue(offset, limit, backwards = false) { - if (offset !== undefined) { - return backwards ? -offset : offset; - } - if (limit !== undefined) { - return 0; - } - return undefined; -} -/** - * Calculates the new offset based on initial offset, current offset, and the number of logs. - * - * @param {boolean} [fetchLogsByTypeSeparately] A flag to determine if logs are fetched by type separately. - * @param {number} [initialOffset] The initial offset. - * @param {number} [currentOffset] The current offset. - * @param {any[]} [logs=[]] The array of logs. - * - * @returns {number | undefined} - new offset - */ -function calculateNewOffset(initialOffset = 0, currentOffset = 0, logs: any[] = []): number | undefined { - const logCount = logs.length; - - if (initialOffset !== currentOffset && logCount + initialOffset >= currentOffset) return currentOffset; - return logCount + initialOffset; -} - -/** - * Calculate the availability of more logs. - * - * @param {number | undefined} logLimit - The limit for the logs. - * @param {any} rawComponent - The raw component object containing logs. - * @param {string} logType - Type of log ('logs', 'tagLogs', 'snapLogs'). - * @param {boolean | undefined} currentHasMoreLogs - Current state of having more logs. - * - * @returns {boolean | undefined} - Whether there are more logs available. - */ -function calculateHasMoreLogs( - // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) - logLimit?: number, - rawComponent?: any, - logType = 'logs', - currentHasMoreLogs?: boolean -): boolean | undefined { - if (!logLimit) return false; - if (rawComponent === undefined) return undefined; - if (currentHasMoreLogs === undefined) return !!rawComponent?.[logType]?.length; - return currentHasMoreLogs; -} -/** provides data to component ui page, making sure both variables and return value are safely typed and memoized */ -export function useComponentQuery( - componentId: string, - host: string, - filters?: Filters, - skip?: boolean -): ComponentQueryResult { - const idRef = useRef(componentId); - idRef.current = componentId; - const { fetchLogsByTypeSeparately = false, log, tagLog, snapLog } = filters || {}; - const { - logHead: tagLogHead, - logOffset: tagLogOffset, - logSort: tagLogSort, - logLimit: tagLogLimit, - takeHeadFromComponent: tagLogTakeHeadFromComponent, - logStartFrom: tagStartFrom, - logUntil: tagUntil, - } = tagLog || {}; - const { logHead, logOffset, logSort, logLimit, takeHeadFromComponent, logType, logStartFrom, logUntil } = log || {}; - const { - logHead: snapLogHead, - logOffset: snapLogOffset, - logSort: snapLogSort, - logLimit: snapLogLimit, - takeHeadFromComponent: snapLogTakeHeadFromComponent, - logStartFrom: snapStartFrom, - logUntil: snapUntil, - } = snapLog || {}; - const variables = { - id: componentId, - extensionId: host, - fetchLogsByTypeSeparately, - snapLogOffset: getOffsetValue(snapLogOffset, snapLogLimit), - tagLogOffset: getOffsetValue(tagLogOffset, tagLogLimit), - logOffset: getOffsetValue(logOffset, logLimit), - logLimit, - snapLogLimit, - tagLogLimit, - logType, - logHead, - snapLogHead, - tagLogHead, - logStartFrom, - snapStartFrom, - tagStartFrom, - logUntil, - snapUntil, - tagUntil, - logSort, - snapLogSort, - tagLogSort, - takeHeadFromComponent, - snapLogTakeHeadFromComponent, - tagLogTakeHeadFromComponent, - }; - const offsetRef = useRef(logOffset); - const tagOffsetRef = useRef(tagLogOffset); - const snapOffsetRef = useRef(snapLogOffset); - const hasMoreLogs = useRef(undefined); - const hasMoreTagLogs = useRef(undefined); - const hasMoreSnapLogs = useRef(undefined); - const { data, error, loading, subscribeToMore, fetchMore, ...rest } = useDataQuery(GET_COMPONENT, { - variables, - skip, - errorPolicy: 'all', - }); - - const rawComponent = data?.getHost?.get; - const rawTags: Array = rawComponent?.tagLogs ?? []; - const rawSnaps: Array = rawComponent?.snapLogs ?? []; - const rawCompLogs: Array = rawComponent?.logs ?? mergeLogs(rawTags, rawSnaps); - offsetRef.current = useMemo( - () => calculateNewOffset(logOffset, offsetRef.current, rawCompLogs), - [rawCompLogs, fetchLogsByTypeSeparately, logOffset] - ); - - tagOffsetRef.current = useMemo( - () => calculateNewOffset(tagLogOffset, tagOffsetRef.current, rawTags), - [rawTags, fetchLogsByTypeSeparately, tagLogOffset] - ); - - snapOffsetRef.current = useMemo( - () => calculateNewOffset(snapLogOffset, snapOffsetRef.current, rawSnaps), - [rawSnaps, fetchLogsByTypeSeparately, snapLogOffset] - ); - - hasMoreLogs.current = useMemo( - () => calculateHasMoreLogs(logLimit, rawComponent, 'logs', hasMoreLogs.current), - [rawCompLogs] - ); - - hasMoreTagLogs.current = useMemo( - () => calculateHasMoreLogs(tagLogLimit, rawComponent, 'tagLogs', hasMoreTagLogs.current), - [rawTags] - ); - - hasMoreSnapLogs.current = useMemo( - () => calculateHasMoreLogs(snapLogLimit, rawComponent, 'snapLogs', hasMoreSnapLogs.current), - [rawSnaps] - ); - - const loadMoreLogs = React.useCallback( - async (backwards = false) => { - const offset = getOffsetValue(offsetRef.current, logLimit, backwards); - - if (logLimit) { - await fetchMore({ - variables: { - logOffset: offset, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const fetchedComponent = fetchMoreResult.getHost.get; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedLogs = mergeLogs(prevComponent.logs, fetchedComponent.logs); - if (updatedLogs.length > prevComponent.logs.length) { - offsetRef.current = fetchedComponent.logs.length + offset; - // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) - hasMoreLogs.current = true; - } - - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - logs: updatedLogs, - }, - }, - }; - } - - return prev; - }, - }); - } - }, - [logLimit, fetchMore] - ); - - const loadMoreTags = React.useCallback( - async (backwards = false) => { - const offset = getOffsetValue(tagOffsetRef.current, tagLogLimit, backwards); - - if (tagLogLimit) { - await fetchMore({ - variables: { - tagLogOffset: offset, - tagLogLimit, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const fetchedComponent = fetchMoreResult.getHost.get; - const prevTags = prevComponent.tagLogs; - const fetchedTags = fetchedComponent.tagLogs ?? []; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedTags = mergeLogs(prevTags, fetchedTags); - if (updatedTags.length > prevTags.length) { - tagOffsetRef.current = fetchedTags.length + offset; - // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) - hasMoreTagLogs.current = true; - } - - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - tagLogs: updatedTags, - }, - }, - }; - } - - return prev; - }, - }); - } - }, - [tagLogLimit, fetchMore] - ); - - const loadMoreSnaps = React.useCallback( - async (backwards = false) => { - const offset = getOffsetValue(snapOffsetRef.current, snapLogLimit, backwards); - - if (snapLogLimit) { - await fetchMore({ - variables: { - snapLogOffset: offset, - snapLogLimit, - }, - updateQuery: (prev, { fetchMoreResult }) => { - if (!fetchMoreResult) return prev; - - const prevComponent = prev.getHost.get; - const prevSnaps = prevComponent.snapLogs ?? []; - const fetchedComponent = fetchMoreResult.getHost.get; - const fetchedSnaps = fetchedComponent.snapLogs ?? []; - if (fetchedComponent && ComponentID.isEqualObj(prevComponent.id, fetchedComponent.id)) { - const updatedSnaps = mergeLogs(prevSnaps, fetchedSnaps); - if (updatedSnaps.length > prevSnaps.length) { - snapOffsetRef.current = fetchedSnaps.length + offset; - // @todo account for limit (the API gives the nearest nodes to the limit, not the exact limit) - hasMoreSnapLogs.current = true; - } - - return { - ...prev, - getHost: { - ...prev.getHost, - get: { - ...prevComponent, - snapLogs: updatedSnaps, - }, - }, - }; - } - - return prev; - }, - }); - } - }, - [snapLogLimit, fetchMore] - ); - - useEffect(() => { - // @TODO @Kutner fix subscription for scope - if (host !== 'teambit.workspace/workspace') { - return () => {}; - } - - const unsubAddition = subscribeToMore({ - document: SUB_SUBSCRIPTION_ADDED, - variables: { - fetchLogsByTypeSeparately, - }, - updateQuery: (prev, { subscriptionData }) => { - const prevComponent = prev?.getHost?.get; - const addedComponent = subscriptionData?.data?.componentAdded?.component; - - if (!addedComponent || prevComponent) return prev; - - if (idRef.current === addedComponent.id.name) { - return { - ...prev, - getHost: { - ...prev.getHost, - get: addedComponent, - }, - }; - } - - return prev; - }, - }); - - const unsubChanges = subscribeToMore({ - document: SUB_COMPONENT_CHANGED, - variables: { - fetchLogsByTypeSeparately, - }, - updateQuery: (prev, { subscriptionData }) => { - if (!subscriptionData.data) return prev; - - const prevComponent = prev?.getHost?.get; - const updatedComponent = subscriptionData?.data?.componentChanged?.component; - - const isUpdated = updatedComponent && ComponentID.isEqualObj(prevComponent?.id, updatedComponent?.id); - - if (isUpdated) { - return { - ...prev, - getHost: { - ...prev.getHost, - get: updatedComponent, - }, - }; - } - - return prev; - }, - }); - - const unsubRemoval = subscribeToMore({ - document: SUB_COMPONENT_REMOVED, - variables: { - fetchLogsByTypeSeparately, - }, - updateQuery: (prev, { subscriptionData }) => { - if (!subscriptionData.data) return prev; - - const prevComponent = prev?.getHost?.get; - const removedIds: ComponentIdObj[] | undefined = subscriptionData?.data?.componentRemoved?.componentIds; - if (!prevComponent || !removedIds?.length) return prev; - - const isRemoved = removedIds.some((removedId) => ComponentID.isEqualObj(removedId, prevComponent.id)); - - if (isRemoved) { - return { - ...prev, - getHost: { - ...prev.getHost, - get: null, - }, - }; - } - - return prev; - }, - }); - - return () => { - unsubChanges(); - unsubAddition(); - unsubRemoval(); - offsetRef.current = undefined; - }; - }, []); - - const idDepKey = rawComponent?.id - ? `${rawComponent?.id?.scope}/${rawComponent?.id?.name}@${rawComponent?.id?.version}}` - : undefined; - - const id: ComponentID | undefined = useMemo( - () => (rawComponent ? ComponentID.fromObject(rawComponent.id) : undefined), - [idDepKey] - ); - - const componentError = - error && !data ? new ComponentError(500, error.message) : !rawComponent && !loading && new ComponentError(404); - - const component = useMemo( - () => (rawComponent ? ComponentModel.from({ ...rawComponent, host, logs: rawCompLogs }) : undefined), - [id?.toString(), rawCompLogs] - ); - - const componentDescriptor = useMemo(() => { - const aspectList = { - entries: rawComponent?.aspects.map((aspectObject) => { - return { - ...aspectObject, - aspectId: aspectObject.id, - aspectData: aspectObject.data, - }; - }), - }; - - return id ? ComponentDescriptor.fromObject({ id: id.toString(), aspectList }) : undefined; - }, [id?.toString()]); - - const snaps = useMemo(() => { - return rawComponent?.snapLogs; - }, [rawComponent?.snapLogs]); - - const tags = useMemo(() => { - return rawComponent?.tagLogs; - }, [rawComponent?.tagLogs]); - - return useMemo(() => { - return { - componentDescriptor, - component, - componentLogs: { - loadMoreLogs, - loadMoreSnaps, - loadMoreTags, - hasMoreSnaps: hasMoreSnapLogs.current, - hasMoreTags: hasMoreTagLogs.current, - hasMoreLogs: hasMoreLogs.current, - snaps, - tags, - }, - error: componentError || undefined, - loading, - ...rest, - }; - }, [host, component, componentDescriptor, componentError, hasMoreLogs, hasMoreSnapLogs, hasMoreTagLogs, snaps, tags]); -} - -interface Log { - id: string; - date: string; -} -function mergeLogs(logs1: Log[] = [], logs2: Log[] = []): Log[] { - const logMap = new Map(); - const result: Log[] = []; - - let index1 = 0; - let index2 = 0; - - while (index1 < logs1.length && index2 < logs2.length) { - if (Number(logs1[index1].date) >= Number(logs2[index2].date)) { - if (!logMap.has(logs1[index1].id)) { - logMap.set(logs1[index1].id, logs1[index1]); - result.push(logs1[index1]); - } - index1 += 1; - } else { - if (!logMap.has(logs2[index2].id)) { - logMap.set(logs2[index2].id, logs2[index2]); - result.push(logs2[index2]); - } - index2 += 1; - } - } - - while (index1 < logs1.length) { - if (!logMap.has(logs1[index1].id)) { - logMap.set(logs1[index1].id, logs1[index1]); - result.push(logs1[index1]); - } - index1 += 1; - } - - while (index2 < logs2.length) { - if (!logMap.has(logs2[index2].id)) { - logMap.set(logs2[index2].id, logs2[index2]); - result.push(logs2[index2]); - } - index2 += 1; - } - - return result; -} diff --git a/scopes/component/component/ui/use-component.tsx b/scopes/component/component/ui/use-component.tsx deleted file mode 100644 index cebbe47852fb..000000000000 --- a/scopes/component/component/ui/use-component.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; -import { ComponentQueryResult, Filters, useComponentQuery } from './use-component-query'; - -export type UseComponentOptions = { - version?: string; - logFilters?: Filters; - customUseComponent?: UseComponentType; - skip?: boolean; -}; - -export type UseComponentType = (id: string, host: string, filters?: Filters, skip?: boolean) => ComponentQueryResult; - -export function useComponent(host: string, id?: string, options?: UseComponentOptions): ComponentQueryResult { - const query = useQuery(); - const { version, logFilters, customUseComponent, skip } = options || {}; - const componentVersion = (version || query.get('version')) ?? undefined; - - const componentIdStr = id && withVersion(id, componentVersion); - const targetUseComponent = customUseComponent || useComponentQuery; - - return targetUseComponent(componentIdStr || '', host, logFilters, skip || !id); -} - -function withVersion(id: string, version?: string) { - if (!version) return id; - if (id.includes('@')) return id; - return `${id}@${version}`; -} diff --git a/scopes/component/component/ui/use-component/use-component.fragments.ts b/scopes/component/component/ui/use-component/use-component.fragments.ts index 120b168bf223..adc4fd494d2a 100644 --- a/scopes/component/component/ui/use-component/use-component.fragments.ts +++ b/scopes/component/component/ui/use-component/use-component.fragments.ts @@ -55,7 +55,6 @@ export const componentOverviewFields = gql` } ${componentIdFields} `; -console.log('🚀 ~ file: use-component.fragments.ts:58 ~ componentOverviewFields:', componentOverviewFields); export const componentFields = gql` fragment componentFields on Component { @@ -69,6 +68,7 @@ export const componentFields = gql` tags { version } + } ${componentOverviewFields} `; @@ -172,7 +172,11 @@ export const GET_COMPONENT = gql` `; export const GET_COMPONENT_WITH_LOGS = gql` - query Component($extensionId: String!, $id: String!) { + query Component( + $extensionId: String! + $id: String! + ${COMPONENT_QUERY_LOG_FIELDS} + ) { getHost(id: $extensionId) { id # used for GQL caching get(id: $id) { diff --git a/scopes/component/component/ui/use-component/use-component.model.ts b/scopes/component/component/ui/use-component/use-component.model.ts index f7dbc7945b69..fd8c5f655ad4 100644 --- a/scopes/component/component/ui/use-component/use-component.model.ts +++ b/scopes/component/component/ui/use-component/use-component.model.ts @@ -1,6 +1,7 @@ import { ComponentDescriptor } from '@teambit/component-descriptor'; import { LegacyComponentLog } from '@teambit/legacy-component-log'; -import { ComponentError, ComponentModel } from '..'; +import { ComponentError } from '../component-error'; +import { ComponentModel } from '../component-model'; export type LogFilter = { logOffset?: number; From 2c1ebd1f207ac84aba09e737de8e475393a468c8 Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 16 May 2023 18:57:12 -0400 Subject: [PATCH 18/20] clean up --- .../ui/use-component/use-component-logs.ts | 25 ------------------- .../use-component/use-component.fragments.ts | 6 +++-- .../ui/use-component/use-component.model.ts | 1 - 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/scopes/component/component/ui/use-component/use-component-logs.ts b/scopes/component/component/ui/use-component/use-component-logs.ts index 41d73686d495..ce286f69ec7c 100644 --- a/scopes/component/component/ui/use-component/use-component-logs.ts +++ b/scopes/component/component/ui/use-component/use-component-logs.ts @@ -1,7 +1,6 @@ import React, { useMemo, useRef } from 'react'; import { LegacyComponentLog } from '@teambit/legacy-component-log'; import { useDataQuery } from '@teambit/ui-foundation.ui.hooks.use-data-query'; -import { ComponentDescriptor } from '@teambit/component-descriptor'; import { ComponentID } from '@teambit/component-id'; import { calculateHasMoreLogs, calculateNewOffset, getOffsetValue, mergeLogs } from './use-component.utils'; import { ComponentLogsResult, Filters } from './use-component.model'; @@ -210,31 +209,7 @@ export function useComponentLogs( ? new ComponentError(500, error.message) : (!rawComponent && !loading && new ComponentError(404)) || undefined; - const idDepKey = rawComponent?.id - ? `${rawComponent?.id?.scope}/${rawComponent?.id?.name}@${rawComponent?.id?.version}}` - : undefined; - - const id: ComponentID | undefined = useMemo( - () => (rawComponent ? ComponentID.fromObject(rawComponent.id) : undefined), - [idDepKey] - ); - - const componentDescriptor = useMemo(() => { - const aspectList = { - entries: rawComponent?.aspects.map((aspectObject) => { - return { - ...aspectObject, - aspectId: aspectObject.id, - aspectData: aspectObject.data, - }; - }), - }; - - return id ? ComponentDescriptor.fromObject({ id: id.toString(), aspectList }) : undefined; - }, [id?.toString()]); - return { - componentDescriptor, loading, error: componentError, componentLogs: { diff --git a/scopes/component/component/ui/use-component/use-component.fragments.ts b/scopes/component/component/ui/use-component/use-component.fragments.ts index adc4fd494d2a..59e05424f1ce 100644 --- a/scopes/component/component/ui/use-component/use-component.fragments.ts +++ b/scopes/component/component/ui/use-component/use-component.fragments.ts @@ -74,7 +74,9 @@ export const componentFields = gql` export const componentFieldsWithLogs = gql` fragment componentFieldWithLogs on Component { - ...componentFields + id { + ...componentIdFields + } logs( type: $logType offset: $logOffset @@ -130,7 +132,7 @@ export const componentFieldsWithLogs = gql` tag } } - ${componentFields} + ${componentIdFields} `; export const COMPONENT_QUERY_LOG_FIELDS = ` diff --git a/scopes/component/component/ui/use-component/use-component.model.ts b/scopes/component/component/ui/use-component/use-component.model.ts index fd8c5f655ad4..cfd208c066b9 100644 --- a/scopes/component/component/ui/use-component/use-component.model.ts +++ b/scopes/component/component/ui/use-component/use-component.model.ts @@ -37,7 +37,6 @@ export type ComponentQueryResult = { }; export type ComponentLogsResult = { - componentDescriptor?: ComponentDescriptor; componentLogs?: ComponentLogs; error?: ComponentError; loading?: boolean; From 09acbaf37845057ef96494d0487ff8c492a1db3a Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Tue, 16 May 2023 18:59:30 -0400 Subject: [PATCH 19/20] clean up --- .../ui/component-tooltip/Untitled-1.yml | 789 ------------------ 1 file changed, 789 deletions(-) delete mode 100644 scopes/component/ui/component-tooltip/Untitled-1.yml diff --git a/scopes/component/ui/component-tooltip/Untitled-1.yml b/scopes/component/ui/component-tooltip/Untitled-1.yml deleted file mode 100644 index 5f2bc6150471..000000000000 --- a/scopes/component/ui/component-tooltip/Untitled-1.yml +++ /dev/null @@ -1,789 +0,0 @@ -[ - { - id: "060c458da72872729a87d8051ef08e190a25d751", - attr: { hash: "060c458da72872729a87d8051ef08e190a25d751" }, - _inEdges: - [ - "ff44ae21a26570f6395bc316417f0b6fb1bca930->060c458da72872729a87d8051ef08e190a25d751", - ], - _outEdges: - [ - "060c458da72872729a87d8051ef08e190a25d751->484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", - ], - }, - - { - id: "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", - attr: { hash: "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482" }, - _inEdges: - [ - "060c458da72872729a87d8051ef08e190a25d751->484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482", - ], - _outEdges: - [ - "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482->7803e482a4943a6e31deb9b38c80d735f75916ae", - ], - }, - - { - id: "7803e482a4943a6e31deb9b38c80d735f75916ae", - attr: { hash: "7803e482a4943a6e31deb9b38c80d735f75916ae" }, - _inEdges: - [ - "484ba5c2ebcbb22ef6d5090eec6032d6a4fd1482->7803e482a4943a6e31deb9b38c80d735f75916ae", - ], - _outEdges: - [ - "7803e482a4943a6e31deb9b38c80d735f75916ae->7451e0eb12563c8c63b53dc9ddb141de5918b1d7", - ], - }, - - { - id: "7451e0eb12563c8c63b53dc9ddb141de5918b1d7", - attr: { hash: "7451e0eb12563c8c63b53dc9ddb141de5918b1d7" }, - _inEdges: - [ - "7803e482a4943a6e31deb9b38c80d735f75916ae->7451e0eb12563c8c63b53dc9ddb141de5918b1d7", - ], - _outEdges: - [ - "7451e0eb12563c8c63b53dc9ddb141de5918b1d7->8293c41d17d451d758725c7531c98138b9370b09", - ], - }, - - { - id: "8293c41d17d451d758725c7531c98138b9370b09", - attr: { hash: "8293c41d17d451d758725c7531c98138b9370b09" }, - _inEdges: - [ - "7451e0eb12563c8c63b53dc9ddb141de5918b1d7->8293c41d17d451d758725c7531c98138b9370b09", - ], - _outEdges: - [ - "8293c41d17d451d758725c7531c98138b9370b09->de5b08e7da761f619d2499eaa0720ad29fc77eb2", - ], - }, - - { - id: "de5b08e7da761f619d2499eaa0720ad29fc77eb2", - attr: { hash: "de5b08e7da761f619d2499eaa0720ad29fc77eb2" }, - _inEdges: - [ - "8293c41d17d451d758725c7531c98138b9370b09->de5b08e7da761f619d2499eaa0720ad29fc77eb2", - ], - _outEdges: - [ - "de5b08e7da761f619d2499eaa0720ad29fc77eb2->f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", - ], - }, - - { - id: "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", - attr: { hash: "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c" }, - _inEdges: - [ - "de5b08e7da761f619d2499eaa0720ad29fc77eb2->f94061d4ba59bef342b2ffba7cbe7d3568a9c52c", - ], - _outEdges: - [ - "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->6e1e136d27542e751f0390d2c33b722f34d89a10", - "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->edbb65f181cceb87d9dda7c3c37d08ea601ec651", - ], - }, - - { - id: "6e1e136d27542e751f0390d2c33b722f34d89a10", - attr: { hash: "6e1e136d27542e751f0390d2c33b722f34d89a10" }, - _inEdges: - [ - "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->6e1e136d27542e751f0390d2c33b722f34d89a10", - ], - _outEdges: - [ - "6e1e136d27542e751f0390d2c33b722f34d89a10->e8f760d67664a04c4fde116183a4ca6bd7c9bfce", - ], - }, - - { - id: "e8f760d67664a04c4fde116183a4ca6bd7c9bfce", - attr: { hash: "e8f760d67664a04c4fde116183a4ca6bd7c9bfce" }, - _inEdges: - [ - "6e1e136d27542e751f0390d2c33b722f34d89a10->e8f760d67664a04c4fde116183a4ca6bd7c9bfce", - ], - _outEdges: - [ - "e8f760d67664a04c4fde116183a4ca6bd7c9bfce->d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", - ], - }, - - { - id: "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", - attr: { hash: "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6" }, - _inEdges: - [ - "e8f760d67664a04c4fde116183a4ca6bd7c9bfce->d7c6b760ceb136780fcabe2c16dffcbf2a7456f6", - ], - _outEdges: - [ - "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->136aab756693cfa6283f45f02b2b6aedb71e3199", - "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->16be08d1f9207f58a3b13b643a5f6df83b00851c", - ], - }, - - { - id: "136aab756693cfa6283f45f02b2b6aedb71e3199", - attr: { hash: "136aab756693cfa6283f45f02b2b6aedb71e3199" }, - _inEdges: - [ - "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->136aab756693cfa6283f45f02b2b6aedb71e3199", - ], - _outEdges: - [ - "136aab756693cfa6283f45f02b2b6aedb71e3199->d18b9728f2cd80571ea7f070ec2a62e40461b51e", - ], - }, - - { - id: "d18b9728f2cd80571ea7f070ec2a62e40461b51e", - attr: { hash: "d18b9728f2cd80571ea7f070ec2a62e40461b51e" }, - _inEdges: - [ - "136aab756693cfa6283f45f02b2b6aedb71e3199->d18b9728f2cd80571ea7f070ec2a62e40461b51e", - ], - _outEdges: - [ - "d18b9728f2cd80571ea7f070ec2a62e40461b51e->cea5d5c39bb9a33b3af8e4f45df5722d58acae36", - "d18b9728f2cd80571ea7f070ec2a62e40461b51e->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", - ], - }, - - { - id: "cea5d5c39bb9a33b3af8e4f45df5722d58acae36", - attr: { hash: "cea5d5c39bb9a33b3af8e4f45df5722d58acae36" }, - _inEdges: - [ - "d18b9728f2cd80571ea7f070ec2a62e40461b51e->cea5d5c39bb9a33b3af8e4f45df5722d58acae36", - ], - _outEdges: - [ - "cea5d5c39bb9a33b3af8e4f45df5722d58acae36->c2a1ea45e7e2906d888355ea069303de347ef968", - ], - }, - - { - id: "c2a1ea45e7e2906d888355ea069303de347ef968", - attr: { hash: "c2a1ea45e7e2906d888355ea069303de347ef968" }, - _inEdges: - [ - "edbb65f181cceb87d9dda7c3c37d08ea601ec651->c2a1ea45e7e2906d888355ea069303de347ef968", - "cea5d5c39bb9a33b3af8e4f45df5722d58acae36->c2a1ea45e7e2906d888355ea069303de347ef968", - ], - _outEdges: - [ - "c2a1ea45e7e2906d888355ea069303de347ef968->0b6993ef2933137d9f009e1a55ac1603072c0065", - ], - }, - - { - id: "0b6993ef2933137d9f009e1a55ac1603072c0065", - attr: { hash: "0b6993ef2933137d9f009e1a55ac1603072c0065" }, - _inEdges: - [ - "c2a1ea45e7e2906d888355ea069303de347ef968->0b6993ef2933137d9f009e1a55ac1603072c0065", - ], - _outEdges: - [ - "0b6993ef2933137d9f009e1a55ac1603072c0065->33d5a8b8bf37f022b906740d2cd22d505073a34a", - ], - }, - - { - id: "33d5a8b8bf37f022b906740d2cd22d505073a34a", - attr: { hash: "33d5a8b8bf37f022b906740d2cd22d505073a34a" }, - _inEdges: - [ - "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->33d5a8b8bf37f022b906740d2cd22d505073a34a", - "0b6993ef2933137d9f009e1a55ac1603072c0065->33d5a8b8bf37f022b906740d2cd22d505073a34a", - "5f61a84fd0303994d1a31830b829c3d0f9ddeffa->33d5a8b8bf37f022b906740d2cd22d505073a34a", - ], - _outEdges: - [ - "33d5a8b8bf37f022b906740d2cd22d505073a34a->8c18ba98ff5fa4becf72e22602d108a061d39c96", - ], - }, - - { - id: "8c18ba98ff5fa4becf72e22602d108a061d39c96", - attr: { hash: "8c18ba98ff5fa4becf72e22602d108a061d39c96" }, - _inEdges: - [ - "33d5a8b8bf37f022b906740d2cd22d505073a34a->8c18ba98ff5fa4becf72e22602d108a061d39c96", - "10c7a8a81f5c7c86def9e2af55f3172d44a778cc->8c18ba98ff5fa4becf72e22602d108a061d39c96", - ], - _outEdges: - [ - "8c18ba98ff5fa4becf72e22602d108a061d39c96->30f947c6b332ea104cb643b2a635952d9a4b1774", - ], - }, - - { - id: "30f947c6b332ea104cb643b2a635952d9a4b1774", - attr: { hash: "30f947c6b332ea104cb643b2a635952d9a4b1774" }, - _inEdges: - [ - "8c18ba98ff5fa4becf72e22602d108a061d39c96->30f947c6b332ea104cb643b2a635952d9a4b1774", - ], - _outEdges: - [ - "30f947c6b332ea104cb643b2a635952d9a4b1774->3c14f556f1d32bc459963b8ea7464e6b896c3531", - ], - }, - - { - id: "3c14f556f1d32bc459963b8ea7464e6b896c3531", - attr: { hash: "3c14f556f1d32bc459963b8ea7464e6b896c3531" }, - _inEdges: - [ - "30f947c6b332ea104cb643b2a635952d9a4b1774->3c14f556f1d32bc459963b8ea7464e6b896c3531", - ], - _outEdges: - [ - "3c14f556f1d32bc459963b8ea7464e6b896c3531->97354a80b5aa0a430f89827f6810afd2ab4900ca", - ], - }, - - { - id: "97354a80b5aa0a430f89827f6810afd2ab4900ca", - attr: { hash: "97354a80b5aa0a430f89827f6810afd2ab4900ca" }, - _inEdges: - [ - "3c14f556f1d32bc459963b8ea7464e6b896c3531->97354a80b5aa0a430f89827f6810afd2ab4900ca", - ], - _outEdges: - [ - "97354a80b5aa0a430f89827f6810afd2ab4900ca->4ea6caed5281a2bd2b1d249708b08430b6c1a845", - ], - }, - - { - id: "4ea6caed5281a2bd2b1d249708b08430b6c1a845", - attr: { hash: "4ea6caed5281a2bd2b1d249708b08430b6c1a845" }, - _inEdges: - [ - "97354a80b5aa0a430f89827f6810afd2ab4900ca->4ea6caed5281a2bd2b1d249708b08430b6c1a845", - ], - _outEdges: - [ - "4ea6caed5281a2bd2b1d249708b08430b6c1a845->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", - ], - }, - - { - id: "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", - attr: { hash: "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9" }, - _inEdges: - [ - "4ea6caed5281a2bd2b1d249708b08430b6c1a845->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", - "8d97eff47eb7809463564fc3d861ac85be87dcc1->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", - ], - _outEdges: - [ - "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9->e75cd8c849caf103897a68aa8f6ec3ae7717a384", - ], - }, - - { - id: "e75cd8c849caf103897a68aa8f6ec3ae7717a384", - attr: { hash: "e75cd8c849caf103897a68aa8f6ec3ae7717a384" }, - _inEdges: - [ - "67d4381ff07c0f4f71c7ca093d6a617d98f62fd9->e75cd8c849caf103897a68aa8f6ec3ae7717a384", - ], - _outEdges: - [ - "e75cd8c849caf103897a68aa8f6ec3ae7717a384->d8d7ba85f8ae204a28ec370774c856ea98ea9a52", - ], - }, - - { - id: "d8d7ba85f8ae204a28ec370774c856ea98ea9a52", - attr: { hash: "d8d7ba85f8ae204a28ec370774c856ea98ea9a52" }, - _inEdges: - [ - "e75cd8c849caf103897a68aa8f6ec3ae7717a384->d8d7ba85f8ae204a28ec370774c856ea98ea9a52", - ], - _outEdges: - [ - "d8d7ba85f8ae204a28ec370774c856ea98ea9a52->b9f744da953bdab1724fd48b259a42b3476d7a7d", - ], - }, - - { - id: "b9f744da953bdab1724fd48b259a42b3476d7a7d", - attr: { hash: "b9f744da953bdab1724fd48b259a42b3476d7a7d" }, - _inEdges: - [ - "d8d7ba85f8ae204a28ec370774c856ea98ea9a52->b9f744da953bdab1724fd48b259a42b3476d7a7d", - ], - _outEdges: - [ - "b9f744da953bdab1724fd48b259a42b3476d7a7d->6fa6d42b8580f301b9910a49c70d7b11d2fc357e", - ], - }, - - { - id: "6fa6d42b8580f301b9910a49c70d7b11d2fc357e", - attr: { hash: "6fa6d42b8580f301b9910a49c70d7b11d2fc357e" }, - _inEdges: - [ - "b9f744da953bdab1724fd48b259a42b3476d7a7d->6fa6d42b8580f301b9910a49c70d7b11d2fc357e", - ], - _outEdges: - [ - "6fa6d42b8580f301b9910a49c70d7b11d2fc357e->17eae8caf411633e4553ff7ee6ceab4a0dac835c", - ], - }, - - { - id: "17eae8caf411633e4553ff7ee6ceab4a0dac835c", - attr: { hash: "17eae8caf411633e4553ff7ee6ceab4a0dac835c" }, - _inEdges: - [ - "6fa6d42b8580f301b9910a49c70d7b11d2fc357e->17eae8caf411633e4553ff7ee6ceab4a0dac835c", - ], - _outEdges: - [ - "17eae8caf411633e4553ff7ee6ceab4a0dac835c->e72c0d0b1d31ed52999def993ca425bc8b6eec8d", - ], - }, - - { - id: "e72c0d0b1d31ed52999def993ca425bc8b6eec8d", - attr: { hash: "e72c0d0b1d31ed52999def993ca425bc8b6eec8d" }, - _inEdges: - [ - "17eae8caf411633e4553ff7ee6ceab4a0dac835c->e72c0d0b1d31ed52999def993ca425bc8b6eec8d", - ], - _outEdges: - [ - "e72c0d0b1d31ed52999def993ca425bc8b6eec8d->d2ab69219d828c8822444f5c8a7b92763f4bf9b8", - ], - }, - - { - id: "d2ab69219d828c8822444f5c8a7b92763f4bf9b8", - attr: { hash: "d2ab69219d828c8822444f5c8a7b92763f4bf9b8" }, - _inEdges: - [ - "e72c0d0b1d31ed52999def993ca425bc8b6eec8d->d2ab69219d828c8822444f5c8a7b92763f4bf9b8", - ], - _outEdges: - [ - "d2ab69219d828c8822444f5c8a7b92763f4bf9b8->f1cff5c303f39c5047c6bab98baab1156c48c1b6", - ], - }, - - { - id: "f1cff5c303f39c5047c6bab98baab1156c48c1b6", - attr: { hash: "f1cff5c303f39c5047c6bab98baab1156c48c1b6" }, - _inEdges: - [ - "d2ab69219d828c8822444f5c8a7b92763f4bf9b8->f1cff5c303f39c5047c6bab98baab1156c48c1b6", - ], - _outEdges: - [ - "f1cff5c303f39c5047c6bab98baab1156c48c1b6->2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", - ], - }, - - { - id: "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", - attr: { hash: "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01" }, - _inEdges: - [ - "f1cff5c303f39c5047c6bab98baab1156c48c1b6->2ae41ac40604cb7d68ecfa1ad7432e090bac3f01", - ], - _outEdges: - [ - "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01->241832c97051733499b87d742286a811711dfcfb", - ], - }, - - { - id: "241832c97051733499b87d742286a811711dfcfb", - attr: { hash: "241832c97051733499b87d742286a811711dfcfb" }, - _inEdges: - [ - "2ae41ac40604cb7d68ecfa1ad7432e090bac3f01->241832c97051733499b87d742286a811711dfcfb", - ], - _outEdges: - [ - "241832c97051733499b87d742286a811711dfcfb->efca1e0bfe4a8347e46048ee9dae0cc726461cb8", - ], - }, - - { - id: "efca1e0bfe4a8347e46048ee9dae0cc726461cb8", - attr: { hash: "efca1e0bfe4a8347e46048ee9dae0cc726461cb8" }, - _inEdges: - [ - "241832c97051733499b87d742286a811711dfcfb->efca1e0bfe4a8347e46048ee9dae0cc726461cb8", - ], - _outEdges: - [ - "efca1e0bfe4a8347e46048ee9dae0cc726461cb8->f9f52eb71d5d020f53a67ebf2c98049969553772", - ], - }, - - { - id: "f9f52eb71d5d020f53a67ebf2c98049969553772", - attr: { hash: "f9f52eb71d5d020f53a67ebf2c98049969553772" }, - _inEdges: - [ - "efca1e0bfe4a8347e46048ee9dae0cc726461cb8->f9f52eb71d5d020f53a67ebf2c98049969553772", - ], - _outEdges: - [ - "f9f52eb71d5d020f53a67ebf2c98049969553772->8eb3a70cf0752b54fa2f9651ccde79563112bad3", - ], - }, - - { - id: "8eb3a70cf0752b54fa2f9651ccde79563112bad3", - attr: { hash: "8eb3a70cf0752b54fa2f9651ccde79563112bad3" }, - _inEdges: - [ - "f9f52eb71d5d020f53a67ebf2c98049969553772->8eb3a70cf0752b54fa2f9651ccde79563112bad3", - ], - _outEdges: - [ - "8eb3a70cf0752b54fa2f9651ccde79563112bad3->6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", - ], - }, - - { - id: "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", - attr: { hash: "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f" }, - _inEdges: - [ - "8eb3a70cf0752b54fa2f9651ccde79563112bad3->6164e5aa0f92ac872eb54641b4284fc3ec94aa0f", - ], - _outEdges: - [ - "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f->73d23a8fa2214261cacd174afdcf12d5cca86acb", - ], - }, - - { - id: "73d23a8fa2214261cacd174afdcf12d5cca86acb", - attr: { hash: "73d23a8fa2214261cacd174afdcf12d5cca86acb" }, - _inEdges: - [ - "6164e5aa0f92ac872eb54641b4284fc3ec94aa0f->73d23a8fa2214261cacd174afdcf12d5cca86acb", - ], - _outEdges: [], - }, - - { - id: "7fa90951d4ec0646f3368ccdbbbe89fe3de09212", - attr: { hash: "7fa90951d4ec0646f3368ccdbbbe89fe3de09212" }, - _inEdges: - [ - "d18b9728f2cd80571ea7f070ec2a62e40461b51e->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", - "d2b7f87cb7f217f31076cd23ad903d73aeabb85d->7fa90951d4ec0646f3368ccdbbbe89fe3de09212", - ], - _outEdges: - [ - "7fa90951d4ec0646f3368ccdbbbe89fe3de09212->0e42b353336a3b03c6c63765b7cf3258b725c559", - ], - }, - - { - id: "0e42b353336a3b03c6c63765b7cf3258b725c559", - attr: { hash: "0e42b353336a3b03c6c63765b7cf3258b725c559" }, - _inEdges: - [ - "7fa90951d4ec0646f3368ccdbbbe89fe3de09212->0e42b353336a3b03c6c63765b7cf3258b725c559", - ], - _outEdges: - [ - "0e42b353336a3b03c6c63765b7cf3258b725c559->5f61a84fd0303994d1a31830b829c3d0f9ddeffa", - ], - }, - - { - id: "5f61a84fd0303994d1a31830b829c3d0f9ddeffa", - attr: { hash: "5f61a84fd0303994d1a31830b829c3d0f9ddeffa" }, - _inEdges: - [ - "0e42b353336a3b03c6c63765b7cf3258b725c559->5f61a84fd0303994d1a31830b829c3d0f9ddeffa", - ], - _outEdges: - [ - "5f61a84fd0303994d1a31830b829c3d0f9ddeffa->33d5a8b8bf37f022b906740d2cd22d505073a34a", - ], - }, - - { - id: "16be08d1f9207f58a3b13b643a5f6df83b00851c", - attr: { hash: "16be08d1f9207f58a3b13b643a5f6df83b00851c" }, - _inEdges: - [ - "d7c6b760ceb136780fcabe2c16dffcbf2a7456f6->16be08d1f9207f58a3b13b643a5f6df83b00851c", - ], - _outEdges: - [ - "16be08d1f9207f58a3b13b643a5f6df83b00851c->b45b33d7c424984790725b545e9f8826a4a26e90", - ], - }, - - { - id: "b45b33d7c424984790725b545e9f8826a4a26e90", - attr: { hash: "b45b33d7c424984790725b545e9f8826a4a26e90" }, - _inEdges: - [ - "16be08d1f9207f58a3b13b643a5f6df83b00851c->b45b33d7c424984790725b545e9f8826a4a26e90", - ], - _outEdges: - [ - "b45b33d7c424984790725b545e9f8826a4a26e90->2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", - ], - }, - - { - id: "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", - attr: { hash: "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8" }, - _inEdges: - [ - "b45b33d7c424984790725b545e9f8826a4a26e90->2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8", - ], - _outEdges: - [ - "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8->d1f10da8f64d3023c23428e7d65be0e633ce8f04", - ], - }, - - { - id: "d1f10da8f64d3023c23428e7d65be0e633ce8f04", - attr: { hash: "d1f10da8f64d3023c23428e7d65be0e633ce8f04" }, - _inEdges: - [ - "2c6f1ef1663049a478ac0b7e55df37b9c1c9a3c8->d1f10da8f64d3023c23428e7d65be0e633ce8f04", - ], - _outEdges: - [ - "d1f10da8f64d3023c23428e7d65be0e633ce8f04->dd7a7047f2c1c0f899f396e8769d3380073b4243", - ], - }, - - { - id: "dd7a7047f2c1c0f899f396e8769d3380073b4243", - attr: { hash: "dd7a7047f2c1c0f899f396e8769d3380073b4243" }, - _inEdges: - [ - "d1f10da8f64d3023c23428e7d65be0e633ce8f04->dd7a7047f2c1c0f899f396e8769d3380073b4243", - ], - _outEdges: - [ - "dd7a7047f2c1c0f899f396e8769d3380073b4243->b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", - ], - }, - - { - id: "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", - attr: { hash: "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3" }, - _inEdges: - [ - "dd7a7047f2c1c0f899f396e8769d3380073b4243->b372ba9e83dcb0b73f86ce733cff60a0895ec1e3", - ], - _outEdges: - [ - "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3->8c89c7ff1dc8da4795ab4b173549d074dd564aa7", - ], - }, - - { - id: "8c89c7ff1dc8da4795ab4b173549d074dd564aa7", - attr: { hash: "8c89c7ff1dc8da4795ab4b173549d074dd564aa7" }, - _inEdges: - [ - "b372ba9e83dcb0b73f86ce733cff60a0895ec1e3->8c89c7ff1dc8da4795ab4b173549d074dd564aa7", - ], - _outEdges: - [ - "8c89c7ff1dc8da4795ab4b173549d074dd564aa7->596c3f35749b39d4ec449e0f382521da5747f081", - ], - }, - - { - id: "596c3f35749b39d4ec449e0f382521da5747f081", - attr: { hash: "596c3f35749b39d4ec449e0f382521da5747f081" }, - _inEdges: - [ - "8c89c7ff1dc8da4795ab4b173549d074dd564aa7->596c3f35749b39d4ec449e0f382521da5747f081", - ], - _outEdges: - [ - "596c3f35749b39d4ec449e0f382521da5747f081->f33f4b872f92bee46b104d94a7aad3474c91c53d", - ], - }, - - { - id: "f33f4b872f92bee46b104d94a7aad3474c91c53d", - attr: { hash: "f33f4b872f92bee46b104d94a7aad3474c91c53d" }, - _inEdges: - [ - "596c3f35749b39d4ec449e0f382521da5747f081->f33f4b872f92bee46b104d94a7aad3474c91c53d", - ], - _outEdges: - [ - "f33f4b872f92bee46b104d94a7aad3474c91c53d->49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", - ], - }, - - { - id: "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", - attr: { hash: "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e" }, - _inEdges: - [ - "f33f4b872f92bee46b104d94a7aad3474c91c53d->49a178a6d8b41b0bf4fb31008ddcc2e9e914662e", - ], - _outEdges: - [ - "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e->ec1e05ae1f7b0f835b890b359459228d89b3788e", - ], - }, - - { - id: "ec1e05ae1f7b0f835b890b359459228d89b3788e", - attr: { hash: "ec1e05ae1f7b0f835b890b359459228d89b3788e" }, - _inEdges: - [ - "49a178a6d8b41b0bf4fb31008ddcc2e9e914662e->ec1e05ae1f7b0f835b890b359459228d89b3788e", - ], - _outEdges: - [ - "ec1e05ae1f7b0f835b890b359459228d89b3788e->7c49a291b011c7d9c67a3c44f09cb018021b1d81", - ], - }, - - { - id: "7c49a291b011c7d9c67a3c44f09cb018021b1d81", - attr: { hash: "7c49a291b011c7d9c67a3c44f09cb018021b1d81" }, - _inEdges: - [ - "ec1e05ae1f7b0f835b890b359459228d89b3788e->7c49a291b011c7d9c67a3c44f09cb018021b1d81", - ], - _outEdges: - [ - "7c49a291b011c7d9c67a3c44f09cb018021b1d81->aa081b26db3039b4a39be9fde0d0595b2770625c", - ], - }, - - { - id: "aa081b26db3039b4a39be9fde0d0595b2770625c", - attr: { hash: "aa081b26db3039b4a39be9fde0d0595b2770625c" }, - _inEdges: - [ - "7c49a291b011c7d9c67a3c44f09cb018021b1d81->aa081b26db3039b4a39be9fde0d0595b2770625c", - ], - _outEdges: - [ - "aa081b26db3039b4a39be9fde0d0595b2770625c->7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", - ], - }, - - { - id: "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", - attr: { hash: "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1" }, - _inEdges: - [ - "aa081b26db3039b4a39be9fde0d0595b2770625c->7f06967106c0370d08cc8294b4adbb1d1ec0e3a1", - ], - _outEdges: - [ - "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1->ad88d251fdef37f9e4b619b346099475c2a6a13d", - ], - }, - - { - id: "ad88d251fdef37f9e4b619b346099475c2a6a13d", - attr: { hash: "ad88d251fdef37f9e4b619b346099475c2a6a13d" }, - _inEdges: - [ - "7f06967106c0370d08cc8294b4adbb1d1ec0e3a1->ad88d251fdef37f9e4b619b346099475c2a6a13d", - ], - _outEdges: - [ - "ad88d251fdef37f9e4b619b346099475c2a6a13d->336c861d90196d45d3df2b6c553e56810765fb4b", - ], - }, - - { - id: "336c861d90196d45d3df2b6c553e56810765fb4b", - attr: { hash: "336c861d90196d45d3df2b6c553e56810765fb4b" }, - _inEdges: - [ - "ad88d251fdef37f9e4b619b346099475c2a6a13d->336c861d90196d45d3df2b6c553e56810765fb4b", - ], - _outEdges: - [ - "336c861d90196d45d3df2b6c553e56810765fb4b->8d97eff47eb7809463564fc3d861ac85be87dcc1", - ], - }, - - { - id: "8d97eff47eb7809463564fc3d861ac85be87dcc1", - attr: { hash: "8d97eff47eb7809463564fc3d861ac85be87dcc1" }, - _inEdges: - [ - "336c861d90196d45d3df2b6c553e56810765fb4b->8d97eff47eb7809463564fc3d861ac85be87dcc1", - ], - _outEdges: - [ - "8d97eff47eb7809463564fc3d861ac85be87dcc1->67d4381ff07c0f4f71c7ca093d6a617d98f62fd9", - ], - }, - - { - id: "edbb65f181cceb87d9dda7c3c37d08ea601ec651", - attr: { hash: "edbb65f181cceb87d9dda7c3c37d08ea601ec651" }, - _inEdges: - [ - "f94061d4ba59bef342b2ffba7cbe7d3568a9c52c->edbb65f181cceb87d9dda7c3c37d08ea601ec651", - ], - _outEdges: - [ - "edbb65f181cceb87d9dda7c3c37d08ea601ec651->28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", - "edbb65f181cceb87d9dda7c3c37d08ea601ec651->c2a1ea45e7e2906d888355ea069303de347ef968", - ], - }, - - { - id: "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", - attr: { hash: "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3" }, - _inEdges: - [ - "edbb65f181cceb87d9dda7c3c37d08ea601ec651->28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3", - ], - _outEdges: - [ - "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->10c7a8a81f5c7c86def9e2af55f3172d44a778cc", - "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->33d5a8b8bf37f022b906740d2cd22d505073a34a", - ], - }, - - { - id: "10c7a8a81f5c7c86def9e2af55f3172d44a778cc", - attr: { hash: "10c7a8a81f5c7c86def9e2af55f3172d44a778cc" }, - _inEdges: - [ - "28cce4514ec9a3aa0d0cda4ab84cf7a6c8df0ca3->10c7a8a81f5c7c86def9e2af55f3172d44a778cc", - ], - _outEdges: - [ - "10c7a8a81f5c7c86def9e2af55f3172d44a778cc->8c18ba98ff5fa4becf72e22602d108a061d39c96", - ], - }, -] From 46ba4bac1d12b21dd56868e38392313934d0d2bf Mon Sep 17 00:00:00 2001 From: Luv Kapur Date: Wed, 17 May 2023 07:54:11 -0400 Subject: [PATCH 20/20] fix handling getting logs for lane components without head filter --- scopes/scope/scope/scope.main.runtime.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scopes/scope/scope/scope.main.runtime.ts b/scopes/scope/scope/scope.main.runtime.ts index ed48e65e6a16..cda63331ccf3 100644 --- a/scopes/scope/scope/scope.main.runtime.ts +++ b/scopes/scope/scope/scope.main.runtime.ts @@ -780,11 +780,10 @@ export class ScopeMain implements ComponentFactory { const headRef = head ? componentModel.getRef(head) : componentModel.head; - if (!headRef) return []; - - if (!startFromOffset && !stopAtOffset) { + if (!headRef || (startFromOffset === undefined && stopAtOffset === undefined)) { return this.legacyScope.loadComponentLogs(id._legacy, shortHash, startFrom, stopAt ? [stopAt] : undefined); } + const versionHistory = await componentModel.getAndPopulateVersionHistory(this.legacyScope.objects, headRef); const versionGraph = versionHistory.getGraph(); const startFromRef = startFrom ? componentModel.getRef(startFrom) : undefined;