Skip to content

Refactor resequences epic for better declarative composition #1

@dev-msp

Description

@dev-msp

Refactor resequences epic for better declarative composition

The current resequences epic in client/lib/keys.ts handles grid selection → time range conversion in a single monolithic function. While functional, it could be more literate and composable by breaking down the transformation pipeline into smaller, named operations.

Current Issues

  1. Dense transformation logic - The concatMap contains complex coordinate conversion, data lookup, and edge case handling all in one place
  2. Mixed abstraction levels - Low-level array operations mixed with high-level domain logic
  3. Hard to test - The entire transformation is one big function
  4. Not reusable - Grid coordinate logic is embedded in the epic

Proposed Refactor

Break the epic into a pipeline of smaller, named transformations:

// Domain-specific operators
const gridSelectionsOnly = () => pipe(
  map((s: State) => s.view),
  filter((view): view is GridView => view.type === 'grid'),
  distinctBy(selectionModeKey),
  filter(isDoubleSelection)
);

const toGridCoordinates = () => pipe(
  map(({ input: { selection } }) => 
    parseGridSelection(selection.value)
  )
);

const toTimeRange = (gridData: Observable<GridData>) => pipe(
  withLatestFrom(gridData),
  map(([coordinates, data]) => 
    coordinatesToTimeRange(coordinates, data)
  )
);

// Pure functions for transformations
const parseGridSelection = (selection: string[]): [number, number] => { /* ... */ };
const coordinatesToTimeRange = (coords: [number, number], data: GridData): TraversalCommand => { /* ... */ };
const selectionModeKey = (view: GridView) => /* ... */;
const isDoubleSelection = (view: GridView): view is DoubleSelectionView => /* ... */;

// Composed epic
export const resequences: AppEpic<TraversalCommand> = (cmds, state) => {
  const gridData$ = state.pipe(map(select.data), shareReplay(1));
  
  return state.pipe(
    gridSelectionsOnly(),
    toGridCoordinates(),
    toTimeRange(gridData$)
  );
};

Benefits

  • Testable components - Each transformation can be unit tested
  • Reusable logic - Grid coordinate parsing could be used elsewhere
  • Clear intent - Each operator has a single, named responsibility
  • Easier debugging - Can log/tap between stages
  • Type safety - Smaller functions are easier to type correctly

Implementation Notes

  • Keep the existing behavior exactly the same
  • Extract pure functions first, then build operators around them
  • Consider moving grid coordinate logic to a separate module
  • Maintain the same error handling and edge cases

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions