From 42fa918dc2daf95453a0aeebc68eec4f227eb2ac Mon Sep 17 00:00:00 2001 From: mariz Date: Fri, 8 Dec 2023 16:04:13 +0800 Subject: [PATCH 1/4] wip: read alert --- web/public/assets/svg/icon_close.svg | 5 ++ web/public/assets/svg/icon_logs.svg | 16 +++++ web/src/components/Notification.tsx | 89 +++++++++++++++++----------- web/src/components/ui/button.tsx | 4 +- web/src/index.css | 2 + web/tailwind.config.js | 1 + 6 files changed, 80 insertions(+), 37 deletions(-) create mode 100644 web/public/assets/svg/icon_close.svg create mode 100644 web/public/assets/svg/icon_logs.svg diff --git a/web/public/assets/svg/icon_close.svg b/web/public/assets/svg/icon_close.svg new file mode 100644 index 0000000..ce587e1 --- /dev/null +++ b/web/public/assets/svg/icon_close.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/public/assets/svg/icon_logs.svg b/web/public/assets/svg/icon_logs.svg new file mode 100644 index 0000000..0a55068 --- /dev/null +++ b/web/public/assets/svg/icon_logs.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/web/src/components/Notification.tsx b/web/src/components/Notification.tsx index f238e38..0a83406 100644 --- a/web/src/components/Notification.tsx +++ b/web/src/components/Notification.tsx @@ -9,8 +9,10 @@ import { notificationDataAtom } from '@/global/states.ts' import { useSetAtom } from 'jotai' import useAlerts from '@/hooks/events/useAlerts' import { getEntityIdFromKeys } from '@dojoengine/utils' +import useLocalStorage from '@/hooks/useLocalStorage' type AlertType = { + id: string, position: { x: number, y: number @@ -21,23 +23,33 @@ type AlertType = { timestamp: bigint } -const Alert: React.FC = ({ position, caller, message }) => { +type AlertProp = AlertType & { + className?: string +} +const Alert: React.FC = ({ position, caller, message, className, id }) => { const { setup: { components: { App }, }, + account: { + account + } } = useDojo() + const [ readAlerts, setReadAlerts ] = useLocalStorage(`pixelaw::read_alerts::${account.address}`, []) + const hasReadAlert = readAlerts.includes(id) + const app = useComponentValue(App, getEntityIdFromKeys([BigInt(caller)])) const name = felt252ToString(app?.name ?? 'caller') const setNotificationData = useSetAtom(notificationDataAtom) const handleOnClickNotification = () => { + if (!hasReadAlert) setReadAlerts(prevReadAlerts => [...prevReadAlerts, id]) setNotificationData({ x: position.x, y: position.y, - pixelType: name, + pixelType: caller, }) } @@ -45,23 +57,18 @@ const Alert: React.FC = ({ position, caller, message }) => {
-
-
-
-
-

{name.toUpperCase()}: {message}

-
- +
{name} - {message}
+ {!hasReadAlert && ( +
+
+
+ )}
) } @@ -70,36 +77,49 @@ export default function Notification() { const [ isOpen, setIsOpen ] = React.useState(false) const alerts = useAlerts() - const hasNotification = alerts.data && alerts.data?.length > 0 + + const { + account: { + account + } + } = useDojo() + + const [ readAlerts ] = useLocalStorage(`pixelaw::read_alerts::${account.address}`, []) + + const hasNotification = alerts.data ? + alerts.data.filter(alert => !readAlerts.includes(alert.id)).length > 0 : false return ( <> + {!isOpen && ( + )}
@@ -107,7 +127,7 @@ export default function Notification() { className={cn( [ 'h-full', - 'flex flex-col gap-y-sm' + 'flex flex-col' ])} >
-
-

Alerts

+

Event Logs

{(alerts?.data ?? []).map(alert => ( - + ))}
diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index d41b7b1..29d6dba 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -19,7 +19,7 @@ const buttonVariants = cva( ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", icon: 'flex flex-col gap-y-xs', - notification: 'bg-brand-violet rounded-br-xl' + notification: 'bg-brand-body rounded-3xl' }, size: { default: "h-[54px] max-w-[330px] w-full px-4 py-2", @@ -27,7 +27,7 @@ const buttonVariants = cva( lg: "h-11 rounded-md px-8", icon: "p-0", 'walletHeader': 'h-9 w-[184px] px-4 py-2 rounded', - 'notification': 'w-[74px] h-[64px]', + 'notification': 'w-[25px] h-[25px]', }, }, defaultVariants: { diff --git a/web/src/index.css b/web/src/index.css index 1b41870..84d99b2 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -53,6 +53,8 @@ --brand-violetAccent04: 264 100% 80%; --brand-skyblue: 188 80% 48%; --brand-danger: 0 100% 50%; + + --new-primary: 268, 100%, 80%; } .dark { diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 91d98c2..02a4d9b 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -17,6 +17,7 @@ module.exports = { }, extend: { colors: { + 'new-primary': 'hsl(var(--new-primary))', 'brand-blackAccent': 'hsl(var(--brand-blackAccent))', 'brand-redAccent': 'hsl(var(--brand-redAccent))', 'brand-body': 'hsl(var(--brand-body))', From b5bde4f0bf38870399df594d55ecb98e7880035a Mon Sep 17 00:00:00 2001 From: renzo-ov Date: Tue, 12 Dec 2023 11:00:47 +0800 Subject: [PATCH 2/4] chore: update ui design --- web/public/assets/svg/icon_chevron_left.svg | 10 +- web/public/assets/svg/icon_chevron_right.svg | 10 +- web/src/components/Notification.tsx | 3 +- web/src/components/ParamPicker.tsx | 100 +++++++---- web/src/components/Plugin.tsx | 176 ++++++++++--------- web/src/components/ui/ButtonGroup.tsx | 10 +- web/src/components/ui/button.tsx | 2 +- web/src/components/ui/dialog.tsx | 2 +- web/tailwind.config.js | 16 +- 9 files changed, 192 insertions(+), 137 deletions(-) diff --git a/web/public/assets/svg/icon_chevron_left.svg b/web/public/assets/svg/icon_chevron_left.svg index 7e3e7fd..01fc215 100644 --- a/web/public/assets/svg/icon_chevron_left.svg +++ b/web/public/assets/svg/icon_chevron_left.svg @@ -1,5 +1,9 @@ - - - + + + + + + + diff --git a/web/public/assets/svg/icon_chevron_right.svg b/web/public/assets/svg/icon_chevron_right.svg index 7fb52d4..26802d1 100644 --- a/web/public/assets/svg/icon_chevron_right.svg +++ b/web/public/assets/svg/icon_chevron_right.svg @@ -1,5 +1,9 @@ - - - + + + + + + + diff --git a/web/src/components/Notification.tsx b/web/src/components/Notification.tsx index 0a83406..ee40e56 100644 --- a/web/src/components/Notification.tsx +++ b/web/src/components/Notification.tsx @@ -97,8 +97,9 @@ export default function Notification() { size={'notification'} className={cn( [ - 'fixed left-5 top-20 z-40', + 'fixed left-3 top-[70px] z-40', 'font-emoji text-[28px]', + 'bg-[#2A0D39] rounded-full h-[48px] w-[48px]' ])} onClick={() => setIsOpen(true)} > diff --git a/web/src/components/ParamPicker.tsx b/web/src/components/ParamPicker.tsx index 59aa3f6..b1f72ec 100644 --- a/web/src/components/ParamPicker.tsx +++ b/web/src/components/ParamPicker.tsx @@ -1,14 +1,15 @@ import React from 'react' -import { Dialog, DialogContent, DialogFooter } from '@/components/ui/dialog' +import { Dialog, DialogContent, DialogFooter, DialogOverlay } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import ButtonGroup from '@/components/ui/ButtonGroup' +import { cn } from '@/lib/utils.ts' type ParamDefinition = { name: string, type: 'number' | 'string' | 'enum', - variants?: {name: string, value: number}[], + variants?: { name: string, value: number }[], structDefinition?: Record } @@ -28,31 +29,51 @@ type PropsType = { type EnumPickerPropsType = { value?: number, label: string - variants: {name: string, value: number}[], + variants: { name: string, value: number }[], onChange?: (value: number) => void } -const EnumPicker: React.FC = ( { label, value, variants, onChange }) => { +const EnumPicker: React.FC = ({ label, value, variants, onChange }) => { const id = `enum-group-${label}` return ( - <> - +
+ + { return { label: variant.name, value: variant.value }})} + options={variants.map(variant => { + return { label: variant.name, value: variant.value } + })} value={value} onChange={(newValue) => { if (onChange) onChange(newValue ?? 0) }} /> - +
) } -const ParamPicker: React.FC = ({ instruction, value, onChange, onSelect, params, onSubmit, open, onOpenChange }) => { - const needsSubmitButton = params.length > 1 || !!(params.filter(param => param.type === 'number' || param.type === 'string')).length +const ParamPicker: React.FC = ({ + instruction, + value, + onChange, + onSelect, + params, + onSubmit, + open, + onOpenChange, +}) => { + const needsSubmitButton = params.length > 1 || !!(params.filter(param => param.type === 'number' || param.type === 'string')).length const handleOnChange = (newValue: any, paramName: string) => { - const finalizedValue = {...value, [paramName]: newValue} + const finalizedValue = { ...value, [paramName]: newValue } // means there is only one param and that param is an enum if (!needsSubmitButton && !!onSelect) { @@ -67,9 +88,16 @@ const ParamPicker: React.FC = ({ instruction, value, onChange, onSele return ( - + + {hasInstruction && ( -
{instruction}
+

{instruction}

)}
{params.map((param) => { @@ -84,44 +112,44 @@ const ParamPicker: React.FC = ({ instruction, value, onChange, onSele variants={param.variants ?? []} onChange={(e) => handleOnChange(e, param.name)} /> - ); + ) case 'number': return ( - handleOnChange(e.target.valueAsNumber, param.name)} - /> - ); + handleOnChange(e.target.valueAsNumber, param.name)} + /> + ) case 'string': return ( - handleOnChange(e.target.value, param.name)} - /> - ); + handleOnChange(e.target.value, param.name)} + /> + ) default: - return null; + return null } })}
{needsSubmitButton && ( - + )}
- ); + ) } -export default ParamPicker; +export default ParamPicker diff --git a/web/src/components/Plugin.tsx b/web/src/components/Plugin.tsx index 2d613bd..9ff56a5 100644 --- a/web/src/components/Plugin.tsx +++ b/web/src/components/Plugin.tsx @@ -30,12 +30,12 @@ const PluginButton = ({ system, onSelect, expanded, selected }: PluginButtonProp const { setup: { components: { - App - } + App, + }, }, } = useDojo() - const entityId = getEntityIdFromKeys([BigInt(system)]) + const entityId = getEntityIdFromKeys([ BigInt(system) ]) const app = useComponentValue(App, entityId) const name = felt252ToString(app?.name ?? 'app name') const icon = felt252ToUnicode(app?.icon ?? 'app icon') @@ -43,21 +43,20 @@ const PluginButton = ({ system, onSelect, expanded, selected }: PluginButtonProp return (
{ if (onSelect) onSelect(name) }} >

{name} @@ -81,89 +80,92 @@ export default function Plugin() { const { setup: { components: { - App, AppName - } + App, AppName, + }, }, } = useDojo() - const [isOpen, setIsOpen] = React.useState(false) + const [ isOpen, setIsOpen ] = React.useState(false) - const [gameMode, setGameMode] = useAtom(gameModeAtom) - const selectedAppId = getEntityIdFromKeys([BigInt(shortString.encodeShortString(gameMode))]) + const [ gameMode, setGameMode ] = useAtom(gameModeAtom) + const selectedAppId = getEntityIdFromKeys([ BigInt(shortString.encodeShortString(gameMode)) ]) const selectedApp = useComponentValue(AppName, selectedAppId) const positionWithAddressAndType = useAtomValue(positionWithAddressAndTypeAtom) - const apps = useEntityQuery([Has(App)]) + const apps = useEntityQuery([ Has(App) ]) - return ( - <> -
-
-
- -
+ return ( + <> +
+ +
-
- { - apps - .map((app) => { - return ( - setGameMode(name)} - expanded={isOpen} - /> - ) - }) - } -
+
+
+
+ { + apps + .map((app) => { + return ( + setGameMode(name)} + expanded={isOpen} + /> + ) + }) + } +
-
-
-
- - - ) +
+
+
+ + + ) } diff --git a/web/src/components/ui/ButtonGroup.tsx b/web/src/components/ui/ButtonGroup.tsx index b02dca9..b356f48 100644 --- a/web/src/components/ui/ButtonGroup.tsx +++ b/web/src/components/ui/ButtonGroup.tsx @@ -1,5 +1,6 @@ import { Key } from 'react' import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils.ts' type PropsType = { id?: string @@ -10,10 +11,15 @@ type PropsType = { const ButtonGroup = ({ value, options, onChange, id }: PropsType) => { return ( -
+
{options.map(option => (