GitFOSS
.ts
TypeScript
(application/typescript)
// 1st-party
import type { ReactIsland } from "@ethicdevs/react-monolith";
// 3rd-party
import React, { useCallback, useMemo, useState } from "react";
// app
import type { RepositoryObject } from "../types";
import { AppRoute } from "../routes.defs";
import { Grid } from "../components/Grid";
import { buildRouteLink } from "../utils/shared";

export interface RepositoryCommitSummaryLineProps {
  commit: RepositoryObject;
  currentRef: string;
  orgSlug: string;
  repoSlug: string;
  defaultFullSubjectVisible?: boolean;
}

const MAX_COMMIT_LINE_LENGTH = 60;
const TRAILING_CHAR = " ...";
const TRAILING_CHAR_LENGTH = TRAILING_CHAR.length;

const RepositoryCommitSummaryLine: ReactIsland<
  RepositoryCommitSummaryLineProps
> = ({ orgSlug, repoSlug, commit, defaultFullSubjectVisible = false }) => {
  const [isFullSubjectShown, setIsFullSubjectShown] = useState<boolean>(
    defaultFullSubjectVisible
  );

  const toggleFullSubjectVisibility = useCallback(
    (ev: React.MouseEvent<HTMLSpanElement>) => {
      ev.preventDefault();
      setIsFullSubjectShown((prevVisibility) => !prevVisibility);
    },
    [setIsFullSubjectShown]
  );

  const isSubjectTooLongForDisplay = useMemo(
    () => commit.subject.length > MAX_COMMIT_LINE_LENGTH,
    [commit.subject.length]
  );

  const subject = useMemo(
    () =>
      isSubjectTooLongForDisplay
        ? `${commit.subject.substring(
            0,
            MAX_COMMIT_LINE_LENGTH - TRAILING_CHAR_LENGTH
          )}`
        : commit.subject,
    [commit.subject]
  );

  return (
    <Grid.Col fluid nowrap>
      <Grid.Row
        gap={8}
        alignItems={"stretch"}
        style={{ flexWrap: "wrap-reverse" }}
      >
        <Grid.Col flex={"1 0 calc(100% - 220px)"} style={{ minWidth: 360 }}>
          <strong>{commit.author.name}</strong>
          <span style={{ marginTop: 8 }}>
            <a
              href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
                orgSlug,
                repoSlug,
                objectId: commit.commit,
              })}
            >
              {subject}
            </a>
            {isSubjectTooLongForDisplay ? (
              <span onClick={toggleFullSubjectVisibility}>{TRAILING_CHAR}</span>
            ) : null}
          </span>
        </Grid.Col>
        <Grid.Col
          alignItems={"flex-end"}
          style={{
            textAlign: "right",
            flex: "1 0 200px",
            width: 200,
            minWidth: 200,
          }}
        >
          <span>
            <a
              href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
                orgSlug,
                repoSlug,
                objectId: commit.commit,
              })}
            >
              {commit.abbreviated_commit}
            </a>
            {commit.abbreviated_parent.trim() != "" ? (
              <a
                href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
                  orgSlug,
                  repoSlug,
                  objectId: commit.parent,
                })}
              >
                {` (parent ${commit.abbreviated_parent})`}
              </a>
            ) : null}
          </span>
          <span style={{ marginTop: 8 }}>
            {new Date(commit.author.date).toLocaleString()}
          </span>
        </Grid.Col>
      </Grid.Row>
      {isFullSubjectShown ? (
        <code style={{ marginTop: 8 }}>{commit.subject}</code>
      ) : null}
    </Grid.Col>
  );
};

RepositoryCommitSummaryLine.displayName = "RepositoryCommitSummaryLine";
export default RepositoryCommitSummaryLine;