From 84b0aba8ec810bd58ed980c9bb46b521c7fdd5bd Mon Sep 17 00:00:00 2001 From: 0xshora <112358132134.fibon@gmail.com> Date: Sun, 7 Jul 2024 15:11:57 +0200 Subject: [PATCH 1/2] chore: fix some UI/UX --- src/components/Loading/Loading.module.css | 20 +------- src/components/Loading/Loading.tsx | 28 +++++++++-- src/components/MenuBar/PxCounter.tsx | 4 +- src/dojo/createSystemCalls.ts | 9 +++- src/dojo/generated.ts | 24 ++++----- src/hooks/useGetPixelsToReset.ts | 42 +++++++--------- src/vite-env.d.ts | 50 +++++++++---------- src/webtools/components/Viewport/constants.ts | 4 +- 8 files changed, 89 insertions(+), 92 deletions(-) diff --git a/src/components/Loading/Loading.module.css b/src/components/Loading/Loading.module.css index 186e047..d9c0516 100644 --- a/src/components/Loading/Loading.module.css +++ b/src/components/Loading/Loading.module.css @@ -1,23 +1,7 @@ .loadingContainer { - @apply fixed inset-0 z-50 flex items-center justify-center bg-primary/90; + @apply fixed inset-0 z-50 flex items-center justify-center bg-bg-primary/90; } .loadingText { @apply text-3xl font-bold text-white; - animation: loadingAnimation 1s steps(4, end) infinite; -} - -@keyframes loadingAnimation { - 0% { - content: 'Loading.'; - } - 25% { - content: 'Loading..'; - } - 50% { - content: 'Loading...'; - } - 75% { - content: 'Loading...'; - } -} +} \ No newline at end of file diff --git a/src/components/Loading/Loading.tsx b/src/components/Loading/Loading.tsx index 05adf56..a094c75 100644 --- a/src/components/Loading/Loading.tsx +++ b/src/components/Loading/Loading.tsx @@ -1,9 +1,27 @@ +import React, { useState, useEffect } from 'react'; import styles from './Loading.module.css'; -const Loading = () => ( -
- Loading... -
-); +const Loading: React.FC = () => { + const [loadingText, setLoadingText] = useState('Loading.'); + + useEffect(() => { + const interval = setInterval(() => { + setLoadingText(prev => { + if (prev === 'Loading...') { + return 'Loading.'; + } + return prev + '.'; + }); + }, 500); + + return () => clearInterval(interval); + }, []); + + return ( +
+ {loadingText} +
+ ); +}; export default Loading; diff --git a/src/components/MenuBar/PxCounter.tsx b/src/components/MenuBar/PxCounter.tsx index e86d382..cf73cfe 100644 --- a/src/components/MenuBar/PxCounter.tsx +++ b/src/components/MenuBar/PxCounter.tsx @@ -12,8 +12,8 @@ const PxCounter = () => { const player = usePlayer(address); const pixelRecoveryRate = usePixelRecoveryRate(); - const playerPx = player?.data?.current_px ?? 10; - const maxPx = player?.data?.max_px ?? 10; + const playerPx = player?.data?.current_px ?? 10; // Default to 10 if player is not loaded + const maxPx = player?.data?.max_px ?? 10; // Default to 10 if player is not loaded const recoveryRate = pixelRecoveryRate?.data?.rate ?? 0; const playerLastDate = player?.data?.last_date ?? 0; diff --git a/src/dojo/createSystemCalls.ts b/src/dojo/createSystemCalls.ts index 5f111d4..6f50bbe 100644 --- a/src/dojo/createSystemCalls.ts +++ b/src/dojo/createSystemCalls.ts @@ -110,12 +110,17 @@ export function createSystemCalls({ client }: { client: IWorld }) { await new Promise((resolve) => setTimeout(resolve, 1000)); }; - const activateProposal = async (account: AccountInterface, gameId: number, index: number, clearData?: {x: number, y: number}[]) => { + const activateProposal = async ( + account: AccountInterface, + gameId: number, + index: number, + clearData?: { x: number; y: number }[], + ) => { const { transaction_hash } = await client.actions.activateProposal({ account, gameId, index, - clearData + clearData, }); await account.waitForTransaction(transaction_hash, { diff --git a/src/dojo/generated.ts b/src/dojo/generated.ts index 18312ea..1532bfd 100644 --- a/src/dojo/generated.ts +++ b/src/dojo/generated.ts @@ -94,23 +94,22 @@ export async function setupWorld(provider: DojoProvider) { account, gameId, index, - clearData + clearData, }: { account: AccountInterface; gameId: number; index: number; - clearData?: {x: number, y: number}[] + clearData?: { x: number; y: number }[]; }) => { - - const clearDataArgs: number[] = [] + const clearDataArgs: number[] = []; if (clearData) { - clearData.forEach(({x, y}) => { - clearDataArgs.push(x) - clearDataArgs.push(y) - }) + clearData.forEach(({ x, y }) => { + clearDataArgs.push(x); + clearDataArgs.push(y); + }); } - if (clearData) console.log(clearDataArgs) + if (clearData) console.log(clearDataArgs); try { return await provider.execute( @@ -118,12 +117,7 @@ export async function setupWorld(provider: DojoProvider) { { contractAddress: PROPOSAL_CONTRACT_ADDRESS, entrypoint: 'activate_proposal', - calldata: [ - gameId, - index, - clearData?.length ?? 0, - ...clearDataArgs - ], + calldata: [gameId, index, clearData?.length ?? 0, ...clearDataArgs], }, { skipValidate: true, diff --git a/src/hooks/useGetPixelsToReset.ts b/src/hooks/useGetPixelsToReset.ts index 6705c00..e17eb87 100644 --- a/src/hooks/useGetPixelsToReset.ts +++ b/src/hooks/useGetPixelsToReset.ts @@ -1,16 +1,16 @@ -import {useMutation} from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import { GraphQLClient } from 'graphql-request'; import GetResetPixels from '@/../graphql/GetPixelsToReset.graphql'; import { useSettingsStore } from '@/stores/SettingsStore.ts'; -import useBoard from "@/hooks/useBoard.ts"; -import {GAME_ID} from "@/global/constants.ts"; +import useBoard from '@/hooks/useBoard.ts'; +import { GAME_ID } from '@/global/constants.ts'; type Data = { pixelModels: { edges: { node: { - x: number, - y: number + x: number; + y: number; }; }[]; }; @@ -21,27 +21,23 @@ const useGetPixelsToReset = () => { const baseUrl = settings?.config?.toriiUrl ?? 'http://localhost:8080'; const gqlClient = new GraphQLClient(`${baseUrl}/graphql`); - const board = useBoard(GAME_ID) + const board = useBoard(GAME_ID); return useMutation({ mutationKey: ['usePixelRecoveryRate'], - mutationFn: async ({color}: {color: number}) => { - if (!board.data) throw new Error('board data not yet loaded') - const result: Data = await gqlClient - .request( - GetResetPixels, - { - color, - xGTE: board.data.origin.x, - xLTE: board.data.origin.x + board.data.width - 1, - yGTE: board.data.origin.y, - yLTE: board.data.origin.y + board.data.height - 1, - limit: board.data.height * board.data.width - } - ); - return result.pixelModels.edges.map(({ node: { x, y }}) => { - return { x, y } - }) + mutationFn: async ({ color }: { color: number }) => { + if (!board.data) throw new Error('board data not yet loaded'); + const result: Data = await gqlClient.request(GetResetPixels, { + color, + xGTE: board.data.origin.x, + xLTE: board.data.origin.x + board.data.width - 1, + yGTE: board.data.origin.y, + yLTE: board.data.origin.y + board.data.height - 1, + limit: board.data.height * board.data.width, + }); + return result.pixelModels.edges.map(({ node: { x, y } }) => { + return { x, y }; + }); }, retryDelay: (failureCount) => failureCount * 1_000, }); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 4c1b5db..f71fc57 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,25 +1,25 @@ /// type ImportMetaEnv = { - // Auto-generated by `npx vite-envs update-types` and hot-reloaded by the `vite-env` plugin - // You probably want to add `/src/vite-env.d.ts` to your .prettierignore - BASE_URL: string - MODE: string - DEV: boolean - PROD: boolean - PUBLIC_RPC_URL: string - PUBLIC_TORII_URL: string - PUBLIC_RELAY_URL: string - PUBLIC_SERVER_URL: string - MASTER_ADDRESS: string - MASTER_PRIVATE_KEY: string - WORLD_ADDRESS: string - ACCOUNT_CLASS_HASH: string - FEETOKEN_ADDRESS: string - SERVER_PORT: string - CORE_VERSION: string - PUBLIC_MANIFEST_URL: string - // @user-defined-start + // Auto-generated by `npx vite-envs update-types` and hot-reloaded by the `vite-env` plugin + // You probably want to add `/src/vite-env.d.ts` to your .prettierignore + BASE_URL: string; + MODE: string; + DEV: boolean; + PROD: boolean; + PUBLIC_RPC_URL: string; + PUBLIC_TORII_URL: string; + PUBLIC_RELAY_URL: string; + PUBLIC_SERVER_URL: string; + MASTER_ADDRESS: string; + MASTER_PRIVATE_KEY: string; + WORLD_ADDRESS: string; + ACCOUNT_CLASS_HASH: string; + FEETOKEN_ADDRESS: string; + SERVER_PORT: string; + CORE_VERSION: string; + PUBLIC_MANIFEST_URL: string; + // @user-defined-start /* * Here you can define your own special variables * that would be available on `import.meta.env` but @@ -29,16 +29,16 @@ type ImportMetaEnv = { */ SSR: boolean; // @user-defined-end -} +}; interface ImportMeta { - // Auto-generated by `npx vite-envs update-types` + // Auto-generated by `npx vite-envs update-types` - url: string + url: string; - readonly hot?: import('vite-envs/types/hot').ViteHotContext + readonly hot?: import('vite-envs/types/hot').ViteHotContext; - readonly env: ImportMetaEnv + readonly env: ImportMetaEnv; - glob: import('vite-envs/types/importGlob').ImportGlobFunction + glob: import('vite-envs/types/importGlob').ImportGlobFunction; } diff --git a/src/webtools/components/Viewport/constants.ts b/src/webtools/components/Viewport/constants.ts index c1c1e53..ec2fc56 100644 --- a/src/webtools/components/Viewport/constants.ts +++ b/src/webtools/components/Viewport/constants.ts @@ -1,6 +1,6 @@ -export const ZOOM_TILEMODE = 2699; +export const ZOOM_TILEMODE = 1799; export const ZOOM_FACTOR = 100; export const ZOOM_MAX = 10000; // 100 pixels per cell side -export const ZOOM_MIN = 2700; // 0.5 pixels per cell side +export const ZOOM_MIN = 1800; // 0.5 pixels per cell side // export const ZOOM_MIN = 50 // 0.5 pixels per cell side export const ZOOM_SCALEFACTOR = 1.1; From c43c0b283e0a3327bcd532ec031e037cd6ed620f Mon Sep 17 00:00:00 2001 From: 0xshora <112358132134.fibon@gmail.com> Date: Sun, 7 Jul 2024 16:06:36 +0200 Subject: [PATCH 2/2] feat: add emoji to each address --- .../Avatar/emojiAvatarForAddress.ts | 80 +++++++++++++++++++ src/components/MenuBar/MenuBar.tsx | 4 +- src/components/ProposalList/ProposalItem.tsx | 6 +- src/global/utils.ts | 9 +++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 src/components/Avatar/emojiAvatarForAddress.ts diff --git a/src/components/Avatar/emojiAvatarForAddress.ts b/src/components/Avatar/emojiAvatarForAddress.ts new file mode 100644 index 0000000..54ef9fc --- /dev/null +++ b/src/components/Avatar/emojiAvatarForAddress.ts @@ -0,0 +1,80 @@ +const colors = [ + '#FC5C54', + '#FFD95A', + '#E95D72', + '#6A87C8', + '#5FD0F3', + '#75C06B', + '#FFDD86', + '#5FC6D4', + '#FF949A', + '#FF8024', + '#9BA1A4', + '#EC66FF', + '#FF8CBC', + '#FF9A23', + '#C5DADB', + '#A8CE63', + '#71ABFF', + '#FFE279', + '#B6B1B6', + '#FF6780', + '#A575FF', + '#4D82FF', + '#FFB35A', +] as const; + +const avatars = [ + { color: colors[0], emoji: '🌶' }, + { color: colors[1], emoji: '🤑' }, + { color: colors[2], emoji: '🐙' }, + { color: colors[3], emoji: '🫐' }, + { color: colors[4], emoji: '🐳' }, + { color: colors[0], emoji: '🤶' }, + { color: colors[5], emoji: '🌲' }, + { color: colors[6], emoji: '🌞' }, + { color: colors[7], emoji: '🐒' }, + { color: colors[8], emoji: '🐵' }, + { color: colors[9], emoji: '🦊' }, + { color: colors[10], emoji: '🐼' }, + { color: colors[11], emoji: '🦄' }, + { color: colors[12], emoji: '🐷' }, + { color: colors[13], emoji: '🐧' }, + { color: colors[8], emoji: '🦩' }, + { color: colors[14], emoji: '👽' }, + { color: colors[0], emoji: '🎈' }, + { color: colors[8], emoji: '🍉' }, + { color: colors[1], emoji: '🎉' }, + { color: colors[15], emoji: '🐲' }, + { color: colors[16], emoji: '🌎' }, + { color: colors[17], emoji: '🍊' }, + { color: colors[18], emoji: '🐭' }, + { color: colors[19], emoji: '🍣' }, + { color: colors[1], emoji: '🐥' }, + { color: colors[20], emoji: '👾' }, + { color: colors[15], emoji: '🥦' }, + { color: colors[0], emoji: '👹' }, + { color: colors[17], emoji: '🙀' }, + { color: colors[4], emoji: '⛱' }, + { color: colors[21], emoji: '⛵️' }, + { color: colors[17], emoji: '🥳' }, + { color: colors[8], emoji: '🤯' }, + { color: colors[22], emoji: '🤠' }, +] as const; + +function hashCode(text: string) { + let hash = 0; + if (text.length === 0) return hash; + for (let i = 0; i < text.length; i++) { + const chr = text.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; + } + return hash; +} + +export function emojiAvatarForAddress(address: string) { + const resolvedAddress = typeof address === 'string' ? address : ''; + const avatarIndex = Math.abs(hashCode(resolvedAddress.toLowerCase()) % avatars.length); + return avatars[avatarIndex ?? 0]; +} diff --git a/src/components/MenuBar/MenuBar.tsx b/src/components/MenuBar/MenuBar.tsx index b07d4b6..66d4378 100644 --- a/src/components/MenuBar/MenuBar.tsx +++ b/src/components/MenuBar/MenuBar.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import styles from './MenuBar.module.css'; -import { formatWalletAddress } from '@/global/utils.ts'; +import { formatWalletAddressWithEmoji } from '@/global/utils.ts'; import PxCounter from '@/components/MenuBar/PxCounter.tsx'; interface MenuBarProps { @@ -45,7 +45,7 @@ const MenuBar: React.FC = ({ address, endTime }) => {
{timeLeft}
-
{formatWalletAddress(address || '')}
+
{formatWalletAddressWithEmoji(address || '')}
diff --git a/src/components/ProposalList/ProposalItem.tsx b/src/components/ProposalList/ProposalItem.tsx index bacf84e..873bd06 100644 --- a/src/components/ProposalList/ProposalItem.tsx +++ b/src/components/ProposalList/ProposalItem.tsx @@ -3,7 +3,7 @@ import { usePixelawProvider } from '@/providers/PixelawProvider.tsx'; import { ProposalType } from '@/global/types.ts'; import { numRGBAToHex } from '@/webtools/utils.ts'; import { GAME_ID, NEEDED_YES_PX } from '@/global/constants.ts'; -import { formatWalletAddress, toastContractError, formatTimeRemaining, formatTimeRemainingFotTitle } from '@/global/utils.ts'; +import { formatWalletAddressWithEmoji, toastContractError, formatTimeRemaining, formatTimeRemainingFotTitle } from '@/global/utils.ts'; import { type ProposalDataType } from '@/hooks/useProposals.ts'; import useGetPixelsToReset from "@/hooks/useGetPixelsToReset.ts"; @@ -30,7 +30,7 @@ const createProposalTitle = (proposalType: ProposalType, target_args_1: number, case ProposalType.AddNewColor: return `Adding A New Color: ${numRGBAToHex(target_args_1).toUpperCase()}`; case ProposalType.ResetToWhiteByColor: - return `Make A Disaster: ${numRGBAToHex(target_args_1).toUpperCase()}`; + return `Reset To White: ${numRGBAToHex(target_args_1).toUpperCase()}`; case ProposalType.ExtendGameEndTime: return `Extend Game End Time: ${formatTimeRemainingFotTitle(target_args_1)}`; case ProposalType.ExpandArea: @@ -167,7 +167,7 @@ const ProposalItem: React.FC = ({ proposal, onStartVote, filter, sear
- proposed by {formatWalletAddress(proposal.author.toString())} + proposed by {formatWalletAddressWithEmoji(proposal.author.toString())}
{ return address; }; +export const formatWalletAddressWithEmoji = (address: string) => { + const avatar = emojiAvatarForAddress(address); + if (address.length > 10) { + return avatar.emoji + `${address.slice(0, 4)}...${address.slice(-4)}`; + } + return avatar.emoji + address; +}; + // Takes a RGB hex nr and converts it to numeric rgba (0 alpha) export const coordinateToPosition = (coord: Coordinate): Position => { return { x: coord[0], y: coord[1] };