diff --git a/src/components/user-settings/fortune-wheel/FortuneWheelSettingsView.tsx b/src/components/user-settings/fortune-wheel/FortuneWheelSettingsView.tsx deleted file mode 100644 index 91cc44c..0000000 --- a/src/components/user-settings/fortune-wheel/FortuneWheelSettingsView.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { IWheelAdminPrize, IWheelAdminPrizeEdit } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; -import { LocalizeText } from '../../api'; -import { Column, Flex, Text } from '../../common'; -import { useFortuneWheel } from '../../hooks'; -import { NitroCard } from '../../layout'; - -interface EditRow -{ - id: number; - category: string; - num: number; - weight: number; - label: string; -} - -interface CategoryDef -{ - key: string; - labelKey: string; -} - -const CATEGORIES: CategoryDef[] = [ - { key: 'item', labelKey: 'rarevalues.editor.cat.item' }, - { key: 'diamanti', labelKey: 'achievements.activitypoint.5' }, - { key: 'duckets', labelKey: 'achievements.activitypoint.0' }, - { key: 'crediti', labelKey: 'credits' }, - { key: 'giri', labelKey: 'rarevalues.editor.cat.spin' }, - { key: 'nulla', labelKey: 'rarevalues.editor.cat.nothing' } -]; - -const prizeToCategory = (prize: IWheelAdminPrize): string => -{ - switch(prize.type) - { - case 'item': return 'item'; - case 'points': return (prize.pointsType === 5) ? 'diamanti' : 'duckets'; - case 'credits': return 'crediti'; - case 'spin': return 'giri'; - default: return 'nulla'; - } -}; - -const prizeToNum = (prize: IWheelAdminPrize): number => - (prize.type === 'item') ? (parseInt(prize.value) || 0) : prize.amount; - -const rowToEdit = (row: EditRow): IWheelAdminPrizeEdit => -{ - const base = { id: row.id, weight: row.weight, label: row.label }; - - switch(row.category) - { - case 'item': return { ...base, type: 'item', value: String(row.num), amount: 1, pointsType: 0 }; - case 'diamanti': return { ...base, type: 'points', value: '', amount: row.num, pointsType: 5 }; - case 'duckets': return { ...base, type: 'points', value: '', amount: row.num, pointsType: 0 }; - case 'crediti': return { ...base, type: 'credits', value: '', amount: row.num, pointsType: 0 }; - case 'giri': return { ...base, type: 'spin', value: '', amount: row.num, pointsType: 0 }; - default: return { ...base, type: 'nothing', value: '', amount: 0, pointsType: 0 }; - } -}; - -interface FortuneWheelSettingsViewProps -{ - onClose: () => void; -} - -export const FortuneWheelSettingsView: FC = ({ onClose }) => -{ - const { adminPrizes = [], loadAdminPrizes = null, saveAdminPrizes = null } = useFortuneWheel(); - const [ editRows, setEditRows ] = useState([]); - - useEffect(() => - { - if(loadAdminPrizes) loadAdminPrizes(); - }, [ loadAdminPrizes ]); - - useEffect(() => - { - setEditRows(adminPrizes.map(prize => ({ - id: prize.id, - category: prizeToCategory(prize), - num: prizeToNum(prize), - weight: prize.weight, - label: prize.label - }))); - }, [ adminPrizes ]); - - const updateRow = (id: number, patch: Partial) => - setEditRows(prev => prev.map(row => (row.id === id) ? { ...row, ...patch } : row)); - - return ( - - - - - - { LocalizeText('rarevalues.editor.type') } - { LocalizeText('rarevalues.editor.value') } - { LocalizeText('rarevalues.editor.weight') } - { LocalizeText('rarevalues.editor.label') } - - - { editRows.map(row => ( - - - updateRow(row.id, { num: parseInt(event.target.value) || 0 }) } - className="w-16 rounded border border-black/20 bg-white px-1 py-0.5 text-sm text-[#1f2d34] disabled:opacity-40" /> - updateRow(row.id, { weight: parseInt(event.target.value) || 0 }) } - className="w-12 rounded border border-black/20 bg-white px-1 py-0.5 text-sm text-[#1f2d34]" /> - updateRow(row.id, { label: event.target.value }) } - className="min-w-0 grow rounded border border-black/20 bg-white px-1 py-0.5 text-sm text-[#1f2d34]" /> - - )) } - { !editRows.length && - { LocalizeText('wheel.settings.empty') } } - - - - - - ); -}; diff --git a/src/components/user-settings/fortune-wheel/FortuneWheelView.tsx b/src/components/user-settings/fortune-wheel/FortuneWheelView.tsx deleted file mode 100644 index 37aa94b..0000000 --- a/src/components/user-settings/fortune-wheel/FortuneWheelView.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { AddLinkEventTracker, GetRoomEngine, ILinkEventTracker, IWheelPrize, RemoveLinkEventTracker } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useMemo, useRef, useState } from 'react'; -import { LocalizeText } from '../../api'; -import { Column, Flex, LayoutAvatarImageView, LayoutBadgeImageView, LayoutCurrencyIcon, LayoutImage, Text } from '../../common'; -import { useFortuneWheel, useHasPermission } from '../../hooks'; -import { NitroCard } from '../../layout'; -import { FortuneWheelSettingsView } from './FortuneWheelSettingsView'; - -// Stock UI palette (white / light-blue / grey / black). -const SLICE_COLORS = [ '#eef2f5', '#c3dcec' ]; -const RIM = '#4c606c'; -const WHEEL_SIZE = 420; -const ICON_RADIUS = 150; -const FULL_TURNS = 5; - -const renderPrizeIcon = (prize: IWheelPrize) => -{ - switch(prize.type) - { - case 'item': - return ; - case 'badge': - return ; - case 'credits': - return ( - - - { prize.amount } - ); - case 'points': - return ( - - - { prize.amount } - ); - case 'spin': - return +{ prize.amount }; - default: - return ; - } -}; - -export const FortuneWheelView: FC<{}> = () => -{ - const [ isVisible, setIsVisible ] = useState(false); - const [ isSettingsOpen, setIsSettingsOpen ] = useState(false); - const { freeSpins, extraSpins, spinCost, spinCostType, prizes, recentWins, pendingPrizeId, isSpinning, open, spin, buySpin, finishSpin } = useFortuneWheel(); - const canManage = useHasPermission('acc_wheeladmin'); - const [ rotation, setRotation ] = useState(0); - const rotationRef = useRef(0); - const prizesRef = useRef([]); - prizesRef.current = prizes; - - useEffect(() => - { - const linkTracker: ILinkEventTracker = { - linkReceived: (url: string) => - { - const parts = url.split('/'); - if(parts.length < 2) return; - - switch(parts[1]) - { - case 'show': setIsVisible(true); return; - case 'hide': setIsVisible(false); return; - case 'toggle': setIsVisible(prev => !prev); return; - } - }, - eventUrlPrefix: 'fortune-wheel/', - }; - - AddLinkEventTracker(linkTracker); - - return () => RemoveLinkEventTracker(linkTracker); - }, []); - - useEffect(() => - { - if(isVisible) open(); - }, [ isVisible, open ]); - - // Drive the spin animation when the server reports the winning slice. - useEffect(() => - { - if(pendingPrizeId < 0) return; - - const list = prizesRef.current; - const idx = list.findIndex(prize => prize.id === pendingPrizeId); - - if(!list.length || (idx < 0)) - { - finishSpin(); - return; - } - - const sliceAngle = 360 / list.length; - const centerAngle = ((idx + 0.5) * sliceAngle); - const current = rotationRef.current; - const target = (current - (current % 360)) + (FULL_TURNS * 360) + (360 - centerAngle); - - rotationRef.current = target; - setRotation(target); - }, [ pendingPrizeId, finishSpin ]); - - const sliceAngle = prizes.length ? (360 / prizes.length) : 0; - - const background = useMemo(() => - { - if(!prizes.length) return SLICE_COLORS[0]; - - const stops = prizes.map((_, i) => `${ SLICE_COLORS[i % 2] } ${ i * sliceAngle }deg ${ (i + 1) * sliceAngle }deg`).join(', '); - return `conic-gradient(${ stops })`; - }, [ prizes, sliceAngle ]); - - if(!isVisible) return null; - - const canSpin = ((freeSpins + extraSpins) > 0) && !isSpinning && (prizes.length > 0); - - return ( - - setIsVisible(false) } /> - - - -
-
-
{ if(isSpinning) finishSpin(); } }> - { prizes.map((_, i) => ( -
- )) } - { prizes.map((prize, i) => - { - const centerAngle = ((i + 0.5) * sliceAngle); - return ( -
-
- { renderPrizeIcon(prize) } -
-
); - }) } -
-
-
- { LocalizeText('wheel.free.today', [ 'count' ], [ freeSpins.toString() ]) } - { LocalizeText('wheel.extra', [ 'count' ], [ extraSpins.toString() ]) } - - - - { canManage && - } - - - - { LocalizeText('wheel.winners') } - - { recentWins.map((win, i) => ( - -
- -
- - { win.username } - { win.prizeLabel } - -
- )) } - { !recentWins.length && - { LocalizeText('wheel.winners.empty') } } -
-
- - - { canManage && isSettingsOpen && - setIsSettingsOpen(false) } /> } - - ); -}; diff --git a/src/components/user-settings/rare-values/RareValuesView.tsx b/src/components/user-settings/rare-values/RareValuesView.tsx deleted file mode 100644 index c03f787..0000000 --- a/src/components/user-settings/rare-values/RareValuesView.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { AddLinkEventTracker, GetRoomEngine, GetSessionDataManager, ILinkEventTracker, IRareValue, RemoveLinkEventTracker } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useMemo, useState } from 'react'; -import { LocalizeFormattedNumber, LocalizeText } from '../../api'; -import { Column, Flex, LayoutCurrencyIcon, LayoutImage, Text } from '../../common'; -import { useRareValues } from '../../hooks'; -import { NitroCard, NitroInput } from '../../layout'; - -interface RareValueRow -{ - spriteId: number; - name: string; - iconUrl: string; - value: IRareValue; -} - -export const RareValuesView: FC<{}> = () => -{ - const [ isVisible, setIsVisible ] = useState(false); - const [ searchValue, setSearchValue ] = useState(''); - const { values = null, loaded = false } = useRareValues(); - - useEffect(() => - { - const linkTracker: ILinkEventTracker = { - linkReceived: (url: string) => - { - const parts = url.split('/'); - if(parts.length < 2) return; - - switch(parts[1]) - { - case 'show': setIsVisible(true); return; - case 'hide': setIsVisible(false); return; - case 'toggle': setIsVisible(prev => !prev); return; - } - }, - eventUrlPrefix: 'rare-values/', - }; - - AddLinkEventTracker(linkTracker); - - return () => RemoveLinkEventTracker(linkTracker); - }, []); - - const rows = useMemo(() => - { - if(!values) return []; - - const list: RareValueRow[] = []; - - values.forEach((value, spriteId) => - { - if(value.points <= 0) return; - - const floorData = GetSessionDataManager().getFloorItemData(spriteId); - const wallData = floorData ? null : GetSessionDataManager().getWallItemData(spriteId); - const data = (floorData ?? wallData); - - if(!data) return; - - const iconUrl = (floorData - ? GetRoomEngine().getFurnitureFloorIconUrl(spriteId) - : GetRoomEngine().getFurnitureWallIconUrl(spriteId)); - - list.push({ spriteId, name: (data.name || data.className || `#${ spriteId }`), iconUrl, value }); - }); - - list.sort((a, b) => (b.value.points - a.value.points)); - - return list; - }, [ values ]); - - const filtered = useMemo(() => - { - const query = searchValue.trim().toLocaleLowerCase(); - - if(!query) return rows; - - return rows.filter(row => row.name.toLocaleLowerCase().includes(query)); - }, [ rows, searchValue ]); - - if(!isVisible) return null; - - return ( - - setIsVisible(false) } /> - - - setSearchValue(event.target.value) } /> - - { !loaded && - { LocalizeText('rarevalues.loading') } } - { (loaded && !filtered.length) && - { LocalizeText('rarevalues.empty') } } - { filtered.map(row => ( - - - { row.name } - - { LocalizeFormattedNumber(row.value.points) } - - - - )) } - - - - - ); -};