GitFOSS
.ts
TypeScript
(application/typescript)
// 1st-party
import type { ReactIsland } from "@ethicdevs/react-monolith";
// 3rd-party
import React, { useCallback, useMemo, useState } from "react";
// generated via script[generate:prisma]
import type { Organization, PullRequest, Repository } from "@prisma/client";
// app
import type {
  AppThemeScheme,
  RepositoryFileDiff,
  RepositoryWithParentAndForkedFromRepos,
} from "../types";
import { Card } from "../components/Card.styled";
import { Grid } from "../components/Grid";
import { IslandWrapper } from "../components/IslandWrapper.styled";
// app islands
import RepositoryFilesDiffsList from "./RepositoryFilesDiffsList";
import PullRequestSourceSelect, {
  PullRequestSourceSelection,
  PullRequestSourceSelectionChangeEvent,
} from "./PullRequestSourceSelect";

export enum PullRequestFormState {
  CONFIGURE = "configure",
  COMPARE = "compare",
  DETAILS = "input_details",
  ERROR = "error",
}

export interface RepositoryPullRequestCreateFormPropsCommon {
  errorMessage?: string | null;
}

export interface RepositoryPullRequestTarget {
  parentOrg: Organization;
  repo: Repository;
  branch: string;
}
export type RepositoryPullRequestCreateFormPropsByState<
  S extends PullRequestFormState = PullRequestFormState
> = S extends PullRequestFormState.CONFIGURE
  ? {
      sources: {
        [orgSlug: string]: {
          org: Organization;
          repos: {
            [repoKey: string]: {
              repo: RepositoryWithParentAndForkedFromRepos;
              branches: string[];
            };
          };
        };
      };
      target: RepositoryPullRequestTarget;
    }
  : S extends PullRequestFormState.COMPARE
  ? {
      fileDiffs: RepositoryFileDiff[];
      source: RepositoryPullRequestTarget;
      target: RepositoryPullRequestTarget;
    }
  : S extends PullRequestFormState.DETAILS
  ? {
      canCurrentUserSubmitPullRequest: boolean;
      pullRequest: PullRequest;
    }
  : S extends PullRequestFormState.ERROR
  ? {
      errorMessage: string;
    }
  : never;

export type RepositoryPullRequestCreateFormVariant =
  | {
      state: PullRequestFormState.CONFIGURE;
      data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.CONFIGURE>;
    }
  | {
      state: PullRequestFormState.COMPARE;
      data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.COMPARE>;
    }
  | {
      state: PullRequestFormState.DETAILS;
      data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.DETAILS>;
    }
  | {
      state: PullRequestFormState.ERROR;
      data: RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.ERROR>;
    };

export interface RepositoryPullRequestCreateFormProps
  extends RepositoryPullRequestCreateFormPropsCommon {
  parentOrgSlug: string;
  repoSlug: string;
  themeScheme: AppThemeScheme;
  variant: RepositoryPullRequestCreateFormVariant;
}

// PullRequestFormState.CONFIGURE
const isConfigureState = (
  s: PullRequestFormState
): s is PullRequestFormState.CONFIGURE =>
  typeof s !== "undefined" && s != null && s === PullRequestFormState.CONFIGURE;
const isConfigureStateData = (
  s: PullRequestFormState,
  i: unknown
): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.CONFIGURE> =>
  typeof i !== "undefined" && i != null && s === PullRequestFormState.CONFIGURE;
// PullRequestFormState.COMPARE
const isCompareState = (
  s: PullRequestFormState
): s is PullRequestFormState.COMPARE =>
  typeof s !== "undefined" && s != null && s === PullRequestFormState.COMPARE;
const isCompareStateData = (
  s: PullRequestFormState,
  i: unknown
): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.COMPARE> =>
  typeof i !== "undefined" && i != null && s === PullRequestFormState.COMPARE;
// PullRequestFormState.DETAILS
const isDetailsState = (
  s: PullRequestFormState
): s is PullRequestFormState.DETAILS =>
  typeof s !== "undefined" && s != null && s === PullRequestFormState.DETAILS;
const isDetailsStateData = (
  s: PullRequestFormState,
  i: unknown
): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.DETAILS> =>
  typeof i !== "undefined" && i != null && s === PullRequestFormState.DETAILS;
// PullRequestFormState.ERROR
const isErrorState = (
  s: PullRequestFormState
): s is PullRequestFormState.ERROR =>
  typeof s !== "undefined" && s != null && s === PullRequestFormState.ERROR;
const isErrorStateData = (
  s: PullRequestFormState,
  i: unknown
): i is RepositoryPullRequestCreateFormPropsByState<PullRequestFormState.ERROR> =>
  typeof i !== "undefined" && i != null && s === PullRequestFormState.ERROR;

const RepositoryPullRequestCreateForm: ReactIsland<RepositoryPullRequestCreateFormProps> =
  ({ parentOrgSlug, repoSlug, themeScheme, variant: { state, data } }) => {
    const [selectedSource, setSelectedSource] =
      useState<null | PullRequestSourceSelection>(null);
    const [selectedTarget, setSelectedTarget] =
      useState<null | PullRequestSourceSelection>(null);

    const onSourceSelectionChange = useCallback(
      (ev: PullRequestSourceSelectionChangeEvent) => {
        setSelectedSource(ev.data);
      },
      [setSelectedSource]
    );

    const onTargetSelectionChange = useCallback(
      (ev: PullRequestSourceSelectionChangeEvent) => {
        setSelectedTarget(ev.data);
      },
      [setSelectedTarget]
    );

    const isCompareButtonEnabled = useMemo(() => {
      const isSameRepoInSameOrg =
        selectedSource != null &&
        selectedTarget != null &&
        selectedSource.selectedRepo.repo.organization.slug ===
          selectedTarget.selectedRepo.repo.organization.slug &&
        selectedSource.selectedRepo.repo.slug ===
          selectedTarget.selectedRepo.repo.slug;
      return !!(
        selectedSource != null &&
        selectedTarget != null &&
        isSameRepoInSameOrg === false &&
        selectedSource.selectedBranch != null &&
        selectedTarget.selectedBranch != null
      );
    }, [selectedSource, selectedTarget]);

    isCompareButtonEnabled;

    // PullRequestFormState.CONFIGURE
    if (isConfigureState(state) && isConfigureStateData(state, data)) {
      return (
        <Grid.Col fluid nowrap>
          <form
            method={"POST"}
            action={`/${parentOrgSlug}/${repoSlug}/pulls/new`}
            style={{ width: "100%" }}
          >
            <Card themeScheme={themeScheme}>
              <input type={"hidden"} name={"state_from"} value={state} />
              <input
                type={"hidden"}
                name={"state_dest"}
                value={PullRequestFormState.COMPARE}
              />
              <Grid.Row fluid alignItems={"center"}>
                <div data-islandid={`${PullRequestSourceSelect.name}$$1`}>
                  <PullRequestSourceSelect
                    namePrefix={"target"}
                    defaultSource={{
                      org: Object.values(data.sources)[0].org,
                      repo: Object.values(
                        Object.values(data.sources)[0].repos
                      )[0].repo,
                      branch: Object.values(
                        Object.values(data.sources)[0].repos
                      )[0].branches[0],
                    }}
                    sources={data.sources}
                    onSelectionChange={onTargetSelectionChange}
                  />
                </div>
                <div style={{ margin: "0 8px" }}>
                  <span>vs.</span>
                </div>
                <div data-islandid={`${PullRequestSourceSelect.name}$$0`}>
                  <PullRequestSourceSelect
                    namePrefix={"source"}
                    defaultSource={{
                      org: Object.values(data.sources)[0].org,
                      repo: Object.values(
                        Object.values(data.sources)[0].repos
                      )[0].repo,
                      branch: Object.values(
                        Object.values(data.sources)[0].repos
                      )[0].branches[0],
                    }}
                    sources={data.sources}
                    onSelectionChange={onSourceSelectionChange}
                  />
                </div>
                <button
                  // disabled={isCompareButtonEnabled === false}
                  type={"submit"}
                  style={{ marginLeft: 8 }}
                >
                  Compare
                </button>
              </Grid.Row>
            </Card>
          </form>
        </Grid.Col>
      );
    }
    // PullRequestFormState.COMPARE
    if (isCompareState(state) && isCompareStateData(state, data)) {
      if (data.fileDiffs.length <= 0) {
        return (
          <Grid.Col fluid nowrap>
            <h1>Nothing to compare from... 🤔</h1>
            <p>
              The source and the target you've just selected seems to be equals.
            </p>
            <p>
              Try comparing two branches that have some differences before you
              can create a pull request from the produced diff.
            </p>
            <a href={`/${parentOrgSlug}/${repoSlug}/pulls/new`}>Try again</a>
          </Grid.Col>
        );
      }

      return (
        <Grid.Col fluid nowrap>
          <form
            method={"POST"}
            action={`/${parentOrgSlug}/${repoSlug}/pulls/new`}
            style={{ width: "100%" }}
          >
            <input type={"hidden"} name={"state_from"} value={state} />
            <input
              type={"hidden"}
              name={"state_dest"}
              value={PullRequestFormState.DETAILS}
            />
            <input
              type={"hidden"}
              name={"source_parent_org_slug"}
              value={data.source.parentOrg.slug}
            />
            <input
              type={"hidden"}
              name={"source_repository_slug"}
              value={data.source.repo.slug}
            />
            <input
              type={"hidden"}
              name={"source_repository_from_branch"}
              value={data.source.branch}
            />
            <input
              type={"hidden"}
              name={"target_parent_org_slug"}
              value={data.target.parentOrg.slug}
            />
            <input
              type={"hidden"}
              name={"target_repository_slug"}
              value={data.target.repo.slug}
            />
            <input
              type={"hidden"}
              name={"target_repository_dest_branch"}
              value={data.target.branch}
            />
            <Grid.Row fluid>
              <Grid.Col fluid>
                <Card themeScheme={themeScheme} style={{ width: "100%" }}>
                  <Grid.Col fluid>
                    <label htmlFor={"summary"}>Summary</label>
                    <input
                      type={"text"}
                      name={"summary"}
                      style={{ width: "100%" }}
                    />
                  </Grid.Col>
                  <Grid.Col fluid style={{ marginTop: 8 }}>
                    <label htmlFor={"description"}>Description</label>
                    <textarea
                      name={"description"}
                      style={{
                        width: "100%",
                        maxWidth: "100%",
                        minWidth: "100%",
                        minHeight: 180,
                      }}
                    ></textarea>
                  </Grid.Col>
                  <Grid.Col
                    fluid
                    style={{ marginTop: 8 }}
                    alignItems={"flex-end"}
                  >
                    <button type={"submit"}>Create Pull Request</button>
                  </Grid.Col>
                </Card>
              </Grid.Col>
              <Grid.Col flex={0.3} style={{ marginLeft: 16 }}>
                <Card themeScheme={themeScheme} style={{ width: "100%" }}>
                  <span></span>
                </Card>
              </Grid.Col>
            </Grid.Row>
          </form>
          <IslandWrapper
            data-islandid={`${RepositoryFilesDiffsList.name}$$0`}
            style={{ marginTop: 16 }}
          >
            <RepositoryFilesDiffsList
              filesDiffs={data.fileDiffs}
              themeScheme={themeScheme}
              orgSlug={data.source.parentOrg.slug}
              repoSlug={data.source.repo.slug}
              commitHash={""}
            />
          </IslandWrapper>
        </Grid.Col>
      );
    }
    // PullRequestFormState.DETAILS
    if (isDetailsState(state) && isDetailsStateData(state, data)) {
      return (
        <Grid.Col fluid nowrap>
          <form
            method={"POST"}
            // action={`/${data.source.parentOrg.slug}/${data.source.repo.slug}/pulls/new`}
          >
            <input type={"hidden"} name={"state_from"} value={state} />
            <button type={"submit"}>Submit the Pull Request</button>
          </form>
          <div>
            <pre>
              <code>{JSON.stringify(data, null, 2)}</code>
            </pre>
          </div>
        </Grid.Col>
      );
    }
    // PullRequestFormState.ERROR
    if (isErrorState(state) && isErrorStateData(state, data)) {
      const { errorMessage } = data;
      return (
        <Grid.Col fluid nowrap>
          <h1>Woops, an error occurred:</h1>
          <div>{errorMessage}</div>
        </Grid.Col>
      );
    }
    return null;
  };

RepositoryPullRequestCreateForm.displayName = "RepositoryPullRequestCreateForm";
export default RepositoryPullRequestCreateForm;

GitFOSS • v0.2.0 (#421408f) • MIT License