Files
vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts
Louis Knight-Webb ea5954c8f5 Refactor WorkspacesLayout (#2052)
* init refactor

* changes context

* wip

* logs context

* workspaces layout context breakdown

* sidebar context

* move diffs to workspace context

* compress workspaces layout

* refactors

* types

* always show archived
2026-01-14 22:07:00 +00:00

182 lines
5.5 KiB
TypeScript

import { useMemo } from 'react';
import { useUiPreferencesStore } from '@/stores/useUiPreferencesStore';
import { useDiffViewStore, useDiffViewMode } from '@/stores/useDiffViewStore';
import { useWorkspaceContext } from '@/contexts/WorkspaceContext';
import { useUserSystem } from '@/components/ConfigProvider';
import { useDevServer } from '@/hooks/useDevServer';
import { useBranchStatus } from '@/hooks/useBranchStatus';
import { useExecutionProcessesContext } from '@/contexts/ExecutionProcessesContext';
import type { Workspace, Merge } from 'shared/types';
import type {
ActionVisibilityContext,
ActionDefinition,
ActionIcon,
DevServerState,
} from './index';
import { resolveLabel } from './index';
import type { CommandBarPage } from './pages';
/**
* Hook that builds the visibility context from stores/context.
* Used by both NavbarContainer and CommandBarDialog to evaluate
* action visibility and state conditions.
*/
export function useActionVisibilityContext(): ActionVisibilityContext {
const layout = useUiPreferencesStore();
const { workspace, workspaceId, isCreateMode, repos } = useWorkspaceContext();
const diffPaths = useDiffViewStore((s) => s.diffPaths);
const diffViewMode = useDiffViewMode();
const expanded = useUiPreferencesStore((s) => s.expanded);
const { config } = useUserSystem();
const { isStarting, isStopping, runningDevServers } =
useDevServer(workspaceId);
const { data: branchStatus } = useBranchStatus(workspaceId);
const { isAttemptRunningVisible } = useExecutionProcessesContext();
return useMemo(() => {
// Compute isAllDiffsExpanded
const diffKeys = diffPaths.map((p) => `diff:${p}`);
const isAllDiffsExpanded =
diffKeys.length > 0 && diffKeys.every((k) => expanded[k] !== false);
// Compute dev server state
const devServerState: DevServerState = isStarting
? 'starting'
: isStopping
? 'stopping'
: runningDevServers.length > 0
? 'running'
: 'stopped';
// Compute git state from branch status
const hasOpenPR =
branchStatus?.some((repo) =>
repo.merges?.some(
(m: Merge) => m.type === 'pr' && m.pr_info.status === 'open'
)
) ?? false;
const hasUnpushedCommits =
branchStatus?.some((repo) => (repo.remote_commits_ahead ?? 0) > 0) ??
false;
return {
rightMainPanelMode: layout.rightMainPanelMode,
isLeftSidebarVisible: layout.isLeftSidebarVisible,
isLeftMainPanelVisible: layout.isLeftMainPanelVisible,
isRightSidebarVisible: layout.isRightSidebarVisible,
isCreateMode,
hasWorkspace: !!workspace,
workspaceArchived: workspace?.archived ?? false,
hasDiffs: diffPaths.length > 0,
diffViewMode,
isAllDiffsExpanded,
editorType: config?.editor?.editor_type ?? null,
devServerState,
runningDevServers,
hasGitRepos: repos.length > 0,
hasMultipleRepos: repos.length > 1,
hasOpenPR,
hasUnpushedCommits,
isAttemptRunning: isAttemptRunningVisible,
};
}, [
layout.rightMainPanelMode,
layout.isLeftSidebarVisible,
layout.isLeftMainPanelVisible,
layout.isRightSidebarVisible,
isCreateMode,
workspace,
repos,
diffPaths,
diffViewMode,
expanded,
config?.editor?.editor_type,
isStarting,
isStopping,
runningDevServers,
branchStatus,
isAttemptRunningVisible,
]);
}
/**
* Helper to check if an action is visible given the current context.
* If the action has no isVisible condition, it's always visible.
*/
export function isActionVisible(
action: ActionDefinition,
ctx: ActionVisibilityContext
): boolean {
return action.isVisible ? action.isVisible(ctx) : true;
}
/**
* Helper to check if a page is visible given the current context.
* If the page has no isVisible condition, it's always visible.
*/
export function isPageVisible(
page: CommandBarPage,
ctx: ActionVisibilityContext
): boolean {
return page.isVisible ? page.isVisible(ctx) : true;
}
/**
* Helper to check if an action is active given the current context.
* If the action has no isActive callback, returns false.
*/
export function isActionActive(
action: ActionDefinition,
ctx: ActionVisibilityContext
): boolean {
return action.isActive ? action.isActive(ctx) : false;
}
/**
* Helper to check if an action is enabled given the current context.
* If the action has no isEnabled callback, returns true (enabled by default).
*/
export function isActionEnabled(
action: ActionDefinition,
ctx: ActionVisibilityContext
): boolean {
return action.isEnabled ? action.isEnabled(ctx) : true;
}
/**
* Get the icon for an action, considering dynamic icon callbacks.
* Falls back to the static icon property.
*/
export function getActionIcon(
action: ActionDefinition,
ctx: ActionVisibilityContext
): ActionIcon {
return action.getIcon ? action.getIcon(ctx) : action.icon;
}
/**
* Get the tooltip for an action, considering dynamic tooltip callbacks.
* Falls back to the resolved label.
*/
export function getActionTooltip(
action: ActionDefinition,
ctx: ActionVisibilityContext
): string {
return action.getTooltip ? action.getTooltip(ctx) : resolveLabel(action);
}
/**
* Get the label for an action, considering dynamic label callbacks.
* Falls back to the resolved static label.
*/
export function getActionLabel(
action: ActionDefinition,
ctx: ActionVisibilityContext,
workspace?: Workspace
): string {
return action.getLabel
? action.getLabel(ctx)
: resolveLabel(action, workspace);
}