GitFOSS
feat(repository): improve treeview so its possible to go upper in the tree
+ 168
- 7
@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1663723391190,
+  "_generatedAtUnix": 1663726257814,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "islands": {

...
@@ -22,7 +22,7 @@
       "pathSourceMap": "./public/.islands/RepositoryInitialSetup.bundle.js.map"
     },
     "RepositoryTreeView": {
-      "hash": "a554d26afd55e725eeaedac229be2790d72bca85",
+      "hash": "f1affa1ed5c3dc2c543bf07bd799d582bb1292de",
       "pathSource": "./app/islands/RepositoryTreeView.tsx",
       "pathBundle": "./public/.islands/RepositoryTreeView.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryTreeView.bundle.js.map"

...
@@ -53,6 +53,10 @@
       "hash": "e21209cae1f4aaab6678ac937259c085084abbff",
       "pathSource": "./app/views/repository/RepositoryBrowserView.tsx"
     },
+    "RepositoryCommitsLogView": {
+      "hash": "2cae832cc936ac1d69fda4ad0ef1a470f1424933",
+      "pathSource": "./app/views/repository/RepositoryCommitsLogView.tsx"
+    },
     "RepositoryCreateView": {
       "hash": "f141b710674ecd55db0fa429ab73901e30001a39",
       "pathSource": "./app/views/repository/RepositoryCreateView.tsx"

new file
app/controllers/repository/getRepositoryCommitsLogView.ts
@@ -0,0 +1,40 @@
+// 1st-party
+import type { ReqHandler } from "@ethicdevs/react-monolith";
+// app
+import { AppRoute, AppRoutesParams } from "../../routes";
+// app services
+import { makeOrganizationService } from "../../services/organization";
+import { makeRepositoryService } from "../../services/repository";
+// app views
+import RepositoryCommitsLogView, {
+  RepositoryCommitsLogViewProps,
+} from "../../views/repository/RepositoryCommitsLogView";
+
+const getRepositoryCommitsLogView: ReqHandler = async (request, reply) => {
+  const { orgSlug, repoSlug } =
+    request.params as AppRoutesParams[AppRoute.REPOSITORY_BROWSER]["params"];
+
+  const orgService = makeOrganizationService({ request });
+  const repoService = makeRepositoryService({ request });
+
+  const parentOrg = await orgService.getOrganizationBySlug(orgSlug);
+  const repo = await repoService.getRepository(orgSlug, repoSlug);
+
+  if (repo == null) {
+    return reply.status(404).callNotFound();
+  }
+
+  const history = await repoService.getRepositoryCommitLog(repo);
+
+  const reqHandler = reply.makeRequestHandler(request, reply);
+  return reqHandler<RepositoryCommitsLogViewProps>(
+    RepositoryCommitsLogView.name,
+    {
+      parentOrg,
+      repo,
+      history,
+    }
+  );
+};
+
+export default getRepositoryCommitsLogView;

app/controllers/repository/index.ts
@@ -1,4 +1,5 @@
 import { default as getRepositoryBrowserView } from "./getRepositoryBrowserView";
+import { default as getRepositoryCommitsLogView } from "./getRepositoryCommitsLogView";
 import { default as getRepositoryCreateView } from "./getRepositoryCreateView";
 import { default as getRepositoryDetailsView } from "./getRepositoryDetailsView";
 import { default as getRepositoryExploreView } from "./getRepositoryExploreView";

...
@@ -6,6 +7,7 @@ import { default as postRepositoryCreateAction } from "./postRepositoryCreateAct
 
 export const RepositoryController = {
   getRepositoryBrowserView,
+  getRepositoryCommitsLogView,
   getRepositoryCreateView,
   getRepositoryDetailsView,
   getRepositoryExploreView,

app/islands/RepositoryTreeView.tsx
@@ -37,6 +37,22 @@ const RepositoryTreeView: ReactIsland<RepositoryTreeViewProps> = ({
     [orgSlug, repoSlug, currPath]
   );
 
+  const currPathParts = currPath.split("/");
+
+  let prevPath: string | null = currPathParts
+    .slice(0, currPathParts.length - 2)
+    .join("/");
+  prevPath = prevPath.trim() === "" ? null : prevPath;
+  prevPath = prevPath == null ? "/" : prevPath;
+
+  const prevPathLink =
+    prevPath === "/"
+      ? `/${orgSlug}/${repoSlug}`
+      : `/${orgSlug}/${repoSlug}/main/tree/${
+          prevPath.endsWith("/") ? prevPath : `${prevPath}/`
+        }`;
+  const shouldShowPrevPath = currPath !== "/";
+
   return (
     <StyledRepositoryTreeViewContainer>
       <div>

...
@@ -52,9 +68,15 @@ const RepositoryTreeView: ReactIsland<RepositoryTreeViewProps> = ({
         </span>
         {" ∙ "}
         <span>{new Date(lastCommit.author.date).toUTCString()}</span>
+        <a href={`/${orgSlug}/${repoSlug}/commits`}>History</a>
       </div>
       <div>
         <ul>
+          {shouldShowPrevPath && (
+            <li key={"go-previous"}>
+              <a href={prevPathLink}>..</a>
+            </li>
+          )}
           {repoFiles.map((file) => {
             const fileLink = buildRepoFileLink(file);
             return (

@@ -24,6 +24,7 @@ export enum AppRoute {
   AUTH_LOGOUT_ACTION = "auth.logout.action",
   USER_DASHBOARD = "user.dashboard",
   REPOSITORY_EXPLORE = "repository.explore",
+  REPOSITORY_COMMITS_LOG = "repository.commits_log",
   REPOSITORY_CREATE = "repository.create",
   REPOSITORY_CREATE_ACTION = "repository.create.action",
   REPOSITORY_DETAILS = "repository.details",

...
@@ -53,6 +54,12 @@ export interface AppRoutesParams extends IRouteParams {
   [AppRoute.AUTH_LOGOUT_ACTION]: undefined;
   [AppRoute.USER_DASHBOARD]: undefined;
   [AppRoute.REPOSITORY_EXPLORE]: undefined;
+  [AppRoute.REPOSITORY_COMMITS_LOG]: {
+    params: {
+      orgSlug: string;
+      repoSlug: string;
+    };
+  };
   [AppRoute.REPOSITORY_CREATE]: undefined;
   [AppRoute.REPOSITORY_CREATE_ACTION]: {
     body: {

...
@@ -128,6 +135,21 @@ export const AppRoutesSchemas: Record<AppRoute, undefined | FastifySchema> = {
   [AppRoute.AUTH_LOGOUT_ACTION]: undefined,
   [AppRoute.USER_DASHBOARD]: undefined,
   [AppRoute.REPOSITORY_EXPLORE]: undefined,
+  [AppRoute.REPOSITORY_COMMITS_LOG]: {
+    params: {
+      type: "object",
+      required: ["orgSlug", "repoSlug"],
+      additionalProperties: false,
+      properties: {
+        orgSlug: {
+          type: "string",
+        },
+        repoSlug: {
+          type: "string",
+        },
+      },
+    },
+  },
   [AppRoute.REPOSITORY_CREATE]: undefined,
   [AppRoute.REPOSITORY_CREATE_ACTION]: {
     body: {

...
@@ -308,6 +330,14 @@ const RootAppRouter: AppRouter = () => {
           path={"/repo/explore"}
           handler={RepositoryController.getRepositoryExploreView}
         />
+        <Router.Route
+          name={AppRoute.REPOSITORY_COMMITS_LOG}
+          method={"GET"}
+          path={"/:orgSlug/:repoSlug/commits"}
+          preHandler={loggedOrLoginRedirect}
+          schema={AppRoutesSchemas[AppRoute.REPOSITORY_COMMITS_LOG]}
+          handler={RepositoryController.getRepositoryCommitsLogView}
+        />
         <Router.Route
           name={AppRoute.REPOSITORY_CREATE}
           method={"GET"}

app/services/repository/getRepositoryCommitLog.ts
@@ -16,7 +16,7 @@ const makeGetRepositoryCommitLog: ServiceMethodFactory<
   [Repository, string | undefined, string | undefined, boolean | undefined],
   Promise<RepositoryLog[]>
 > = ({ request }) => {
-  return async (repo, path = undefined, ref = "HEAD", onlyLast = false) => {
+  return async (repo, path = "", ref = "HEAD", onlyLast = false) => {
     const parentOrg = await request.prisma.organization.findUnique({
       where: {
         id: repo.organizationId,

...
@@ -37,12 +37,11 @@ const makeGetRepositoryCommitLog: ServiceMethodFactory<
     var format =
       "{%n  ^@^commit^@^: ^@^%H^@^,%n  ^@^abbreviated_commit^@^: ^@^%h^@^,%n  ^@^tree^@^: ^@^%T^@^,%n  ^@^abbreviated_tree^@^: ^@^%t^@^,%n  ^@^parent^@^: ^@^%P^@^,%n  ^@^abbreviated_parent^@^: ^@^%p^@^,%n  ^@^refs^@^: ^@^%D^@^,%n  ^@^encoding^@^: ^@^%e^@^,%n  ^@^subject^@^: ^@^%s^@^,%n  ^@^sanitized_subject_line^@^: ^@^%f^@^,%n  ^@^body^@^: ^@^%b^@^,%n  ^@^commit_notes^@^: ^@^%N^@^,%n  ^@^verification_flag^@^: ^@^%G?^@^,%n  ^@^signer^@^: ^@^%GS^@^,%n  ^@^signer_key^@^: ^@^%GK^@^,%n  ^@^author^@^: {%n    ^@^name^@^: ^@^%aN^@^,%n    ^@^email^@^: ^@^%aE^@^,%n    ^@^date^@^: ^@^%aD^@^%n  },%n  ^@^commiter^@^: {%n    ^@^name^@^: ^@^%cN^@^,%n    ^@^email^@^: ^@^%cE^@^,%n    ^@^date^@^: ^@^%cD^@^%n  }%n},";
 
-    path;
     const args = [
       "log",
       "--quiet",
       `--pretty=format:${format}`,
-      onlyLast ? "-1" : "",
+      onlyLast ? "-1" : null,
       ref,
       path != null && path.trim() !== "" ? "--" : null,
       path != null && path.trim() !== "" ? "-p" : null,

...
@@ -52,8 +51,6 @@ const makeGetRepositoryCommitLog: ServiceMethodFactory<
       cwd: repoPath,
     });
 
-    console.log(`$ git ${args.join(" ")}`);
-
     const gitLogResult = await new Promise<RepositoryLog[]>(
       (resolve, reject) => {
         let buffer = [] as string[];

new file
app/views/repository/RepositoryCommitsLogView.tsx
@@ -0,0 +1,66 @@
+// 1st-party
+import type { ReactView } from "@ethicdevs/react-monolith";
+// 3rd-party
+import React from "react";
+// generated via script[generate:prisma]
+import type { Organization, Repository } from "@prisma/client";
+// app
+import type { CommonProps, RepositoryLog } from "../../types";
+import { Layout, PageWrapper } from "../../components";
+
+export interface RepositoryCommitsLogViewProps extends CommonProps {
+  history: RepositoryLog[];
+  parentOrg: Organization;
+  repo: Repository;
+}
+
+const RepositoryCommitsLogView: ReactView<RepositoryCommitsLogViewProps> = ({
+  commonProps,
+  history,
+  parentOrg,
+  repo,
+}) => {
+  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>
+          {" / Commits"}
+        </h1>
+        <div style={{ width: "100%" }}>
+          <ul>
+            {history.map((log) => (
+              <li key={log.tree}>
+                <a
+                  href={`/${parentOrg.slug}/${repo.slug}/commits/${log.commit}`}
+                >
+                  <strong>{log.author.name}</strong>
+                  {" ∙ "}
+                  <span>{log.subject}</span>
+                  {" - "}
+                  <span>
+                    {log.abbreviated_commit}
+                    {log.abbreviated_parent.trim() != ""
+                      ? ` ∙ parent ${log.abbreviated_parent}`
+                      : ""}
+                  </span>
+                  {" ∙ "}
+                  <span>{new Date(log.author.date).toUTCString()}</span>
+                </a>
+              </li>
+            ))}
+          </ul>
+        </div>
+      </PageWrapper>
+    </Layout>
+  );
+};
+
+RepositoryCommitsLogView.displayName = "RepositoryCommitsLogView";
+export default RepositoryCommitsLogView;