Files
openhuman/docs/src/02-state-management.md
T
Steven EnamakelandGitHub 58969667d9 fix: add ESLint and Prettier configuration (#15)
* ran prettier

* Refactor ESLint configuration to use ES module syntax and enhance TypeScript support

- Converted CommonJS `require` statements to ES module `import` syntax for better compatibility with modern JavaScript.
- Added new paths to ignore in ESLint configuration to exclude additional directories.
- Updated TypeScript file patterns to be more specific, improving linting accuracy.
- Adjusted React hooks rules to allow certain patterns, enhancing flexibility in component design.

These changes improve the maintainability and clarity of the ESLint configuration, aligning it with current best practices.

* Refactor ESLint configuration to use ES module syntax and enhance TypeScript support

- Converted CommonJS `require` statements to ES module `import` syntax for better compatibility with modern JavaScript.
- Added new paths to ignore in ESLint configuration to exclude additional directories.
- Updated TypeScript file patterns to be more specific, improving linting accuracy.
- Introduced new React hooks rules and adjusted existing rules for better adherence to best practices.
- Made minor adjustments to import statements across various files for consistency and clarity.

These changes improve the overall linting setup and ensure better code quality across the project.

* Refactor import statements across multiple files for consistency

- Updated import statements to use TypeScript's `type` syntax for type imports, enhancing clarity and consistency across the codebase.
- Consolidated imports from the same module into single statements, improving readability and maintainability.

These changes streamline the code structure and align with best practices for TypeScript imports.

* Refactor import statements in memory manager for improved clarity

- Updated import statements to consolidate type imports and enhance readability.
- Removed redundant imports, streamlining the code structure in the memory manager file.

These changes align with best practices for TypeScript imports and improve maintainability.

* Add Husky for pre-commit and pre-push hooks

- Introduced Husky to manage Git hooks, enhancing the development workflow.
- Added pre-commit and pre-push scripts to enforce code formatting and linting checks before commits and pushes.
- Updated package.json to include Husky as a dependency and added a prepare script for setup.

These changes improve code quality and ensure adherence to formatting and linting standards during the development process.

* Refactor import statements for improved clarity and consistency

- Updated import statements across multiple files to consolidate type imports and enhance readability.
- Adjusted the order of imports for better organization and alignment with best practices in TypeScript.

These changes streamline the code structure and improve maintainability throughout the project.

* ran formatter

* Refactor import statements and improve code formatting across multiple files

- Consolidated and reordered import statements for better clarity and consistency in `SkillsGrid.tsx`, `SkillProvider.tsx`, and `index.ts`.
- Enhanced readability by adjusting formatting and removing redundant lines.
- These changes align with best practices for TypeScript imports and improve overall maintainability of the codebase.

* Refactor and optimize code in multiple components

- Removed redundant properties from the `STATUS_DISPLAY` object in `SkillsGrid.tsx` to streamline status handling.
- Consolidated import statements in `SettingsModal.tsx` for improved organization.
- Simplified state management and error handling in `BillingPanel.tsx`, enhancing performance and readability.
- Added `REHYDRATE` import to `index.ts` for better state persistence management.

These changes improve code clarity, maintainability, and align with best practices in TypeScript development.

* Consolidate import statements in SettingsModal.tsx for improved organization

* Add Prettier and ESLint checks to typecheck workflow

- Integrated a Prettier formatting check to ensure code style consistency.
- Added an ESLint step to enforce code quality and catch potential issues.
- These enhancements improve the development workflow by automating formatting and linting checks during the typecheck process.

* Add activeSkillDescription state to ConnectionsPanel and ConnectStep

- Introduced activeSkillDescription state in both ConnectionsPanel and ConnectStep components to store and manage skill descriptions.
- Updated the SkillSetupModal to accept skillDescription as a prop, enhancing the modal's functionality and data handling.

These changes improve the user experience by providing more detailed information about skills during the connection setup process.

* Enhance pre-push hook to include TypeScript compile check

- Added a TypeScript compile check to the pre-push script, ensuring that code compiles successfully before pushing.
- Updated error handling to include compile errors alongside formatting and linting issues, providing clearer feedback to developers.

These changes improve the reliability of the codebase by preventing non-compiling code from being pushed.

* Update GitHub workflows for pull request handling and publishing logic

- Modified the package-and-publish workflow to support pull request events, ensuring proper handling of branches.
- Adjusted the SHOULD_PUBLISH environment variable to differentiate between pull requests and main branch events.
- Updated the pr-protection workflow to focus solely on the main branch, removing references to the master branch.

These changes enhance the CI/CD process by refining branch handling and improving clarity in workflow conditions.
2026-02-02 06:24:50 +05:30

6.6 KiB

State Management

The application uses Redux Toolkit with Redux-Persist for robust state management.

Store Configuration

File: store/index.ts

// Combines all slices with persistence
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['auth', 'telegram'], // Persisted slices
};

Redux State Structure

RootState = {
  auth: {
    token: string | null, // JWT (persisted)
    isOnboardedByUser: Record<string, boolean>, // Per-user flag (persisted)
  },
  socket: {
    byUser: Record<
      string,
      {
        // Per user ID
        status: 'connecting' | 'connected' | 'disconnected';
        socketId: string | null;
      }
    >,
  },
  user: { profile: User | null, loading: boolean, error: string | null },
  telegram: {
    byUser: Record<string, TelegramState>, // Per Telegram user (persisted)
  },
};

Slices

Auth Slice (store/authSlice.ts)

Manages JWT token and per-user onboarding status.

State:

interface AuthState {
  token: string | null;
  isOnboardedByUser: Record<string, boolean>;
}

Actions:

  • setToken(token: string) - Store JWT after login
  • clearToken() - Remove token on logout
  • setOnboarded({ userId, isOnboarded }) - Mark user as onboarded

Selectors (store/authSelectors.ts):

  • selectToken - Get current JWT
  • selectIsOnboarded(userId) - Check if user completed onboarding

Socket Slice (store/socketSlice.ts)

Tracks Socket.io connection status per user.

State:

interface SocketState {
  byUser: Record<
    string,
    { status: 'connecting' | 'connected' | 'disconnected'; socketId: string | null }
  >;
}

Actions:

  • setSocketStatus({ userId, status }) - Update connection status
  • setSocketId({ userId, socketId }) - Store socket ID
  • clearSocketState(userId) - Clear user's socket state

Selectors (store/socketSelectors.ts):

  • selectSocketStatus(userId) - Get connection status
  • selectIsSocketConnected(userId) - Boolean connected check

User Slice (store/userSlice.ts)

Stores user profile data.

State:

interface UserState {
  profile: User | null;
  loading: boolean;
  error: string | null;
}

Actions:

  • setUser(user) - Store user profile
  • setUserLoading(loading) - Set loading state
  • setUserError(error) - Set error state
  • clearUser() - Clear profile on logout

Telegram Slice (store/telegram/)

Complex nested state management for Telegram integration.

Files:

  • index.ts - Slice exports (actions, thunks)
  • types.ts - Entity and state interfaces
  • reducers.ts - Synchronous reducers
  • extraReducers.ts - Async thunk handlers
  • thunks.ts - Async operations

State Structure:

telegram.byUser[telegramUserId] = {
  connectionStatus: "disconnected" | "connecting" | "connected" | "error",
  authStatus: "not_authenticated" | "authenticating" | "authenticated" | "error",
  currentUser: TelegramUser | null,
  sessionString: string | null,              // Stored here, NOT localStorage
  chats: Record<string, TelegramChat>,
  chatsOrder: string[],
  messages: Record<chatId, Record<msgId, TelegramMessage>>,
  threads: Record<chatId, TelegramThread[]>
}

Reducers:

  • setCurrentUser - Store authenticated Telegram user
  • setSessionString - Store MTProto session (for persistence)
  • setConnectionStatus - Update connection state
  • setAuthStatus - Update authentication state
  • addChat / updateChat - Manage chat list
  • addMessage / updateMessage - Manage message history
  • setThreads - Store thread data

Thunks (store/telegram/thunks.ts):

  • initializeTelegram(userId) - Initialize MTProto client
  • connectTelegram(userId) - Establish Telegram connection
  • fetchChats(userId) - Load chat list
  • fetchMessages({ userId, chatId }) - Load message history
  • disconnectTelegram(userId) - Clean disconnect

Selectors (store/telegramSelectors.ts):

  • selectTelegramState(userId) - Get full Telegram state
  • selectTelegramConnectionStatus(userId) - Get connection status
  • selectTelegramAuthStatus(userId) - Get auth status
  • selectTelegramChats(userId) - Get chat list
  • selectTelegramMessages(userId, chatId) - Get messages for chat

Typed Hooks

File: store/hooks.ts

// Use these instead of plain useDispatch/useSelector
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Persistence Configuration

What's Persisted

  • auth.token - JWT for authentication
  • auth.isOnboardedByUser - Per-user onboarding status
  • telegram.byUser - Telegram state (sessions, chats, etc.)

What's NOT Persisted

  • socket - Connection state (reconnects on app start)
  • user.loading / user.error - Transient UI states
  • Telegram loading/error states

Storage Backend

Redux-Persist uses localStorage adapter by default. This is the ONLY acceptable use of localStorage in the application.

Usage Examples

Reading State

import { useAppSelector } from '../store/hooks';

function MyComponent() {
  const token = useAppSelector(state => state.auth.token);
  const isConnected = useAppSelector(state => state.socket.byUser[userId]?.status === 'connected');
  const chats = useAppSelector(state => state.telegram.byUser[userId]?.chats);
}

Dispatching Actions

import { clearToken, setToken } from '../store/authSlice';
import { useAppDispatch } from '../store/hooks';
import { initializeTelegram } from '../store/telegram/thunks';

function MyComponent() {
  const dispatch = useAppDispatch();

  // Sync action
  const handleLogin = (token: string) => {
    dispatch(setToken(token));
  };

  // Async thunk
  const handleConnect = async () => {
    await dispatch(initializeTelegram(userId)).unwrap();
  };
}

Using Selectors

import { selectIsOnboarded } from '../store/authSelectors';
import { useAppSelector } from '../store/hooks';
import { selectTelegramConnectionStatus } from '../store/telegramSelectors';

function MyComponent({ userId }) {
  const isOnboarded = useAppSelector(state => selectIsOnboarded(state, userId));
  const connectionStatus = useAppSelector(state => selectTelegramConnectionStatus(state, userId));
}

Best Practices

  1. Always use typed hooks - useAppDispatch and useAppSelector
  2. Use selectors for derived state - Memoized and testable
  3. Keep thunks in separate files - Better organization
  4. Per-user state scoping - Key state by user ID
  5. Avoid localStorage - Use Redux-Persist instead

Previous: Architecture Overview | Next: Services Layer