GitFOSS
.ts
TypeScript
(application/typescript)
// 3rd-party
import React, { useEffect, useState } from "react";
// app
import type { LayoutProps, RepositoryCountersDTO } from "../types";
import { Env } from "../env";

// A lightweight HOC that loads SSR counters from /api/repos/:orgSlug/:repoSlug/counters
// and injects them as `layoutCounters` prop into the wrapped Layout component.
export function withLayoutCounters<P extends object>(
  WrappedComponent: React.FC<P & LayoutProps>,
): React.FC<P & LayoutProps> {
  const ComponentWithCounters: React.FC<P & LayoutProps> = ({
    orgSlug,
    repoSlug,
    ...props
  }) => {
    const [layoutCounters, setLayoutCounters] = useState<
      RepositoryCountersDTO | undefined
    >(undefined);

    // SSR: on first render, layoutCounters may be pre-populated by the server.
    // Client: fetch the API endpoint to hydrate counters.
    useEffect(() => {
      // SSR: allow server to inject counters via HTML if possible; fallback to fetch
      const baseUrl = `${Env.DEPLOYMENT_SCHEME}://${Env.DEPLOYMENT_DOMAIN}${Env.DEPLOYMENT_SCHEME !== "https" ? `:${Env.PORT}` : ""}`;
      const endpoint = `${baseUrl}/api/repos/${orgSlug}/${repoSlug}/counters`;

      console.log("hoc(layoutCounters): will fetch url:", endpoint);

      fetch(endpoint, { credentials: "same-origin" })
        .then((r) => (r.ok ? r.json() : {}))
        .then((data) => {
          if (data && Object.keys(data).length > 0) {
            console.log("hoc(layoutCounters): fetched data:", data);
            setLayoutCounters(data);
          }
        })
        .catch((err) => {
          const { message } = err as Error;
          console.error("hoc(layoutCounters): fetch error:", message);
        });
    }, [orgSlug, repoSlug]);

    const WrappedComponentEl = WrappedComponent as React.FC<P & LayoutProps>;

    return (
      <WrappedComponentEl
        {...(props as unknown as P)}
        {...(props as unknown as LayoutProps)}
        orgSlug={orgSlug}
        repoSlug={repoSlug}
        layoutCounters={layoutCounters}
      />
    );
  };

  return ComponentWithCounters;
}