.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;
}

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

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

    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;