Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lanes: Lane Compare + SnapsDistance GQL API #7011

Merged
merged 10 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions scopes/lanes/lanes/lanes.graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export function lanesSchema(lanesMainRuntime: LanesMain): Schema {
diffOutput: String
}

type SnapDistance {
onSource: [String!]!
onTarget: [String!]!
common: String
}

type FieldsDiff {
fieldName: String!
diffOutput: String
Expand Down Expand Up @@ -60,6 +66,8 @@ export function lanesSchema(lanesMainRuntime: LanesMain): Schema {
"""
changes: [String!]
upToDate: Boolean
snapsDistance: SnapDistance
unrelated: Boolean
}

type LaneDiffStatus {
Expand Down
75 changes: 58 additions & 17 deletions scopes/lanes/lanes/lanes.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import ComponentWriterAspect, { ComponentWriterMain } from '@teambit/component-w
import { SnapsDistance } from '@teambit/legacy/dist/scope/component-ops/snaps-distance';
import { MergingMain, MergingAspect } from '@teambit/merging';
import { ChangeType } from '@teambit/lanes.entities.lane-diff';
import { NoCommonSnap } from '@teambit/legacy/dist/scope/exceptions/no-common-snap';
import { LanesAspect } from './lanes.aspect';
import {
LaneCmd,
Expand All @@ -52,6 +53,12 @@ import { LanesDeleteRoute } from './lanes.delete.route';

export { Lane };

export type SnapsDistanceObj = {
onSource: string[];
onTarget: string[];
common?: string;
};

export type LaneResults = {
lanes: LaneData[];
currentLane?: string | null;
Expand Down Expand Up @@ -82,11 +89,12 @@ export type LaneComponentDiffStatus = {
changeType?: ChangeType;
changes?: ChangeType[];
upToDate?: boolean;
snapsDistance?: SnapsDistanceObj;
unrelated?: boolean;
};

export type LaneDiffStatusOptions = {
skipChanges?: boolean;
skipUpToDate?: boolean;
};

export type LaneDiffStatus = {
Expand Down Expand Up @@ -416,7 +424,12 @@ export class LanesMain {
* @param targetHead head on the target lane. leave empty if the target is main
* @returns
*/
async getSnapsDistance(componentId: ComponentID, sourceHead?: string, targetHead?: string): Promise<SnapsDistance> {
async getSnapsDistance(
componentId: ComponentID,
sourceHead?: string,
targetHead?: string,
throws?: boolean
): Promise<SnapsDistance> {
if (!sourceHead && !targetHead)
throw new Error(`getDivergeData got sourceHead and targetHead empty. at least one of them should be populated`);
const modelComponent = await this.scope.legacyScope.getModelComponent(componentId._legacy);
Expand All @@ -425,6 +438,7 @@ export class LanesMain {
repo: this.scope.legacyScope.objects,
sourceHead: sourceHead ? Ref.from(sourceHead) : modelComponent.head || null,
targetHead: targetHead ? Ref.from(targetHead) : modelComponent.head || null,
throws,
});
}

Expand Down Expand Up @@ -609,21 +623,23 @@ export class LanesMain {
targetLaneId?: LaneId,
options?: LaneDiffStatusOptions
): Promise<LaneDiffStatus> {
const sourceLane = await this.loadLane(sourceLaneId);
if (!sourceLane) throw new Error(`unable to find ${sourceLaneId.toString()} in the scope`);
const sourceLaneComponents = sourceLaneId.isDefault()
? (await this.getLaneDataOfDefaultLane())?.components.map((main) => ({ id: main.id, head: Ref.from(main.head) }))
: (await this.loadLane(sourceLaneId))?.components;

const targetLane = targetLaneId ? await this.loadLane(targetLaneId) : undefined;
const targetLaneIds = targetLane?.toBitIds();
const host = this.componentAspect.getHost();
const diffProps = compact(
await Promise.all(
sourceLane.components.map(async (comp) => {
const componentId = await host.resolveComponentId(comp.id);
const sourceVersionObj = (await this.scope.legacyScope.objects.load(comp.head)) as Version;
if (sourceVersionObj.isRemoved()) {
(sourceLaneComponents || []).map(async ({ id, head }) => {
const componentId = await host.resolveComponentId(id);
const sourceVersionObj = (await this.scope.legacyScope.objects.load(head)) as Version;
if (sourceVersionObj?.isRemoved()) {
return null;
}
const headOnTargetLane = targetLaneIds
? targetLaneIds.searchWithoutVersion(comp.id)?.version
? targetLaneIds.searchWithoutVersion(id)?.version
: await this.getHeadOnMain(componentId);

if (headOnTargetLane) {
Expand All @@ -633,7 +649,7 @@ export class LanesMain {
}
}

const sourceHead = comp.head.toString();
const sourceHead = head.toString();
const targetHead = headOnTargetLane;

return { componentId, sourceHead, targetHead };
Expand All @@ -657,16 +673,29 @@ export class LanesMain {
sourceHead: string,
targetHead?: string,
options?: LaneDiffStatusOptions
) {
const snapsDistance = !options?.skipUpToDate
? await this.getSnapsDistance(componentId, sourceHead, targetHead)
: undefined;
): Promise<LaneComponentDiffStatus> {
const snapsDistance = await this.getSnapsDistance(componentId, sourceHead, targetHead, false);

if (snapsDistance?.err) {
const noCommonSnap = snapsDistance.err instanceof NoCommonSnap;

return {
componentId,
sourceHead,
targetHead,
upToDate: snapsDistance?.isUpToDate(),
unrelated: noCommonSnap || undefined,
changes: [],
};
}

const commonSnap = snapsDistance?.commonSnapBeforeDiverge;

const getChanges = async (): Promise<ChangeType[]> => {
if (!targetHead) return [ChangeType.NEW];
if (!commonSnap) return [ChangeType.NEW];

const compare = await this.componentCompare.compare(
componentId.changeVersion(targetHead).toString(),
componentId.changeVersion(commonSnap.hash).toString(),
componentId.changeVersion(sourceHead).toString()
);

Expand Down Expand Up @@ -695,7 +724,19 @@ export class LanesMain {
const changes = !options?.skipChanges ? await getChanges() : undefined;
const changeType = changes ? changes[0] : undefined;

return { componentId, changeType, changes, sourceHead, targetHead, upToDate: snapsDistance?.isUpToDate() };
return {
componentId,
changeType,
changes,
sourceHead,
targetHead: commonSnap?.hash,
upToDate: snapsDistance?.isUpToDate(),
snapsDistance: {
onSource: snapsDistance?.snapsOnSourceOnly.map((s) => s.hash) ?? [],
onTarget: snapsDistance?.snapsOnTargetOnly.map((s) => s.hash) ?? [],
common: snapsDistance?.commonSnapBeforeDiverge?.hash,
},
};
}

async addLaneReadme(readmeComponentIdStr: string, laneName?: string): Promise<{ result: boolean; message?: string }> {
Expand Down
9 changes: 6 additions & 3 deletions scopes/lanes/lanes/lanes.ui.runtime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export class LanesUI {
}

getLanesComparePage() {
return <LaneComparePage getLaneCompare={this.getLaneCompare} />;
return <LaneComparePage getLaneCompare={this.getLaneCompare} groupByScope={this.lanesHost === 'workspace'} />;
}

getMenuRoutes() {
Expand Down Expand Up @@ -209,10 +209,13 @@ export class LanesUI {
{
props: {
href: '~compare',
children: 'Lane Compare',
children: 'Compare',
},
order: 2,
hide: () => true,
hide: () => {
const { lanesModel } = useLanes();
return !lanesModel?.viewedLane || lanesModel?.lanes.length < 2;
},
},
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const QUERY_LANE_DIFF_STATUS = gql`
targetHead
changes
upToDate
unrelated
}
}
}
Expand Down Expand Up @@ -80,6 +81,7 @@ export const useLaneDiffStatus: UseLaneDiffStatus = ({ baseId, compareId, option
targetLane: LaneId.from(data.lanes.diffStatus.target.name, data.lanes.diffStatus.target.scope).toString(),
diff: data.lanes.diffStatus.componentsStatus.map((c) => ({
...c,
changes: c.changes || [],
componentId: ComponentID.fromObject(c.componentId).toString(),
})),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,49 @@
flex-direction: column;
height: 100%;
width: 100%;
// overflow: scroll;
}
.top {
display: flex;
padding: 32px;
padding: 24px;
padding-bottom: 8px;
align-items: center;
width: 60%;

> div {
display: flex;
}
}

.bottom {
display: flex;
overflow: scroll;
}

.compareLane {
padding: 8px;
background: var(--surface-neutral-focus-color);
border-radius: 6px;
margin: 0px 4px;
width: fit-content;
// same height as the lane selector
height: 34px;
box-sizing: border-box;
font-size: var(--bit-p-xs, 14px);
align-items: center;
}

.baseSelectorContainer {
padding: 0px 4px;
width: 100%;
min-width: 100px;
max-width: 200px;
> div:first-of-type {
width: 100%;
}
}

.laneIcon {
padding-right: 4px;
height: 16px;
font-size: var(--bit-p-sm, 16px);
}
59 changes: 52 additions & 7 deletions scopes/lanes/ui/compare/lane-compare-page/lane-compare-page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,71 @@
import React, { HTMLAttributes } from 'react';
import React, { HTMLAttributes, useState, useEffect, useMemo } from 'react';
import { LaneCompareProps } from '@teambit/lanes';
import { useLanes } from '@teambit/lanes.hooks.use-lanes';
import { LaneSelector } from '@teambit/lanes.ui.inputs.lane-selector';
import { LaneModel } from '@teambit/lanes.ui.models.lanes-model';
import { LaneId } from '@teambit/lane-id';
import { LaneIcon } from '@teambit/lanes.ui.icons.lane-icon';

import styles from './lane-compare-page.module.scss';

export type LaneComparePageProps = {
getLaneCompare: (props: LaneCompareProps) => React.ReactNode;
groupByScope?: boolean;
} & HTMLAttributes<HTMLDivElement>;

export function LaneComparePage({ getLaneCompare, ...rest }: LaneComparePageProps) {
export function LaneComparePage({ getLaneCompare, groupByScope, ...rest }: LaneComparePageProps) {
const { lanesModel } = useLanes();
const [base, setBase] = useState<LaneModel | undefined>();
const defaultLane = lanesModel?.getDefaultLane();
const compare = lanesModel?.viewedLane;
const nonMainLanes = lanesModel?.getNonMainLanes();
useEffect(() => {
if (!base && !compare?.id.isDefault() && defaultLane) {
setBase(defaultLane);
}
if (!base && compare?.id.isDefault() && (nonMainLanes?.length ?? 0) > 0) {
setBase(nonMainLanes?.[0]);
}
}, [defaultLane, compare?.id.toString(), nonMainLanes?.length]);

if (!lanesModel) return null;
const LaneCompareComponent = useMemo(() => {
return getLaneCompare({ base, compare });
}, [base?.id.toString(), compare?.id.toString()]);

const compare = lanesModel.getDefaultLane();
const base = lanesModel.getNonMainLanes()[0];
const lanes: Array<LaneId> = useMemo(() => {
const mainLaneId = defaultLane?.id;
const nonMainLaneIds = nonMainLanes?.map((lane) => lane.id) || [];
const allLanes = (mainLaneId && [mainLaneId, ...nonMainLaneIds]) || nonMainLaneIds;
return allLanes.filter((l) => l.toString() !== compare?.id.toString());
}, [base?.id.toString(), compare?.id.toString(), lanesModel?.lanes.length]);

const LaneCompareComponent = getLaneCompare({ base, compare });
if (!lanesModel) return null;
if (!lanesModel.viewedLane) return null;
if (!base) return null;

return (
<div {...rest} className={styles.laneComparePage}>
{LaneCompareComponent}
<div className={styles.top}>
<div>Compare</div>
<div className={styles.compareLane}>
<LaneIcon className={styles.laneIcon} />
{compare?.id.name}
</div>
<div>with</div>
<div className={styles.baseSelectorContainer}>
<LaneSelector
selectedLaneId={base.id}
className={styles.baseSelector}
lanes={lanes}
groupByScope={groupByScope}
getHref={() => ''}
onLaneSelected={(laneId) => {
setBase(lanesModel?.lanes.find((l) => l.id.toString() === laneId.toString()));
}}
/>
</div>
</div>
<div className={styles.bottom}>{LaneCompareComponent}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import '~@teambit/ui-foundation.ui.constants.z-indexes/z-indexes.module.scss';

.rootLaneCompare {
position: relative;
height: 100%;
Expand Down Expand Up @@ -42,7 +40,7 @@
top: 0;
left: 0;
background-color: var(--background-color);
z-index: $modal-z-index;
z-index: 1;
padding: 0;
}

Expand Down
Loading