Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[client] mobile app #2737

Merged
merged 76 commits into from
Mar 10, 2025
Merged

[client] mobile app #2737

merged 76 commits into from
Mar 10, 2025

Conversation

r0man1337
Copy link
Contributor

@r0man1337 r0man1337 commented Mar 10, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a refreshed mobile client with interactive pages for Login, Realm, Chat, Trade, and Settings.
    • Enhanced user interfaces with dynamic chat functionalities, direct messaging, resource management, and real-time notifications.
    • Added features like theme toggling, responsive navigation headers/footers, and intuitive production controls.
  • Documentation

    • Published a comprehensive README that outlines the app’s architecture and feature layout.
  • Chores

    • Updated environment configurations and various development settings for improved stability.

Copy link

vercel bot commented Mar 10, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
eternum ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 10, 2025 5:55pm
eternum-docs ❌ Failed (Inspect) Mar 10, 2025 5:55pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
eternum-landing ⬜️ Ignored (Inspect) Visit Preview Mar 10, 2025 5:55pm

Copy link
Contributor

coderabbitai bot commented Mar 10, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces a major expansion to the Eternum Mobile project. New configuration files, environment samples, TypeScript and Vite configs have been added to establish the project’s toolchain. A comprehensive set of new UI components, pages, and widgets has been developed, covering chat, login, realm management, trade, and several game-specific functionalities. Additionally, new providers, hooks, and shared utilities have been implemented to support state management and blockchain interactions, alongside cleanup changes in the legacy world map scene.

Changes

File(s) Change Summary
Configuration & Environment Files
.gitignore, README.md, package.json, postcss.config.js, tailwind.config.js,
tsconfig.app.json, tsconfig.json, tsconfig.node.json, vite.config.ts,
.env.local.sample, .env.mainnet, .env.production, .env.sepolia, .env.slot, .knip.json
Added new configuration files, environment samples, and TypeScript/Vite settings for project initialization and build process.
Core App Setup & Routing
client/apps/eternum-mobile/src/app/app.tsx, main.tsx, router.tsx, dojoConfig.ts, env.ts, eslint.config.js, index.html
Introduced application entry point, routing configuration, Dojo and environment setup, and ESLint configuration for streamlined development.
UI Components & Pages
src/pages/{chat, login, realm, settings, trade}/**, Layout, ThemeProvider, ModeToggle, NumericInput
Added new React components and pages for login, realm, chat, trade, and settings, including layouts, themed providers, and interactive UI for game features.
Shared UI & Utility Modules
src/shared/ui/{badge, button, card, collapsible, drawer, dropdown-menu, input, numeric-keyboard, progress-circle, resource-amount, resource-icon, scroll-area, select, sheet, tabs, tooltip}.tsx,
src/shared/lib/{utils.ts, addresses.ts}
Added a suite of shared UI components and utility functions to improve code reuse, styling, and helper functionalities across the application.
State Management & Providers
src/shared/hooks/{use-auth.ts, use-account-store.ts, use-address-store.ts},
src/shared/hooks/context/{dojo-context.tsx, starknet-provider.tsx, policies.ts, signing-policy.ts}
Introduced new providers and hooks to manage application state, authentication, account connectivity, and contract policies.
Widgets & Game-Specific Elements
src/widgets/{arrived-donkeys, claim, footer, header, labor-widget, nearby-enemies, realm-info-header, resources-card, swap-input, upgrade-castle}/**
Added new game-specific widgets and exports including chat interfaces, direct messaging, resource displays, labor and upgrade systems, and navigation components.
Other Changes
client/apps/game/src/three/scenes/worldmap.tsx
Removed unused destructuring in the world map scene to simplify the control flow without affecting functionality.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App as Eternum Mobile App
    participant Router as React Router
    participant Providers as {Theme, Dojo, Starknet} Providers
    Note over App: App initialization starts
    User->>App: Launch application
    App->>Providers: Initialize theme, blockchain & account providers
    Providers-->>App: Providers set up successfully
    App->>Router: Configure routing (Login, Realm, Chat, Trade, Settings)
    Router-->>App: Render appropriate page based on auth state
    User->>App: Interact with UI (e.g., login, chat, upgrade)
    App->>Providers: Process user actions via hooks/providers
Loading

Suggested Reviewers

  • bob0005

Poem

I'm a little rabbit, swift and keen,
Hopping through code in a digital scene.
New pages and widgets, shiny and bright,
Transformed the project from morn until night.
With a twitch of my nose, I give a cheer,
For these changes, so fresh, bring the future near!
🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 39

🧹 Nitpick comments (111)
client/apps/eternum-mobile/eslint.config.js (1)

1-28: Good ESLint configuration, but consider adding more rules.

The configuration correctly uses the flat config format and includes essential plugins for React development. However, consider enhancing it with:

  1. Import ordering rules
  2. Accessibility (a11y) rules
  3. Path alias configurations

You could add import ordering and a11y rules by including:

import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
+import eslintPluginImport from 'eslint-plugin-import'
+import eslintPluginJsxA11y from 'eslint-plugin-jsx-a11y'
import tseslint from 'typescript-eslint'

export default tseslint.config(
  { ignores: ['dist'] },
  {
    extends: [
      js.configs.recommended, 
      ...tseslint.configs.recommended,
+     'plugin:import/recommended',
+     'plugin:import/typescript',
+     'plugin:jsx-a11y/recommended',
    ],
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
+     'import': eslintPluginImport,
+     'jsx-a11y': eslintPluginJsxA11y,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true },
      ],
+     'import/order': ['error', {
+       'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
+       'newlines-between': 'always',
+       'alphabetize': { 'order': 'asc', 'caseInsensitive': true }
+     }],
    },
  },
)
client/apps/eternum-mobile/src/shared/ui/tooltip.tsx (1)

6-6: Consider adding usage documentation for TooltipProvider.

It would be helpful to add a comment explaining that TooltipProvider should wrap Tooltip components at the application root or component level.

 const TooltipProvider = TooltipPrimitive.Provider
+// Note: Wrap your application or component tree with TooltipProvider
+// Example: <TooltipProvider>...</TooltipProvider>
client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (2)

19-39: Enhance accessibility for keyboard navigation in DropdownMenuSubTrigger

While the implementation looks good overall, the ChevronRight icon at line 35 lacks an accessible label which may impact screen reader users.

Consider adding an aria-label to improve accessibility:

-    <ChevronRight className="ml-auto" />
+    <ChevronRight className="ml-auto" aria-hidden="true" />

This change makes it clear that the icon is decorative and should be ignored by screen readers, which will instead rely on the component's content for context.


93-115: Add aria-checked attribute to enhance accessibility in DropdownMenuCheckboxItem

The checkbox item is missing an explicit aria-checked attribute, which would improve screen reader announcement of the checked state.

  <DropdownMenuPrimitive.CheckboxItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    checked={checked}
+   aria-checked={checked}
    {...props}
  >

This enhancement ensures both the visual and programmatic states are aligned for assistive technologies.

packages/core/src/constants/realmLevels.ts (1)

15-17: Consider simplifying the enum conversion logic.

While the current implementation works, the type casting approach is somewhat complex. A simpler approach might be more readable and maintainable.

export const getLevelName = (level: RealmLevels): string => {
-  return RealmLevelNames[RealmLevels[level] as keyof typeof RealmLevelNames];
+  // Direct mapping based on level value
+  return Object.values(RealmLevelNames)[level];
};

Additionally, consider adding validation to ensure the level is within the valid range of enum values.

client/apps/eternum-mobile/src/shared/hooks/use-auth.ts (1)

1-13: Consider enhancing authentication store with additional features

The Zustand store provides a basic boolean authentication state, which works for simple use cases. For a production mobile app, consider enhancing this with:

  • User profile information storage
  • Token management (JWT/refresh tokens)
  • Session expiration handling
  • Persistent storage integration
import { create } from "zustand";
+ import { persist } from "zustand/middleware";

interface AuthState {
  isAuthenticated: boolean;
+  token: string | null;
+  user: { id: string; username: string } | null;
  login: () => void;
+  loginWithCredentials: (username: string, password: string) => Promise<void>;
  logout: () => void;
}

-export const useAuth = create<AuthState>((set) => ({
+export const useAuth = create<AuthState>()(
+  persist(
+    (set) => ({
      isAuthenticated: false,
+      token: null,
+      user: null,
      login: () => set({ isAuthenticated: true }),
+      loginWithCredentials: async (username, password) => {
+        // API call implementation here
+        set({ isAuthenticated: true, token: "sample-token", user: { id: "1", username } });
+      },
      logout: () => set({ isAuthenticated: false, token: null, user: null }),
+    }),
+    {
+      name: "auth-storage",
+    }
+  )
-}));
client/apps/eternum-mobile/src/pages/chat/ui/tabs/events-tab.tsx (1)

31-47: Add empty state handling and optimize rendering

The component doesn't handle the case where there are no events to display. Also, consider formatting timestamps consistently and optimizing the component.

import { ScrollArea } from "@/shared/ui/scroll-area";
+ import { format } from "date-fns";
+ import { useMemo } from "react";

interface Event {
  id: string;
  sender: string;
  timestamp: string;
  message: string;
}

// Dummy events for development...

export function EventsTab() {
+  // In production, replace with real data fetching
+  const events = useMemo(() => dummyEvents, []);
+  
  return (
    <ScrollArea className="h-full p-4">
      <div className="space-y-4">
-        {dummyEvents.map((event) => (
+        {events.length === 0 ? (
+          <div className="text-center py-10 text-muted-foreground">
+            <p>No events to display</p>
+          </div>
+        ) : (
+          events.map((event) => (
            <div key={event.id} className="flex flex-col">
              <div className="flex items-center gap-2">
                <span className="font-semibold text-orange-500">{event.sender}</span>
                <span className="text-sm text-muted-foreground">{event.timestamp}</span>
              </div>
              <p className="text-sm">{event.message}</p>
            </div>
-        ))}
+          ))
+        )}
      </div>
    </ScrollArea>
  );
}
client/apps/eternum-mobile/package.json (1)

6-11: Consider adding testing and type checking scripts.

The package.json is well-structured, but it's missing scripts for testing. Consider adding:

"scripts": {
  "dev": "vite --host",
  "build": "tsc -b && vite build",
  "lint": "eslint .",
  "preview": "vite preview",
+ "typecheck": "tsc --noEmit",
+ "test": "vitest run",
+ "test:watch": "vitest"
}

This would help with development workflow and continuous integration.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/claim-tab.tsx (1)

3-48: Consider replacing dummy data with a data fetching mechanism.

While using mock data is fine for initial development, the DUMMY_CLAIMS constant contains hardcoded values that should eventually be replaced with actual data from an API or store.

Consider creating a hook or service to fetch claims data:

// In a separate hook file
export function useClaimData() {
  const [claims, setClaims] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchClaims() {
      try {
        // Replace with actual API call
        const data = await fetchClaimsFromAPI();
        setClaims(data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }
    
    fetchClaims();
  }, []);

  return { claims, loading, error };
}
client/apps/eternum-mobile/src/widgets/footer/ui/footer.tsx (2)

10-11: Consider padding for iOS safe areas

For iOS devices, you may want to add padding for the safe area at the bottom to ensure the footer doesn't overlap with system UI elements.

-    <nav className="fixed bottom-0 left-0 right-0 bg-background border-t">
+    <nav className="fixed bottom-0 left-0 right-0 bg-background border-t pb-safe">

Consider adding a CSS variable in your global styles:

:root {
  --safe-area-bottom: env(safe-area-inset-bottom, 0px);
}

.pb-safe {
  padding-bottom: var(--safe-area-bottom);
}

12-20: Reduce code duplication in navigation links

The navigation links have a repeated pattern. Consider extracting this into a reusable component or function.

+  const NavLink = ({
+    to,
+    icon: Icon,
+    label,
+  }: {
+    to: string;
+    icon: React.ComponentType<{ size: number }>;
+    label: string;
+  }) => (
+    <Link
+      to={to}
+      className={`flex flex-col items-center ${
+        currentPath === to ? "text-foreground" : "text-muted-foreground"
+      } hover:text-foreground`}
+    >
+      <Icon size={24} />
+      <span className="text-xs">{label}</span>
+    </Link>
+  );

   return (
     <nav className="fixed bottom-0 left-0 right-0 bg-background border-t">
       <div className="container flex justify-around items-center h-16">
-        <Link
-          to={ROUTES.REALM}
-          className={`flex flex-col items-center ${
-            currentPath === ROUTES.REALM ? "text-foreground" : "text-muted-foreground"
-          } hover:text-foreground`}
-        >
-          <Home size={24} />
-          <span className="text-xs">Realm</span>
-        </Link>
+        <NavLink to={ROUTES.REALM} icon={Home} label="Realm" />

-        <Link
-          to={ROUTES.TRADE}
-          className={`flex flex-col items-center ${
-            currentPath === ROUTES.TRADE ? "text-foreground" : "text-muted-foreground"
-          } hover:text-foreground`}
-        >
-          <ShoppingCart size={24} />
-          <span className="text-xs">Trade</span>
-        </Link>
+        <NavLink to={ROUTES.TRADE} icon={ShoppingCart} label="Trade" />

-        <Link
-          to={ROUTES.CHAT}
-          className={`flex flex-col items-center ${
-            currentPath === ROUTES.CHAT ? "text-foreground" : "text-muted-foreground"
-          } hover:text-foreground`}
-        >
-          <MessageCircle size={24} />
-          <span className="text-xs">Chat</span>
-        </Link>
+        <NavLink to={ROUTES.CHAT} icon={MessageCircle} label="Chat" />

-        <Link
-          to={ROUTES.SETTINGS}
-          className={`flex flex-col items-center ${
-            currentPath === ROUTES.SETTINGS ? "text-foreground" : "text-muted-foreground"
-          } hover:text-foreground`}
-        >
-          <Settings size={24} />
-          <span className="text-xs">Settings</span>
-        </Link>
+        <NavLink to={ROUTES.SETTINGS} icon={Settings} label="Settings" />
       </div>
     </nav>
   );

Also applies to: 22-30, 32-40, 42-50

client/apps/eternum-mobile/index.html (1)

35-40: Use absolute URLs for Open Graph images

The Open Graph image URL should be absolute to ensure it works correctly when shared on social media platforms.

    <!-- Open Graph / Social Media -->
    <meta property="og:type" content="website" />
    <meta property="og:title" content="Eternum Mobile" />
    <meta property="og:description" content="Eternum Mobile - Companion App" />
-    <meta property="og:image" content="/og-image.png" />
+    <meta property="og:image" content="https://your-domain.com/og-image.png" />
    <meta property="og:site_name" content="Eternum Mobile" />
client/apps/eternum-mobile/src/shared/store/use-address-store.ts (2)

10-15: Consider adding persistence and reset functionality

The address store could benefit from persistence across sessions and a reset method for logout scenarios.

-import { create } from "zustand";
+import { create } from "zustand";
+import { persist } from "zustand/middleware";

type AddressStore = {
  loading: boolean;
  addressName: undefined | string;
  setAddressName: (addressName: string | undefined) => void;
  setLoading: (loading: boolean) => void;
+  reset: () => void;
};

-export const useAddressStore = create<AddressStore>((set) => ({
+export const useAddressStore = create<AddressStore>()(
+  persist(
+    (set) => ({
  loading: false,
  addressName: undefined,
  setAddressName: (addressName: string | undefined) => set({ addressName }),
  setLoading: (loading: boolean) => set({ loading }),
+  reset: () => set({ loading: false, addressName: undefined }),
-}));
+    }),
+    {
+      name: "eternum-address-store",
+    }
+  )
+);

10-15: Add selectors for performance optimization

Consider adding selectors to optimize component re-renders when using the store.

export const useAddressStore = create<AddressStore>((set) => ({
  loading: false,
  addressName: undefined,
  setAddressName: (addressName: string | undefined) => set({ addressName }),
  setLoading: (loading: boolean) => set({ loading }),
}));

+// Selectors for performance optimization
+export const useAddressLoading = () => useAddressStore((state) => state.loading);
+export const useAddressName = () => useAddressStore((state) => state.addressName);
client/apps/eternum-mobile/src/shared/lib/addresses.ts (1)

4-7: Consider refactoring for consistency and better error handling

The implementation is functionally correct, but the style differs from other functions in this file. Consider standardizing the implementation style across all functions and adding error handling.

 export const getResourceAddresses = () => {
-  const addresses = getSeasonAddresses(env.VITE_PUBLIC_CHAIN as Chain).resources;
-  return addresses;
+  return getSeasonAddresses(env.VITE_PUBLIC_CHAIN as Chain).resources;
 };
client/apps/eternum-mobile/src/shared/store/use-account-store.ts (1)

1-17: Consider adding cleanup and initialization methods

The store handles basic state management but lacks lifecycle methods for cleanup and initialization. Consider adding methods to handle connecting/disconnecting and proper resource cleanup.

 interface AccountState {
   account: Account | AccountInterface | null;
   setAccount: (account: Account | AccountInterface | null) => void;
   connector: ControllerConnector | null;
-  setConnector: (connector: ControllerConnector) => void;
+  setConnector: (connector: ControllerConnector | null) => void;
+  disconnect: () => void;
 }

 export const useAccountStore = create<AccountState>((set) => ({
   account: null,
   setAccount: (account) => set({ account }),
   connector: null,
-  setConnector: (connector) => set({ connector }),
+  setConnector: (connector: ControllerConnector | null) => set({ connector }),
+  disconnect: () => set({ account: null, connector: null }),
 }));
client/apps/eternum-mobile/src/shared/hooks/context/signing-policy.ts (2)

25-25: Use constants for chain identifiers

The code uses string literals for chain identification which could lead to errors if values change. Consider using constants for these values.

+const CHAIN_IDS = {
+  MAINNET: "SN_MAIN",
+  TESTNET: "SN_SEPOLIA",
+};

 export const signingPolicy: SignMessagePolicy[] = [
   // ...
   domain: {
     name: "Eternum",
     version: "1",
-    chainId: env.VITE_PUBLIC_CHAIN == "mainnet" ? "SN_MAIN" : "SN_SEPOLIA",
+    chainId: env.VITE_PUBLIC_CHAIN === "mainnet" ? CHAIN_IDS.MAINNET : CHAIN_IDS.TESTNET,
     revision: "1",
   },
   // ...

13-19: Consider renaming type to follow naming conventions

The type name s1_eternum-Message has unconventional formatting with a hyphen. Consider using a more standard naming convention like camel case or pascal case.

-      "s1_eternum-Message": [
+      "S1EternumMessage": [
         { name: "identity", type: "ContractAddress" },
         { name: "channel", type: "shortstring" },
         { name: "content", type: "string" },
         { name: "timestamp", type: "felt" },
         { name: "salt", type: "felt" },
       ],

Also update the primaryType reference:

-    primaryType: "s1_eternum-Message",
+    primaryType: "S1EternumMessage",
client/apps/eternum-mobile/src/shared/hooks/context/policies.ts (1)

1-717: Consider modularizing this large policy configuration.

This file contains a large constant with numerous contract configurations which may be difficult to maintain. Consider breaking this down into separate modules by contract functionality or domain area.

You could refactor this into smaller modules like:

// Import individual policy groups
import { vrfPolicies } from './policies/vrf';
import { battlePolicies } from './policies/battle';
import { buildingPolicies } from './policies/building';
// etc.

export const policies: ContractPolicies = {
  ...vrfPolicies,
  ...battlePolicies,
  ...buildingPolicies,
  // ...other policy groups
};
client/apps/eternum-mobile/src/app/app.tsx (1)

10-17: Add error handling for background image loading.

The background image might fail to load, but there's no fallback handling in case of errors.

  return (
    <div className={`min-h-screen text-foreground relative ${!showBgImage ? "bg-background" : ""}`}>
      {showBgImage && (
        <div
          className="absolute inset-0 bg-cover bg-center opacity-50 -z-10"
          style={{ backgroundImage: `url(${bgImage})` }}
+         onError={(e) => {
+           console.error("Failed to load background image");
+           e.currentTarget.style.display = "none";
+           // Alternative: setShowBgImage(false);
+         }}
        />
      )}
      <RouterProvider router={router} />
    </div>
  );
client/apps/eternum-mobile/src/pages/login/ui/login-page.tsx (2)

16-31: Add loading state for better user experience.

There's no loading indicator during the login process, which might confuse users if the login takes time.

+ import { useState } from "react";
  // other imports...

  export function LoginPage() {
    const navigate = useNavigate();
    const { login } = useAuth();
+   const [isLoading, setIsLoading] = useState(false);

-   const handleConnect = () => {
-     login();
-     navigate({ to: ROUTES.REALM });
-   };
+   const handleConnect = async () => {
+     setIsLoading(true);
+     try {
+       await login();
+       navigate({ to: ROUTES.REALM });
+     } catch (error) {
+       console.error("Login failed:", error);
+     } finally {
+       setIsLoading(false);
+     }
+   };

    return (
      // ...
-         <Button onClick={handleConnect} className="w-full">
-           Connect
+         <Button onClick={handleConnect} className="w-full" disabled={isLoading}>
+           {isLoading ? "Connecting..." : "Connect"}
          </Button>
      // ...
    );
  }

24-24: Confusing UI message with functional button.

The message "Login functionality coming soon" contradicts having a working Connect button.

Either remove the message if login is already implemented, or disable the button if login isn't fully functional yet:

- <p className="text-center text-muted-foreground">Login functionality coming soon</p>
+ <p className="text-center text-muted-foreground">Connect with your wallet to access Eternum</p>
client/apps/eternum-mobile/tailwind.config.js (1)

56-56: Consider using exact version for tailwindcss-animate plugin.

Specifying the exact version of the plugin would ensure consistent builds across different environments.

- plugins: [require("tailwindcss-animate")],
+ plugins: [require("tailwindcss-animate"/*@x.y.z*/)],

Replace the comment with the specific version you're using, which can be found in your package.json.

client/apps/eternum-mobile/README.md (2)

24-24: Enhance URL formatting for better presentation

Convert the bare URL to a properly formatted Markdown link.

-Detailed documentation: https://feature-sliced.design/
+Detailed documentation: [Feature-Sliced Design](https://feature-sliced.design/)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

24-24: Bare URL used
null

(MD034, no-bare-urls)


31-38: Add language specifiers to code blocks

Adding language specifiers to fenced code blocks improves syntax highlighting and readability.

-```
+```bash
 src/
   - app/
   - pages/
   - widgets/
   - features/
   - shared/

Make similar changes for the other code blocks on lines 54 and 85.


Also applies to: 54-73, 85-92

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

31-31: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

</blockquote></details>
<details>
<summary>client/apps/eternum-mobile/src/shared/ui/input.tsx (1)</summary><blockquote>

`10-13`: **Consider extracting the long class string to a constant**

The long Tailwind class string could be extracted to a named constant for better readability and maintainability. This would make the component's JSX cleaner.

```diff
 const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
   ({ className, type, ...props }, ref) => {
+    const baseInputClasses = "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
     return (
       <input
         type={type}
         className={cn(
-          "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
+          baseInputClasses,
           className
         )}
         ref={ref}
client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (1)

29-37: Improve error handling for missing resources

Currently, the component logs a warning and returns null when a resource isn't found. This could lead to layout issues if the parent doesn't handle null children.

 export const ResourceIcon = ({ resourceId, size = 24, className, showTooltip = false }: ResourceIconProps) => {
   // Get the resource data based on ID
   const resourceData = resources.find((r: Resource) => r.id === resourceId);
 
   if (!resourceData) {
     console.warn(`Resource with ID ${resourceId} not found`);
-    return null;
+    // Provide a fallback icon or placeholder to prevent layout issues
+    return (
+      <div 
+        className={cn("bg-muted flex items-center justify-center", className)}
+        style={{ width: size, height: size }}
+      >
+        ?
+      </div>
+    );
   }
client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (3)

5-63: Replace dummy data with actual data fetching

The component currently uses hardcoded dummy data for labor buildings and resource balances. While this is fine for initial development, consider:

  1. Moving this data to a separate constants file if it will remain as placeholder data
  2. Replacing it with actual data fetching from an API or state management solution
  3. Adding a loading state while fetching real data

Consider refactoring to use a custom hook for data fetching:

// In a separate hook file
export function useLaborBuildings() {
  const [buildings, setBuildings] = useState(dummyLaborBuildings);
  const [resources, setResources] = useState(dummyResourceBalances);
  const [isLoading, setIsLoading] = useState(false);
  
  useEffect(() => {
    // Replace with actual API call
    const fetchData = async () => {
      setIsLoading(true);
      try {
        // const data = await api.fetchLaborBuildings();
        // setBuildings(data);
        // const resources = await api.fetchResourceBalances();
        // setResources(resources);
      } catch (error) {
        console.error(error);
      } finally {
        setIsLoading(false);
      }
    };
    
    fetchData();
  }, []);
  
  return { buildings, resources, isLoading };
}

69-80: Replace console.log with actual implementation

The callback handlers currently only log actions to the console. While this is acceptable for initial development:

Consider implementing the actual production management logic:

-  const handleStartProduction = useCallback((buildingId: string, mode: "raw" | "labor") => {
-    console.log("Start production", { buildingId, mode });
-  }, []);
+  const handleStartProduction = useCallback(async (buildingId: string, mode: "raw" | "labor") => {
+    try {
+      // Add actual implementation
+      // await api.startProduction(buildingId, mode);
+      // Optionally update local state or trigger a refetch
+    } catch (error) {
+      // Handle errors appropriately
+    }
+  }, []);

// Apply similar changes to handlePauseProduction and handleExtendProduction

82-95: Consider adding loading and empty states

The current implementation renders the list of buildings without handling loading or empty states.

Add appropriate states to improve user experience:

  return (
    <div className="space-y-4">
+     {isLoading && <div className="p-4 text-center">Loading buildings...</div>}
+     {!isLoading && buildings.length === 0 && (
+       <div className="p-4 text-center">No labor buildings available</div>
+     )}
-     {dummyLaborBuildings.map((building) => (
+     {!isLoading && buildings.map((building) => (
        <LaborWidget
          key={building.id}
          building={building}
-         resourceBalances={dummyResourceBalances}
+         resourceBalances={resources}
          onStartProduction={handleStartProduction}
          onPauseProduction={handlePauseProduction}
          onExtendProduction={handleExtendProduction}
        />
      ))}
    </div>
  );
client/apps/eternum-mobile/dojoConfig.ts (2)

17-17: Consider adding validation for environment variables.

The non-null assertion (!) on VITE_PUBLIC_CHAIN assumes it will always be present. If it's missing, this could lead to runtime errors.

-const manifest = getGameManifest(VITE_PUBLIC_CHAIN! as Chain);
+const chain = VITE_PUBLIC_CHAIN as Chain | undefined;
+if (!chain) {
+  throw new Error("VITE_PUBLIC_CHAIN environment variable is required");
+}
+const manifest = getGameManifest(chain);

31-33: Remove console logs before production deployment.

Console logs should generally be removed before deploying to production as they can impact performance and expose information unnecessarily.

-const config = ETERNUM_CONFIG();
-console.log("logging eternum configuration json from file");
-console.log({ config });
+const config = ETERNUM_CONFIG();
+// For debugging purposes only, remove before production
+if (process.env.NODE_ENV === 'development') {
+  console.log("logging eternum configuration json from file");
+  console.log({ config });
+}
client/apps/eternum-mobile/src/widgets/claim/ui/claim.tsx (3)

19-22: Implement the claim functionality as noted in TODO.

The claim button currently only logs to the console. You should implement the actual claiming logic.

Would you like me to suggest an implementation for the claim functionality based on your application architecture?


45-47: Add loading state to the button when processing the claim.

The button should show a loading state during the claim process to prevent multiple submissions and provide feedback to the user.

+  const [isLoading, setIsLoading] = useState(false);
+
   const handleClaim = () => {
+    setIsLoading(true);
     // TODO: Implement claim functionality
     console.log("Claiming resources for", entityType, entityId);
+    // After completion
+    // setIsLoading(false);
   };

-      <Button className="w-full" variant="default" onClick={handleClaim}>
+      <Button className="w-full" variant="default" onClick={handleClaim} disabled={isLoading}>
+        {isLoading ? "Processing..." : "Claim resources"}
       </Button>

25-48: Add accessibility enhancements to the Card component.

Improve accessibility by adding appropriate ARIA attributes and making sure the interface is keyboard-navigable.

-    <Card className="p-4 space-y-4">
+    <Card className="p-4 space-y-4" role="region" aria-labelledby="claim-title">
-      <h3 className="text-lg flex items-center gap-2">
+      <h3 id="claim-title" className="text-lg flex items-center gap-2">
client/apps/eternum-mobile/src/pages/realm/ui/realm-page.tsx (1)

21-26: Add event tracking for tab switching.

Consider adding analytics tracking for tab switching to understand user behavior.

+import { trackEvent } from "@/shared/lib/analytics"; // Assuming an analytics library

   const switchTab = (tab: string) => {
+    trackEvent('realm_tab_switch', { from: activeTab, to: tab });
     setActiveTab(tab);
   };
client/apps/eternum-mobile/src/pages/trade/ui/trade-page.tsx (1)

65-67: Improve the swap button accessibility and UX.

Add more context to the button and improve its accessibility.

-      <Button className="w-full" size="lg" disabled={!buyAmount || !sellAmount} onClick={() => setIsConfirmOpen(true)}>
-        Swap {sellAmount} {sellResource?.trait} for {buyAmount} {buyResource?.trait}
+      <Button 
+        className="w-full" 
+        size="lg" 
+        disabled={!buyAmount || !sellAmount} 
+        onClick={() => setIsConfirmOpen(true)}
+        aria-label={`Swap ${sellAmount} ${sellResource?.trait} for ${buyAmount} ${buyResource?.trait}`}
+      >
+        {!buyAmount || !sellAmount 
+          ? "Enter amount to swap" 
+          : `Swap ${sellAmount} ${sellResource?.trait} for ${buyAmount} ${buyResource?.trait}`}
       </Button>
client/apps/eternum-mobile/src/pages/chat/ui/chat-page.tsx (5)

38-50: Enhance accessibility with ARIA attributes for tabs

The tab interface would benefit from additional accessibility attributes to ensure it's usable with screen readers and keyboard navigation.

- <TabsList className="w-full grid grid-cols-3">
+ <TabsList className="w-full grid grid-cols-3" aria-label="Chat categories">
-   <TabsTrigger value="global">Global</TabsTrigger>
+   <TabsTrigger value="global" aria-controls="global-tab-content">Global</TabsTrigger>
-   <TabsTrigger value="events" className="relative">
+   <TabsTrigger value="events" className="relative" aria-controls="events-tab-content">
      Events
-     <Badge className="absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center z-10">3</Badge>
+     <Badge className="absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center z-10" aria-label="3 unread events">3</Badge>
    </TabsTrigger>
-   <TabsTrigger value="dm" className="relative">
+   <TabsTrigger value="dm" className="relative" aria-controls="dm-tab-content">
      DM
-     <Badge className="absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center z-10">5</Badge>
+     <Badge className="absolute -top-2 -right-2 h-5 w-5 flex items-center justify-center z-10" aria-label="5 unread messages">5</Badge>
    </TabsTrigger>
  </TabsList>

53-54: Add ID to Tab content for accessibility

To properly connect the TabsTrigger with its corresponding TabsContent, add an ID that matches the aria-controls attribute.

- <TabsContent value="global" className="h-full m-0">
+ <TabsContent id="global-tab-content" value="global" className="h-full m-0">

57-58: Add ID to Tab content for accessibility

As with the Global tab, add an ID to connect with aria-controls.

- <TabsContent value="events" className="h-full m-0">
+ <TabsContent id="events-tab-content" value="events" className="h-full m-0">

61-66: Add ID to Tab content for accessibility

As with the other tabs, add an ID to connect with aria-controls.

- <TabsContent value="dm" className="h-full m-0">
+ <TabsContent id="dm-tab-content" value="dm" className="h-full m-0">

24-28: Add user validation in handleSelectUser

The handleSelectUser function should validate that a valid user object is being passed.

const handleSelectUser = (user: User) => {
+  if (!user || !user.id) {
+    console.error("Invalid user selected");
+    return;
+  }
  setSelectedUser(user);
  setActiveTab("dm");
  setIsDMDrawerOpen(false);
};
client/apps/eternum-mobile/src/app/providers/theme-provider.tsx (2)

29-29: Validate theme from localStorage

Add validation to ensure the theme retrieved from localStorage is a valid Theme type before using it.

- const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme);
+ const [theme, setTheme] = useState<Theme>(() => {
+   const storedTheme = localStorage.getItem(storageKey);
+   // Check if stored theme is a valid Theme type
+   return (storedTheme === "dark" || storedTheme === "light" || storedTheme === "system") 
+     ? storedTheme as Theme 
+     : defaultTheme;
+ });

64-64: Improve error message for useTheme hook

Make the error message more descriptive by explaining the proper usage.

- if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
+ if (context === undefined) 
+   throw new Error("useTheme must be used within a ThemeProvider. Ensure the component calling useTheme is wrapped in a ThemeProvider component.");
client/apps/eternum-mobile/src/shared/ui/card.tsx (5)

5-17: Use semantic HTML elements for better accessibility

The Card component should use a more semantic HTML element like article instead of a generic div for better accessibility and SEO.

const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLElement,
+ React.HTMLAttributes<HTMLElement>
>(({ className, ...props }, ref) => (
-  <div
+  <article
    ref={ref}
    className={cn(
      "rounded-lg border bg-card text-card-foreground shadow-sm",
      className
    )}
    {...props}
-  />
+  />
))

32-44: Use appropriate heading element for CardTitle

CardTitle should use a heading element (h2, h3, etc.) instead of a div for better semantics and accessibility.

const CardTitle = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLHeadingElement,
+ React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
-  <div
+  <h3
    ref={ref}
    className={cn(
      "text-2xl font-semibold leading-none tracking-tight",
      className
    )}
    {...props}
-  />
+  />
))

47-56: Use paragraph element for CardDescription

CardDescription should use a paragraph element instead of a div for better semantics.

const CardDescription = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement>
+ HTMLParagraphElement,
+ React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
-  <div
+  <p
    ref={ref}
    className={cn("text-sm text-muted-foreground", className)}
    {...props}
-  />
+  />
))

1-79: Add JSDoc comments to components

Add JSDoc comments to each component to improve documentation and developer experience.

Here's an example for the Card component:

+/**
+ * Card is a container component that provides a flexible and extensible content container with a
+ * default border, background, and shadow.
+ */
const Card = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  // Component implementation
))

Similarly, add JSDoc comments for CardHeader, CardTitle, CardDescription, CardContent, and CardFooter.


1-79: Use consistent coding style with semicolons

The codebase appears to use semicolons in other files, but this file doesn't use them. Adopt a consistent style across all files.

- import * as React from "react"
+ import * as React from "react";

- import { cn } from "@/shared/lib/utils"
+ import { cn } from "@/shared/lib/utils";

// Continue adding semicolons throughout the file
client/apps/eternum-mobile/src/pages/chat/ui/chat-input.tsx (4)

20-25: Add input validation before sending messages

Validate that the message isn't empty before sending to prevent empty messages in the chat.

onKeyDown={(e) => {
  if (e.key === "Enter" && !e.shiftKey) {
    e.preventDefault();
-   onSend();
+   if (value.trim()) {
+     onSend();
+   }
  }
}}

29-31: Add validation and visual feedback for send button

Disable the send button when there's no content and add validation before sending.

- <Button onClick={onSend} className="shrink-0">
+ <Button 
+   onClick={() => value.trim() && onSend()} 
+   className="shrink-0"
+   disabled={!value.trim()}
+   aria-label="Send message"
+ >
    Send
  </Button>

14-16: Add aria-label to the mention button

Improve accessibility by adding an aria-label to the mention button.

- <Button variant="outline" size="icon" className="shrink-0" onClick={onMentionClick}>
+ <Button variant="outline" size="icon" className="shrink-0" onClick={onMentionClick} aria-label="Mention a user">
    @
  </Button>

27-27: Add visual hint for Enter key shortcut

Add a placeholder or title to indicate that users can press Enter to send messages.

- placeholder="Type a message..."
+ placeholder="Type a message... (Press Enter to send)"
+ title="Press Enter to send, Shift+Enter for new line"
client/apps/eternum-mobile/src/widgets/resources-card/ui/resources-card.tsx (2)

24-25: Hard-coded filter criteria limit component reusability

The hard-coded filter resource.id < 23 makes assumptions about the data structure and limits the component's reusability.

Make the filter criteria configurable via props to increase component reusability:

interface ResourcesCardProps {
  className?: string;
+  resourceFilter?: (resource: any) => boolean;
}

export const ResourcesCard = ({ 
  className,
+  resourceFilter = (resource) => resource.id < 23 
}: ResourcesCardProps) => {
  // ...
  
  const resourceAmounts = React.useMemo(() => resources
-    .filter((resource) => resource.id < 23)
+    .filter(resourceFilter)
    .map((resource) => ({
      id: resource.id,
      amount: Math.floor(Math.random() * 1000),
    })), [resourceFilter]);

16-18: Consider adding more comprehensive props interface

The current ResourcesCardProps interface is minimal, only allowing for className customization. For a more reusable component, consider adding props that would allow for customizing behavior and data.

Expand the interface to include props for custom resource data and callbacks:

interface ResourcesCardProps {
  className?: string;
+  resources?: ResourceAmount[];
+  onTradeResource?: (resourceId: number) => void;
+  title?: string;
}
client/apps/eternum-mobile/src/shared/ui/tabs.tsx (1)

6-6: Consider adding JSDoc comments for exported components

Adding JSDoc comments would improve developer experience when using these components.

+/**
+ * Root component for tabs that manages state and context for all tab components
+ */
const Tabs = TabsPrimitive.Root
client/apps/eternum-mobile/src/app/index.css (2)

1-1: Consider optimizing Google Fonts import for performance

The current import includes multiple font families with a wide range of weights and styles, which could negatively impact page load performance.

Optimize the Google Fonts import by:

  1. Only including the specific weights and styles you need
  2. Consider using font-display: swap for better perceived performance
  3. Or consider loading fonts locally using @font-face if they're used extensively

Example of a more optimized import:

@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Sedan&family=Bodoni+Moda:wght@400;700&family=Bokor&display=swap&font-display=swap");

76-106: Animation duration could benefit from a CSS variable

The animation durations are hardcoded as 0.2s. For better maintainability and consistency, consider using a CSS variable.

@layer base {
  :root {
    /* ... existing variables ... */
+   --animation-duration: 0.2s;
  }
}

@layer utilities {
  .animate-collapsible-down {
-   animation: collapsible-down 0.2s ease-out;
+   animation: collapsible-down var(--animation-duration) ease-out;
  }

  .animate-collapsible-up {
-   animation: collapsible-up 0.2s ease-out;
+   animation: collapsible-up var(--animation-duration) ease-out;
  }
}
client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (2)

11-32: Extract utility function outside component for reusability

The getDangerLevel function is a pure utility function that doesn't depend on component state or props, so it can be moved outside the component file or to a shared utilities file.

Move this function to a shared utilities file:

// In a new file: shared/lib/game-utils.ts
export const getDangerLevel = (distance: number) => {
  if (distance < 6)
    return {
      level: "high",
      color: "text-red-500",
      bgColor: "bg-red-500/10",
      icon: AlertTriangle,
    };
  // ...rest of function
};

// In this file:
+import { getDangerLevel } from "@/shared/lib/game-utils";

60-63: Implement dynamic button text based on danger level

The "View Details" button text is static regardless of the danger level. Consider making the button text more contextual based on the danger level for better user experience.

export const NearbyEnemies = ({ entityId, onView }: NearbyEnemiesProps) => {
  const { enemiesCount, distance } = React.useMemo(() => generateDummyData(), [entityId]);
-  const { color, bgColor, icon: Icon } = getDangerLevel(distance);
+  const { level, color, bgColor, icon: Icon } = getDangerLevel(distance);
  
  // ...
  
  <Button variant="secondary" size="sm" className="w-full font-semibold" onClick={onView}>
-   View Details
+   {level === "high" ? "Urgent: View Threat" : level === "medium" ? "Check Enemies" : "View Details"}
    <Eye className="w-4 h-4 ml-2" />
  </Button>
client/apps/eternum-mobile/env.ts (2)

18-22: Improve boolean transformation for clarity and type safety

The current string-to-boolean transformation checks for the exact string "true", but the default value is set as a string "false" which will be transformed to false. This works but could be clearer.

  VITE_PUBLIC_GRAPHICS_DEV: z
    .string()
    .transform((v) => v === "true")
    .optional()
-   .default("false"),
+   .default("false")
+   .pipe(z.boolean()),

This ensures the type is correctly inferred as boolean throughout your application.


27-30: Apply consistent boolean transformation pattern

Similar to the GRAPHICS_DEV flag, this boolean transformation could be improved for type safety.

  VITE_PUBLIC_CONSTRUCTION_FLAG: z
    .string()
    .transform((v) => v === "true")
-   .optional(),
+   .optional()
+   .pipe(z.boolean().optional()),
client/apps/eternum-mobile/src/widgets/upgrade-castle/ui/upgrade-castle.tsx (1)

13-14: Add validation for castle level prop

The component doesn't validate that the castleLevel is a positive number, which could lead to errors when accessing level information or image paths.

Consider adding a validation check at the beginning of the component:

export const UpgradeCastle = ({ castleLevel, onUpgrade }: UpgradeCastleProps) => {
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
+  
+  // Ensure castle level is valid
+  if (castleLevel <= 0) {
+    console.error('Invalid castle level:', castleLevel);
+    return null;
+  }
client/apps/eternum-mobile/src/widgets/header/ui/header.tsx (3)

29-29: Improve array access safety

Using at(-1)?.pathname could return undefined if the matches array is empty, which might lead to runtime errors.

- const currentPath = matches.at(-1)?.pathname;
+ const currentPath = matches.length > 0 ? matches.at(-1)?.pathname : undefined;

17-22: Consider moving navigation array outside component for optimization

Defining the navigation array inside the component will cause it to be recreated on every render, which is unnecessary since it's static.

- const navigation = [
+ const NAVIGATION = [
  { name: "Realm", href: ROUTES.REALM },
  { name: "Trade", href: ROUTES.TRADE },
  { name: "Chat", href: ROUTES.CHAT },
  { name: "Settings", href: ROUTES.SETTINGS },
];

export function Header() {

Then use NAVIGATION throughout the component instead of navigation.


44-52: Replace SVG with an icon component for consistency

Since you're already using Lucide icons elsewhere (in upgrade-castle.tsx), consider using their Menu icon here for consistency.

- <svg
-   className="h-6 w-6"
-   fill="none"
-   stroke="currentColor"
-   viewBox="0 0 24 24"
-   xmlns="http://www.w3.org/2000/svg"
- >
-   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
- </svg>
+ <Menu className="h-6 w-6" />

Don't forget to add the import: import { Menu } from "lucide-react";

client/apps/eternum-mobile/src/widgets/swap-input/ui/swap-input.tsx (2)

25-27: Consider preserving fractional values when calculating percentages.

Using Math.floor when calculating percentages will discard fractional parts, which could lead to inaccuracies especially with small balance values. Consider preserving decimal precision for better user experience.

const handlePercentageClick = (percentage: number) => {
-  onAmountChange?.(Math.floor((balance * percentage) / 100));
+  onAmountChange?.((balance * percentage) / 100);
};

88-93: Add fallback behavior for optional callbacks.

The component uses optional chaining for callbacks, but doesn't define any fallback behavior when callbacks aren't provided. Consider adding local state fallbacks for better component independence.

<NumericInput
  inputClassName="text-2xl h-16 text-center"
  value={amount}
- onChange={onAmountChange}
+ onChange={(value) => {
+   onAmountChange?.(value);
+   // Add any local fallback behavior if needed when onAmountChange is not provided
+ }}
  description={`${selectedResource?.trait}`}
/>
client/apps/eternum-mobile/src/widgets/labor-widget/model/types.ts (3)

3-6: Add JSDoc comments to enhance type documentation.

Adding JSDoc comments to interfaces improves code readability and helps developers understand the purpose and constraints of each interface.

+ /**
+  * Represents a quantity of a specific resource.
+  */
export interface ResourceAmount {
  resourceId: ResourcesIds;
  amount: number;
}

8-22: Enhance documentation for the LaborBuilding interface.

The LaborBuilding interface is complex with multiple properties. Adding more detailed JSDoc comments would help clarify the purpose and relationship between properties.

+ /**
+  * Represents a building that produces resources using labor.
+  * Handles both raw production mode and labor-enhanced production mode.
+  */
export interface LaborBuilding {
  id: string;
  resourceId: ResourcesIds;
+  /** Remaining production time in seconds */
  productionTimeLeft: number; // in seconds
+  /** Whether the building is currently producing */
  isActive: boolean;
+  /** Amount of resources produced per production cycle */
  outputAmount: number;
+  /** Number of workers assigned to this building */
  population: number;
+  /** Whether this building supports enhanced production with labor */
  hasLaborMode: boolean;
  // Current input amounts
  inputs: ResourceAmount[];
  laborInputs: ResourceAmount[];
  // Fixed consumption rates per second
  consumptionRates: ResourceAmount[];
  laborConsumptionRates: ResourceAmount[];
}

29-35: Clarify the meaning of "raw" and "labor" modes.

The "raw" and "labor" mode terminology in callback functions might not be immediately clear to other developers. Consider adding documentation or using more descriptive enum values.

+ /**
+  * Props for the LaborBuilding component.
+  */
export interface LaborBuildingProps {
  building: LaborBuilding;
  resourceBalances: ResourceBalance[];
+  /** Start production in either basic (raw) or enhanced (labor) mode */
  onStartProduction: (buildingId: string, mode: "raw" | "labor") => void;
+  /** Pause ongoing production */
  onPauseProduction: (buildingId: string) => void;
+  /** Extend existing production time in either basic (raw) or enhanced (labor) mode */
  onExtendProduction: (buildingId: string, mode: "raw" | "labor") => void;
}
client/apps/eternum-mobile/src/main.tsx (2)

17-18: Validate environment variables before usage.

The code uses environment variables directly without validation. Missing or invalid environment variables could cause runtime errors.

+// Import at the top of the file
+import { z } from "zod";
+
+// Validate environment variables
+const envSchema = z.object({
+  VITE_PUBLIC_VRF_PROVIDER_ADDRESS: z.string().min(1),
+  VITE_PUBLIC_CHAIN: z.enum(["local", "testnet", "mainnet"]),
+});
+
+// Function to validate env
+function validateEnv() {
+  try {
+    return envSchema.parse(env);
+  } catch (error) {
+    console.error("Invalid environment configuration:", error);
+    throw new Error("Application cannot start due to configuration issues");
+  }
+}

async function main() {
+  const validatedEnv = validateEnv();
+
   const setupResult = await setup(
     { ...dojoConfig },
     {
-      vrfProviderAddress: env.VITE_PUBLIC_VRF_PROVIDER_ADDRESS,
-      useBurner: env.VITE_PUBLIC_CHAIN === "local",
+      vrfProviderAddress: validatedEnv.VITE_PUBLIC_VRF_PROVIDER_ADDRESS,
+      useBurner: validatedEnv.VITE_PUBLIC_CHAIN === "local",
     },
     // ...rest of the code

34-44: Add loading state for better user experience.

The application doesn't display any loading state during the async setup process, which might lead to a poor user experience, especially if setup takes time.

+// Add this at the top of the file
+function renderLoadingState() {
+  document.getElementById("root")!.innerHTML = `
+    <div style="display: flex; justify-content: center; align-items: center; height: 100vh;">
+      <div style="text-align: center;">
+        <div style="border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 2s linear infinite; margin: 0 auto;"></div>
+        <p style="margin-top: 16px;">Loading Eternum Mobile...</p>
+      </div>
+    </div>
+    <style>
+      @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
+    </style>
+  `;
+}

async function main() {
+  // Display loading state before async operations
+  renderLoadingState();
+
  try {
    const setupResult = await setup(
      // ... setup code ...
    );
    
    const eternumConfig = ETERNUM_CONFIG();
    configManager.setDojo(setupResult.components, eternumConfig);

    // Replace loading state with actual app
    createRoot(document.getElementById("root")!).render(
      <StrictMode>
        <ThemeProvider>
          <StarknetProvider>
            <DojoProvider value={setupResult}>
              <App />
            </DojoProvider>
          </StarknetProvider>
        </ThemeProvider>
      </StrictMode>,
    );
  } catch (error) {
    // ... error handling ...
  }
}
client/apps/eternum-mobile/src/pages/chat/ui/tabs/global-chat-tab.tsx (3)

45-45: Use a more robust ID generation method.

Using Date.now().toString() for message IDs could lead to duplicate IDs if multiple messages are sent in the same millisecond. Consider using a UUID library for more reliable unique IDs.

+import { v4 as uuidv4 } from 'uuid';

// Later in the handleSend function
const newMessage: Message = {
-  id: Date.now().toString(),
+  id: uuidv4(),
  sender: "You",
  // rest of the message properties
};

58-73: Implement virtualization for improved performance with large message lists.

As the chat grows, rendering all messages could impact performance. Consider implementing virtualization to only render messages that are visible in the viewport.

+import { useVirtualizer } from '@tanstack/react-virtual';

// Inside component
+const parentRef = useRef<HTMLDivElement>(null);
+
+const rowVirtualizer = useVirtualizer({
+  count: messages.length,
+  getScrollElement: () => parentRef.current,
+  estimateSize: () => 80, // Estimate height of each message
+  overscan: 5,
+});

return (
  <div className="flex flex-col h-full">
-    <ScrollArea className="flex-1 p-4 pb-16">
-      <div className="space-y-4">
-        {messages.map((message) => (
-          <div key={message.id} className="flex flex-col">
-            <div className="flex items-center gap-2">
-              <span className="font-semibold">{message.sender}</span>
-              <span className="text-sm text-muted-foreground">{message.timestamp}</span>
-            </div>
-            <p className="text-sm">{message.message}</p>
-          </div>
-        ))}
-        <div ref={scrollRef} />
-      </div>
-    </ScrollArea>
+    <ScrollArea className="flex-1 p-4 pb-16" ref={parentRef}>
+      <div 
+        style={{
+          height: `${rowVirtualizer.getTotalSize()}px`,
+          width: '100%',
+          position: 'relative',
+        }}
+      >
+        {rowVirtualizer.getVirtualItems().map((virtualItem) => {
+          const message = messages[virtualItem.index];
+          return (
+            <div 
+              key={message.id}
+              className="flex flex-col absolute top-0 left-0 w-full"
+              style={{
+                height: `${virtualItem.size}px`,
+                transform: `translateY(${virtualItem.start}px)`,
+              }}
+            >
+              <div className="flex items-center gap-2">
+                <span className="font-semibold">{message.sender}</span>
+                <span className="text-sm text-muted-foreground">{message.timestamp}</span>
+              </div>
+              <p className="text-sm">{message.message}</p>
+            </div>
+          );
+        })}
+        <div ref={scrollRef} />
+      </div>
+    </ScrollArea>

46-47: Dynamically determine sender name from user context.

The sender name is hardcoded as "You", which won't be appropriate if the application needs to support multiple users or user identification. Consider retrieving the sender name from user context or authentication state.

+// Add at the top level imports
+import { useUserContext } from "@/shared/hooks/context/user-context";

// Inside component
+const { currentUser } = useUserContext();

const newMessage: Message = {
  id: Date.now().toString(),
-  sender: "You",
+  sender: currentUser?.displayName || "You",
  timestamp: new Date().toLocaleTimeString([], {
    hour: "2-digit",
    minute: "2-digit",
  }),
client/apps/eternum-mobile/src/pages/realm/ui/tabs/overview-tab.tsx (4)

90-92: Remove console.log statements before production.

Console logs should be removed from production code as they can impact performance and potentially expose sensitive information.

const handleStartProduction = useCallback((buildingId: string, mode: "raw" | "labor") => {
-  console.log("Start production", { buildingId, mode });
+  // TODO: Implement production start logic
}, []);

94-96: Remove console.log statements before production.

Console logs should be removed from production code.

const handlePauseProduction = useCallback((buildingId: string) => {
-  console.log("Pause production", { buildingId });
+  // TODO: Implement production pause logic
}, []);

98-100: Remove console.log statements before production.

Console logs should be removed from production code.

const handleExtendProduction = useCallback((buildingId: string, mode: "raw" | "labor") => {
-  console.log("Extend production", { buildingId, mode });
+  // TODO: Implement production extension logic
}, []);

11-72: Consider moving dummy data outside the component file.

The dummy data takes up a significant portion of the file. Consider moving it to a separate file to improve readability and maintainability of this component.

Create a new file like dummy-data.ts:

import { ResourcesIds } from "@bibliothecadao/eternum";

export const dummyLaborBuildings = [
  // ... existing data
];

export const dummyResourceBalances = [
  // ... existing data
];

Then import it in this file:

import { ArrivedDonkeys } from "@/widgets/arrived-donkeys";
import { LaborWidget } from "@/widgets/labor-widget";
import { NearbyEnemies } from "@/widgets/nearby-enemies";
import { ResourcesCard } from "@/widgets/resources-card";
import { UpgradeCastle } from "@/widgets/upgrade-castle";
-import { ResourcesIds } from "@bibliothecadao/eternum";
import { useCallback } from "react";
import { useRealmTabs } from "../realm-page";
+import { dummyLaborBuildings, dummyResourceBalances } from "@/shared/lib/dummy-data";
client/apps/eternum-mobile/src/shared/ui/progress-circle.tsx (2)

11-17: Refactor variable calculations into a lookup object for better readability.

The strokeWidth and radius calculations can be refactored into lookup objects similar to the sizeClasses for better readability and maintainability.

export const ProgressCircle = ({ progress, size = "md", children, className }: ProgressCircleProps) => {
  const normalizedProgress = Math.min(100, Math.max(0, progress));
-  const strokeWidth = size === "sm" ? 2 : size === "md" ? 3 : 4;
-  const radius = size === "sm" ? 12 : size === "md" ? 18 : 24;
+  const strokeWidths = {
+    sm: 2,
+    md: 3,
+    lg: 4,
+  };
+  const radiuses = {
+    sm: 12,
+    md: 18,
+    lg: 24,
+  };
+  const strokeWidth = strokeWidths[size];
+  const radius = radiuses[size];
  const circumference = 2 * Math.PI * radius;
  const offset = circumference - (normalizedProgress / 100) * circumference;

30-49: Add a11y attributes to SVG element.

For better accessibility, consider adding ARIA attributes to the SVG element.

return (
  <div className={cn("relative inline-flex items-center justify-center", sizeClasses[size], className)}>
-    <svg className="w-full h-full -rotate-90">
+    <svg 
+      className="w-full h-full -rotate-90" 
+      role="img" 
+      aria-valuemin="0" 
+      aria-valuemax="100" 
+      aria-valuenow={normalizedProgress}
+      aria-label={`Progress: ${normalizedProgress}%`}
+    >
      {/* Background circle */}
      <circle cx="50%" cy="50%" r={radius} className="fill-none stroke-muted" strokeWidth={strokeWidth} />
      {/* Progress circle */}
      <circle
        cx="50%"
        cy="50%"
        r={radius}
        className="fill-none stroke-primary transition-all duration-300 ease-in-out"
        strokeWidth={strokeWidth}
        strokeDasharray={circumference}
        strokeDashoffset={offset}
        strokeLinecap="round"
      />
    </svg>
    <div className={cn("absolute inset-0 flex items-center justify-center", textSizeClasses[size])}>{children}</div>
  </div>
);
client/apps/eternum-mobile/src/widgets/realm-info-header/ui/realm-info-header.tsx (2)

35-37: Specify locale in toLocaleString for consistent formatting.

Using toLocaleString without a specified locale can lead to inconsistent number formatting across different user environments.

<Badge variant="secondary" className="font-mono">
-  {balance.toLocaleString()} $LORDS
+  {balance.toLocaleString('en-US')} $LORDS
</Badge>

57-57: Simplify the RealmLevel lookup for better readability.

The double indexing using RealmLevelNames[RealmLevels[realmLevel]] is complex and can be error-prone. Consider simplifying this lookup.

<span className="text-3xl font-normal text-muted-foreground">
-  {RealmLevelNames[RealmLevels[realmLevel]]}
+  {RealmLevelNames[realmLevel] || `Level ${realmLevel}`}
</span>

If the above simplification isn't possible due to the structure of RealmLevelNames and RealmLevels, consider creating a helper function to make this code more readable.

client/apps/eternum-mobile/src/shared/ui/resource-amount.tsx (2)

34-34: Add error handling for resource lookup.

There's no error handling if findResourceById fails or returns undefined. Although the component conditionally renders the resource name only if the resource exists, consider adding more robust error handling.

-  const resource = findResourceById(resourceId);
+  const resource = findResourceById(resourceId) || null;
+  
+  // Optional: Log an error if resource is not found (for debugging)
+  if (!resource && process.env.NODE_ENV === 'development') {
+    console.warn(`Resource with ID ${resourceId} not found`);
+  }

37-41: Consider adding a tooltip for truncated resource names.

In small containers or with long resource names, the text might get truncated. Consider adding a tooltip to show the full resource name.

<div className={cn("flex items-center", direction === "vertical" && "flex-col", sizeClasses[size], className)}>
  <ResourceIcon resourceId={resourceId} size={iconSizes[size]} />
  <span className="font-medium">{amount}</span>
-  {showName && resource && <span className="text-muted-foreground capitalize">{resource.trait}</span>}
+  {showName && resource && (
+    <span 
+      className="text-muted-foreground capitalize truncate" 
+      title={resource.trait}
+    >
+      {resource.trait}
+    </span>
+  )}
</div>
client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (2)

18-22: Consider using a more robust approach for resource filtering

The current approach to filtering and mapping resource addresses works but could be more readable.

-const LORDS = resourceAddresses["LORDS"][1].toString();
-const otherResources = Object.entries(resourceAddresses)
-  .filter(([key]) => key !== "LORDS")
-  .map(([_, [__, address]]) => address)
-  .toString();
+const LORDS = resourceAddresses["LORDS"][1].toString();
+const otherResources = Object.values(resourceAddresses)
+  .filter((_, index, arr) => resourceAddresses["LORDS"] !== arr[index])
+  .map(([_, address]) => address.toString());

57-73: Consider extracting configuration to a separate file

The StarknetProvider component contains a lot of configuration. Consider extracting it to a separate configuration file to improve maintainability.

client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-drawer.tsx (1)

32-37: Optimize reduce operations to avoid O(n²) complexity

Using spread syntax in reduce operations can lead to O(n²) time complexity for large arrays.

-  const [inputAmounts, setInputAmounts] = useState<ResourceAmounts>(
-    building.inputs.reduce((acc, input) => ({ ...acc, [input.resourceId]: input.amount }), {}),
-  );
-  const [laborInputAmounts, setLaborInputAmounts] = useState<ResourceAmounts>(
-    building.laborInputs.reduce((acc, input) => ({ ...acc, [input.resourceId]: input.amount }), {}),
-  );
+  const [inputAmounts, setInputAmounts] = useState<ResourceAmounts>(() => {
+    const initialAmounts: ResourceAmounts = {};
+    building.inputs.forEach(input => {
+      initialAmounts[input.resourceId] = input.amount;
+    });
+    return initialAmounts;
+  });
+  const [laborInputAmounts, setLaborInputAmounts] = useState<ResourceAmounts>(() => {
+    const initialAmounts: ResourceAmounts = {};
+    building.laborInputs.forEach(input => {
+      initialAmounts[input.resourceId] = input.amount;
+    });
+    return initialAmounts;
+  });
🧰 Tools
🪛 Biome (1.9.4)

[error] 33-33: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 36-36: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

client/apps/eternum-mobile/src/shared/ui/numeric-input.tsx (2)

54-66: Consider adding min, max, and step constraints

The numeric input doesn't have constraints on minimum or maximum values, which could lead to invalid inputs.

 <Input
   type="number"
   value={value}
   readOnly
   inputMode="none"
+  min={0}  /* Or appropriate min value */
+  max={9999}  /* Or appropriate max value */
+  step={1}  /* Or appropriate step value */
   onFocus={(e) => {
     e.preventDefault();
     e.target.blur();
     setShowKeyboard(true);
   }}
   onClick={() => setShowKeyboard(true)}
   className={cn(inputClassName)}
   {...props}
 />

68-87: Add a way to reset or clear the input value

There's currently no way to clear the input or reset it to zero except by repeatedly pressing backspace.

Consider adding a "Clear" or "Reset" button in the numeric keyboard component or in the drawer footer alongside the Confirm button.

client/apps/eternum-mobile/src/widgets/upgrade-castle/ui/upgrade-drawer.tsx (2)

17-22: Consider extracting mock data to a separate file

The hardcoded mock upgrade cost data should be extracted to a dedicated mock data file or service. This would improve code organization and make it easier to replace with real API calls later.

- // Mock upgrade cost data - in real app this would come from the backend
- const MOCK_UPGRADE_COST = [
-   { id: ResourcesIds.Stone, amount: 1000000 },
-   { id: ResourcesIds.Wood, amount: 1000000 },
-   { id: ResourcesIds.Gold, amount: 1000000 },
-   { id: ResourcesIds.Mithral, amount: 1000000 },
- ];

Move this to a dedicated mock service file that could later be replaced with real API calls.


29-36: Consider adding a cleanup function to the useEffect

The setTimeout creates a potential memory leak if the component unmounts before the timeout completes. Use a cleanup function to clear the timeout.

useEffect(() => {
  if (!isOpen) {
-   setTimeout(() => {
+   const timer = setTimeout(() => {
      setStep("cost");
      setError("");
    }, 500);
+   return () => clearTimeout(timer);
  }
}, [isOpen]);
client/apps/eternum-mobile/src/pages/chat/ui/dm-chat.tsx (3)

40-48: Use a more robust ID generation method

Using Date.now().toString() for message IDs could potentially lead to collisions if multiple messages are sent in the same millisecond. Consider using a more robust ID generation method like UUID.

+ import { v4 as uuidv4 } from 'uuid';

const newMessage: Message = {
-  id: Date.now().toString(),
+  id: uuidv4(),
  sender: "You",
  timestamp: new Date().toLocaleTimeString([], {
    hour: "2-digit",
    minute: "2-digit",
  }),
  message: inputValue.trim(),
};

33-35: Consider performance implications of smooth scrolling

The smooth scrolling behavior may cause performance issues on lower-end devices when there are many messages. Consider making this configurable or using a more performant approach.

useEffect(() => {
-  scrollRef.current?.scrollIntoView({ behavior: "smooth" });
+  // Option 1: Make behavior configurable
+  scrollRef.current?.scrollIntoView({ behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' });
+  
+  // Option 2: Use a more direct approach for better performance
+  // if (scrollRef.current) {
+  //   const parent = scrollRef.current.parentElement;
+  //   if (parent) {
+  //     parent.scrollTop = parent.scrollHeight;
+  //   }
+  // }
}, [messages]);

37-52: Add debounce to the handleSend function

The current implementation allows rapid sending of messages without any rate limiting. Consider adding a debounce mechanism to prevent accidental spam.

+ const [isSending, setIsSending] = useState(false);

const handleSend = () => {
-  if (!inputValue.trim()) return;
+  if (!inputValue.trim() || isSending) return;
+  
+  setIsSending(true);

  const newMessage: Message = {
    id: Date.now().toString(),
    sender: "You",
    timestamp: new Date().toLocaleTimeString([], {
      hour: "2-digit",
      minute: "2-digit",
    }),
    message: inputValue.trim(),
  };

  setMessages((prev) => [...prev, newMessage]);
  setInputValue("");
+  
+  // Reset sending state after a short delay
+  setTimeout(() => setIsSending(false), 500);
};
client/apps/eternum-mobile/src/pages/trade/ui/swap-confirm-drawer.tsx (2)

34-41: Consider adding a cleanup function to the useEffect

Similar to the UpgradeDrawer component, the setTimeout creates a potential memory leak. Add a cleanup function to clear the timeout.

useEffect(() => {
  if (!isOpen) {
-   setTimeout(() => {
+   const timer = setTimeout(() => {
      setState("confirm");
      setError("");
    }, 500);
+   return () => clearTimeout(timer);
  }
}, [isOpen]);

97-107: Extract resource weight information to a utility or config file

Resource weight information should be stored in a central location rather than hardcoded in the component to ensure consistency across the application.

+ // In a separate file like resourceConstants.ts
+ export const RESOURCE_WEIGHTS = {
+   LORDS: 0,
+   FOOD: 0.1,
+   OTHER: 1
+ };

Then import and use this constant in your component.

client/apps/eternum-mobile/src/pages/chat/ui/tabs/dm-tab.tsx (4)

23-45: Move dummy data to a dedicated mock service

The hardcoded dummy users should be moved to a mock service file, making it easier to replace with actual API calls in the future.

- const dummyUsers: User[] = [
-   {
-     id: "1",
-     name: "Alice",
-     guild: "Knights of the Round Table",
-     online: true,
-     unreadCount: 3,
-   },
-   // ...other users
- ];

+ // Import from a mock service
+ import { getDummyUsers } from "@/shared/api/mocks/chat";
+ // ...
+ const [users, setUsers] = useState<User[]>(getDummyUsers());

52-60: Improve selected user handling logic

The current implementation doesn't update an existing user's properties when a selectedUser with the same ID changes. Consider enhancing the logic to handle this case.

useEffect(() => {
  if (selectedUser) {
    const existingUser = users.find((u) => u.id === selectedUser.id);
    if (!existingUser) {
      setUsers((prev) => [...prev, { ...selectedUser, unreadCount: 0 }]);
+   } else if (JSON.stringify(existingUser) !== JSON.stringify(selectedUser)) {
+     // Update existing user if properties have changed
+     setUsers((prev) => 
+       prev.map((u) => u.id === selectedUser.id ? { ...selectedUser, unreadCount: existingUser.unreadCount } : u)
+     );
    }
    setActiveUser(selectedUser);
  }
}, [selectedUser, users]);

66-73: Consider using a functional update for setActiveUser

When setting activeUser to null based on a condition that depends on the current activeUser, it's safer to use the functional update pattern to avoid potential race conditions.

const handleCloseDM = (e: React.MouseEvent, userId: string) => {
  e.stopPropagation();
  setUsers((prev) => prev.filter((user) => user.id !== userId));
- if (activeUser?.id === userId) {
-   setActiveUser(null);
-   onClearSelectedUser();
- }
+ setActiveUser((current) => {
+   if (current?.id === userId) {
+     onClearSelectedUser();
+     return null;
+   }
+   return current;
+ });
};

117-119: Consider more accessible unread count indicator

The unread count badge might benefit from additional accessibility features to ensure users with disabilities can perceive the notification.

{user.unreadCount ? (
- <Badge variant="secondary">{user.unreadCount}</Badge>
+ <Badge variant="secondary" aria-label={`${user.unreadCount} unread messages`}>
+   {user.unreadCount}
+ </Badge>
) : null}
client/apps/eternum-mobile/src/pages/chat/ui/dm-drawer.tsx (3)

8-14: Consider extracting optional fields for better clarity.

Currently, avatar is an optional field, while online is required. If future enhancements add or modify user properties, you might benefit from splitting out separate interfaces or marking additional fields as optional. This keeps the user schema more flexible and less prone to accidental breakage when new properties are introduced.


22-42: Replace dummy data with real data or external configuration.

Using a static array of dummy users is fine for local testing, but for production, consider fetching user data from an API or context store. This ensures real-time updates and consistent data across the application.


48-52: Memoize the filtered user array.

While filtering over a small set of users is acceptable, in larger scenarios, filtering on every render may cause performance bottlenecks. Using a memoized callback or a useMemo for filteredUsers could improve performance if the user list grows.

  const [searchQuery, setSearchQuery] = useState("");
  const [users] = useState<User[]>(dummyUsers);

- const filteredUsers = users.filter(
-   (user) =>
-     user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
-     user.guild.toLowerCase().includes(searchQuery.toLowerCase()),
- );

+ const filteredUsers = useMemo(
+   () =>
+     users.filter(
+       (user) =>
+         user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+         user.guild.toLowerCase().includes(searchQuery.toLowerCase()),
+     ),
+   [searchQuery, users]
+ );
client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (2)

108-116: Handle empty or multiple connectors gracefully.

Using connectors[0] works when only one connector is guaranteed, but consider edge cases:

  • There might be no connectors configured.
  • There might be multiple connectors to choose from.

Implementing a fallback or a user selection mechanism will make the code more robust.

 const connectWallet = async () => {
   try {
     console.log("Attempting to connect wallet...");
-    await connect({ connector: connectors[0] });
+    if (!connectors.length) {
+      console.error("No wallet connectors available.");
+      return;
+    }
+    // Optionally prompt user to pick a connector if multiple are available
+    await connect({ connector: connectors[0] });
     console.log("Wallet connected successfully.");
   } catch (error) {
     console.error("Failed to connect wallet:", error);

187-192: Remove the unnecessary Fragment.

A fragment wrapping the button alone is unnecessary and flagged by static analysis. Eliminate it to simplify the JSX structure.

-            <>
               <Button onClick={connectWallet} className="!bg-[#FCB843] !text-black border-none hover:!bg-[#FCB843]/80">
                 Log In
               </Button>
-            </>
🧰 Tools
🪛 Biome (1.9.4)

[error] 187-191: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

client/apps/eternum-mobile/src/shared/ui/select.tsx (2)

13-32: Well-structured SelectTrigger component with proper TypeScript typing.

The component correctly uses forwardRef for ref forwarding and has proper TypeScript typing with ElementRef and ComponentPropsWithoutRef. Good job setting the displayName for improved debugging experience.

Consider adding JSDoc comments to document the component's purpose and props for better developer experience.

+/**
+ * The trigger component for the Select dropdown.
+ * Displays the selected value and opens the dropdown when clicked.
+ */
 const SelectTrigger = React.forwardRef<
   React.ElementRef<typeof SelectPrimitive.Trigger>,
   React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
 >(({ className, children, ...props }, ref) => (

1-158: Overall excellent implementation of a composable Select component.

This file demonstrates a high-quality implementation of a composable Select component based on Radix UI primitives. The code is well-structured, properly typed, and follows React best practices including:

  1. Proper ref forwarding
  2. Consistent styling approach with the cn utility
  3. Clear component composition
  4. Setting display names for better debugging
  5. TypeScript type safety throughout

One suggestion for further improvement would be to add comprehensive test coverage for these components, especially testing keyboard navigation and screen reader compatibility for accessibility.

Consider creating unit tests for these components to ensure they work as expected, particularly focusing on:

  • Keyboard navigation
  • Screen reader compatibility
  • Proper rendering of selected items
  • Opening/closing behavior
client/apps/eternum-mobile/src/shared/ui/mode-toggle.tsx (2)

7-26: Consider adding visual indication of the active theme.

The component correctly implements theme switching functionality with good accessibility (sr-only text). However, there's no visual indication in the dropdown menu of which theme is currently active.

Consider adding visual feedback to show the currently selected theme:

 <DropdownMenuContent align="end">
-  <DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
-  <DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
-  <DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
+  <DropdownMenuItem onClick={() => setTheme("light")}>
+    Light {theme === "light" && <CheckIcon className="ml-2 h-4 w-4" />}
+  </DropdownMenuItem>
+  <DropdownMenuItem onClick={() => setTheme("dark")}>
+    Dark {theme === "dark" && <CheckIcon className="ml-2 h-4 w-4" />}
+  </DropdownMenuItem>
+  <DropdownMenuItem onClick={() => setTheme("system")}>
+    System {theme === "system" && <CheckIcon className="ml-2 h-4 w-4" />}
+  </DropdownMenuItem>
 </DropdownMenuContent>

You'll need to add the import for CheckIcon:

-import { Moon, Sun } from "lucide-react";
+import { Check as CheckIcon, Moon, Sun } from "lucide-react";

And update the useTheme hook usage:

-const { setTheme } = useTheme();
+const { theme, setTheme } = useTheme();

8-8: Add error handling for context usage.

The component assumes the ThemeProvider context is available. Consider adding error handling for when the hook is used outside of a ThemeProvider.

-  const { setTheme } = useTheme();
+  const theme = useTheme();
+  
+  if (!theme) {
+    // Either return null, a fallback UI, or throw a descriptive error
+    console.error("ModeToggle must be used within a ThemeProvider");
+    return null;
+  }
+  
+  const { setTheme } = theme;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f571ac and a1326b2.

⛔ Files ignored due to path filters (48)
  • client/apps/eternum-mobile/public/game-apple-touch-icon-180x180.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/game-favicon.ico is excluded by !**/*.ico
  • client/apps/eternum-mobile/public/game-maskable-icon-512x512.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/game-pwa-192x192.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/game-pwa-512x512.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/game-pwa-64x64.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/castles/castle-0.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/01.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/02.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/03.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/04.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/05.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/06.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/covers/07.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/eternum-logo-black.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/eternum-logo-white.svg is excluded by !**/*.svg
  • client/apps/eternum-mobile/public/images/resources/1.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/10.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/11.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/12.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/13.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/14.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/15.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/16.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/17.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/18.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/19.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/2.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/20.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/21.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/22.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/24.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/25.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/26.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/27.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/28.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/29.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/3.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/30.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/31.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/4.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/5.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/6.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/7.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/8.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/images/resources/9.png is excluded by !**/*.png
  • client/apps/eternum-mobile/public/og-image.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (99)
  • .gitignore (1 hunks)
  • client/apps/eternum-mobile/.gitignore (1 hunks)
  • client/apps/eternum-mobile/README.md (1 hunks)
  • client/apps/eternum-mobile/components.json (1 hunks)
  • client/apps/eternum-mobile/dojoConfig.ts (1 hunks)
  • client/apps/eternum-mobile/env.ts (1 hunks)
  • client/apps/eternum-mobile/eslint.config.js (1 hunks)
  • client/apps/eternum-mobile/index.html (1 hunks)
  • client/apps/eternum-mobile/package.json (1 hunks)
  • client/apps/eternum-mobile/postcss.config.js (1 hunks)
  • client/apps/eternum-mobile/src/app/app.tsx (1 hunks)
  • client/apps/eternum-mobile/src/app/config/router.tsx (1 hunks)
  • client/apps/eternum-mobile/src/app/index.css (1 hunks)
  • client/apps/eternum-mobile/src/app/providers/theme-provider.tsx (1 hunks)
  • client/apps/eternum-mobile/src/app/ui/layout.tsx (1 hunks)
  • client/apps/eternum-mobile/src/main.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/chat-input.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/chat-page.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/dm-chat.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/dm-drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/tabs/dm-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/tabs/events-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/chat/ui/tabs/global-chat-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/login/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/login/ui/login-page.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/realm-page.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/claim-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/military-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/overview-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/settings/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/settings/ui/settings-page.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/trade/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/trade/ui/swap-confirm-drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/trade/ui/trade-page.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/config/config.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/consts/routes.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/policies.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/signing-policy.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/use-auth.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/lib/addresses.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/lib/utils.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/store/use-account-store.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/store/use-address-store.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/types.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/badge.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/button.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/card.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/collapsible.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/input.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/mode-toggle.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/numeric-input.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/numeric-keyboard.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/progress-circle.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/resource-amount.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/scroll-area.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/select.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/sheet.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/tabs.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/tooltip.tsx (1 hunks)
  • client/apps/eternum-mobile/src/vite-env.d.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/arrived-donkeys/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/arrived-donkeys/ui/arrived-donkeys.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/claim/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/claim/ui/claim.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/footer/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/footer/ui/footer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/header/index.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/header/ui/header.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/labor-widget/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/labor-widget/model/types.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-widget.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/nearby-enemies/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/realm-info-header/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/realm-info-header/ui/realm-info-header.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/resources-card/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/resources-card/ui/resources-card.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/swap-input/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/swap-input/ui/swap-input.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/upgrade-castle/index.ts (1 hunks)
  • client/apps/eternum-mobile/src/widgets/upgrade-castle/ui/upgrade-castle.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/upgrade-castle/ui/upgrade-drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/tailwind.config.js (1 hunks)
  • client/apps/eternum-mobile/tsconfig.app.json (1 hunks)
  • client/apps/eternum-mobile/tsconfig.json (1 hunks)
  • client/apps/eternum-mobile/tsconfig.node.json (1 hunks)
  • client/apps/eternum-mobile/vite.config.ts (1 hunks)
  • packages/core/src/constants/realmLevels.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
client/apps/eternum-mobile/tsconfig.node.json

[error] 9-9: JSON standard does not allow comments.

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: JSON standard does not allow comments.

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

client/apps/eternum-mobile/tsconfig.app.json

[error] 10-10: JSON standard does not allow comments.

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: JSON standard does not allow comments.

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 31-31: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-33: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-drawer.tsx

[error] 33-33: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 36-36: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx

[error] 187-191: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🪛 markdownlint-cli2 (0.17.2)
client/apps/eternum-mobile/README.md

24-24: Bare URL used
null

(MD034, no-bare-urls)


31-31: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


54-54: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


85-85: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🔇 Additional comments (91)
client/apps/eternum-mobile/src/vite-env.d.ts (1)

1-1: Vite Client Type Reference Added

The new reference to Vite's client types enhances TypeScript support, improving type checking and autocompletion for Vite-related features in the mobile app.

client/apps/eternum-mobile/src/pages/settings/ui/settings-page.tsx (1)

1-28: Clean, well-structured settings page component.

The implementation follows good React practices with a clear component structure, semantic HTML, and appropriate styling classes. The placeholder messages for upcoming features are user-friendly.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/military-tab.tsx (1)

1-16: Well-implemented tab component with clean structure.

The component is properly designed with a clear interface that allows for custom styling through the className prop. The placeholder comment clearly indicates where future content will be added.

client/apps/eternum-mobile/src/shared/ui/tooltip.tsx (1)

1-28: Well-implemented tooltip component with proper structure.

The component correctly leverages Radix UI primitives and uses React's forwardRef. The styling and animation classes are comprehensive.

client/apps/eternum-mobile/src/shared/ui/badge.tsx (1)

1-36: Well-structured Badge component with good variant system!

This Badge component implementation uses class-variance-authority (cva) to create a flexible component with different visual styles. The approach allows for easy extension while maintaining consistent styling patterns.

The component properly handles className and variant props, with appropriate defaults. The use of the cn utility function for class name composition is a good practice.

client/apps/eternum-mobile/src/shared/ui/scroll-area.tsx (1)

1-46: Well-implemented ScrollArea with proper ref forwarding!

The implementation of ScrollArea and ScrollBar components using Radix UI primitives follows best practices:

  • Uses React.forwardRef for both components
  • Properly sets displayName for both components
  • Handles orientation variants for ScrollBar
  • Maintains appropriate styling and accessibility
client/apps/eternum-mobile/src/shared/ui/drawer.tsx (1)

1-116: Well-structured Drawer component suite with responsive design considerations!

The Drawer implementation using the Vaul library creates a comprehensive set of components that work together seamlessly:

  • Proper component composition pattern
  • Good use of forwardRef where appropriate
  • Responsive styling (e.g., "text-center sm:text-left" in DrawerHeader)
  • Visual affordances like the drag indicator at line 49

The implementation will work well for mobile interfaces where bottom drawers are a common pattern.

client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (1)

1-198: Well-implemented dropdown menu system with good component composition!

Overall, this dropdown menu implementation provides a comprehensive set of components that follow best practices:

  • Good use of Radix UI primitives
  • Proper ref forwarding and displayName handling
  • Consistent styling patterns
  • Well-structured component hierarchy

The animation properties on the dropdown content (lines 66-67) provide a polished user experience with smooth transitions.

client/apps/eternum-mobile/src/widgets/claim/index.ts (1)

1-1: Export of Claim Component

The export statement is succinct and correctly exposes the Claim component from its UI module, which supports modular and reusable component management.

client/apps/eternum-mobile/src/widgets/footer/index.tsx (1)

1-1: Export of Footer Component

The export statement properly re-exports the Footer component, ensuring that it can be easily imported elsewhere in the application, which supports a clean module structure.

client/apps/eternum-mobile/src/widgets/header/index.tsx (1)

1-1: Export of Header Component

This export statement effectively exposes the Header component from its UI module. It is clear and aligns well with the modular design practices used throughout this mobile app.

client/apps/eternum-mobile/src/widgets/nearby-enemies/index.ts (1)

1-1: Export of NearbyEnemies Component

The export is straightforward and meets the expectation for maintaining a well-organized component structure. It facilitates the reuse of the NearbyEnemies component in other areas of the application.

client/apps/eternum-mobile/src/widgets/arrived-donkeys/index.ts (1)

1-1: Export of ArrivedDonkeys Component

The code correctly re-exports the ArrivedDonkeys component from its UI module, ensuring that it can be accessed easily by other parts of the application. The design follows a consistent export pattern with the rest of the modules.

client/apps/eternum-mobile/src/widgets/swap-input/index.ts (1)

1-2: Appropriate Export for SwapInput Component
The export statement cleanly re-exports the SwapInput component from its UI module. This centralizes the component’s import path for easier consumption elsewhere in the app.

client/apps/eternum-mobile/src/pages/settings/index.tsx (1)

1-2: Correct Export for SettingsPage
The file correctly exports the SettingsPage component from the ./ui/settings-page module, making it accessible through a centralized path.

client/apps/eternum-mobile/src/pages/login/index.tsx (1)

1-2: Proper Export for LoginPage
The export statement here properly re-exports the LoginPage component from the ./ui/login-page module. This promotes modularity and simplifies imports in other parts of the application.

client/apps/eternum-mobile/src/widgets/resources-card/index.ts (1)

1-2: Valid Export for ResourcesCard Component
This file provides a straightforward export of the ResourcesCard component from the UI module, adhering to the centralized export design pattern used throughout the project.

client/apps/eternum-mobile/src/widgets/upgrade-castle/index.ts (1)

1-2: Clear Export for UpgradeCastle Component
The export solidly exposes the UpgradeCastle component from its UI folder, ensuring it can be conveniently imported in other parts of the codebase.

client/apps/eternum-mobile/src/pages/trade/index.tsx (1)

1-1: Clean barrel export implementation.

This export follows the barrel pattern, which is a good practice for managing module exports and simplifying imports in other parts of the application.

client/apps/eternum-mobile/src/pages/chat/index.tsx (1)

1-1: Well-structured barrel export.

This export follows the same pattern as the trade page, maintaining consistency across the codebase. The barrel pattern makes importing the ChatPage component easier from other parts of the application.

client/apps/eternum-mobile/src/widgets/realm-info-header/index.ts (1)

1-1: Consistent component export structure.

The export follows the established barrel pattern used throughout the project, which is good for maintainability and code organization.

client/apps/eternum-mobile/src/shared/types.ts (1)

1-3: Strong typing implementation for routes.

This is a good TypeScript pattern that creates a type-safe union from the ROUTES constant. This approach ensures that any usage of AppRoute will be restricted to valid route values and will automatically update if new routes are added to the ROUTES object.

client/apps/eternum-mobile/src/pages/realm/index.tsx (1)

1-1: Clean export statement.

The barrel file pattern used here follows React best practices for organizing and exporting components, making imports cleaner in parent modules.

client/apps/eternum-mobile/postcss.config.js (1)

1-6: Standard PostCSS configuration.

The configuration correctly sets up Tailwind CSS and Autoprefixer plugins with the recommended empty configuration objects.

client/apps/eternum-mobile/src/shared/ui/collapsible.tsx (1)

1-9: Clean implementation of Radix UI Collapsible components

The code cleanly re-exports Radix UI Collapsible components, making them available for consistent use throughout the application. This approach allows for potential future customization without changing import paths across the codebase.

.gitignore (1)

6-9: Appropriate Git ignore patterns added

The added Git ignore patterns are appropriate for excluding system files, temporary directories, and development-specific resources from version control.

client/apps/eternum-mobile/package.json (1)

12-33: Dependencies look appropriate for a modern React mobile application.

The selection of dependencies is well-considered, including Radix UI components for accessibility, Tanstack Router for navigation, and Zustand for state management - all excellent choices for a mobile web application.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/claim-tab.tsx (1)

50-63: LGTM! Component structure looks good.

The ClaimTab component has a clean structure, properly maps through the claims data, and sets appropriate keys for the list items.

client/apps/eternum-mobile/src/app/ui/layout.tsx (2)

5-15: LGTM! Layout structure follows best practices.

The Layout component has a clean structure with header, main content area, and footer. Using flex-1 on the main element ensures it takes up all available space.


9-9:

❓ Verification inconclusive

Verify the padding-bottom value doesn't cause layout issues.

The pb-20 class adds significant padding to the bottom of the main content. Ensure this doesn't cause content to be cut off or create excessive whitespace, especially on smaller screens.


🏁 Script executed:

#!/bin/bash
# Look for any related styles or adjustments for this padding in other files
rg -l "pb-20" --type tsx --type css

Length of output: 63


Below is a revised review comment incorporating a request for additional verification. Since the initial search for the “pb-20” class using the file type flag “tsx” produced an error, please re-run the commands below to better locate its usage in TSX and CSS files. Once you have the output, manually verify that the significant bottom padding isn’t causing layout issues (e.g., content cutoff or excessive whitespace on smaller screens).

Padding Verification – Please Re-run and Confirm Effects

  • Check TSX Usage: Ensure that the <main className="flex-1 pb-20"> element isn’t adversely impacting layout.
  • Verify CSS Rules: Confirm that there aren’t any conflicting or cascading styles in CSS that might adjust the padding.

Please execute the revised search commands and share the output:

Once you provide the results, we can verify if the pb-20 class usage is safe across all displays.


🏁 Script executed:

#!/bin/bash
echo "Searching for 'pb-20' usage in TSX files..."
rg -l "className=.*pb-20" -g "*.tsx"

echo "Searching for 'pb-20' usage in CSS files..."
rg -l "pb-20" -g "*.css"

Length of output: 440


Responsive Layout Verification – Confirm Padding Impact

The pb-20 class is currently applied in the <main className="flex-1 pb-20"> element in client/apps/eternum-mobile/src/app/ui/layout.tsx. Our updated search shows no conflicting usage in CSS files, which suggests this Tailwind utility is only applied here. However, please manually verify on various devices (especially smaller screens) to ensure that the added bottom padding does not cause content cutoff or introduce excessive whitespace.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/index.ts (1)

1-4: LGTM! Clean barrel exports for tab components.

This barrel file follows best practices for organizing and exporting related components.

client/apps/eternum-mobile/.gitignore (1)

1-25: LGTM! Standard .gitignore for a React/Vite project.

This .gitignore file properly excludes common files and directories that shouldn't be committed to version control:

  • Build artifacts and dependencies
  • Log files
  • IDE/editor files
  • Environment variables
client/apps/eternum-mobile/src/widgets/footer/ui/footer.tsx (1)

5-8: Robust current path detection

The use of useMatches and getting the last match with at(-1) is a good approach for determining the current path in Tanstack Router.

client/apps/eternum-mobile/tsconfig.json (1)

4-11:

❓ Verification inconclusive

Verify external paths configuration

The paths for @config and @contracts point to locations outside the project directory. Ensure these dependencies are properly resolved during build time and consider a more robust approach for referencing external modules.


🏁 Script executed:

#!/bin/bash
# Verify that the paths defined in tsconfig.json exist
echo "Checking if config utils exist:"
if [ -d "../../../config/utils/utils" ]; then
  echo "✅ Config utils directory exists"
else
  echo "❌ Config utils directory does not exist at '../../../config/utils/utils'"
fi

echo "Checking if contracts utils exist:"
if [ -d "../../../contracts/utils/utils" ]; then
  echo "✅ Contracts utils directory exists"
else
  echo "❌ Contracts utils directory does not exist at '../../../contracts/utils/utils'"
fi

# Check for imports using these paths
echo "\nChecking for imports using @config:"
rg "from\s+['\"]@config['\"]" --type ts --type tsx

echo "\nChecking for imports using @contracts:"
rg "from\s+['\"]@contracts['\"]" --type ts --type tsx

Length of output: 919


Attention: Verify External Module Paths

The current tsconfig.json in client/apps/eternum-mobile defines the aliases @config and @contracts with paths pointing outside the project directory (i.e., "../../../config/utils/utils" and "../../../contracts/utils/utils"). Our automated checks indicate that these directories are not found relative to the project root. Please manually verify the following:

  • Path Existence: Confirm whether these external directories are expected to be available at runtime/build time. If these paths are correct, ensure the external modules are properly mounted or referenced.
  • Configuration Accuracy: If the directories should exist but don’t, update the paths in tsconfig.json to reflect the accurate location.
  • Robust Resolution: Consider adopting a more resilient approach (such as using package aliases or a dedicated module resolution strategy) to manage external dependencies instead of relying on relative paths that reach outside the project scope.
client/apps/eternum-mobile/src/shared/store/use-address-store.ts (1)

3-8: Well-defined store interface

The store type definition is clear and concise with appropriate types for each property and method.

client/apps/eternum-mobile/components.json (3)

1-21: LGTM: Configuration follows ShadCN UI standards

The components.json configuration follows the standard ShadCN UI schema and provides appropriate settings for the Eternum mobile application, including style, Tailwind settings, and path aliases.


6-12:

✅ Verification successful

Verify Tailwind configuration file path

The configuration specifies "tailwind.config.js" at the root level. Ensure this file exists and is properly configured.


🏁 Script executed:

#!/bin/bash
# Check if tailwind.config.js exists
if [ -f client/apps/eternum-mobile/tailwind.config.js ]; then
  echo "✅ tailwind.config.js exists"
else
  echo "❌ tailwind.config.js not found"
fi

# Check if the CSS file exists
if [ -f client/apps/eternum-mobile/src/app/index.css ]; then
  echo "✅ src/app/index.css exists"
else
  echo "❌ src/app/index.css not found"
fi

Length of output: 244


Tailwind configuration verified – no changes required

The verification confirms that both the tailwind.config.js and the CSS file at src/app/index.css exist at the specified paths.

  • client/apps/eternum-mobile/tailwind.config.js exists.
  • client/apps/eternum-mobile/src/app/index.css exists.

13-19:

✅ Verification successful

Verify path alias configuration in tsconfig.json

The aliases defined here use the "@/" prefix, which requires corresponding path mappings in your tsconfig.json file. Ensure these are properly configured.

Run the following to verify the path mappings in tsconfig.json:


🏁 Script executed:

#!/bin/bash
# Look for path mappings in tsconfig.json
cat client/apps/eternum-mobile/tsconfig.json | grep -A 20 "paths"

Length of output: 223


Alias mapping configuration verified – no changes are required.

I ran the provided script to inspect the paths configuration in client/apps/eternum-mobile/tsconfig.json, which confirms that the "@/*": ["./src/*"] mapping is present. This covers all the @/ aliases defined in your components.json (e.g., "components": "@/widgets", "utils": "@/shared/lib/utils", etc.), ensuring they resolve correctly.

client/apps/eternum-mobile/src/shared/hooks/context/policies.ts (1)

1-2: Import type appears to be used correctly.

The ContractPolicies type is properly imported from the controller package.

client/apps/eternum-mobile/src/app/app.tsx (2)

1-3: Imports look good.

The imports are correctly defined for the router implementation.


9-19: Well-structured JSX with appropriate styling classes.

The component returns a well-structured JSX with appropriate styling classes. The conditional rendering of the background image and styling is implemented correctly.

client/apps/eternum-mobile/src/pages/login/ui/login-page.tsx (2)

1-6: Imports are properly organized.

The imports are organized well, bringing in necessary components and hooks from their respective locations.


17-23: Good UI card component structure.

The card UI structure with appropriate heading hierarchy and descriptions provides a good user experience.

client/apps/eternum-mobile/tailwind.config.js (2)

1-57: Well-structured Tailwind configuration using CSS variables.

This configuration follows best practices by using CSS variables for theming, making it easy to switch between light and dark modes or other themes.


2-5: Configuration is set up correctly for dark mode and content scanning.

The configuration correctly enables class-based dark mode and scans appropriate file types for content.

client/apps/eternum-mobile/tsconfig.node.json (1)

1-24: A solid TypeScript configuration for Node environment

This configuration is well structured with appropriate settings for a modern TypeScript project. The strict type-checking options and linting configurations will help maintain code quality.

Consider adding "esModuleInterop": true to improve compatibility with CommonJS modules if you plan to use any libraries that are not built as ES modules.

🧰 Tools
🪛 Biome (1.9.4)

[error] 9-9: JSON standard does not allow comments.

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: JSON standard does not allow comments.

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

client/apps/eternum-mobile/README.md (1)

1-93: Well-structured documentation of the architecture

The README provides a comprehensive overview of the project structure following Feature-Sliced Design. This clear explanation of the architecture and its components will be valuable for onboarding new developers.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

24-24: Bare URL used
null

(MD034, no-bare-urls)


31-31: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


54-54: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


85-85: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

client/apps/eternum-mobile/vite.config.ts (3)

26-26: Verify if external configuration should remain commented

There's a commented out configuration for external dependencies.

Is this intentional? If React and React DOM should be treated as external dependencies, uncomment this line. Otherwise, consider removing the commented code.


1-49: Well-configured Vite setup with proper optimizations

The configuration includes appropriate plugins, path aliases, and build optimizations for a React application. The manual chunking for vendor dependencies and sourcemap configurations are well thought out.


46-48:

✅ Verification successful

Verify the excluded dependencies from optimization

The configuration excludes specific packages from Vite's dependency optimization.


🏁 Script executed:

#!/bin/bash
# Check if these packages are being used in the codebase and if there's a reason to exclude them
echo "Checking for js-big-decimal usage:"
rg -A 1 -B 1 "js-big-decimal" --type ts

echo "Checking for @bibliothecadao/eternum usage:"
rg -A 1 -B 1 "@bibliothecadao/eternum" --type ts

Length of output: 66130


Dependency Exclusions Verified

The verification shows that:

  • js-big-decimal: Its appearance is confined to the Vite config files (e.g., in both mobile and game apps), and no other usage patterns were detected. This supports the decision to exclude it from dependency optimization.
  • @bibliothecadao/eternum: This package is used extensively across the codebase. Excluding it from Vite’s dependency optimization appears to be intentional to prevent potential bundling issues with such a widely used dependency.

No further changes are needed as these exclusions are justified. Please ensure that the rationale for excluding these packages is documented for future reference.

client/apps/eternum-mobile/src/widgets/labor-widget/index.ts (1)

1-3:

✅ Verification successful

Clean barrel exports following best practices

The file properly exports the types and components, following the best practice of using barrel files to simplify imports across the application.

I recommend verifying that all the exported types and components exist in their respective files to prevent any import errors.


🏁 Script executed:

#!/bin/bash
# Verify that the exported types and components exist
echo "Checking for LaborBuilding, LaborBuildingProps, ResourceBalance types:"
rg -l "export (type |interface )(LaborBuilding|LaborBuildingProps|ResourceBalance)" --type ts client/apps/eternum-mobile/src/widgets/labor-widget/model/

echo "Checking for LaborDrawer component:"
rg -l "export (function|const) LaborDrawer" --type ts client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-drawer/

echo "Checking for LaborWidget component:"
rg -l "export (function|const) LaborWidget" --type ts client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-widget/

Length of output: 1002


I'll execute a new script to search for the component files in case they are files (and not directories) with names matching "labor-drawer" and "labor-widget".


🏁 Script executed:

#!/bin/bash
echo "Searching for labor-drawer file(s):"
fd -e ts -e tsx "labor-drawer" client/apps/eternum-mobile/src/widgets/labor-widget/ui/

echo "Searching for labor-widget file(s):"
fd -e ts -e tsx "labor-widget" client/apps/eternum-mobile/src/widgets/labor-widget/ui/

Length of output: 479


Final Verification Approved: Barrel Exports are Correct

  • The type exports (LaborBuilding, LaborBuildingProps, ResourceBalance) are confirmed in client/apps/eternum-mobile/src/widgets/labor-widget/model/types.ts.
  • The LaborDrawer component is present in client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-drawer.tsx.
  • The LaborWidget component is present in client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-widget.tsx.

All exported modules exist and are correctly referenced.

client/apps/eternum-mobile/src/shared/ui/input.tsx (1)

5-19: Well-crafted Input component using React.forwardRef

The implementation uses React.forwardRef correctly to allow parent components to access the underlying input element. The component properly spreads props and merges class names with a default styling.

client/apps/eternum-mobile/src/widgets/arrived-donkeys/ui/arrived-donkeys.tsx (2)

6-9: Props interface defined correctly

The interface for ArrivedDonkeysProps is well-defined and appropriate for the component.


11-16: Replace dummy data generator with actual data source for production

The current implementation uses random values for demonstration purposes. This should be replaced with actual data from an API or state management system before deploying to production.

Is this dummy data intended to be replaced before release? If so, you might want to add a TODO comment to make this clear, or implement the actual data fetching logic.

client/apps/eternum-mobile/src/shared/lib/utils.ts (2)

5-7: Good utility for merging class names

The cn function properly combines tailwind classes using clsx and twMerge, which is a recommended pattern for handling dynamic class names in tailwind projects.


9-10: Type-safe route helper promotes consistency

This helper ensures routes remain type-safe throughout the application, which is good practice.

client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (3)

5-24: Well-documented props interface

The ResourceIconProps interface is well-documented with JSDoc comments that clearly describe each prop's purpose and default values.


38-49: Verify resource image paths

The component references images at /images/resources/${resourceData.id}.png. Ensure these image paths are correct and the assets are included in the project.

Please verify that the resource images are available at the specified path in your build output. These should be in the public directory or handled by your asset bundler.


51-67: Clean implementation of conditional tooltip

The conditional rendering of the tooltip is well-structured and follows good practices for optional UI elements.

client/apps/eternum-mobile/src/shared/ui/numeric-keyboard.tsx (1)

1-31: Well-implemented NumericKeyboard component

The component is well-structured with:

  • Clear props interface definition
  • Good organization of keys in a 2D array for layout
  • Efficient rendering with React.Fragment to avoid extra DOM nodes
  • Proper use of the cn utility for class name composition
  • Consistent button styling with appropriate sizing

The implementation provides a clean, reusable interface for numeric input with backspace support.

client/apps/eternum-mobile/tsconfig.app.json (3)

10-17: TypeScript bundler configuration is appropriate for a modern web application

The bundler mode configuration is well set up with:

  • moduleResolution: "bundler" for compatibility with tools like Vite
  • allowImportingTsExtensions to support importing .ts files directly
  • isolatedModules for better compatibility with transpilers
  • moduleDetection: "force" to ensure consistent module detection
  • noEmit: true which is correct for bundler workflows
  • React JSX support properly configured

These settings align well with modern TypeScript development practices for client applications.

🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: JSON standard does not allow comments.

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


18-24: Good type-checking strictness configuration

The linting configuration is appropriately strict and includes:

  • strict: true for comprehensive type safety
  • noUnusedLocals and noUnusedParameters to keep code clean
  • noFallthroughCasesInSwitch to prevent logical errors
  • noUncheckedSideEffectImports to manage import side effects

These strict settings will help catch potential issues early in the development process.

🧰 Tools
🪛 Biome (1.9.4)

[error] 18-18: JSON standard does not allow comments.

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


25-32: Path aliases for improved import readability

The path configuration is well-structured with:

  • A clear baseUrl setup
  • Aliases that make imports more readable (@/* for src)
  • Proper inclusion of utility modules from config and contracts

This approach will keep imports consistent and maintainable across the codebase.

🧰 Tools
🪛 Biome (1.9.4)

[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 31-31: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 32-32: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

client/apps/eternum-mobile/src/app/config/router.tsx (3)

29-40: Login route handles authentication state correctly

The login route implementation properly:

  • Redirects authenticated users to the realm page
  • Is accessible to unauthenticated users
  • Uses consistent pattern with the protected routes for authentication checking

This bidirectional authentication handling ensures users always land on the appropriate page.


66-80: Clean route tree organization and type registration

The route tree is well-organized with:

  • Clear parent-child relationships
  • Proper type registration via the module declaration
  • Export of the router instance for application use

This structure ensures type safety throughout the application when using the router.


16-27:

❓ Verification inconclusive

Verify authentication check behavior in router beforeLoad hooks

The protected route implementation correctly redirects unauthenticated users, but be aware that using useAuth.getState() in the beforeLoad hook assumes the auth state is already initialized when navigation occurs.

Make sure the auth state is properly initialized before any navigation attempts by testing the following scenarios:

  1. Direct navigation to a protected route without authentication
  2. Navigation after logout
  3. Page refresh while on a protected route

🏁 Script executed:

#!/bin/bash
# Search for auth initialization code to verify it's properly set up before router usage
echo "Checking auth initialization:"
rg -A 10 "useAuth.setState|initializeAuth" --type tsx --type ts

Length of output: 162


Action: Re-run Auth Initialization Verification in TS/TSX Files

The previous script failed due to the unrecognized file type "tsx." Please run the updated script below to search for any auth initialization code (using keywords like useAuth.setState or initializeAuth) across both .ts and .tsx files. This will help us verify that the auth state is properly initialized before any router navigation occurs.

After executing this script, please review the output manually to confirm that the authentication state is being properly initialized prior to navigation attempts.


🏁 Script executed:

#!/bin/bash
echo "Verifying auth initialization in TSX and TS files:"
rg -g "*.tsx" -g "*.ts" -A 10 "useAuth.setState|initializeAuth" .

Length of output: 178


Action: Verify Auth Initialization and Navigation Scenarios

The protected route implementation correctly redirects unauthenticated users. However, since our automated search for explicit auth initialization patterns (such as useAuth.setState or initializeAuth) did not return any results, please manually verify that the auth state is fully initialized before any navigation occurs. Specifically, confirm the following:

  • Direct navigation to a protected route without authentication should trigger redirection.
  • Navigation after logout must reinitialize the auth state appropriately.
  • A page refresh on a protected route should not lead to an uninitialized auth state.

If the auth initialization is handled implicitly or in a different part of the application, consider documenting the flow to ensure these scenarios remain robust.

client/apps/eternum-mobile/src/widgets/resources-card/ui/resources-card.tsx (1)

60-62: ⚠️ Potential issue

Missing key prop in mapped elements

When mapping over arrays to generate React elements, a unique key prop should be provided. Line 60 starts a mapping function, but the key is only added to the div element on line 65.

Add a key to the immediate child of the mapping function:

{resourceAmounts.map(({ id, amount }) => {
  const resource = resources.find((r) => r.id === id);
  if (!resource) return null;

  return (
-   <div key={resource.id} className="flex items-center justify-between">
+   <div key={resource.id} className="flex items-center justify-between">
      <div className="flex items-center space-x-4">

Likely an incorrect or invalid review comment.

client/apps/eternum-mobile/src/shared/ui/tabs.tsx (1)

8-51: LGTM! Well-structured tab components with proper accessibility

The implementation follows best practices for React component composition:

  • Correctly uses React.forwardRef for proper ref forwarding
  • Sets display names properly for better debugging
  • Includes focus-visible styles for keyboard navigation accessibility
  • Properly spreads props to underlying components
client/apps/eternum-mobile/src/app/index.css (1)

11-65: Well-organized theme variables with light/dark mode support

The CSS variables for theming are well-structured and provide comprehensive theming capabilities with both light and dark mode support. The organization follows best practices.

client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (1)

6-9: entityId is passed but not used in the component

The component accepts an entityId prop but doesn't use it for anything. This suggests that either the ID should be used or it's unnecessary.

If entityId is meant to be used for fetching real data in the future, consider adding a comment explaining this. Otherwise, you might want to remove it from the props interface.

interface NearbyEnemiesProps {
  entityId: number; // Add comment: Will be used for fetching real enemy data in future implementation
  onView: () => void;
}

Additionally, the onView handler should likely use the entityId:

-  <Button variant="secondary" size="sm" className="w-full font-semibold" onClick={onView}>
+  <Button variant="secondary" size="sm" className="w-full font-semibold" onClick={() => onView(entityId)}>

And update the prop type:

interface NearbyEnemiesProps {
  entityId: number;
-  onView: () => void;
+  onView: (entityId: number) => void;
}
client/apps/eternum-mobile/src/shared/ui/button.tsx (2)

7-34: Well-structured button component with flexible variants

The button implementation follows best practices for a reusable UI component:

  • Uses class-variance-authority for managing variants
  • Includes appropriate focus states and accessibility attributes
  • Properly handles disabled states
  • SVG sizing is consistently applied

42-54: Good implementation of forwarded ref and asChild pattern

The component correctly implements:

  • React.forwardRef for proper ref forwarding
  • Radix UI's Slot pattern via asChild prop
  • Type safety throughout the implementation
client/apps/eternum-mobile/src/widgets/swap-input/ui/swap-input.tsx (1)

75-75: Ensure balance display consistency across the component.

The resource balance is displayed twice - once in the resource selection drawer and once in the main component. Since these balance values are independent displays of the same random number, they could become inconsistent on re-renders. Ensure the balance is derived from a consistent source.

client/apps/eternum-mobile/src/widgets/labor-widget/ui/labor-widget.tsx (3)

23-27: Nice utility function for formatting time

The formatTime function correctly converts seconds into hours and minutes format, making the remaining production time more readable for users.


29-49: Clean and well-structured UI component

The card layout provides a good visual hierarchy with resource information on the left and controls on the right. The conditional rendering of the remaining time badge is also handled appropriately.


51-59: Proper prop passing to LaborDrawer component

All necessary props are correctly passed to the LaborDrawer component, including the drawer state management functions.

client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (2)

11-14: Well-defined Starknet chain ID enum

The enum provides clear string representations of the chain IDs with helpful comments.


30-55:

❓ Verification inconclusive

Avoid type casting with never

The connector is cast using a suspicious pattern as never as Connector which could hide type incompatibilities.

Consider creating proper type definitions or using a more explicit conversion method. Verify the compatibility between ControllerConnector and the expected Connector type.


🏁 Script executed:

#!/bin/bash
# Check the type definitions for ControllerConnector and Connector
rg -t typescript "export (type|interface) Connector" --include "*.ts" --include "*.tsx"
rg -t typescript "export (default )?class ControllerConnector" --include "*.ts" --include "*.tsx"

Length of output: 1073


Action Required: Revisit Suspicious Type Casting

The code still uses a questionable type conversion pattern (as never as Connector) in client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (lines 30-55). This pattern risks obscuring genuine type incompatibilities between the ControllerConnector implementation and the expected Connector type. Please eliminate the double casting and either:

• Define or refine explicit type interfaces for ControllerConnector so that it naturally satisfies the Connector type, or
• Use a more precise conversion method that makes your intent clear while ensuring type safety.

For manual verification, re-run the commands below (with updated glob patterns) to check the definitions:

rg "export (type|interface) Connector" -g "*.ts" -g "*.tsx"
rg "export (default )?class ControllerConnector" -g "*.ts" -g "*.tsx"

---

🏁 Script executed:

```shell

#!/bin/bash
# Re-run verification for Connector type definitions using updated glob patterns
rg "export (type|interface) Connector" -g "*.ts" -g "*.tsx"
rg "export (default )?class ControllerConnector" -g "*.ts" -g "*.tsx"

Length of output: 255


Action Required: Manually Verify Connector Type Definitions

The current implementation in
client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (lines 30–55) still employs a double cast pattern (as never as Connector) that risks masking genuine type incompatibilities. Without clear output from automated searches for the Connector interface and the ControllerConnector class, it’s unclear if the types are properly defined. Please manually verify that:

  • The ControllerConnector class correctly implements or conforms to the expected Connector interface.
  • Removing or replacing the double casting with a proper, explicit conversion (or refined type definitions) won’t introduce issues.

For further manual checks, consider re-running these commands on your codebase:

rg "export (type|interface) Connector" -g "*.ts" -g "*.tsx"
rg "export (default )?class ControllerConnector" -g "*.ts" -g "*.tsx"
client/apps/eternum-mobile/src/widgets/upgrade-castle/ui/upgrade-drawer.tsx (2)

38-47: Good error handling implementation

The error handling in the handleUpgrade function is well-implemented, properly differentiating between Error instances and other error types.


49-151: Good state management for different upgrade steps

The renderStepContent function effectively manages different visual states of the upgrade process with clear separation of concerns.

client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (1)

26-30: Early environment checks look good.

This setup ensures the application fails fast if required environment variables are missing. It's a best practice to validate these critical variables at startup.

client/apps/eternum-mobile/src/shared/ui/sheet.tsx (2)

31-48: Commendable usage of class-variance-authority for variants.

The sheetVariants approach effectively manages multiple positional variants with minimal duplication. This keeps the styling consistent and maintainable.


58-64: Well-structured content composition.

Using SheetPortal and SheetOverlay properly encapsulates the animations and overlay logic, ensuring an accessible and maintainable layout. Good use of React.forwardRef for Radix UI components as well.

client/apps/eternum-mobile/src/shared/ui/select.tsx (6)

1-5: Clean import structure, good use of named imports.

The imports are well-organized, importing specific components and icons needed for the implementation. The utility function cn for class name merging is a good practice.


7-11: Good pattern for re-exporting primitive components.

Directly re-exporting the Radix UI primitives allows for a clean public API while maintaining the ability to extend them with custom styling later if needed.


33-49: ScrollButton components follow consistent patterns.

Both SelectScrollUpButton and SelectScrollDownButton are implemented consistently, with proper typing and styling. The components correctly forward refs and merge classNames.

Also applies to: 50-67


68-99: SelectContent has good animation and positioning logic.

The SelectContent component implements proper animation states using data attributes and handles different positioning scenarios well. The use of SelectPrimitive.Portal ensures the dropdown renders at the correct DOM level.

One accessibility consideration: ensure that keyboard navigation works properly within the dropdown menu for users who don't use a mouse.

Can you verify that the component properly handles keyboard navigation (arrow keys, Enter, Escape) since this is critical for accessibility?


100-111: Good implementation of SelectLabel, SelectItem, and SelectSeparator.

These components follow the same consistent pattern as the others, with proper ref forwarding and className merging. The SelectItem correctly implements the item indicator for the selected state.

Also applies to: 112-134, 135-146


147-158: Clean named exports for all components.

The export pattern makes it easy for consumers to import only the components they need, promoting tree-shaking. All components follow a consistent naming convention.

client/apps/eternum-mobile/src/pages/chat/index.ts (1)

1-5: Well-structured barrel file for chat-related components.

This is a well-organized barrel file that exports all the necessary components for the chat functionality. The structure follows good practices for module organization.

client/apps/eternum-mobile/src/shared/consts/routes.ts (1)

1-7: LGTM! Route constants are well defined.

The routes constant is properly defined with descriptive names and follows the best practice of using as const for type safety. This approach ensures consistent route references throughout the application.

Comment on lines +4 to +7
export const ETERNUM_CONFIG = () => {
const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN! as Chain);
return config;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding validation and fallback for missing environment variables.

The non-null assertion (!) on env.VITE_PUBLIC_CHAIN assumes this environment variable will always be set, which could lead to runtime errors if it's missing.

export const ETERNUM_CONFIG = () => {
-  const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN! as Chain);
+  if (!env.VITE_PUBLIC_CHAIN) {
+    throw new Error("VITE_PUBLIC_CHAIN environment variable is not defined");
+  }
+  const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN as Chain);
  return config;
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const ETERNUM_CONFIG = () => {
const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN! as Chain);
return config;
};
export const ETERNUM_CONFIG = () => {
if (!env.VITE_PUBLIC_CHAIN) {
throw new Error("VITE_PUBLIC_CHAIN environment variable is not defined");
}
const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN as Chain);
return config;
};

Comment on lines +10 to +29
const dummyEvents: Event[] = [
{
id: "1",
sender: "System",
timestamp: "09:55 AM",
message: "Guild War has started!",
},
{
id: "2",
sender: "System",
timestamp: "10:00 AM",
message: "Your army has arrived at the destination.",
},
{
id: "3",
sender: "System",
timestamp: "10:05 AM",
message: "Resource production increased by 10%",
},
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Replace dummy data with real data source in production

The dummy events array is appropriate for development or as placeholder content, but should be replaced with real data in production.


🏁 Script executed:

#!/bin/bash
# Check if there are any API calls or data fetching mechanisms in the codebase

echo "Searching for API calls in the events-related files..."
rg -A 2 -B 2 "fetch|axios|api\.get|useQuery" --glob "**/*event*" 

Length of output: 185


Production Data Integration Required for Events

The file client/apps/eternum-mobile/src/pages/chat/ui/tabs/events-tab.tsx (lines 10–29) currently uses a hard-coded dummyEvents array, and our search didn’t reveal any evidence of a real data fetching mechanism (e.g., API calls via fetch, axios, or similar hooks). Before production, please replace this dummy data with a proper API integration or data fetching solution.

Comment on lines 52 to 54
"peerDependencies": {
"starknet": "6.11.0"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify Starknet peer dependency version.

You've specified an exact version for the Starknet peer dependency. Make sure this specific version (6.11.0) is compatible with all dependent packages and that you have a strategy for managing version updates.


🌐 Web query:

Is Starknet 6.11.0 the latest stable version?

💡 Result:

Starknet 6.11.0 is not the latest stable version. According to the npm registry, the current stable release is 6.23.1 as of February 2025[6]. The Starknet.js documentation explicitly states that version 6.11.0 is no longer actively maintained and directs users to upgrade to the latest version for updated features and support[9].

Key Details:

  • Latest Starknet.js version: 6.23.1 (published a month ago as of February 2025)[6].
  • Deprecation notice: Version 6.11.0 is flagged as outdated in official docs, with critical updates like improved type definitions and compatibility fixes available in newer releases[9][18].
  • Protocol vs. SDK versions: While Starknet’s protocol is advancing toward v0.13.4 and v0.14.0 for decentralization and performance[1][13], the JavaScript library follows a separate versioning scheme (e.g., 6.x.x)[6].

For developers, upgrading to 6.23.1 is recommended to ensure compatibility with Starknet’s latest features, including Cairo-native optimizations and enhanced RPC support[6][13].

Citations:


Update Starknet Peer Dependency Version

  • File: client/apps/eternum-mobile/package.json (Lines 52-54)
  • Issue: The peer dependency currently specifies Starknet version 6.11.0, which is now outdated.
  • Recommendation: Update to the latest stable version (6.23.1) to benefit from the latest features, improved type definitions, and enhanced compatibility.
  • Action: Ensure that all dependent packages are compatible with the new version before making the change.
-  "peerDependencies": {
-    "starknet": "6.11.0"
-  }
+  "peerDependencies": {
+    "starknet": "6.23.1"
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"peerDependencies": {
"starknet": "6.11.0"
}
"peerDependencies": {
"starknet": "6.23.1"
}

Comment on lines +7 to +32
resources: [
{ id: 3, amount: 100 }, // Wood
{ id: 1, amount: 50 }, // Stone
],
},
{
entityId: 2,
entityType: "army" as const,
resources: [
{ id: 7, amount: 200 }, // Gold
{ id: 4, amount: 75 }, // Copper
{ id: 8, amount: 150 }, // Silver
{ id: 11, amount: 25 }, // Cold Iron
],
},
{
entityId: 3,
entityType: "donkey" as const,
resources: [
{ id: 3, amount: 300 }, // Wood
{ id: 1, amount: 200 }, // Stone
{ id: 2, amount: 150 }, // Coal
{ id: 4, amount: 100 }, // Copper
{ id: 5, amount: 50 }, // Ironwood
{ id: 6, amount: 25 }, // Obsidian
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type definitions for resources and consider using an enum for resource IDs.

Resource IDs are currently using magic numbers with comments to indicate their meaning. This approach is error-prone and makes the code harder to maintain.

// Add an enum for resource types
enum ResourceType {
  Stone = 1,
  Coal = 2,
  Wood = 3,
  Copper = 4,
  Ironwood = 5,
  Obsidian = 6,
  Gold = 7,
  Silver = 8,
  // Add other resource types
}

// Define interfaces for your data structures
interface Resource {
  id: ResourceType;
  amount: number;
}

interface Claim {
  entityId: number;
  entityType: 'donkey' | 'army';
  resources: Resource[];
}

// Then use them in your constant
const DUMMY_CLAIMS: Claim[] = [
  {
    entityId: 1,
    entityType: 'donkey',
    resources: [
      { id: ResourceType.Wood, amount: 100 },
      { id: ResourceType.Stone, amount: 50 },
    ],
  },
  // ...other claims
];

Comment on lines +18 to +21
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve accessibility by allowing user scaling

The current viewport meta tag prevents users from zooming the page, which can create accessibility issues. Consider allowing users to zoom for better accessibility.

    <meta
      name="viewport"
-      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
+      content="width=device-width, initial-scale=1.0, viewport-fit=cover"
    />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<meta
name="viewport"
- content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
+ content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/>

Comment on lines +65 to +85
const handleOutputChange = (value: number) => {
setOutputAmount(value);
// Here you would typically recalculate input amounts based on the new output
// For now, we'll just scale them proportionally
const scale = value / building.outputAmount;
if (activeTab === "raw") {
setInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
} else {
setLaborInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for output amount and document scaling behavior

The scaling of input amounts is done proportionally, but this might lead to unexpected behavior if the output amount is set to zero.

 const handleOutputChange = (value: number) => {
+  // Prevent division by zero and negative values
+  if (value <= 0) {
+    value = 1;
+  }
   setOutputAmount(value);
   // Here you would typically recalculate input amounts based on the new output
   // For now, we'll just scale them proportionally
   const scale = value / building.outputAmount;
   if (activeTab === "raw") {
     setInputAmounts(
       (prev) =>
         Object.fromEntries(
           Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
         ) as ResourceAmounts,
     );
   } else {
     setLaborInputAmounts(
       (prev) =>
         Object.fromEntries(
           Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
         ) as ResourceAmounts,
     );
   }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleOutputChange = (value: number) => {
setOutputAmount(value);
// Here you would typically recalculate input amounts based on the new output
// For now, we'll just scale them proportionally
const scale = value / building.outputAmount;
if (activeTab === "raw") {
setInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
} else {
setLaborInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
}
};
const handleOutputChange = (value: number) => {
+ // Prevent division by zero and negative values
+ if (value <= 0) {
+ value = 1;
+ }
setOutputAmount(value);
// Here you would typically recalculate input amounts based on the new output
// For now, we'll just scale them proportionally
const scale = value / building.outputAmount;
if (activeTab === "raw") {
setInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
} else {
setLaborInputAmounts(
(prev) =>
Object.fromEntries(
Object.entries(prev).map(([key, value]) => [key, Math.ceil(Number(value) * scale)]),
) as ResourceAmounts,
);
}
};

Comment on lines +209 to +223
onClick={() => {
const updatedBuilding = {
...building,
outputAmount,
inputs: building.inputs.map((input) => ({
...input,
amount: inputAmounts[input.resourceId] ?? input.amount,
})),
laborInputs: building.laborInputs.map((input) => ({
...input,
amount: laborInputAmounts[input.resourceId] ?? input.amount,
})),
};
onStartProduction(updatedBuilding.id, activeTab);
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider validating input values before starting production

The code doesn't validate whether the user has sufficient resources before allowing production to start.

 onClick={() => {
+  // Validate resource balances before starting production
+  const activeInputs = activeTab === "raw" ? building.inputs : building.laborInputs;
+  const currentAmounts = activeTab === "raw" ? inputAmounts : laborInputAmounts;
+  
+  const insufficientResources = activeInputs.some(input => {
+    const requiredAmount = currentAmounts[input.resourceId] ?? input.amount;
+    const balance = resourceBalances.find(b => b.resourceId === input.resourceId)?.balance || 0;
+    return requiredAmount > balance;
+  });
+  
+  if (insufficientResources) {
+    // Handle insufficient resources (could show an alert or disable the button)
+    return;
+  }
+
   const updatedBuilding = {
     ...building,
     outputAmount,
     inputs: building.inputs.map((input) => ({
       ...input,
       amount: inputAmounts[input.resourceId] ?? input.amount,
     })),
     laborInputs: building.laborInputs.map((input) => ({
       ...input,
       amount: laborInputAmounts[input.resourceId] ?? input.amount,
     })),
   };
   onStartProduction(updatedBuilding.id, activeTab);
 }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick={() => {
const updatedBuilding = {
...building,
outputAmount,
inputs: building.inputs.map((input) => ({
...input,
amount: inputAmounts[input.resourceId] ?? input.amount,
})),
laborInputs: building.laborInputs.map((input) => ({
...input,
amount: laborInputAmounts[input.resourceId] ?? input.amount,
})),
};
onStartProduction(updatedBuilding.id, activeTab);
}}
onClick={() => {
// Validate resource balances before starting production
const activeInputs = activeTab === "raw" ? building.inputs : building.laborInputs;
const currentAmounts = activeTab === "raw" ? inputAmounts : laborInputAmounts;
const insufficientResources = activeInputs.some(input => {
const requiredAmount = currentAmounts[input.resourceId] ?? input.amount;
const balance = resourceBalances.find(b => b.resourceId === input.resourceId)?.balance || 0;
return requiredAmount > balance;
});
if (insufficientResources) {
// Handle insufficient resources (could show an alert or disable the button)
return;
}
const updatedBuilding = {
...building,
outputAmount,
inputs: building.inputs.map((input) => ({
...input,
amount: inputAmounts[input.resourceId] ?? input.amount,
})),
laborInputs: building.laborInputs.map((input) => ({
...input,
amount: laborInputAmounts[input.resourceId] ?? input.amount,
})),
};
onStartProduction(updatedBuilding.id, activeTab);
}}

Comment on lines +36 to +49
const handleKeyPress = (key: string) => {
if (!onChange) return;

if (key === "⌫") {
// Handle backspace
onChange(Math.floor(value / 10));
} else {
// Handle number or decimal point
const newValue = value.toString() + key;
if (!isNaN(parseFloat(newValue))) {
onChange(parseFloat(newValue));
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve backspace handling for decimal values

The current backspace implementation only handles integer values correctly. When dealing with decimal values, dividing by 10 and flooring will lead to unexpected behavior.

 const handleKeyPress = (key: string) => {
   if (!onChange) return;

   if (key === "⌫") {
     // Handle backspace
-    onChange(Math.floor(value / 10));
+    const valueStr = value.toString();
+    if (valueStr.length > 0) {
+      const newValueStr = valueStr.slice(0, -1);
+      onChange(newValueStr.length > 0 ? parseFloat(newValueStr) : 0);
+    }
   } else {
     // Handle number or decimal point
     const newValue = value.toString() + key;
     if (!isNaN(parseFloat(newValue))) {
       onChange(parseFloat(newValue));
     }
   }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleKeyPress = (key: string) => {
if (!onChange) return;
if (key === "⌫") {
// Handle backspace
onChange(Math.floor(value / 10));
} else {
// Handle number or decimal point
const newValue = value.toString() + key;
if (!isNaN(parseFloat(newValue))) {
onChange(parseFloat(newValue));
}
}
};
const handleKeyPress = (key: string) => {
if (!onChange) return;
if (key === "⌫") {
// Handle backspace
const valueStr = value.toString();
if (valueStr.length > 0) {
const newValueStr = valueStr.slice(0, -1);
onChange(newValueStr.length > 0 ? parseFloat(newValueStr) : 0);
}
} else {
// Handle number or decimal point
const newValue = value.toString() + key;
if (!isNaN(parseFloat(newValue))) {
onChange(parseFloat(newValue));
}
}
};

}: ConfirmDrawerProps) => {
const [state, setState] = useState<SwapState>("confirm");
const [error, setError] = useState<string>("");
const [timeLeft, setTimeLeft] = useState("0 hrs 10 mins");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace hardcoded timeLeft with prop or derived value

The timeLeft state is initialized with a hardcoded value but should be calculated based on trade parameters or passed as a prop.

- const [timeLeft, setTimeLeft] = useState("0 hrs 10 mins");
+ // Option 1: Accept as prop
+ // Add to ConfirmDrawerProps interface:
+ // timeLeft: string;
+ 
+ // Option 2: Calculate based on trade parameters
+ const calculateTimeLeft = () => {
+   // Calculate based on distance, resource weight, etc.
+   const hours = Math.floor(/* calculation */);
+   const minutes = Math.floor(/* calculation */);
+   return `${hours} hrs ${minutes} mins`;
+ };
+ const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [timeLeft, setTimeLeft] = useState("0 hrs 10 mins");
// Option 1: Accept as prop
// Add to ConfirmDrawerProps interface:
// timeLeft: string;
// Option 2: Calculate based on trade parameters
const calculateTimeLeft = () => {
// Calculate based on distance, resource weight, etc.
const hours = Math.floor(/* calculation */);
const minutes = Math.floor(/* calculation */);
return `${hours} hrs ${minutes} mins`;
};
const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());

Comment on lines +74 to +89
<span className="text-muted-foreground">Travel Time</span>
<span>0 hrs 10 mins</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Total Transfer Weight</span>
<span>2 kg</span>
</div>
<div className="flex justify-between items-center">
<span className="text-muted-foreground">Donkeys Burnt for Transfer</span>
<div className="flex items-center gap-1">
<span>1</span>
<ResourceIcon resourceId={25} size={20} />
<span className="text-sm text-muted-foreground">[1000600]</span>
</div>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Move hardcoded trade information to props or config

The travel time, weight, and donkey information are hardcoded. These should be calculated based on actual trade parameters or passed as props.

interface ConfirmDrawerProps {
  isOpen: boolean;
  onClose: () => void;
  sellAmount: number;
  buyAmount: number;
  sellResource: Resources;
  buyResource: Resources;
  onConfirm: () => Promise<void>;
+ travelTime: string;
+ transferWeight: string;
+ donkeyCount: number;
+ donkeyResourceId: number;
+ donkeyTokenId: string;
}

Then use these props in the render function instead of hardcoded values.

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (1)

41-43: 🛠️ Refactor suggestion

Remove @ts-ignore and fix random data generation issues

The @ts-ignore comment masks potential TypeScript errors. Additionally, generating random data on each render causes unstable UI behavior.

-// @ts-ignore
 export const NearbyEnemies = ({ entityId, onView }: NearbyEnemiesProps) => {
-  const { enemiesCount, distance } = generateDummyData();
+  const { enemiesCount, distance } = React.useMemo(() => generateDummyData(), [entityId]);
🧹 Nitpick comments (9)
client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (4)

9-9: Consider adding runtime validation for resourceId range.

The JSDoc specifies that resourceId should be from 1 to 31, but there's no runtime validation of this constraint.

export const ResourceIcon = ({ resourceId, size = 24, className, showTooltip = false }: ResourceIconProps) => {
+  // Validate resource ID range
+  if (resourceId < 1 || resourceId > 31) {
+    console.warn(`Resource ID ${resourceId} is out of valid range (1-31)`);
+    return null;
+  }
  // Get the resource data based on ID
  const resourceData = resources.find((r: Resources) => r.id === resourceId);

33-36: Consider adding a fallback image for missing resources.

Currently, if a resource is not found, the component returns null, which could lead to UI gaps. Consider adding a fallback image to maintain UI consistency.

if (!resourceData) {
  console.warn(`Resource with ID ${resourceId} not found`);
-  return null;
+  return (
+    <img
+      src="/images/resources/fallback.png"
+      alt="Resource not found"
+      className={cn("object-contain", className)}
+      style={{
+        width: size,
+        height: size,
+      }}
+      loading="lazy"
+    />
+  );
}

40-40: Consider extracting the resource image path to a constant or configuration.

The path to resource images is hardcoded, which might make it difficult to update if the structure changes.

+const RESOURCE_IMAGE_PATH = "/images/resources";
+
export const ResourceIcon = ({ resourceId, size = 24, className, showTooltip = false }: ResourceIconProps) => {
  // ...
  const icon = (
    <img
-      src={`/images/resources/${resourceData.id}.png`}
+      src={`${RESOURCE_IMAGE_PATH}/${resourceData.id}.png`}
      alt={resourceData.trait}
      // ...
    />
  );
  // ...
};

29-68: Consider memoizing the component for performance optimization.

If this component is used in lists or renders frequently with the same props, consider wrapping it with React.memo for performance optimization.

-export const ResourceIcon = ({ resourceId, size = 24, className, showTooltip = false }: ResourceIconProps) => {
+export const ResourceIcon = React.memo(({ resourceId, size = 24, className, showTooltip = false }: ResourceIconProps) => {
  // Component implementation...
-};
+});
client/apps/eternum-mobile/src/pages/trade/ui/swap-confirm-drawer.tsx (1)

44-53: Enforce single confirmation and add optional cleanup steps.
Your try/catch usage is good for capturing errors. If you want to prevent double submits, disable the "Confirm Swap" button upon click or once handleConfirm starts. Also consider whether a finally block is needed to handle cleanup or analytics logging.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (3)

1-4: Consider adding type definitions for component props.

While the imports look good, it would be beneficial to define explicit TypeScript interfaces for the component's props and any data structures it uses, which would improve code maintainability and provide better IntelliSense support.


78-92: Add handling for empty state and loading state.

The component currently assumes that data is always available. Consider adding loading indicators and empty state handling to improve user experience when data is being fetched or when no labor buildings are available.

return (
  <div className="space-y-4">
+   {dummyLaborBuildings.length === 0 ? (
+     <div className="text-center p-4">
+       <p className="text-gray-500">No labor buildings available</p>
+     </div>
+   ) : (
      {dummyLaborBuildings.map((building) => (
        <LaborWidget
          key={building.id}
          building={building}
          resourceBalances={dummyResourceBalances}
          onStartProduction={handleStartProduction}
          onPauseProduction={handlePauseProduction}
          onExtendProduction={handleExtendProduction}
        />
      ))}
+   )}
  </div>
);

1-92: Add component documentation and consider modularizing the dummy data.

Adding JSDoc comments to the component would improve maintainability. Also, consider moving the dummy data to a separate file to keep the component file cleaner and focused on rendering logic.

+/**
+ * ManageTab - A tab component for managing labor buildings in the realm.
+ * Displays a list of labor buildings with their production status and allows
+ * the user to start, pause, or extend production.
+ */
export function ManageTab() {
  // Component implementation...
}
client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (1)

34-39: Consider adding randomization limits based on game logic

The current random data generation provides arbitrary values between fixed ranges. Consider adjusting these ranges based on actual game mechanics or player level to make the demo data more realistic.

 const generateDummyData = () => {
-  const enemiesCount = Math.floor(Math.random() * 10) + 1;
-  const distance = Math.floor(Math.random() * 20) + 1;
+  // Adjust ranges based on game mechanics
+  const maxEnemies = 10; // Could be based on game difficulty or player level
+  const maxDistance = 20; // Could be based on visibility range or map size
+  const enemiesCount = Math.floor(Math.random() * maxEnemies) + 1;
+  const distance = Math.floor(Math.random() * maxDistance) + 1;
   return { enemiesCount, distance };
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1326b2 and 9f3aa9d.

📒 Files selected for processing (6)
  • client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (1 hunks)
  • client/apps/eternum-mobile/src/pages/trade/ui/swap-confirm-drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/arrived-donkeys/ui/arrived-donkeys.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (1 hunks)
  • client/apps/eternum-mobile/src/widgets/realm-info-header/ui/realm-info-header.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/apps/eternum-mobile/src/widgets/realm-info-header/ui/realm-info-header.tsx
🔇 Additional comments (17)
client/apps/eternum-mobile/src/shared/ui/resource-icon.tsx (4)

1-3: Well-organized imports with clear structure.

The imports are well organized, clearly separating utility functions, external resources, and UI components.


5-24: Well-documented interface with clear JSDoc comments.

The ResourceIconProps interface is thoroughly documented with JSDoc comments that clearly explain the purpose of each prop, including default values and constraints.


38-49: Great use of lazy loading for resource images.

Using the loading="lazy" attribute on the image is a good practice for performance optimization, especially for a mobile application where resource conservation is important.


55-67: Well-implemented tooltip with semantic structure.

The tooltip implementation is well-structured and provides a good user experience by showing detailed information when needed without cluttering the UI.

client/apps/eternum-mobile/src/pages/trade/ui/swap-confirm-drawer.tsx (6)

31-32: Replace hardcoded timeLeft with a prop or derived value.
Using // @ts-ignore to suppress a potential type error is not ideal, and you are still hardcoding the time (“0 hrs 10 mins”). This repeats a past review suggestion. Consider removing the ignore directive and dynamically calculating or passing “timeLeft” to improve maintainability and correctness.


34-42: Clear the timeout to prevent potential memory leaks.
When closing the drawer, a setTimeout resets the component state. However, if the drawer is quickly reopened, the component re-renders before the timeout completes, which might cause race conditions. Consider storing the timeout ID and clearing it in a cleanup function or when the drawer is reopened.


75-90: Move hardcoded donkey and trade details into props or config.
This content (travel time, weight, donkey info, etc.) should ideally be dynamically derived from real trade parameters or passed in as props, avoiding hardcoded values. This is identical to a previous review suggestion.


120-152: Swapping state logic looks solid.
Blocking the confirmation button is a good way to prevent duplicate requests.


153-185: Success state implementation appears fine.
The success UI layout is clear and the flow transitions smoothly.


186-218: Error state is well-handled.
A clear error message, plus a retry option, provides a user-friendly recovery route.

client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (1)

5-63:

❓ Verification inconclusive

Replace dummy data with real data fetching.

The code uses hardcoded dummy data for labor buildings and resource balances. This is appropriate for initial development, but should be replaced with actual data fetching from your backend before production.

Are there existing data fetching hooks or services in the codebase that could be used here? Consider implementing a custom hook like useLaborBuildings and useResourceBalances to fetch this data.


🏁 Script executed:

#!/bin/bash
# Look for existing data fetching patterns for resources and buildings
echo "Searching for resource and building data fetching patterns..."
rg -A 3 "useResource|useBuilding|fetchResource|fetchBuilding" --type ts

Length of output: 11393


Action Required: Replace Hardcoded Dummy Data with Backend-Fetched Data

Please replace the hardcoded dummy data for labor buildings and resource balances in this file with dynamic data fetched from the backend. Our codebase already leverages data fetching utilities in multiple places—such as using useResourceManager, useResourceBalance, and useBuildings (see, for example, the implementations in packages/react/src/hooks/helpers/use-resources.ts and packages/react/src/hooks/helpers/use-buildings.ts).

  • For labor buildings:
    If no dedicated hook exists, consider implementing a custom hook (e.g. useLaborBuildings) that derives or fetches labor building data, possibly by extending the logic of the existing useBuildings hook.

  • For resource balances:
    Replace the dummy array with a data fetching mechanism using the established patterns (e.g. via useResourceBalance).

Please ensure the new implementations align with our existing data fetching patterns and verify that they integrate correctly with the backend.

client/apps/eternum-mobile/src/widgets/arrived-donkeys/ui/arrived-donkeys.tsx (3)

19-20: Unused prop and unstable rendering due to random data generation

The entityId prop is defined but not used. Additionally, using generateDummyData() directly in the component body will create new random values on every render, causing UI instability.

 export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => {
-  const { donkeysCount } = generateDummyData();
+  // Stabilize random data by using React.useMemo
+  const { donkeysCount } = React.useMemo(() => generateDummyData(), [entityId]);
+  
+  // Use entityId in a side effect to demonstrate its purpose
+  React.useEffect(() => {
+    console.log(`Showing donkeys for entity ID: ${entityId}`);
+  }, [entityId]);

1-4: LGTM! Clean imports using alias paths

The imports use a clean structure with alias paths for better organization and maintainability.


24-42: Well-structured card layout with semantic HTML

The card layout is well-organized with appropriate hierarchy and semantic structure. The use of separate components like CardTitle and CardContent with proper spacing classes creates a clean UI.

client/apps/eternum-mobile/src/widgets/nearby-enemies/ui/nearby-enemies.tsx (3)

11-32: Well-structured danger level helper function

The getDangerLevel function provides a clean way to categorize and style content based on distance thresholds. The consistent return structure with color, background color, and icon makes it easy to use with React components.


47-66: Dynamic styling based on danger level provides clear visual feedback

The component intelligently applies dynamic styling based on the calculated danger level, which provides immediate visual feedback to users about the threat level. The consistent use of styling patterns across different states makes the UI predictable and user-friendly.


6-9: LGTM! Clean props interface

The props interface is clearly defined with appropriate types.

Comment on lines +65 to +77
export function ManageTab() {
const handleStartProduction = useCallback((buildingId: string, mode: "raw" | "labor") => {
console.log("Start production", { buildingId, mode });
}, []);

const handlePauseProduction = useCallback((buildingId: string) => {
console.log("Pause production", { buildingId });
}, []);

const handleExtendProduction = useCallback((buildingId: string, mode: "raw" | "labor") => {
console.log("Extend production", { buildingId, mode });
}, []);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Implement actual functionality for the callback handlers.

The current callback functions only log actions to the console. Before deploying to production, these should be connected to actual API calls or state updates to provide real functionality.


🏁 Script executed:

#!/bin/bash
# Look for similar production management patterns in the codebase
echo "Searching for production management patterns..."
rg -A 5 "startProduction|pauseProduction|extendProduction" --type ts

Length of output: 3963


Action Required: Replace Console Logs with Actual Production Management Calls

The current callback handlers in client/apps/eternum-mobile/src/pages/realm/ui/tabs/manage-tab.tsx (lines 65–77) merely log actions rather than invoking the real production management logic. In other parts of the codebase—such as in packages/core/src/managers/tile-manager.ts and related UI modules—the production actions are implemented by calling API methods (e.g., pauseProduction, resumeProduction) that handle state updates and error handling.

Please update the following handlers:

  • handleStartProduction: Replace the console.log with a call that starts production. Consider leveraging the production manager’s method (e.g., a corresponding startProduction API call) to initiate production.
  • handlePauseProduction: Implement this by calling the actual production pause function (similar to the pauseProduction implementation found in the tile manager).
  • handleExtendProduction: Replace the log with a call that extends production, following the established pattern for production management elsewhere in the codebase.

This update should ensure consistency with similar functionalities in other modules and prepare the code for production deployment.

Comment on lines +18 to +20
// @ts-ignore
export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => {
const { donkeysCount } = generateDummyData();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove @ts-ignore and fix the underlying type issues

The @ts-ignore comment suppresses TypeScript errors, which reduces code quality and can hide potential bugs. If there are TypeScript issues, they should be addressed directly rather than ignored.

-// @ts-ignore
 export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @ts-ignore
export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => {
const { donkeysCount } = generateDummyData();
export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => {
const { donkeysCount } = generateDummyData();

* feat: add Torii local launcher (#2663)

* Add torii-launcher

* address PR comments + restyle

* Add dynamic config

* Finish and polish app
- Add notification
- Add settings to change RPC
- Adds different db directories for all configurations

* fix build issues

* update torii version if wrong one

* fix version check

* update world address

* keep syncing state component mounted even when going to settings

* adding back error logs

* update constants

---------

Co-authored-by: aymericdelab <[email protected]>

* adding new labor system (#2682)

* new production

* new production

* new production

* new production

* new production

* new production

* fix pr comments

* fix pr comments

* fix pr comments

* fix pr comments

* new deploy

* fix bug

* update policies

* update structure entity id in scenes

* update demon hide to labor

* add production module

* first draft new production

* add raw resources into resource

* fix: production output amount

* add labor burn

* adding burn resource for labor

* fix labor bugs and add more realms info

* small fixes

* clean + remove console.logs

* redeployed sepolia and changed config values for labor

* new config

* new config

* new config

* contrct fix

* add transfer resources above modals

* fix overlay

* update policies and capacity info

* selected realm for production

* remove console.log

* feedback

* knip + prettier

* knip

* prettier torii launcher

* update sepolia env

* fix build issues

---------

Co-authored-by: Credence <[email protected]>

* fix infinite rerender

* fix castle not appearing

* fix active / paused production

* update battle simulator (#2686)

* add the combat simulator

* small fix

* remove old battle and pillage simulator

* knip prettier

* add hidden feature

* address comments

* Please update README.md or add scripts folder. (#2688)

* Please update README.md or add scripts folder.
Fixes #2684

* update readme.md

---------

Co-authored-by: Credence <[email protected]>

* Feat/new combat (#2725)

* new production

* new production

* new production

* new production

* new production

* new production

* fix pr comments

* fix pr comments

* fix pr comments

* fix pr comments

* combat: skeleton

* combat: update

* combat: update

* combat: update

* combat: resource update

* combat: resource update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* Optimize contracts by removing models, grouping together and deleting entities (#2690)

* scarb fmt

* combat: update

* contracts: update trade

* contracts: add max trade count

* update

* contracts: update bank

* contracts: update

* scarb fmt

* contracts: initialize structure weight

* contracts: initialize troop capacity

* scarb fmt

* scarb fmt

* contracts: add building capacity perks

* new movement (#2691)

* [client] one hex = one army
Fixes #2677

* fix find paths

* fix pathfinding bug

* fix old army position bug

* remove felt center from client

* remove comment

* fix exploration bug + add biome path

* fix action info and simplify findPaths

* fix max hex cost

* feedback

* new combat client mockup (#2693)

* [client] one hex = one army
Fixes #2677

* fix find paths

* fix pathfinding bug

* fix old army position bug

* remove felt center from client

* remove comment

* fix exploration bug + add biome path

* fix action info and simplify findPaths

* fix max hex cost

* feedback

* refactor find paths and add action paths

* refactor travel path to action path

* fix highlight bug

* show right action info

* v1 of new combat modal

* first draft combat v1

* fix bugs

* improve combat modal

* refactor

* refactor

* Start integrating contract changes to client

* contracts: fix config

* update resource ids

* scarb fmt

* remove unused manifest

* rename

* update

* update

* unique troop type + bug fixes  (#2694)

* limit to one troop type

* can only create attacking armies

* fix bugs

* add help action path

* add help container

* add mockup structure defenses

* add new defense management

* improve styling

* remove console.log

* fix build issues

* update

* delete unused files

* bug fixes

* bug fixes

* update

* contracts: comment out tests

* fix build issues

* deploy locally

* Feat/combat new (#2692)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* update version and components

* components refactoring

* updated building quantity and population models (#2697)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* local deploy + contracts change client refactor

* refactor position owner and building quantity

* start fix sync

* contract updates (#2699)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* contracts: update troops impl

* contracts: explorer creation tests

* contracts: fix explorer add

* contracts: tests explorer add

* scarb fmt

* fix ci

* fix ci

* contracts: fix build

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* contracts: explorer delete tests

* contracts: explorer swap tests

* contracts: explorer swap tests

* contracts: update structure struct

* Upgrade contracts to dojo version 1.2.1 (#2701)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* contracts: update troops impl

* contracts: explorer creation tests

* contracts: fix explorer add

* contracts: tests explorer add

* scarb fmt

* fix ci

* fix ci

* contracts: fix build

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* contracts: explorer delete tests

* contracts: explorer swap tests

* contracts: explorer swap tests

* contracts: update structure struct

* upgrade to dojo v1.2.1

* contracts: fix tests

* contracts: update structure model (#2702)

* contracts: refactor structure owner handling across contracts

* contracts: minor updates

* contracts: rename building_count to packed_counts in StructureBuildings model

* fix package build

* new owner structure

* fix build

* contracts: improve and test troop movement (#2703)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* fix client

* Troop attack tests (#2704)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* contracts: update troop attack

* contracts: add explorer vs explorer tests

* update ci

* contracts: add explorer vs guards tests

* contracts: add guards vs explorers tests

* update occupier model (#2705)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* contracts: update troop attack

* contracts: add explorer vs explorer tests

* update ci

* contracts: add explorer vs guards tests

* contracts: add guards vs explorers tests

* contracts: update occupier model

* fix

* update scarb back

* contracts: upgrade to dojo 1.2.1

* contracts: explore hexes adjacent to structures (#2708)

* feature: allow multiple banks (#2706)

* contracts: feat: regional banks

* feat: regional banks: fix configs

* feat: regional banks: fix configs

* config: bug fixes

* config: fix bank spawn locations

* config: fix bank spawn locations

* comment

* debug client

* contracts: realm pays food cost for explorer travel

* fix bugs

* fix bugs

* add defense slot troops

* fix bugs and add attack

* update libs and allow friendly fire

* update combat simulator

* Allow creation of villages (#2710)

* feat: add villages

* feat: add villages

* contracts: update dojo toml

* client: update call

* contracts: initialize troops resource model && scarb fmt

* fix build issues

---------

Co-authored-by: raschel <[email protected]>
Co-authored-by: aymericdelab <[email protected]>

* contracts: initialize resource arrivals models

* updata dojo.js

* fix combat

* explorer vs structure

* feat: allow players find hypers on the map with the highest chance at the center of the map and less chance with distance (#2711)

* client: fix combat simulator

* update: single vrf call for troop movement

* start structure attacks

* client: fix combat simulator

* client: minor bug fix

* refactor army /structure action

* Update hyperstructure foundations (#2712)

* contracts:
- allow hyp foundation to construct from its balance.
- use hyperstructure count during foundation find probability calc

* config: update for hyperstrcuture foundations

* update dojo js

* fix bugs

* delete army when die, and remove getcomponent value strict

* show more army attack info

* add text info

* remove resource bug

* fix resource production

* start trading refactor

* fix: trade config

* contracts: update resource arrivals

* update to latest dojo.js

* change resource order png

* contracts: allow troop <-> structure resource transfer

* fix resource bug

* Remove onchain quests (#2709)

* Remove onchain quests

* Lint

* Bob/various fixes (#2700)

* Please update README.md or add scripts folder. (#2688)

* Please update README.md or add scripts folder.
Fixes #2684

* update readme.md

---------

Co-authored-by: Credence <[email protected]>

* Granular subsciption to zustand store

* Clean unused properties in game renderer

* Fix docs build

* Delete deprecated assets

* Fix img paths

---------

Co-authored-by: raschel <[email protected]>
Co-authored-by: Credence <[email protected]>

* fix build issues

* update army production (#2713)

* fix contracts

* add missing resources

* new troop tier building fixses

* config: fix

* start trade fix

* change resource inputs and building costs

* contracts: update trade (#2714)

* contracts: update trade

* config: update trade

* scarb fmt

* Fix/building (#2715)

* fix: remove unused buildings types sp the max building category id is 16. this allows `packed_counts` hold count for all building types

* fix: building count store bug

* contracts: enforce structure battle immunity period

* contracts: accept trade bug fix

* contracts: fix swap bug

* fix market modal bugs

* contracts: fix liquidity bug

* market

* fix hyperstructure

* Remove Owner and EntityOwner models (#2716)

* contracts: remove owner model

* client: update guild model

* katana: fix block timestamp issue

* fix swap, liquidity actions and resource arrivals (#2718)

* increase local starting resources

* fix donkey starting

* fix username locally

* change to nano precison

* change gram to nanogram

* contracts: resource arrival time bug fix

* comment

* contracts: fix donkey capacity calculation

* revert back nanograms

* remove comment out

* fix troop tier selection

* fix combat container and modal

* fix combat + naming

* fix population issue

* fix

* fix armies + ready for deployment on sepolia

* refactor config manager

* resource transfer/troop transfer v1

* update sepolia rpc

* update sepolia rpc

* deploy game to sepolia

* client: update sepolia torii urls

* contracts: fix structure to troop transfer bug

* add resource and troop transfer

* refactor and allow ennemies to transfer troops/resources

* change color

* update landing

* contracts: remove position model

* New arrivals (#2719)

* New arrivals

* Bank fees

* Add time ticking down

* Fix transport resource weight

* Formatting

* Formatting

* Fix react hook issue

* start fix weight

* fix bugs

* check free directions

* refactoring + design improvements

* fix

* cleaning, knip, formatting

* fix build landing

---------

Co-authored-by: aymericdelab <[email protected]>

---------

Co-authored-by: Credence <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: Bob <[email protected]>

* Feat/new combat (#2726)

* new production

* new production

* new production

* new production

* new production

* new production

* fix pr comments

* fix pr comments

* fix pr comments

* fix pr comments

* combat: skeleton

* combat: update

* combat: update

* combat: update

* combat: resource update

* combat: resource update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* combat: update

* Optimize contracts by removing models, grouping together and deleting entities (#2690)

* scarb fmt

* combat: update

* contracts: update trade

* contracts: add max trade count

* update

* contracts: update bank

* contracts: update

* scarb fmt

* contracts: initialize structure weight

* contracts: initialize troop capacity

* scarb fmt

* scarb fmt

* contracts: add building capacity perks

* new movement (#2691)

* [client] one hex = one army
Fixes #2677

* fix find paths

* fix pathfinding bug

* fix old army position bug

* remove felt center from client

* remove comment

* fix exploration bug + add biome path

* fix action info and simplify findPaths

* fix max hex cost

* feedback

* new combat client mockup (#2693)

* [client] one hex = one army
Fixes #2677

* fix find paths

* fix pathfinding bug

* fix old army position bug

* remove felt center from client

* remove comment

* fix exploration bug + add biome path

* fix action info and simplify findPaths

* fix max hex cost

* feedback

* refactor find paths and add action paths

* refactor travel path to action path

* fix highlight bug

* show right action info

* v1 of new combat modal

* first draft combat v1

* fix bugs

* improve combat modal

* refactor

* refactor

* Start integrating contract changes to client

* contracts: fix config

* update resource ids

* scarb fmt

* remove unused manifest

* rename

* update

* update

* unique troop type + bug fixes  (#2694)

* limit to one troop type

* can only create attacking armies

* fix bugs

* add help action path

* add help container

* add mockup structure defenses

* add new defense management

* improve styling

* remove console.log

* fix build issues

* update

* delete unused files

* bug fixes

* bug fixes

* update

* contracts: comment out tests

* fix build issues

* deploy locally

* Feat/combat new (#2692)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* update version and components

* components refactoring

* updated building quantity and population models (#2697)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* local deploy + contracts change client refactor

* refactor position owner and building quantity

* start fix sync

* contract updates (#2699)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* contracts: update troops impl

* contracts: explorer creation tests

* contracts: fix explorer add

* contracts: tests explorer add

* scarb fmt

* fix ci

* fix ci

* contracts: fix build

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* contracts: explorer delete tests

* contracts: explorer swap tests

* contracts: explorer swap tests

* contracts: update structure struct

* Upgrade contracts to dojo version 1.2.1 (#2701)

* fix scarb

* fix scarb

* contract combat calculation equivalence

* fix config

* fix config

* contracts: place single castle

* contracts: new model for structure building quantity and population

* minor restructure

* test wip

* contracts: update troops impl

* contracts: explorer creation tests

* contracts: fix explorer add

* contracts: tests explorer add

* scarb fmt

* fix ci

* fix ci

* contracts: fix build

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* contracts: explorer delete tests

* contracts: explorer swap tests

* contracts: explorer swap tests

* contracts: update structure struct

* upgrade to dojo v1.2.1

* contracts: fix tests

* contracts: update structure model (#2702)

* contracts: refactor structure owner handling across contracts

* contracts: minor updates

* contracts: rename building_count to packed_counts in StructureBuildings model

* fix package build

* new owner structure

* fix build

* contracts: improve and test troop movement (#2703)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* fix client

* Troop attack tests (#2704)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* contracts: update troop attack

* contracts: add explorer vs explorer tests

* update ci

* contracts: add explorer vs guards tests

* contracts: add guards vs explorers tests

* update occupier model (#2705)

* contracts: guard_add entrypoint tests

* contracts: add guard_delete test

* minor variable name update

* minor variable name update

* contracts: update troop battle impl

* contracts: update troop battle impl

* contracts: allow explorer to guard troop swaps

* contracts: allow explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: test explorer to guard troop swaps

* contracts: improve and test troop movement

* contracts: improve and test troop movement

* misc

* misc

* update ci

* update ci

* update ci

* update ci

* contracts: update troop attack

* contracts: add explorer vs explorer tests

* update ci

* contracts: add explorer vs guards tests

* contracts: add guards vs explorers tests

* contracts: update occupier model

* fix

* update scarb back

* contracts: upgrade to dojo 1.2.1

* contracts: explore hexes adjacent to structures (#2708)

* feature: allow multiple banks (#2706)

* contracts: feat: regional banks

* feat: regional banks: fix configs

* feat: regional banks: fix configs

* config: bug fixes

* config: fix bank spawn locations

* config: fix bank spawn locations

* comment

* debug client

* contracts: realm pays food cost for explorer travel

* fix bugs

* fix bugs

* add defense slot troops

* fix bugs and add attack

* update libs and allow friendly fire

* update combat simulator

* Allow creation of villages (#2710)

* feat: add villages

* feat: add villages

* contracts: update dojo toml

* client: update call

* contracts: initialize troops resource model && scarb fmt

* fix build issues

---------

Co-authored-by: raschel <[email protected]>
Co-authored-by: aymericdelab <[email protected]>

* contracts: initialize resource arrivals models

* updata dojo.js

* fix combat

* explorer vs structure

* feat: allow players find hypers on the map with the highest chance at the center of the map and less chance with distance (#2711)

* client: fix combat simulator

* update: single vrf call for troop movement

* start structure attacks

* client: fix combat simulator

* client: minor bug fix

* refactor army /structure action

* Update hyperstructure foundations (#2712)

* contracts:
- allow hyp foundation to construct from its balance.
- use hyperstructure count during foundation find probability calc

* config: update for hyperstrcuture foundations

* update dojo js

* fix bugs

* delete army when die, and remove getcomponent value strict

* show more army attack info

* add text info

* remove resource bug

* fix resource production

* start trading refactor

* fix: trade config

* contracts: update resource arrivals

* update to latest dojo.js

* change resource order png

* contracts: allow troop <-> structure resource transfer

* fix resource bug

* Remove onchain quests (#2709)

* Remove onchain quests

* Lint

* Bob/various fixes (#2700)

* Please update README.md or add scripts folder. (#2688)

* Please update README.md or add scripts folder.
Fixes #2684

* update readme.md

---------

Co-authored-by: Credence <[email protected]>

* Granular subsciption to zustand store

* Clean unused properties in game renderer

* Fix docs build

* Delete deprecated assets

* Fix img paths

---------

Co-authored-by: raschel <[email protected]>
Co-authored-by: Credence <[email protected]>

* fix build issues

* update army production (#2713)

* fix contracts

* add missing resources

* new troop tier building fixses

* config: fix

* start trade fix

* change resource inputs and building costs

* contracts: update trade (#2714)

* contracts: update trade

* config: update trade

* scarb fmt

* Fix/building (#2715)

* fix: remove unused buildings types sp the max building category id is 16. this allows `packed_counts` hold count for all building types

* fix: building count store bug

* contracts: enforce structure battle immunity period

* contracts: accept trade bug fix

* contracts: fix swap bug

* fix market modal bugs

* contracts: fix liquidity bug

* market

* fix hyperstructure

* Remove Owner and EntityOwner models (#2716)

* contracts: remove owner model

* client: update guild model

* katana: fix block timestamp issue

* fix swap, liquidity actions and resource arrivals (#2718)

* increase local starting resources

* fix donkey starting

* fix username locally

* change to nano precison

* change gram to nanogram

* contracts: resource arrival time bug fix

* comment

* contracts: fix donkey capacity calculation

* revert back nanograms

* remove comment out

* fix troop tier selection

* fix combat container and modal

* fix combat + naming

* fix population issue

* fix

* fix armies + ready for deployment on sepolia

* refactor config manager

* resource transfer/troop transfer v1

* update sepolia rpc

* update sepolia rpc

* deploy game to sepolia

* client: update sepolia torii urls

* contracts: fix structure to troop transfer bug

* add resource and troop transfer

* refactor and allow ennemies to transfer troops/resources

* change color

* update landing

* contracts: remove position model

* New arrivals (#2719)

* New arrivals

* Bank fees

* Add time ticking down

* Fix transport resource weight

* Formatting

* Formatting

* Fix react hook issue

* start fix weight

* fix bugs

* check free directions

* refactoring + design improvements

* fix

* cleaning, knip, formatting

* fix build landing

---------

Co-authored-by: aymericdelab <[email protected]>

* Bump controller version + fix sepolia login (#2721)

Co-authored-by: aymericdelab <[email protected]>

* update policies

---------

Co-authored-by: Credence <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: Bob <[email protected]>

* one click produce + remove construction menu + remove logging (#2727)

* remove construction on worldmap

* one click produce + remove construction menu + remove logging

* Fix policies + add script to update them (#2728)

* [client] new models for biomes and buildings (#2735)

* chore: gitignore

* feat: new biomes models

* feat: new buildings

* feat: postprocessing

* fix: fog effect

* feat: vignette

* fix: refactor renderer

* fix: renderer refactor

* fix: mines model

* feat: knights models

* fix some next bugs + refactor (#2738)

* fix subscription

* refactor entity hover + subs

* remove unused input

* knip and format

* fix buid issues

---------

Co-authored-by: tedison <[email protected]>
Co-authored-by: Credence <[email protected]>
Co-authored-by: tedison <[email protected]>
Co-authored-by: Bob <[email protected]>
Co-authored-by: zabanyat.eth <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (11)
client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (4)

109-110: Consider removing console logs for production

There are console logs in the connection flow that should be removed or replaced with proper logging for production builds.

-      console.log("Attempting to connect wallet...");
       await connect({ connector: connectors[0] });
-      console.log("Wallet connected successfully.");

129-166: Add error handling to setUserName function

The setUserName function could fail when calling set_address_name, but there's no error handling. Consider adding try/catch to handle potential errors.

   const setUserName = async () => {
+    try {
       const username = await (connector as ControllerConnector)?.username();
       if (!username) return;

       const usernameFelt = cairoShortStringToFelt(username.slice(0, 31));
       value.systemCalls.set_address_name({
         signer: controllerAccount!,
         name: usernameFelt,
       });
       setAddressName(username);
+    } catch (error) {
+      console.error("Failed to set username:", error);
+    }
   };

155-165: Extract retry limit to a constant

The retry mechanism uses a hardcoded value of 10. Consider extracting this to a named constant for better maintainability.

+const MAX_CONNECTION_RETRIES = 10;

// Then in the component:
   setTimeout(() => {
     setRetries((prevRetries) => {
-      if (prevRetries < 10) {
+      if (prevRetries < MAX_CONNECTION_RETRIES) {
         return prevRetries + 1;
       } else {
         setAccountsInitialized(true);
         return prevRetries;
       }
     });
   }, 100);

186-190: Remove unnecessary Fragment

This Fragment wrapping a single child is redundant and can be simplified.

-            <>
               <Button onClick={connectWallet} className="!bg-[#FCB843] !text-black border-none hover:!bg-[#FCB843]/80">
                 Log In
               </Button>
-            </>
🧰 Tools
🪛 Biome (1.9.4)

[error] 186-190: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

client/apps/eternum-mobile/.env.slot (1)

17-22: Ensure consistent formatting for environment variables

The environment variables have inconsistent formatting - some values are quoted while others are not. For consistency and to avoid potential issues, use quotes for all string values.

-VITE_PUBLIC_CHAIN=slot                            
-VITE_PUBLIC_SLOT=slot-real-cave                          
-VITE_PUBLIC_NODE_URL=https://api.cartridge.gg/x/slot-real-cave/katana                              
-VITE_PUBLIC_TORII=https://api.cartridge.gg/x/slot-real-cave/torii 
+VITE_PUBLIC_CHAIN="slot"                            
+VITE_PUBLIC_SLOT="slot-real-cave"                          
+VITE_PUBLIC_NODE_URL="https://api.cartridge.gg/x/slot-real-cave/katana"                              
+VITE_PUBLIC_TORII="https://api.cartridge.gg/x/slot-real-cave/torii"
client/apps/eternum-mobile/.env.local.sample (1)

17-18: Consider adding informative comments for VRF and fee recipient

For sample files, it's helpful to include comments explaining what these values represent and how users should configure them for local development.

-VITE_PUBLIC_VRF_PROVIDER_ADDRESS="0x0"
-VITE_PUBLIC_CLIENT_FEE_RECIPIENT=0x045c587318c9ebcf2fbe21febf288ee2e3597a21cd48676005a5770a50d433c5
+# VRF provider address for randomness generation
+VITE_PUBLIC_VRF_PROVIDER_ADDRESS="0x0"
+# Address that receives client fees
+VITE_PUBLIC_CLIENT_FEE_RECIPIENT=0x045c587318c9ebcf2fbe21febf288ee2e3597a21cd48676005a5770a50d433c5
client/apps/eternum-mobile/src/shared/ui/select.tsx (2)

13-31: Consider adding aria attributes for better accessibility

The SelectTrigger component would benefit from additional ARIA attributes to enhance accessibility, particularly for screen readers.

<SelectPrimitive.Trigger
  ref={ref}
  className={cn(
    "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
    className,
  )}
+ aria-haspopup="listbox"
  {...props}
>

61-91: Consider adding mobile touch optimization

The SelectContent component would benefit from additional touch-friendly optimizations for mobile devices, especially considering this is for a mobile application.

Add properties to improve touch interaction:

<SelectPrimitive.Content
  ref={ref}
  className={cn(
    "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
    position === "popper" &&
      "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
    className,
  )}
+ touchAction="manipulation"
  position={position}
  {...props}
>
client/apps/eternum-mobile/.env.production (1)

15-18: Correct comment formatting in environment file

Environment files shouldn't contain comments in JavaScript format. Use the standard environment file comment format.

-// optional
+# optional
VITE_PUBLIC_GAME_VERSION="v1.0.0"
VITE_PUBLIC_GRAPHICS_DEV=false
VITE_SOCIAL_LINK=http://bit.ly/3Zz1mpp
client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (2)

19-38: Consider optional aria attributes for custom accessibility.
Though Radix UI handles accessibility out of the box, you could optionally add aria-labels or roles if you need specialized a11y requirements. The forwardRef pattern is well-implemented.


73-89: Minor note on the 'inset' prop.
Consider adding a short comment explaining that 'inset' increases left padding for clarity, especially for new contributors.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3aa9d and ad14a96.

📒 Files selected for processing (28)
  • .knip.json (1 hunks)
  • client/apps/eternum-mobile/.env.local.sample (1 hunks)
  • client/apps/eternum-mobile/.env.mainnet (1 hunks)
  • client/apps/eternum-mobile/.env.production (1 hunks)
  • client/apps/eternum-mobile/.env.sepolia (1 hunks)
  • client/apps/eternum-mobile/.env.slot (1 hunks)
  • client/apps/eternum-mobile/eslint.config.js (1 hunks)
  • client/apps/eternum-mobile/package.json (1 hunks)
  • client/apps/eternum-mobile/postcss.config.js (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/policies.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/signing-policy.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/store/use-account-store.ts (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/badge.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/button.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/card.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/collapsible.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/drawer.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/input.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/scroll-area.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/select.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/sheet.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/tabs.tsx (1 hunks)
  • client/apps/eternum-mobile/src/shared/ui/tooltip.tsx (1 hunks)
  • client/apps/eternum-mobile/tailwind.config.js (1 hunks)
  • client/apps/game/src/three/scenes/worldmap.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • client/apps/game/src/three/scenes/worldmap.tsx
✅ Files skipped from review due to trivial changes (2)
  • client/apps/eternum-mobile/.env.mainnet
  • client/apps/eternum-mobile/.env.sepolia
🚧 Files skipped from review as they are similar to previous changes (12)
  • client/apps/eternum-mobile/postcss.config.js
  • client/apps/eternum-mobile/src/shared/ui/input.tsx
  • client/apps/eternum-mobile/src/shared/ui/collapsible.tsx
  • client/apps/eternum-mobile/src/shared/ui/tooltip.tsx
  • client/apps/eternum-mobile/eslint.config.js
  • client/apps/eternum-mobile/src/shared/store/use-account-store.ts
  • client/apps/eternum-mobile/package.json
  • client/apps/eternum-mobile/src/shared/ui/scroll-area.tsx
  • client/apps/eternum-mobile/src/shared/ui/button.tsx
  • client/apps/eternum-mobile/tailwind.config.js
  • client/apps/eternum-mobile/src/shared/ui/badge.tsx
  • client/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx

[error] 186-190: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🪛 Gitleaks (8.21.2)
client/apps/eternum-mobile/.env.production

2-2: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


4-4: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

client/apps/eternum-mobile/.env.local.sample

2-2: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


4-4: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (38)
.knip.json (1)

30-30: Confirmed: Ignoring eternum-mobile in Knip configuration

The addition of "client/apps/eternum-mobile/**" to the ignore list is appropriate for the new mobile application being introduced in this PR.

client/apps/eternum-mobile/src/shared/hooks/context/policies.ts (1)

1-593: Add documentation for contract addresses and their purposes.

The file contains multiple contract addresses without documentation explaining their purposes or functionalities, making it difficult for other developers to understand their roles in the system.

Add descriptive comments for each contract address:

export const policies = toSessionPolicies({
  contracts: {
+    // Configuration Contract - Manages banks, names and world dispatcher
    "0xcfb18212478b8938e327b106dba3180d879047dcb6a4fbf4bfde1c92aa3834": {
      methods: [
        // ...
      ],
    },
+    // World Configuration Contract - Manages various game settings and parameters
    "0x11a287955d3d3ed0ecb09cd2efa7cff790a4fc51855a9686e7a6550c0b77759": {
      methods: [
        // ...
      ],
    },
+    // VRF Contract - Handles random number generation for the game
    "0x051fea4450da9d6aee758bdeba88b2f665bcbf549d2c61421aa724e9ac0ced8f": {
      methods: [
        {
          name: "VRF",
          description: "Verifiable Random Function",
          entrypoint: "request_random",
        },
      ],
    },
    // ... other contracts
  },
  messages,
});
client/apps/eternum-mobile/src/shared/hooks/context/signing-policy.ts (1)

26-26: Improve error handling for invalid chain values

The code assumes the environment variable is either "mainnet" or something else (defaulting to Sepolia). This could lead to unexpected behavior with invalid values.

 import { env } from "../../../../env";
 
+const VALID_CHAINS = {
+  MAINNET: "mainnet",
+  SEPOLIA: "sepolia",
+};
+
+const CHAIN_IDS = {
+  MAINNET: "SN_MAIN",
+  TESTNET: "SN_SEPOLIA",
+};
+
+const getChainId = (chain: string): string => {
+  switch (chain) {
+    case VALID_CHAINS.MAINNET:
+      return CHAIN_IDS.MAINNET;
+    case VALID_CHAINS.SEPOLIA:
+      return CHAIN_IDS.TESTNET;
+    default:
+      console.warn(`Unknown chain: ${chain}, defaulting to Sepolia`);
+      return CHAIN_IDS.TESTNET;
+  }
+};
+
 export const messages = [
   {
     // ... types definition remains the same
     primaryType: "s1_eternum-Message",
     domain: {
       name: "Eternum",
       version: "1",
-      chainId: env.VITE_PUBLIC_CHAIN == "mainnet" ? "SN_MAIN" : "SN_SEPOLIA",
+      chainId: getChainId(env.VITE_PUBLIC_CHAIN),
       revision: "1",
     },
   },
 ];
client/apps/eternum-mobile/src/shared/ui/card.tsx (1)

5-41: LGTM! Well-structured card components with proper React patterns

The implementation follows React best practices:

  • Using forwardRef for ref forwarding
  • Setting displayNames for each component
  • Composable design pattern for card elements
  • Proper use of className merging with cn utility
client/apps/eternum-mobile/src/shared/ui/tabs.tsx (4)

1-7: Clean imports and utility setup

Good use of importing Radix UI primitives and the utility function for class name management. This sets up a solid foundation for the tab components.


8-22: Well-structured TabsList component

The Tabs component correctly wraps the Radix primitive, and the TabsList implementation follows best practices with forwardRef pattern and proper prop handling. The classnames provide good default styling while allowing for customization.


23-37: Accessible TabsTrigger component

The TabsTrigger component includes proper accessibility features with focus states and appropriate styling for active/inactive states. The use of data-[state=active] selectors ensures visual feedback to users.


38-54: Proper completion of the tabs component set

The TabsContent component completes the tabs implementation with appropriate styling and accessibility features. The export statement makes all components available for use throughout the application.

client/apps/eternum-mobile/src/shared/hooks/context/dojo-context.tsx (5)

1-19: Good setup for account management constants and imports

The imports are comprehensive and the NULL_ACCOUNT constant provides a clean default value for accounts.


20-30: Proper environment variable validation

Good practice checking for required environment variables early in the execution. This prevents cryptic errors later when these values are used.


31-51: Well-structured hooks with proper memoization

The custom hooks useMasterAccount and useRpcProvider use useMemo appropriately to prevent unnecessary re-creation of objects.


53-69: Account synchronization with store

The useControllerAccount hook properly synchronizes the account and connector with the global store.


71-84: Singleton pattern for DojoProvider

Good implementation of ensuring the provider can only be used once in the component tree.

client/apps/eternum-mobile/src/shared/ui/sheet.tsx (6)

1-15: Clean setup for Sheet components

Good use of Radix UI primitives and appropriate exports for the base components.


16-30: Well-implemented SheetOverlay with animation

The SheetOverlay component correctly implements the overlay with proper animations for open/close states using data attributes.


31-49: Comprehensive sheet position variants

The sheetVariants implementation provides a good variety of positioning options with appropriate animations for each direction.


50-69: Accessible SheetContent with close button

The SheetContent component includes a proper close button with an icon and screen reader text, enhancing accessibility.


70-95: Well-structured supporting components

The header, footer, title, and description components provide a consistent structure for sheet content with appropriate styling.


96-107: Complete export of sheet components

All components are properly exported for use throughout the application.

client/apps/eternum-mobile/src/shared/ui/drawer.tsx (5)

1-9: Clean Drawer component setup

Good implementation of the base Drawer component with a configurable background scaling option.


10-24: Proper primitive exports and overlay implementation

The drawer primitives are cleanly exported, and the overlay is properly implemented with correct z-index and styling.


25-45: Well-structured DrawerContent with handle

The DrawerContent component includes a handle for users to grab, which is an important UX consideration for mobile drawers.


46-74: Comprehensive supporting components

The header, footer, title, and description components provide consistent structure and styling for drawer content.


76-87: Complete export of all drawer components

All components are properly exported for use throughout the application.

client/apps/eternum-mobile/.env.slot (1)

10-12: Replace placeholder URLs with actual values

The URLs for image CDN, image proxy, and IPFS gateway are all set to the placeholder "https://abc.com". These should be updated with actual production URLs before deployment.

client/apps/eternum-mobile/src/shared/ui/select.tsx (2)

1-6: LGTM: Appropriate imports and utility usage

The component correctly imports necessary dependencies and the utility function for class name management.


132-143: LGTM: Comprehensive component exports

The file correctly exports all necessary components, making them available for use throughout the application.

client/apps/eternum-mobile/.env.production (1)

9-14: LGTM: Clear environment configuration for production

The configuration for production environment is well-structured with appropriate values for chain, slot, and API endpoints.

client/apps/eternum-mobile/src/shared/ui/dropdown-menu.tsx (10)

1-6: Imports look good.
All external and local utility imports are organized correctly. No issues found.


7-18: Radix re-exports are clean.
The usage of the Root, Trigger, Group, Portal, Sub, and RadioGroup components from Radix UI is straightforward and consistent.


40-53: SubContent implementation is consistent.
Forward ref usage and className merging are handled properly. No issues spotted.


55-71: Good usage of Portal and sideOffset.
This component correctly leverages a Portal for dropdown positioning with a sensible default offset.


91-112: Checkbox item design is accessible.
Visual indicators for item selection are properly shown with the check icon. Overall looks good.


114-134: Radio item structure is consistent.
The approach for radio selection works well and follows Radix UI conventions.


136-148: Label styling is straightforward.
The inset prop and forwardRef usage mirror the rest of the file, maintaining consistency.


150-156: Separator implementation is minimal and effective.
The negative margins and background color align with typical dropdown style.


158-160: Shortcut component usage.
This component cleanly displays shortcuts or hints on the far right side. No issues found.


163-179: Export block is comprehensive.
Exporting all dropdown elements from a single file is convenient for discovery and usage.

Comment on lines +1 to +2
VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec"
VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security risk: Hardcoded private key exposure

This file contains a hardcoded private key in plain text, which poses a significant security risk. Private keys should never be committed to version control, even in environment files.

Consider using a more secure approach:

  1. Remove the private key from this file
  2. Use a secure secret management system
  3. Load secrets at runtime from a secure source

Comment on lines +1 to +2
VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec"
VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security risk: Hardcoded private key in sample file

Even in a sample file, including actual private keys poses a security risk if someone copies this file without changing the values.

Replace with placeholder values to indicate that users should provide their own keys:

-VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec"
-VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912"
+VITE_PUBLIC_MASTER_ADDRESS="YOUR_MASTER_ADDRESS_HERE"
+VITE_PUBLIC_MASTER_PRIVATE_KEY="YOUR_PRIVATE_KEY_HERE"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec"
VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912"
VITE_PUBLIC_MASTER_ADDRESS="YOUR_MASTER_ADDRESS_HERE"
VITE_PUBLIC_MASTER_PRIVATE_KEY="YOUR_PRIVATE_KEY_HERE"
🧰 Tools
🪛 Gitleaks (8.21.2)

2-2: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +1 to +2
VITE_PUBLIC_MASTER_ADDRESS=0x5013b17c43a2b664ec2a38aa45f6d891db1188622ec7cf320411321c3248fb5
VITE_PUBLIC_MASTER_PRIVATE_KEY=0x5013b17c43a2b664ec2a38aa45f6d891db1188622ec7cf320411321c3248fb5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical security issue: Production private key exposure

The private key for the production environment is hardcoded in this file. This is a serious security vulnerability as it could allow unauthorized access to production accounts.

Store sensitive credentials in a secure vault or environment variable system, and load them securely at runtime. Never commit them to version control.

Additionally, the master address and private key are identical, which seems incorrect.

🧰 Tools
🪛 Gitleaks (8.21.2)

2-2: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

@aymericdelab aymericdelab merged commit bcab66f into next Mar 10, 2025
10 of 16 checks passed
@aymericdelab aymericdelab deleted the feat/mobile-update branch March 10, 2025 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants