Julius MarmingeT3 Code Mobile [WIP] (#2013)b3e8c035 months agoHistory
162 lines · 4.8 KB · TypeScript
RawDownload
import { useAtomValue } from "@effect/atom-react";import {  type VcsActionState,  type VcsActionTarget,  EMPTY_VCS_ACTION_ATOM,  EMPTY_VCS_ACTION_STATE,  createVcsActionManager,  getVcsActionTargetKey,  vcsActionStateAtom,} from "@t3tools/client-runtime";import { useCallback, useEffect, useRef, useState } from "react"; import { uuidv4 } from "../lib/uuid";import { appAtomRegistry } from "./atom-registry";import { getEnvironmentClient } from "./environment-session-registry"; export const vcsActionManager = createVcsActionManager({  getRegistry: () => appAtomRegistry,  getClient: (environmentId) => {    const client = getEnvironmentClient(environmentId);    return client ? { ...client.vcs, runChangeRequest: client.git.runStackedAction } : null;  },  getActionId: uuidv4,}); export function useVcsActionState(target: VcsActionTarget): VcsActionState {  const targetKey = getVcsActionTargetKey(target);  const state = useAtomValue(    targetKey !== null ? vcsActionStateAtom(targetKey) : EMPTY_VCS_ACTION_ATOM,  );  return targetKey === null ? EMPTY_VCS_ACTION_STATE : state;} // ---------------------------------------------------------------------------// Git action result notification// --------------------------------------------------------------------------- export interface GitActionResultNotification {  readonly type: "success" | "error";  readonly title: string;  readonly description?: string;  readonly prUrl?: string;} const RESULT_DISMISS_MS = 5_000; type ResultListener = (result: GitActionResultNotification | null) => void;const resultListeners = new Set<ResultListener>();let currentResult: GitActionResultNotification | null = null;let dismissTimer: ReturnType<typeof setTimeout> | null = null; function broadcast(result: GitActionResultNotification | null): void {  currentResult = result;  for (const listener of resultListeners) {    listener(result);  }} export function showGitActionResult(result: GitActionResultNotification): void {  if (dismissTimer) clearTimeout(dismissTimer);  broadcast(result);  dismissTimer = setTimeout(() => broadcast(null), RESULT_DISMISS_MS);} export function dismissGitActionResult(): void {  if (dismissTimer) clearTimeout(dismissTimer);  broadcast(null);} export function useGitActionResultNotification(): {  readonly result: GitActionResultNotification | null;  readonly dismiss: () => void;} {  const [result, setResult] = useState<GitActionResultNotification | null>(currentResult);   useEffect(() => {    resultListeners.add(setResult);    setResult(currentResult);    return () => {      resultListeners.delete(setResult);    };  }, []);   return { result, dismiss: dismissGitActionResult };} // ---------------------------------------------------------------------------// Unified git action progress (combines running state + result notification)// --------------------------------------------------------------------------- export type GitActionProgressPhase = "idle" | "running" | "success" | "error"; export interface GitActionProgress {  readonly phase: GitActionProgressPhase;  readonly label: string | null;  readonly description: string | null;  readonly prUrl?: string;} const EMPTY_PROGRESS: GitActionProgress = {  phase: "idle",  label: null,  description: null,}; function formatElapsedSeconds(ms: number | null): string | null {  if (ms === null) return null;  const elapsed = Math.max(0, Math.floor((Date.now() - ms) / 1000));  if (elapsed < 2) return null;  return `Running for ${elapsed}s`;} export function useGitActionProgress(target: VcsActionTarget): GitActionProgress {  const actionState = useVcsActionState(target);  const { result } = useGitActionResultNotification();   const [, forceUpdate] = useState(0);  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);   const startElapsedTimer = useCallback(() => {    if (intervalRef.current) return;    intervalRef.current = setInterval(() => forceUpdate((n) => n + 1), 1000);  }, []);   const stopElapsedTimer = useCallback(() => {    if (intervalRef.current) {      clearInterval(intervalRef.current);      intervalRef.current = null;    }  }, []);   useEffect(() => {    if (actionState.isRunning) {      startElapsedTimer();    } else {      stopElapsedTimer();    }    return stopElapsedTimer;  }, [actionState.isRunning, startElapsedTimer, stopElapsedTimer]);   if (actionState.isRunning) {    const description =      actionState.lastOutputLine ??      formatElapsedSeconds(actionState.hookStartedAtMs ?? actionState.phaseStartedAtMs);    return {      phase: "running",      label: actionState.currentLabel,      description,    };  }   if (result) {    return {      phase: result.type,      label: result.title,      description: result.description ?? null,      prUrl: result.prUrl,    };  }   return EMPTY_PROGRESS;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t