feat(repository): improve the fork user experience/flow/feature@@ -1,5 +1,5 @@
{
- "_generatedAtUnix": 1664245360775,
+ "_generatedAtUnix": 1664248120245,
"_hashAlgorithm": "sha1",
"_version": 2,
"islands": {
@@ -96,7 +96,7 @@
"pathSource": "./app/views/repository/RepositoryCreateView.tsx"
},
"RepositoryDetailsView": {
- "hash": "34fe3a6129223bed26c7a249c14909b05617b80a",
+ "hash": "07f3255d918dd9da183e98365ef33645a4f3cb6b",
"pathSource": "./app/views/repository/RepositoryDetailsView.tsx"
},
"RepositoryExploreView": {
@@ -1,14 +1,14 @@
// 1st-party
import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
-// generated via script[generate:prisma]
-import type { Repository } from "@prisma/client";
+// app
+import { RepositoryWithForksAndParentRepo } from "../../types";
// service
import type { RepositoryServiceDeps } from "./types";
const makeGetRepository: ServiceMethodFactory<
RepositoryServiceDeps,
[string, string],
- Promise<Repository | null>
+ Promise<RepositoryWithForksAndParentRepo | null>
> = ({ request }) => {
return async (orgSlug, repoSlug) => {
const parentOrg = await request.prisma.organization.findUnique({
@@ -22,6 +22,27 @@ const makeGetRepository: ServiceMethodFactory<
}
const repository = await request.prisma.repository.findFirst({
+ include: {
+ forkedFromRepo: {
+ select: {
+ id: true,
+ slug: true,
+ displayName: true,
+ organization: {
+ select: {
+ id: true,
+ slug: true,
+ displayName: true,
+ },
+ },
+ },
+ },
+ forks: {
+ select: {
+ _count: true,
+ },
+ },
+ },
where: {
slug: repoSlug,
organization: {
@@ -14,6 +14,7 @@ import type {
RepositoryHead,
RepositoryLog,
RepositoryObject,
+ RepositoryWithForksAndParentRepo,
} from "../../types";
export interface CreateRepositoryDTO {
@@ -49,7 +50,10 @@ export interface RepositoryServiceAPI extends ServiceApiContract {
canUserAccessRepository(user: User, repo: Repository): Promise<boolean>;
createRepository(dto: CreateRepositoryDTO): Promise<Repository>;
forkRepository(dto: ForkRepositoryDTO): Promise<Repository>;
- getRepository(orgSlug: string, repoSlug: string): Promise<Repository | null>;
+ getRepository(
+ orgSlug: string,
+ repoSlug: string
+ ): Promise<RepositoryWithForksAndParentRepo | null>;
getRepositoryBranches(repository: Repository): Promise<string[]>;
getRepositoryCommitLog(
repository: Repository,
@@ -1,4 +1,4 @@
-import type { Prisma, GlobalRole } from "@prisma/client";
+import type { Prisma, GlobalRole, Repository } from "@prisma/client";
export type AppThemeScheme = "light" | "dark";
export type WithThemeSchemeProp = {
@@ -144,3 +144,19 @@ export interface RepositoryLog {
}
export type RepositoryObject = RepositoryLog;
+
+export type RepositoryWithForksAndParentRepo = Repository & {
+ forks: {
+ _count: Prisma.RepositoryCountOutputType;
+ }[];
+ forkedFromRepo: {
+ displayName: string | null;
+ id: string;
+ slug: string;
+ organization: {
+ displayName: string | null;
+ id: string;
+ slug: string;
+ };
+ } | null;
+};
@@ -3,7 +3,7 @@ import type { ReactView } from "@ethicdevs/react-monolith";
// 3rd-party
import React from "react";
// generated via script[prisma:generate]
-import type { Organization, Repository, User } from "@prisma/client";
+import type { Organization, User } from "@prisma/client";
// app
import type {
CommonProps,
@@ -11,6 +11,7 @@ import type {
RepositoryFile,
RepositoryFileContent,
RepositoryLog,
+ RepositoryWithForksAndParentRepo,
} from "../../types";
import {
Card,
@@ -36,7 +37,7 @@ export interface RepositoryDetailsViewProps extends CommonProps {
parentOrg: Organization;
path: string;
readmeFileContent: null | RepositoryFileContent;
- repo: Repository;
+ repo: RepositoryWithForksAndParentRepo;
repoHead: null | RepositoryHead;
repoFiles: RepositoryFile[];
tags: string[];
@@ -60,24 +61,57 @@ const RepositoryDetailsView: ReactView<RepositoryDetailsViewProps> = ({
return (
<Layout {...commonProps}>
<PageWrapper>
- <h1>
- <a href={`/${parentOrg.slug}`}>
- {parentOrg.displayName || parentOrg.slug}
- </a>
- {" / "}
- <a href={`/${parentOrg.slug}/${repo.slug}`}>
- {repo.displayName || repo.slug}
- </a>
- {path != null && path.trim() !== "" && path !== "/"
- ? ` / ${path}`
- : ""}
- {" ∙ "}
- <span style={{ textTransform: "capitalize" }}>
- ({repo.visibility.toLowerCase()})
- </span>
- </h1>
- <a href={`/${parentOrg.slug}/${repo.slug}/fork`}>Fork it!</a>
- <Grid.Row fluid style={{ marginTop: 32 }}>
+ <Grid.Col fluid>
+ <h1 style={{ margin: 0, marginTop: 20 }}>
+ <a href={`/${parentOrg.slug}`}>
+ {parentOrg.displayName || parentOrg.slug}
+ </a>
+ {" / "}
+ <a href={`/${parentOrg.slug}/${repo.slug}`}>
+ {repo.displayName || repo.slug}
+ </a>
+ {path != null && path.trim() !== "" && path !== "/"
+ ? ` / ${path}`
+ : ""}
+ {" ∙ "}
+ <span style={{ textTransform: "capitalize" }}>
+ ({repo.visibility.toLowerCase()})
+ </span>
+ </h1>
+ <Grid.Row nowrap fluid style={{ marginTop: 8 }}>
+ <div style={{ flex: 1 }}>
+ {repo.isFork && repo.forkedFromRepo != null ? (
+ <h5 style={{ margin: 0 }}>
+ <span>Forked From</span>
+ {" ∙ "}
+ <a href={`/${repo.forkedFromRepo.organization.slug}`}>
+ {repo.forkedFromRepo.organization.displayName ||
+ repo.forkedFromRepo.organization.slug}
+ </a>
+ {" / "}
+ <a
+ href={`/${repo.forkedFromRepo.organization.slug}/${repo.forkedFromRepo.slug}`}
+ >
+ {repo.forkedFromRepo.displayName ||
+ repo.forkedFromRepo.slug}
+ </a>
+ </h5>
+ ) : (
+ <div />
+ )}
+ </div>
+ <Grid.Row nowrap style={{ minWidth: 90 }}>
+ <span>{repo.forks.length}</span>
+ <a
+ href={`/${parentOrg.slug}/${repo.slug}/fork`}
+ style={{ marginLeft: 8 }}
+ >
+ Fork it!
+ </a>
+ </Grid.Row>
+ </Grid.Row>
+ </Grid.Col>
+ <Grid.Row nowrap fluid style={{ marginTop: 32 }}>
<Grid.Col fluid flex={1}>
{repoHead == null || lastCommit == null ? (
<Card