Files
openhuman/docs/src/08-hooks-utils.md
T
Steven EnamakelandGitHub bfaabd3b86 fix/rename (#20)
* chore: update AlphaHuman version to 0.49.3 and configure updater plugin in tauri.conf.json

- Bumped the AlphaHuman package version in Cargo.lock to 0.49.3.
- Added updater configuration in tauri.conf.json to enable automatic updates with specified endpoints.

* refactor: rename AlphaHuman to OpenHuman across the codebase

- Updated all instances of "AlphaHuman" to "OpenHuman" in comments, tooltips, and constants to reflect the new branding.
- Adjusted relevant documentation and prompts to ensure consistency with the new name.

* refactor: update documentation and configurations to reflect OpenHuman branding

- Replaced all instances of "AlphaHuman" with "OpenHuman" in documentation, comments, and configuration files to ensure consistency with the new branding.
- Updated deep link URLs and related authentication flows to use the new "openhuman://" scheme.
- Adjusted paths and references in the skills system and other related files to align with the new project name.te

* refactor: standardize OpenHuman references and update configurations

- Replaced all instances of "AlphaHuman" with "OpenHuman" across documentation, comments, and configuration files to maintain branding consistency.
- Updated URLs and paths to reflect the new "openhuman://" scheme.
- Adjusted environment variable names and related settings to align with the new project identity.
- Enhanced documentation for clarity and accuracy regarding the OpenHuman framework.r

* chore: update subproject commit reference in skills directory

* refactor: update backend URL to reflect new service domain

- Changed default backend URL from "https://api.openhuman.xyz" to "https://api.tinyhumans.ai" in both JavaScript and Rust configuration files.
- Ensured consistency across the codebase regarding the new backend service endpoint.

* feat: introduce identity and migration modules for OpenHuman

- Added a new identity module to support AIEOS v1.1 JSON format, including structures for identity, psychology, linguistics, motivations, capabilities, physicality, history, and interests.
- Implemented a migration module to facilitate data migration from OpenClaw memory, including SQLite and Markdown sources, with detailed reporting on migration statistics and warnings.
- Established utility functions for handling multimodal content and image processing within the OpenHuman framework.
- Enhanced the agent system with new dispatcher and classifier functionalities to improve tool management and message classification.

* chore: remove Android project files and configurations

- Deleted various Android project files including .editorconfig, .gitignore, build.gradle.kts, gradle.properties, and others to clean up the project structure.
- Removed all related resources, layouts, and source files from the Android app directory to streamline the codebase.
- This cleanup is part of a larger effort to refactor and simplify the project structure.

* refactor: update login flow and remove Telegram integration

- Removed the TelegramLoginButton component and its references from the OAuthLoginSection, streamlining the login options.
- Updated the AppRoutes to remove the login route, reflecting changes in the authentication flow.
- Enhanced the RotatingTetrahedronCanvas component with improved geometry and lighting effects for better visual presentation.
- Adjusted the TypewriterGreeting component's styling for consistency.
- Cleaned up the Welcome page to integrate the OAuthLoginSection directly, improving user experience.

* chore: update subproject commit reference in skills directory

* chore: update test configurations and improve test assertions

- Modified test scripts in package.json to use a specific Vitest configuration file for consistency.
- Updated assertions in loader tests to ensure loading durations are non-negative.
- Enhanced tool loading tests to clarify expected behavior regarding localStorage and cache management.
- Adjusted agent tool registry tests to improve error handling and ensure accurate statistics.
- Refined device detection tests to reflect updated fallback URLs.

* fix: enhance parameter formatting and remove unused components

- Updated the `formatParameters` function to handle cases where schema properties are empty, returning a more informative response.
- Deleted the `DownloadScreen` component and associated device detection utilities to streamline the codebase and remove unused functionality.
- Adjusted tests to reflect changes in the tool loading and agent tool registry, ensuring accuracy in assertions.

* chore: simplify Vitest configuration by removing unused include patterns

- Updated the Vitest configuration to remove unnecessary test file patterns, streamlining the test setup for better clarity and maintainability.

* refactor: update paths and comments for AI configuration and file watching

- Modified Vite configuration to ignore only the `src-tauri` directory.
- Updated logging messages to reflect the correct path for writing AI configuration files.
- Adjusted fetch calls in the file watcher to use the new path for `TOOLS.md`.
- Revised comments and logic in Rust code to clarify the handling of AI configuration file paths, including legacy fallback options.

* chore: remove unused updater secrets from GitHub Actions workflow

- Deleted UPDATER_GIST_URL and UPDATER_GIST_ID environment variables from the package-and-publish workflow, streamlining the configuration.

* chore: comment out Vitest thresholds for clarity

- Commented out the thresholds section in the Vitest configuration to improve clarity and maintainability, as it is currently not in use.

* ran formatter

* chore: update updater public key in tauri configuration

- Replaced the existing public key in the updater plugin configuration with a new value to ensure proper functionality and security.

* chore: update ESLint configuration and refactor components

- Added `localStorage` and `sessionStorage` as readonly globals in ESLint configuration for better linting support.
- Removed unused imports from `SkillsPanel.tsx` to clean up the code.
- Changed the type of `watcherInterval` in `file-watcher.ts` for improved type safety.
- Refactored toast management logic in `Intelligence.tsx` to enhance clarity and maintainability.
- Simplified import statements in `IntelligenceProvider.tsx` for consistency.
- Streamlined object property shorthand in `agentToolRegistry.ts` for cleaner code.

* refactor: improve error handling and type safety in Intelligence component

- Enhanced toast notification logic to defer state updates, preventing potential issues with setState in effects.
- Updated the source filter dispatch to use a more specific type for improved type safety.

* refactor: enhance type safety across various components and services

- Updated type definitions from `any` to `unknown` in multiple files to improve type safety and prevent potential runtime errors.
- Refactored state management in `TauriCommandsPanel` to use more specific types.
- Adjusted context and parameters in several interfaces to ensure consistent typing.
- Added ESLint directive to `polyfills.ts` for intentional global assignments.
- Streamlined type handling in utility functions and API responses for better clarity and maintainability.

* refactor: streamline import statements and improve code clarity

- Consolidated import statements in `agentToolRegistry.ts` and `intelligenceSlice.ts` for better readability.
- Simplified the `createTestStore` function in `test-utils.tsx` to enhance code conciseness.
- Cleaned up the `isExecutionStepProgressEvent` function in `intelligence-chat-api.ts` for improved clarity and maintainability.
2026-03-26 17:04:46 -07:00

9.6 KiB

Hooks & Utilities

Custom React hooks and utility functions.

Custom Hooks

useSocket (hooks/useSocket.ts)

Access Socket.io functionality from any component.

interface UseSocketReturn {
  socket: Socket | null;
  isConnected: boolean;
  emit: (event: string, data: unknown) => void;
  on: (event: string, handler: Function) => void;
  off: (event: string, handler: Function) => void;
  once: (event: string, handler: Function) => void;
}

function useSocket(): UseSocketReturn;

Usage:

import { useSocket } from "../hooks/useSocket";

function ChatInput() {
  const { emit, isConnected } = useSocket();

  const sendMessage = (text: string) => {
    if (isConnected) {
      emit("chat:message", { text });
    }
  };

  return (
    <input
      disabled={!isConnected}
      onKeyDown={(e) => e.key === "Enter" && sendMessage(e.target.value)}
    />
  );
}

With event listeners:

function Notifications() {
  const { on, off } = useSocket();
  const [notifications, setNotifications] = useState([]);

  useEffect(() => {
    const handler = (notification) => {
      setNotifications((prev) => [...prev, notification]);
    };

    on("notification", handler);
    return () => off("notification", handler);
  }, [on, off]);

  return <NotificationList items={notifications} />;
}

useUser (hooks/useUser.ts)

Access user profile data and loading state.

interface UseUserReturn {
  user: User | null;
  loading: boolean;
  error: string | null;
  refetch: () => Promise<void>;
}

function useUser(): UseUserReturn;

Usage:

import { useUser } from "../hooks/useUser";

function ProfileHeader() {
  const { user, loading, error, refetch } = useUser();

  if (loading) return <Skeleton />;
  if (error) return <Error message={error} onRetry={refetch} />;
  if (!user) return null;

  return (
    <div className="profile">
      <Avatar src={user.avatar} />
      <span>
        {user.firstName} {user.lastName}
      </span>
    </div>
  );
}

Settings Modal Hooks

useSettingsNavigation (components/settings/hooks/useSettingsNavigation.ts)

URL-based navigation for settings modal.

interface UseSettingsNavigationReturn {
  currentRoute: string; // Current settings path
  navigateTo: (panel: string) => void; // Navigate to panel
  navigateBack: () => void; // Go back one level
  closeModal: () => void; // Close settings entirely
}

function useSettingsNavigation(): UseSettingsNavigationReturn;

Usage:

import { useSettingsNavigation } from "./hooks/useSettingsNavigation";

function SettingsMenu() {
  const { navigateTo, closeModal } = useSettingsNavigation();

  return (
    <nav>
      <button onClick={() => navigateTo("connections")}>Connections</button>
      <button onClick={() => navigateTo("privacy")}>Privacy</button>
      <button onClick={closeModal}>Close</button>
    </nav>
  );
}

useSettingsAnimation (components/settings/hooks/useSettingsAnimation.ts)

Animation state management for settings modal.

interface UseSettingsAnimationReturn {
  isEntering: boolean; // Modal is animating in
  isExiting: boolean; // Modal is animating out
  animationClass: string; // CSS class for current state
}

function useSettingsAnimation(): UseSettingsAnimationReturn;

Usage:

import { useSettingsAnimation } from "./hooks/useSettingsAnimation";

function SettingsModal() {
  const { animationClass, isExiting } = useSettingsAnimation();

  return <div className={`modal ${animationClass}`}>{/* Content */}</div>;
}

Utilities

Configuration (utils/config.ts)

Environment variable access with defaults.

// Backend URL
export const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'https://api.example.com';

// Debug mode
export const DEBUG = import.meta.env.VITE_DEBUG === 'true';

Usage:

import { BACKEND_URL, DEBUG } from '../utils/config';

const response = await fetch(`${BACKEND_URL}/api/users`);

if (DEBUG) {
  console.log('Response:', response);
}

Build deep link URLs for authentication handoff.

// Build auth deep link
function buildAuthDeepLink(token: string): string;

// Parse deep link URL
function parseDeepLink(url: string): { path: string; params: URLSearchParams };

Usage:

import { buildAuthDeepLink } from '../utils/deeplink';

// Build URL for browser redirect
const deepLink = buildAuthDeepLink(loginToken);
// → "openhuman://auth?token=abc123"

// In web frontend after auth:
window.location.href = deepLink;

Handle incoming deep links in desktop app.

// Setup listener for deep link events
async function setupDesktopDeepLinkListener(): Promise<void>;

Called in main.tsx:

// Lazy import to ensure Tauri IPC is ready
import('./utils/desktopDeepLinkListener').then(m => {
  m.setupDesktopDeepLinkListener().catch(console.error);
});

What it does:

  1. Listens for onOpenUrl events from Tauri deep-link plugin
  2. Parses openhuman://auth?token=... URLs
  3. Calls Rust exchange_token command (bypasses CORS)
  4. Stores session in Redux
  5. Navigates to /onboarding or /home

Loop prevention:

// Set flag before navigation to prevent reprocessing
localStorage.setItem('deepLinkHandled', 'true');
window.location.replace('/');

// On next load, clear flag
if (localStorage.getItem('deepLinkHandled') === 'true') {
  localStorage.removeItem('deepLinkHandled');
  return; // Don't process again
}

URL Opener (utils/openUrl.ts)

Cross-platform URL opening.

// Open URL in system browser
async function openUrl(url: string): Promise<void>;

Usage:

import { openUrl } from '../utils/openUrl';

// Opens in system browser (not in-app WebView)
await openUrl('https://telegram.org/auth');

Implementation:

export async function openUrl(url: string): Promise<void> {
  try {
    // Try Tauri opener plugin first
    const { open } = await import('@tauri-apps/plugin-opener');
    await open(url);
  } catch {
    // Fallback to browser API
    window.open(url, '_blank');
  }
}

Polyfills (polyfills.ts)

Node.js polyfills for browser environment.

The telegram npm package requires Node.js APIs. These are polyfilled:

// polyfills.ts
import { Buffer } from 'buffer';
import process from 'process';
import util from 'util';

window.Buffer = Buffer;
window.process = process;
window.util = util;

Imported at app entry:

// main.tsx
import './polyfills';

// ... rest of app

Vite configuration:

// vite.config.ts
export default defineConfig({
  resolve: { alias: { buffer: 'buffer', process: 'process/browser', util: 'util' } },
  define: { 'process.env': {}, global: 'globalThis' },
});

Types

API Types (types/api.ts)

// API response wrapper
interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
}

// API error
interface ApiError {
  code: string;
  message: string;
  details?: unknown;
}

// User interface
interface User {
  id: string;
  firstName: string;
  lastName?: string;
  username?: string;
  email?: string;
  avatar?: string;
  telegramId?: string;
  subscription?: SubscriptionInfo;
  usage?: UsageInfo;
  createdAt: string;
  updatedAt: string;
}

Onboarding Types (types/onboarding.ts)

// Onboarding step definition
interface OnboardingStep {
  id: string;
  title: string;
  component: React.ComponentType<StepProps>;
}

// Step component props
interface StepProps {
  onNext: () => void;
  onBack: () => void;
}

// Connection option
interface ConnectionOption {
  id: string;
  label: string;
  icon: React.ComponentType;
  description: string;
  comingSoon?: boolean;
}

Static Data

Countries (data/countries.ts)

Country list for phone number input.

interface Country {
  code: string; // "US"
  name: string; // "United States"
  dialCode: string; // "+1"
  flag: string; // "🇺🇸"
}

export const countries: Country[];

Usage:

import { countries } from "../data/countries";

function PhoneInput() {
  const [country, setCountry] = useState(countries[0]);

  return (
    <div>
      <select
        value={country.code}
        onChange={(e) =>
          setCountry(countries.find((c) => c.code === e.target.value))
        }
      >
        {countries.map((c) => (
          <option key={c.code} value={c.code}>
            {c.flag} {c.name} ({c.dialCode})
          </option>
        ))}
      </select>
      <input placeholder="Phone number" />
    </div>
  );
}

Best Practices

Hook Dependencies

Always include dependencies in useEffect:

// Good
useEffect(() => {
  on('event', handler);
  return () => off('event', handler);
}, [on, off, handler]);

// Bad - missing dependencies
useEffect(() => {
  on('event', handler);
  return () => off('event', handler);
}, []);

Cleanup Functions

Always clean up subscriptions:

useEffect(() => {
  const subscription = subscribe();
  return () => subscription.unsubscribe();
}, []);

Error Boundaries

Wrap utility calls in try-catch:

try {
  await openUrl(url);
} catch (error) {
  console.error('Failed to open URL:', error);
  // Fallback behavior
}

Type Safety

Use TypeScript generics for API calls:

const user = await apiClient.get<User>('/users/me');
// user is typed as User

Previous: Providers | Back to Index