feat(pull_requests): add PullRequestDetailsView@@ -1,5 +1,5 @@
{
- "_generatedAtUnix": 1665669388427,
+ "_generatedAtUnix": 1665674159579,
"_hashAlgorithm": "sha1",
"_version": 2,
"islands": {
@@ -114,7 +114,7 @@
"pathSource": "./app/views/repository/RepositoryCreateView.tsx"
},
"RepositoryDetailsView": {
- "hash": "6a0f194e53255248f1fbc10172af6a87f0a8d1c6",
+ "hash": "29d660c09e9ae686b6e7c4c16d171e27fbd9b13c",
"pathSource": "./app/views/repository/RepositoryDetailsView.tsx"
},
"RepositoryExploreView": {
@@ -133,6 +133,10 @@
"hash": "09943e3deb491b00773600dc5510bc6a76cd680b",
"pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestCreateView.tsx"
},
+ "RepositoryPullRequestDetailsView": {
+ "hash": "1ecb2513fa8cee3e027d71aea376466657bb1d56",
+ "pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestDetailsView.tsx"
+ },
"RepositoryPullRequestsView": {
"hash": "1ad2fff2166e8417a428852f7f4579d4f2ece30d",
"pathSource": "./app/views/repositoryPullRequests/RepositoryPullRequestsView.tsx"
@@ -0,0 +1,76 @@
+// 3rd-party
+import React, { VFC } from "react";
+import styled, { css } from "styled-components";
+// app
+import type { WithThemeSchemeProp } from "../types";
+import { NamedColors } from "../utils/style";
+
+export interface InlineCodeProps extends React.HTMLAttributes<HTMLPreElement> {
+ children: string | JSX.Element;
+ fluid?: boolean;
+ codeProps?: React.HTMLAttributes<HTMLElement>;
+}
+
+export const InlineCode: VFC<InlineCodeProps & WithThemeSchemeProp> = ({
+ themeScheme,
+ children,
+ codeProps = {},
+ fluid = false,
+ ...preProps
+}) => {
+ return (
+ <StyledPreTag fluid={fluid} themeScheme={themeScheme} {...preProps}>
+ <StyledCodeTag fluid={fluid} {...codeProps}>
+ {children}
+ </StyledCodeTag>
+ </StyledPreTag>
+ );
+};
+
+const StyledPreTag = styled.pre<
+ WithThemeSchemeProp & Pick<InlineCodeProps, "fluid">
+>`
+ display: inline-block;
+ margin: 0 !important;
+ padding: 0 !important;
+
+ box-shadow: none !important;
+ text-shadow: none !important;
+
+ border-radius: 8px;
+
+ ${({ themeScheme }) => css`
+ background-color: ${NamedColors.CARD[themeScheme]} !important;
+ border: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]} !important;
+ `};
+
+ ${({ fluid }) =>
+ fluid &&
+ css`
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ justify-content: flex-start;
+ min-height: 44px; // to account for the scrollbar overlayed on-top of code
+ width: 100%;
+ max-width: 100%;
+ overflow: auto;
+ `};
+`;
+
+const StyledCodeTag = styled.code<Pick<InlineCodeProps, "fluid">>`
+ display: block;
+
+ min-height: 20px;
+ width: 100%;
+ padding: 4px 8px;
+
+ font-size: 16px;
+ border-radius: 4px !important;
+
+ ${({ fluid }) =>
+ fluid &&
+ css`
+ padding: 8px 16px;
+ `};
+`;
@@ -2,6 +2,7 @@ export { Button, ButtonAnchor } from "./Button.styled";
export { Card } from "./Card.styled";
export { Grid } from "./Grid";
export * as Icons from "./icons";
+export { InlineCode } from "./InlineCode";
export { IslandWrapper } from "./IslandWrapper.styled";
export { Layout } from "./Layout";
export { MarkdownToJsx } from "./MarkdownToJsx";
@@ -9,16 +9,16 @@ import { makePullRequestService } from "../../services/pullRequest";
import { makeRepositoryService } from "../../services/repository";
import { makeUsersService } from "../../services/user";
// app views
-import RepositoryPullRequestsView, {
- RepositoryPullRequestsViewProps,
-} from "../../views/repositoryPullRequests/RepositoryPullRequestsView";
+import RepositoryPullRequestDetailsView, {
+ RepositoryPullRequestDetailsViewProps,
+} from "../../views/repositoryPullRequests/RepositoryPullRequestDetailsView";
const getRepositoryPullRequestDetailsView: ReqHandler = async (
request,
reply
) => {
- const { orgSlug, repoSlug } =
- request.params as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUESTS]["params"];
+ const { orgSlug, repoSlug, pullUid } =
+ request.params as AppRoutesParams[AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]["params"];
const orgService = makeOrganizationService({ request });
const prService = makePullRequestService({ request });
@@ -53,14 +53,18 @@ const getRepositoryPullRequestDetailsView: ReqHandler = async (
}
}
- const pullRequests = await prService.getPullRequestsInRepository(repo);
+ const pullRequest = await prService.getPullRequestByUid(
+ orgSlug,
+ repoSlug,
+ pullUid
+ );
const reqHandler = reply.makeRequestHandler(request, reply);
- return reqHandler<RepositoryPullRequestsViewProps>(
- RepositoryPullRequestsView.name,
+ return reqHandler<RepositoryPullRequestDetailsViewProps>(
+ RepositoryPullRequestDetailsView.name,
{
parentOrg,
- pullRequests,
+ pullRequest,
repo,
}
);
@@ -160,7 +160,7 @@ export interface AppRoutesParams extends IRouteParams {
params: {
orgSlug: string;
repoSlug: string;
- pullUid: string;
+ pullUid: number;
};
body: {
reason_message: string;
@@ -222,21 +222,21 @@ export interface AppRoutesParams extends IRouteParams {
params: {
orgSlug: string;
repoSlug: string;
- pullUid: string;
+ pullUid: number;
};
};
[AppRoute.REPOSITORY_PULL_REQUEST_DETAILS]: {
params: {
orgSlug: string;
repoSlug: string;
- pullUid: string;
+ pullUid: number;
};
};
[AppRoute.REPOSITORY_PULL_REQUEST_MERGE_ACTION]: {
params: {
orgSlug: string;
repoSlug: string;
- pullUid: string;
+ pullUid: number;
};
body: {
merge_summary: string;
@@ -247,7 +247,7 @@ export interface AppRoutesParams extends IRouteParams {
params: {
orgSlug: string;
repoSlug: string;
- pullUid: string;
+ pullUid: number;
};
body: {}; // TODO: define this object shape
};
@@ -8,7 +8,7 @@ import type {
PullRequestServiceDeps,
} from "./types";
-const getPullRequest: ServiceMethodFactory<
+const getPullRequestById: ServiceMethodFactory<
PullRequestServiceDeps,
[string, PullRequestSelectOrIncludes | undefined],
Promise<PullRequest | null>
@@ -25,4 +25,4 @@ const getPullRequest: ServiceMethodFactory<
};
};
-export default getPullRequest;
+export default getPullRequestById;
@@ -0,0 +1,35 @@
+// 1st-party
+import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// generated via script[generate:prisma]
+import type { PullRequest } from "@prisma/client";
+// app services
+import { default as makeGetRepository } from "../repository/getRepository";
+// service
+import type {
+ PullRequestSelectOrIncludes,
+ PullRequestServiceDeps,
+} from "./types";
+
+const getPullRequestByUid: ServiceMethodFactory<
+ PullRequestServiceDeps,
+ [string, string, number, PullRequestSelectOrIncludes | undefined],
+ Promise<PullRequest | null>
+> = ({ request }) => {
+ const getRepository = makeGetRepository({ request });
+ return async (orgSlug, repoSlug, pullRequestUid, selectOrIncludes) => {
+ const repo = await getRepository(orgSlug, repoSlug);
+ if (repo == null) return null;
+ const pullRequest = await request.prisma.pullRequest.findUnique({
+ ...(selectOrIncludes || {}),
+ where: {
+ uid_targetRepositoryId: {
+ targetRepositoryId: repo.id,
+ uid: pullRequestUid,
+ },
+ },
+ });
+ return pullRequest;
+ };
+};
+
+export default getPullRequestByUid;
@@ -4,7 +4,8 @@ import { makeService } from "@ethicdevs/react-monolith";
import type { PullRequestServiceDeps, PullRequestServiceAPI } from "./types";
// service methods
import { default as makeCreatePullRequest } from "./createPullRequest";
-import { default as makeGetPullRequest } from "./getPullRequest";
+import { default as makeGetPullRequestById } from "./getPullRequestById";
+import { default as makeGetPullRequestByUid } from "./getPullRequestByUid";
import { default as makeGetPullRequestsInRepository } from "./getPullRequestsInRepository";
export const makePullRequestService = makeService<
@@ -12,6 +13,7 @@ export const makePullRequestService = makeService<
PullRequestServiceDeps
>({
createPullRequest: makeCreatePullRequest,
- getPullRequest: makeGetPullRequest,
+ getPullRequestById: makeGetPullRequestById,
+ getPullRequestByUid: makeGetPullRequestByUid,
getPullRequestsInRepository: makeGetPullRequestsInRepository,
});
@@ -24,10 +24,16 @@ export type PullRequestSelectOrIncludes =
| { includes?: Prisma.PullRequestInclude };
export interface PullRequestServiceAPI extends ServiceApiContract {
- getPullRequest(
+ getPullRequestById(
pullRequestId: string,
selectOrIncludes?: PullRequestSelectOrIncludes
): Promise<PullRequest | null>;
+ getPullRequestByUid(
+ orgSlug: string,
+ repoSlug: string,
+ pullRequestUid: number,
+ selectOrIncludes?: PullRequestSelectOrIncludes
+ ): Promise<PullRequest | null>;
getPullRequestsInRepository(
repository: Repository,
selectOrIncludes?: PullRequestSelectOrIncludes
@@ -17,6 +17,7 @@ import { NamedColors } from "../../utils/style";
import {
Card,
Grid,
+ InlineCode,
IslandWrapper,
Layout,
MarkdownToJsx,
@@ -205,24 +206,26 @@ const RepositoryDetailsView: ReactView<RepositoryDetailsViewProps> = ({
)}
<p>
{repo.keywords.map(
- (keyword, idx, arr) =>
+ (keyword, idx, self) =>
keyword.trim() !== "" && (
<React.Fragment key={[idx, keyword].join(":")}>
<span>{keyword}</span>
- {idx < arr.length - 1 ? ", " : "."}
+ {idx === self.length - 1 ? "." : ", "}
</React.Fragment>
)
)}
</p>
<p>
<strong>HTTP Clone:</strong>
- <br />
- <code>{cloneUrl.http}</code>
+ <InlineCode fluid themeScheme={commonProps.themeScheme}>
+ {cloneUrl.http}
+ </InlineCode>
</p>
<p>
<strong>SSH Clone:</strong>
- <br />
- <code>{cloneUrl.ssh}</code>
+ <InlineCode fluid themeScheme={commonProps.themeScheme}>
+ {cloneUrl.ssh}
+ </InlineCode>
</p>
<p>
<strong>Branches:</strong>
@@ -242,7 +245,7 @@ const RepositoryDetailsView: ReactView<RepositoryDetailsViewProps> = ({
>
{branch}
</a>
- {idx < self.length - 2 ? ", " : "."}
+ {idx === self.length - 1 ? "." : ", "}
</React.Fragment>
)
)}
@@ -252,10 +255,11 @@ const RepositoryDetailsView: ReactView<RepositoryDetailsViewProps> = ({
<br />
{tags.map(
(tag, idx, self) =>
+ tag != null &&
tag.trim() != "" && (
<React.Fragment key={tag}>
<span>{tag}</span>
- {idx < self.length - 2 ? ", " : "."}
+ {idx === self.length - 1 ? "." : ", "}
</React.Fragment>
)
)}
@@ -0,0 +1,113 @@
+// 1st-party
+import type { ReactView } from "@ethicdevs/react-monolith";
+// 3rd-party
+import React from "react";
+// generated via script[generate:prisma]
+import type { Organization, PullRequest } from "@prisma/client";
+// app
+import type { CommonProps, RepositoryWithForkedFromRepo } from "../../types";
+import {
+ Grid,
+ InlineCode,
+ IslandWrapper,
+ Layout,
+ PageWrapper,
+} from "../../components";
+// app islands
+import RepositoryHero from "../../islands/RepositoryHero";
+
+export interface RepositoryPullRequestDetailsViewProps extends CommonProps {
+ parentOrg: Organization;
+ pullRequest: PullRequest;
+ repo: RepositoryWithForkedFromRepo;
+}
+
+const RepositoryPullRequestDetailsView: ReactView<RepositoryPullRequestDetailsViewProps> =
+ ({ commonProps, parentOrg, pullRequest: pr, repo }) => {
+ return (
+ <Layout {...commonProps}>
+ <PageWrapper>
+ <IslandWrapper data-islandid={`${RepositoryHero.name}$$0`}>
+ <RepositoryHero
+ forkedFromRepo={repo.forkedFromRepo}
+ forksCount={repo.forks.length}
+ parentOrg={parentOrg}
+ path={`Pull Request / ${pr.uid}`}
+ repo={repo}
+ />
+ </IslandWrapper>
+ <Grid.Col fluid style={{ marginTop: 32 }}>
+ <Grid.Col key={pr.id} fluid>
+ <span>
+ <InlineCode themeScheme={commonProps.themeScheme}>
+ {`[${pr.state}]`}
+ </InlineCode>
+ </span>
+ <h1 style={{ margin: 0, marginTop: 8 }}>
+ #{pr.uid} - {pr.summary}
+ </h1>
+ <span style={{ opacity: 0.67, marginTop: 8 }}>
+ wants to merge{" "}
+ <InlineCode themeScheme={commonProps.themeScheme}>
+ {pr.sourceBranch}
+ </InlineCode>{" "}
+ into{" "}
+ <InlineCode themeScheme={commonProps.themeScheme}>
+ {pr.targetBranch}
+ </InlineCode>
+ </span>
+ <Grid.Row
+ fluid
+ alignItems={"center"}
+ style={{ opacity: 0.67, marginTop: 8 }}
+ >
+ {new Date(pr.createdAt).getTime() <=
+ new Date(pr.updatedAt).getTime() && (
+ <span>
+ opened on {new Date(pr.createdAt).toLocaleString()}
+ </span>
+ )}
+ {((pr.closedAt == null &&
+ new Date(pr.updatedAt).getTime() >
+ new Date(pr.createdAt).getTime()) ||
+ (pr.closedAt != null &&
+ new Date(pr.updatedAt).getTime() <
+ new Date(pr.closedAt).getTime())) && (
+ <span>
+ updated on {new Date(pr.updatedAt).toLocaleString()}
+ </span>
+ )}
+ {pr.closedAt != null && (
+ <span>
+ closed on
+ {new Date(pr.closedAt).toLocaleString()}
+ </span>
+ )}
+ </Grid.Row>
+ </Grid.Col>
+ <Grid.Col fluid style={{ marginTop: 32 }}>
+ <a
+ href={`/${parentOrg.slug}/${repo.slug}/pulls/${pr.uid}?action=edit`}
+ >
+ Edit PR
+ </a>
+ <a
+ href={`/${parentOrg.slug}/${repo.slug}/pulls/${pr.uid}?action=delete`}
+ >
+ Delete PR
+ </a>
+ </Grid.Col>
+ <Grid.Col fluid>
+ <pre>
+ <code>{JSON.stringify(pr, null, 2)}</code>
+ </pre>
+ </Grid.Col>
+ </Grid.Col>
+ </PageWrapper>
+ </Layout>
+ );
+ };
+
+RepositoryPullRequestDetailsView.displayName =
+ "RepositoryPullRequestDetailsView";
+export default RepositoryPullRequestDetailsView;