import type { ReactIsland } from "@ethicdevs/react-monolith";
import React, { useCallback } from "react";
import type { Organization, PullRequest, Repository } from "@prisma/client";
import type {
AppThemeScheme,
RepositoryFileDiff,
RepositoryWithParentAndForkedFromRepos,
} from "../types";
import { Card } from "../components/Card.styled";
import { Grid } from "../components/Grid";
import { IslandWrapper } from "../components/IslandWrapper.styled";
import RepositoryFilesDiffsList from "./RepositoryFilesDiffsList";
import PullRequestSourceSelect, {
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: {
[repoSlug: 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;
}
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;
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;
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;
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 onSourceSelectionChange = useCallback(
(ev: PullRequestSourceSelectionChangeEvent) => {
console.warn(
`Selected: ${ev.data.selectedOrg.org.slug} / ${ev.data.selectedRepo.repo.slug} / ${ev.data.selectedBranch}`
);
},
[]
);
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}$$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>
<div style={{ margin: "0 8px" }}>
<span>vs.</span>
</div>
<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={onSourceSelectionChange}
/>
</div>
<button type={"submit"} style={{ marginLeft: 8 }}>
Compare
</button>
</Grid.Row>
</Card>
</form>
</Grid.Col>
);
}
if (isCompareState(state) && isCompareStateData(state, data)) {
if (data.fileDiffs.length <= 0) {
return (
<Grid.Col fluid nowrap>
<h1>Nothing to request Pull from... 🤔</h1>
<p>
The source and the target you've just selected seems to be equals.
Try comparing two branches that have some differences before you
can create a pull request from the diff.
</p>
<a href={`/${parentOrgSlug}/${repoSlug}/pulls/new`}>Go back</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>fuck the police</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>
);
}
if (isDetailsState(state) && isDetailsStateData(state, data)) {
return (
<Grid.Col fluid nowrap>
<form
method={"POST"}
>
<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>
);
}
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;