-
Notifications
You must be signed in to change notification settings - Fork 53
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
[client] mobile app #2737
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Skipped Deployment
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the WalkthroughThis 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
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
Suggested Reviewers
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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:
- Import ordering rules
- Accessibility (a11y) rules
- 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 DropdownMenuSubTriggerWhile 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 DropdownMenuCheckboxItemThe 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 featuresThe 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 renderingThe 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 areasFor 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 linksThe 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 imagesThe 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 functionalityThe 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 optimizationConsider 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 handlingThe 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 methodsThe 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 identifiersThe 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 conventionsThe 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 presentationConvert 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 blocksAdding 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 resourcesCurrently, 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 fetchingThe component currently uses hardcoded dummy data for labor buildings and resource balances. While this is fine for initial development, consider:
- Moving this data to a separate constants file if it will remain as placeholder data
- Replacing it with actual data fetching from an API or state management solution
- 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 implementationThe 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 statesThe 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 (
!
) onVITE_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 tabsThe 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 accessibilityTo 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 accessibilityAs 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 accessibilityAs 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 handleSelectUserThe 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 localStorageAdd 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 hookMake 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 accessibilityThe Card component should use a more semantic HTML element like
article
instead of a genericdiv
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 CardTitleCardTitle 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 CardDescriptionCardDescription 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 componentsAdd 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 semicolonsThe 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 fileclient/apps/eternum-mobile/src/pages/chat/ui/chat-input.tsx (4)
20-25
: Add input validation before sending messagesValidate 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 buttonDisable 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 buttonImprove 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 shortcutAdd 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 reusabilityThe 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 interfaceThe 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 componentsAdding 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.Rootclient/apps/eternum-mobile/src/app/index.css (2)
1-1
: Consider optimizing Google Fonts import for performanceThe 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:
- Only including the specific weights and styles you need
- Consider using font-display: swap for better perceived performance
- 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 variableThe 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 reusabilityThe
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 levelThe "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 safetyThe 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 patternSimilar 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 propThe 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 safetyUsing
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 optimizationDefining 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 ofnavigation
.
44-52
: Replace SVG with an icon component for consistencySince 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 filteringThe 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 fileThe 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²) complexityUsing 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 ofO(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 ofO(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 constraintsThe 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 valueThere'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 fileThe 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 useEffectThe 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 methodUsing
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 scrollingThe 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 functionThe 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 useEffectSimilar 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 fileResource 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 serviceThe 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 logicThe 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 setActiveUserWhen 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 indicatorThe 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, whileonline
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
forfilteredUsers
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 withElementRef
andComponentPropsWithoutRef
. Good job setting thedisplayName
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:
- Proper ref forwarding
- Consistent styling approach with the
cn
utility- Clear component composition
- Setting display names for better debugging
- 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
⛔ 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 AddedThe 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 ofClaim
ComponentThe 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 ofFooter
ComponentThe 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 ofHeader
ComponentThis 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 ofNearbyEnemies
ComponentThe 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 ofArrivedDonkeys
ComponentThe 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 theSwapInput
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 theSettingsPage
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 theLoginPage
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 theResourcesCard
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 theUpgradeCastle
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 componentsThe 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 addedThe 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 cssLength 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 inclient/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 detectionThe use of
useMatches
and getting the last match withat(-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 tsxLength of output: 919
Attention: Verify External Module Paths
The current
tsconfig.json
inclient/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 interfaceThe 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 standardsThe 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" fiLength of output: 244
Tailwind configuration verified – no changes required
The verification confirms that both the
tailwind.config.js
and the CSS file atsrc/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 inclient/apps/eternum-mobile/tsconfig.json
, which confirms that the"@/*": ["./src/*"]
mapping is present. This covers all the@/
aliases defined in yourcomponents.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 environmentThis 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 architectureThe 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 commentedThere'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 optimizationsThe 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 tsLength 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.forwardRefThe 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 correctlyThe interface for ArrivedDonkeysProps is well-defined and appropriate for the component.
11-16
: Replace dummy data generator with actual data source for productionThe 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 namesThe
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 consistencyThis 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 interfaceThe ResourceIconProps interface is well-documented with JSDoc comments that clearly describe each prop's purpose and default values.
38-49
: Verify resource image pathsThe 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 tooltipThe 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 componentThe 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 applicationThe bundler mode configuration is well set up with:
moduleResolution: "bundler"
for compatibility with tools like ViteallowImportingTsExtensions
to support importing .ts files directlyisolatedModules
for better compatibility with transpilersmoduleDetection: "force"
to ensure consistent module detectionnoEmit: 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 configurationThe linting configuration is appropriately strict and includes:
strict: true
for comprehensive type safetynoUnusedLocals
andnoUnusedParameters
to keep code cleannoFallthroughCasesInSwitch
to prevent logical errorsnoUncheckedSideEffectImports
to manage import side effectsThese 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 readabilityThe 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 correctlyThe 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 registrationThe 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 thebeforeLoad
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:
- Direct navigation to a protected route without authentication
- Navigation after logout
- 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 tsLength 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
orinitializeAuth
) 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
orinitializeAuth
) 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 issueMissing 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 accessibilityThe 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 supportThe 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 componentThe 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 variantsThe 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 patternThe 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 timeThe
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 componentThe 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 componentAll 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 enumThe 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
) inclient/apps/eternum-mobile/src/shared/hooks/context/starknet-provider.tsx
(lines 30-55). This pattern risks obscuring genuine type incompatibilities between theControllerConnector
implementation and the expectedConnector
type. Please eliminate the double casting and either:• Define or refine explicit type interfaces for
ControllerConnector
so that it naturally satisfies theConnector
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 theConnector
interface and theControllerConnector
class, it’s unclear if the types are properly defined. Please manually verify that:
- The
ControllerConnector
class correctly implements or conforms to the expectedConnector
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 implementationThe 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 stepsThe 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
andSheetOverlay
properly encapsulates the animations and overlay logic, ensuring an accessible and maintainable layout. Good use ofReact.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
andSelectScrollDownButton
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 ofSelectPrimitive.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.
export const ETERNUM_CONFIG = () => { | ||
const config = getConfigFromNetwork(env.VITE_PUBLIC_CHAIN! as Chain); | ||
return config; | ||
}; |
There was a problem hiding this comment.
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.
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; | |
}; |
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%", | ||
}, | ||
]; |
There was a problem hiding this comment.
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.
"peerDependencies": { | ||
"starknet": "6.11.0" | ||
} |
There was a problem hiding this comment.
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:
- 1: https://www.starknet.io/developers/roadmap/
- 2: https://docs.starknet.io/starknet-versions/version-notes/
- 3: https://github.com/apibara/starknet-react/blob/main/pnpm-lock.yaml
- 4: https://www.panewslab.com/en/articledetails/8339n5zi.html
- 5: https://github.com/starkware-libs/starknet-specs/releases
- 6: https://www.npmjs.com/package/starknet?activeTab=versions
- 7: https://community.starknet.io/c/versions-upgrade/28
- 8: https://github.com/NethermindEth/starknet.go/releases
- 9: https://starknetjs.com/docs/6.11.0/API/
- 10: https://community.starknet.io/t/fall-roadmap-updates/115001
- 11: https://coinmarketcap.com/currencies/starknet-token/
- 12: https://classic.yarnpkg.com/en/package/starknet
- 13: https://www.starknet.io/blog/decentralized-starknet-2025/
- 14: https://docs.swmansion.com/scarb/
- 15: https://docs.starknet.io/resources/compatibility/
- 16: https://bitcoinist.com/starket-strk-price-prediction-2024-2025-2026-starknet-mpeppe-shake-up-bearish-market-with-recent-gains/
- 17: https://docs.starknet.io/starknet-versions/pathfinder-versions/
- 18: Declared types for abi-wan-kanabi are not overriding the default config starknet-io/starknet.js#1192
- 19: https://www.rapidinnovation.io/post/what-is-starknet-a-scalable-layer-2-network-for-ethereum
- 20: https://crates.io/crates/starknet
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.
"peerDependencies": { | |
"starknet": "6.11.0" | |
} | |
"peerDependencies": { | |
"starknet": "6.23.1" | |
} |
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 | ||
], |
There was a problem hiding this comment.
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
];
<meta | ||
name="viewport" | ||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" | ||
/> |
There was a problem hiding this comment.
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.
<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" | |
/> |
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, | ||
); | ||
} | ||
}; |
There was a problem hiding this comment.
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.
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, | |
); | |
} | |
}; |
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); | ||
}} |
There was a problem hiding this comment.
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.
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); | |
}} |
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)); | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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"); |
There was a problem hiding this comment.
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.
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()); |
<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> |
There was a problem hiding this comment.
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.
There was a problem hiding this 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 suggestionRemove @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 oncehandleConfirm
starts. Also consider whether afinally
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 logicThe 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
📒 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, asetTimeout
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
anduseResourceBalances
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 tsLength 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
, anduseBuildings
(see, for example, the implementations inpackages/react/src/hooks/helpers/use-resources.ts
andpackages/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 existinguseBuildings
hook.For resource balances:
Replace the dummy array with a data fetching mechanism using the established patterns (e.g. viauseResourceBalance
).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 generationThe
entityId
prop is defined but not used. Additionally, usinggenerateDummyData()
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 pathsThe imports use a clean structure with alias paths for better organization and maintainability.
24-42
: Well-structured card layout with semantic HTMLThe card layout is well-organized with appropriate hierarchy and semantic structure. The use of separate components like
CardTitle
andCardContent
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 functionThe
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 feedbackThe 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 interfaceThe props interface is clearly defined with appropriate types.
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 }); | ||
}, []); | ||
|
There was a problem hiding this comment.
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 correspondingstartProduction
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.
// @ts-ignore | ||
export const ArrivedDonkeys = ({ entityId, onClaim }: ArrivedDonkeysProps) => { | ||
const { donkeysCount } = generateDummyData(); |
There was a problem hiding this comment.
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.
// @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]>
… feat/mobile-update
There was a problem hiding this 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 productionThere 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 functionThe
setUserName
function could fail when callingset_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 constantThe 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 FragmentThis 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 variablesThe 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 recipientFor 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=0x045c587318c9ebcf2fbe21febf288ee2e3597a21cd48676005a5770a50d433c5client/apps/eternum-mobile/src/shared/ui/select.tsx (2)
13-31
: Consider adding aria attributes for better accessibilityThe 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 optimizationThe 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 fileEnvironment 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/3Zz1mppclient/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
📒 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 configurationThe 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 valuesThe 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 patternsThe 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 setupGood 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 componentThe
Tabs
component correctly wraps the Radix primitive, and theTabsList
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 componentThe 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 setThe 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 importsThe imports are comprehensive and the NULL_ACCOUNT constant provides a clean default value for accounts.
20-30
: Proper environment variable validationGood 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 memoizationThe custom hooks
useMasterAccount
anduseRpcProvider
useuseMemo
appropriately to prevent unnecessary re-creation of objects.
53-69
: Account synchronization with storeThe
useControllerAccount
hook properly synchronizes the account and connector with the global store.
71-84
: Singleton pattern for DojoProviderGood 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 componentsGood use of Radix UI primitives and appropriate exports for the base components.
16-30
: Well-implemented SheetOverlay with animationThe SheetOverlay component correctly implements the overlay with proper animations for open/close states using data attributes.
31-49
: Comprehensive sheet position variantsThe
sheetVariants
implementation provides a good variety of positioning options with appropriate animations for each direction.
50-69
: Accessible SheetContent with close buttonThe SheetContent component includes a proper close button with an icon and screen reader text, enhancing accessibility.
70-95
: Well-structured supporting componentsThe header, footer, title, and description components provide a consistent structure for sheet content with appropriate styling.
96-107
: Complete export of sheet componentsAll components are properly exported for use throughout the application.
client/apps/eternum-mobile/src/shared/ui/drawer.tsx (5)
1-9
: Clean Drawer component setupGood implementation of the base Drawer component with a configurable background scaling option.
10-24
: Proper primitive exports and overlay implementationThe drawer primitives are cleanly exported, and the overlay is properly implemented with correct z-index and styling.
25-45
: Well-structured DrawerContent with handleThe DrawerContent component includes a handle for users to grab, which is an important UX consideration for mobile drawers.
46-74
: Comprehensive supporting componentsThe header, footer, title, and description components provide consistent structure and styling for drawer content.
76-87
: Complete export of all drawer componentsAll components are properly exported for use throughout the application.
client/apps/eternum-mobile/.env.slot (1)
10-12
: Replace placeholder URLs with actual valuesThe 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 usageThe component correctly imports necessary dependencies and the utility function for class name management.
132-143
: LGTM: Comprehensive component exportsThe 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 productionThe 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.
VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec" | ||
VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Remove the private key from this file
- Use a secure secret management system
- Load secrets at runtime from a secure source
VITE_PUBLIC_MASTER_ADDRESS="0x127fd5f1fe78a71f8bcd1fec63e3fe2f0486b6ecd5c86a0466c3a21fa5cfcec" | ||
VITE_PUBLIC_MASTER_PRIVATE_KEY="0xc5b2fcab997346f3ea1c00b002ecf6f382c5f9c9659a3894eb783c5320f912" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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)
VITE_PUBLIC_MASTER_ADDRESS=0x5013b17c43a2b664ec2a38aa45f6d891db1188622ec7cf320411321c3248fb5 | ||
VITE_PUBLIC_MASTER_PRIVATE_KEY=0x5013b17c43a2b664ec2a38aa45f6d891db1188622ec7cf320411321c3248fb5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)
Summary by CodeRabbit
New Features
Documentation
Chores