diff --git a/src/components/purse/PurseClassicView.tsx b/src/components/purse/PurseClassicView.tsx deleted file mode 100644 index d8f6408..0000000 --- a/src/components/purse/PurseClassicView.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { CreateLinkEvent, HabboClubLevelEnum } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { FaChevronDown, FaLanguage, FaQuestionCircle, FaSignOutAlt } from 'react-icons/fa'; -import { ClearRememberLogin, FriendlyTime, GetConfigurationValue, GetRememberLogin, LocalizeText } from '../../api'; -import { Column, Flex, LayoutCurrencyIcon, Text } from '../../common'; -import { usePurse } from '../../hooks'; -import purseIcon from '../../assets/images/rightside/purse.gif'; -import { CurrencyView } from './views/CurrencyView'; -import { SeasonalView } from './views/SeasonalView'; - -export const PurseClassicView: FC<{}> = props => -{ - const { purse = null, hcDisabled = false } = usePurse(); - const [ isOpen, setIsOpen ] = useState(true); - const [ isCompact, setIsCompact ] = useState(false); - - const displayedCurrencies = useMemo(() => GetConfigurationValue('system.currency.types', []), []); - const currencyDisplayNumberShort = useMemo(() => GetConfigurationValue('currency.display.number.short', false), []); - - const getClubText = (() => - { - if (!purse) return null; - - const totalDays = ((purse.clubPeriods * 31) + purse.clubDays); - const minutesUntilExpiration = purse.minutesUntilExpiration; - - if (purse.clubLevel === HabboClubLevelEnum.NO_CLUB) return LocalizeText('purse.clubdays.zero.amount.text'); - else if ((minutesUntilExpiration > -1) && (minutesUntilExpiration < (60 * 24))) return FriendlyTime.shortFormat(minutesUntilExpiration * 60); - else return FriendlyTime.shortFormat(totalDays * 86400); - })(); - - const currencyTypes = useMemo(() => - { - if (!purse || !purse.activityPoints || !purse.activityPoints.size) return []; - - const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0)); - types.sort((a, b) => - { - if (a === 0) return -1; - if (b === 0) return 1; - if (a === 5) return -1; - if (b === 5) return 1; - return a - b; - }); - - return types; - }, [ displayedCurrencies, purse ]); - - const primaryCurrencies = currencyTypes.slice(0, 2); - const seasonalCurrencies = currencyTypes.slice(2); - - useEffect(() => - { - if(isOpen) - { - setIsCompact(false); - return; - } - - const timeout = window.setTimeout(() => setIsCompact(true), 220); - - return () => window.clearTimeout(timeout); - }, [ isOpen ]); - - const handleLogout = useCallback(async (event: React.MouseEvent) => - { - event.stopPropagation(); - - const logoutUrl = GetConfigurationValue('login.logout.endpoint', '/api/auth/logout'); - const ssoTicket = (window.NitroConfig?.['sso.ticket'] as string) ?? ''; - const rememberToken = GetRememberLogin()?.token || ''; - - try - { - await fetch(logoutUrl, { - method: 'POST', - credentials: 'include', - keepalive: true, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Requested-With': 'NitroPurseLogout' - }, - body: JSON.stringify({ ssoTicket, rememberToken }) - }); - } - catch - { /* best-effort — proceed with local logout regardless */ } - - ClearRememberLogin(); - if(window.NitroConfig) window.NitroConfig['sso.ticket'] = ''; - window.location.reload(); - }, []); - - if (!purse) return null; - - return ( - -
-
-
-
setIsOpen(value => !value) }> - -
- -
-
-
- -
-
-
-
-
- - { primaryCurrencies.map(type => ) } -
- { !hcDisabled && -
- { - event.stopPropagation(); CreateLinkEvent('catalog/open/' + GetConfigurationValue>('catalog.links')?.['hc.buy_hc']); - } }> -
- -
-
- HC - { getClubText } -
-
} -
- - - - -
-
-
-
-
-
- { seasonalCurrencies.length > 0 && -
- { seasonalCurrencies.map(type => ) } -
} -
- ); -}; diff --git a/src/components/purse/PurseModernView.tsx b/src/components/purse/PurseModernView.tsx deleted file mode 100644 index e3579fd..0000000 --- a/src/components/purse/PurseModernView.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { CreateLinkEvent } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useMemo } from 'react'; -import { FaChartBar, FaCog, FaSignOutAlt } from 'react-icons/fa'; -import { ClearRememberLogin, GetConfigurationValue, GetRememberLogin, LocalizeText } from '../../api'; -import { Column, LayoutCurrencyIcon } from '../../common'; -import { usePurse } from '../../hooks'; -import { CurrencyView } from './views/CurrencyView'; -import { SeasonalView } from './views/SeasonalView'; - -const localizeWithFallback = (key: string, fallback: string) => -{ - const text = LocalizeText(key); - return (text && text !== key) ? text : fallback; -}; - -export const PurseModernView: FC<{}> = props => -{ - const { purse = null, hcDisabled = false } = usePurse(); - - const displayedCurrencies = useMemo(() => GetConfigurationValue('system.currency.types', []), []); - const currencyDisplayNumberShort = useMemo(() => GetConfigurationValue('currency.display.number.short', false), []); - - const currencyTypes = useMemo(() => - { - if (!purse || !purse.activityPoints || !purse.activityPoints.size) return []; - - const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0)); - types.sort((a, b) => - { - if (a === 0) return -1; - if (b === 0) return 1; - if (a === 5) return -1; - if (b === 5) return 1; - return a - b; - }); - - return types; - }, [ displayedCurrencies, purse ]); - - const hasDiamonds = currencyTypes.indexOf(5) >= 0; - const hasDuckets = currencyTypes.indexOf(0) >= 0; - const otherCurrencies = currencyTypes.filter(type => (type !== 0 && type !== 5)); - - const joinLabel = useMemo(() => localizeWithFallback('purse.join', 'Join'), []); - const earningsLabel = useMemo(() => localizeWithFallback('earnings.title', 'Earnings'), []); - const helpLabel = useMemo(() => localizeWithFallback('help.button.name', 'Help'), []); - - const openClub = useCallback((event: React.MouseEvent) => - { - event.stopPropagation(); - - const page = GetConfigurationValue('hc.buy_hc', 'habbo_club'); - CreateLinkEvent('catalog/open/' + page); - }, []); - - const openEarnings = useCallback((event: React.MouseEvent) => - { - event.stopPropagation(); - CreateLinkEvent('habboUI/open/vault'); - }, []); - - const handleLogout = useCallback(async (event: React.MouseEvent) => - { - event.stopPropagation(); - - const logoutUrl = GetConfigurationValue('login.logout.endpoint', '/api/auth/logout'); - const ssoTicket = (window.NitroConfig?.['sso.ticket'] as string) ?? ''; - const rememberToken = GetRememberLogin()?.token || ''; - - try - { - await fetch(logoutUrl, { - method: 'POST', - credentials: 'include', - keepalive: true, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Requested-With': 'NitroPurseLogout' - }, - body: JSON.stringify({ ssoTicket, rememberToken }) - }); - } - catch - { /* best-effort — proceed with local logout regardless */ } - - ClearRememberLogin(); - if(window.NitroConfig) window.NitroConfig['sso.ticket'] = ''; - window.location.reload(); - }, []); - - if (!purse) return null; - - return ( - -
-
-
- { hasDiamonds && } - - { hasDuckets && } -
-
- { !hcDisabled && - } - -
-
- - - -
-
-
- { otherCurrencies.length > 0 && -
- { otherCurrencies.map(type => ) } -
} -
- ); -}; diff --git a/src/components/purse/PurseView.tsx b/src/components/purse/PurseView.tsx index b039ebd..32d2fe8 100644 --- a/src/components/purse/PurseView.tsx +++ b/src/components/purse/PurseView.tsx @@ -1,7 +1,139 @@ -import { FC } from 'react'; -import { PurseModernView } from './PurseModernView'; +import { CreateLinkEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useMemo } from 'react'; +import { FaChartBar, FaCog, FaSignOutAlt } from 'react-icons/fa'; +import { ClearRememberLogin, GetConfigurationValue, GetRememberLogin, LocalizeText } from '../../api'; +import { Column, LayoutCurrencyIcon } from '../../common'; +import { usePurse } from '../../hooks'; +import { CurrencyView } from './views/CurrencyView'; +import { SeasonalView } from './views/SeasonalView'; + +const localizeWithFallback = (key: string, fallback: string) => +{ + const text = LocalizeText(key); + return (text && text !== key) ? text : fallback; +}; export const PurseView: FC<{}> = props => { - return ; + const { purse = null, hcDisabled = false } = usePurse(); + + const displayedCurrencies = useMemo(() => GetConfigurationValue('system.currency.types', []), []); + const currencyDisplayNumberShort = useMemo(() => GetConfigurationValue('currency.display.number.short', false), []); + + const currencyTypes = useMemo(() => + { + if (!purse || !purse.activityPoints || !purse.activityPoints.size) return []; + + const types = Array.from(purse.activityPoints.keys()).filter(type => (displayedCurrencies.indexOf(type) >= 0)); + types.sort((a, b) => + { + if (a === 0) return -1; + if (b === 0) return 1; + if (a === 5) return -1; + if (b === 5) return 1; + return a - b; + }); + + return types; + }, [ displayedCurrencies, purse ]); + + const hasDiamonds = currencyTypes.indexOf(5) >= 0; + const hasDuckets = currencyTypes.indexOf(0) >= 0; + const otherCurrencies = currencyTypes.filter(type => (type !== 0 && type !== 5)); + + const joinLabel = useMemo(() => localizeWithFallback('purse.join', 'Join'), []); + const earningsLabel = useMemo(() => localizeWithFallback('earnings.title', 'Earnings'), []); + const helpLabel = useMemo(() => localizeWithFallback('help.button.name', 'Help'), []); + + const openClub = useCallback((event: React.MouseEvent) => + { + event.stopPropagation(); + + const page = GetConfigurationValue('hc.buy_hc', 'habbo_club'); + CreateLinkEvent('catalog/open/' + page); + }, []); + + const openEarnings = useCallback((event: React.MouseEvent) => + { + event.stopPropagation(); + CreateLinkEvent('habboUI/open/vault'); + }, []); + + const handleLogout = useCallback(async (event: React.MouseEvent) => + { + event.stopPropagation(); + + const logoutUrl = GetConfigurationValue('login.logout.endpoint', '/api/auth/logout'); + const ssoTicket = (window.NitroConfig?.['sso.ticket'] as string) ?? ''; + const rememberToken = GetRememberLogin()?.token || ''; + + try + { + await fetch(logoutUrl, { + method: 'POST', + credentials: 'include', + keepalive: true, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-Requested-With': 'NitroPurseLogout' + }, + body: JSON.stringify({ ssoTicket, rememberToken }) + }); + } + catch + { /* best-effort — proceed with local logout regardless */ } + + ClearRememberLogin(); + if(window.NitroConfig) window.NitroConfig['sso.ticket'] = ''; + window.location.reload(); + }, []); + + if (!purse) return null; + + return ( + +
+
+
+ { hasDiamonds && } + + { hasDuckets && } +
+
+ { !hcDisabled && + } + +
+
+ + + +
+
+
+ { otherCurrencies.length > 0 && +
+ { otherCurrencies.map(type => ) } +
} +
+ ); }; diff --git a/src/css/purse/PurseClassicView.css b/src/css/purse/PurseClassicView.css deleted file mode 100644 index c3456af..0000000 --- a/src/css/purse/PurseClassicView.css +++ /dev/null @@ -1,420 +0,0 @@ -/* Classic (original) purse style. All selectors are scoped under - .nitro-purse-classic so they never collide with the modern PurseView.css - rules that share class names like .nitro-purse. */ - -.nitro-purse-classic { - width: 100%; -} - -/* Extra (seasonal) currency in classic mode reuses the modern boxed - .nitro-purse__other styling, just constrained to the classic purse width. */ -.nitro-purse__other--classic { - max-width: 125px; -} - -/* The #41403c border on the extra-currency box is new-style-only; - classic mode has no border. */ -.nitro-purse__other--classic .nitro-purse-seasonal-currency { - border: 0; -} - -.nitro-purse-classic .nitro-purse-shell { - width: 100%; - max-width: 188px; - margin-top: 6px; - margin-left: auto; -} - -.nitro-purse-classic .nitro-purse-shell.is-closed { - width: 52px; - max-width: 52px; -} - -.nitro-purse-classic .nitro-purse { - width: 100%; - max-width: none; - margin: 0; - overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.07); - border-radius: 10px; - background: rgba(10, 10, 12, 0.58); - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.05), - 0 8px 18px rgba(0, 0, 0, 0.14); - transition: width 0.24s cubic-bezier(0.22, 1, 0.36, 1), max-width 0.24s cubic-bezier(0.22, 1, 0.36, 1), border-color 0.2s ease; -} - -.nitro-purse-classic .nitro-purse.is-closed { - width: 52px; -} - -.nitro-purse-classic .nitro-purse__header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - padding: 5px 7px; - cursor: pointer; - border-bottom: 1px solid rgba(255, 255, 255, 0.06); - background: linear-gradient(180deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.03)); - transition: background-color 0.2s ease, border-color 0.2s ease; -} - -.nitro-purse-classic .nitro-purse__header.is-closed { - justify-content: flex-end; - gap: 5px; - padding: 5px 6px; -} - -.nitro-purse-classic .nitro-purse__header-main { - display: inline-flex; - align-items: center; -} - -.nitro-purse-classic .nitro-purse__header-main.is-closed { - margin-right: 0; -} - -.nitro-purse-classic .nitro-purse__header-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - opacity: 0.9; -} - -.nitro-purse-classic .nitro-purse__header-image { - display: block; - width: auto; - height: 14px; - object-fit: contain; -} - -.nitro-purse-classic .nitro-purse__header-title { - font-size: 0.82rem; - font-weight: 700; - line-height: 1; - color: rgba(255, 255, 255, 0.92) !important; - letter-spacing: 0.01em; -} - -.nitro-purse-classic .nitro-purse__header-toggle { - display: inline-flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - border-radius: 6px; - color: rgba(255, 255, 255, 0.82); - background: rgba(255, 255, 255, 0.06); - transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.2s ease; -} - -.nitro-purse-classic .nitro-purse__header-toggle.is-open { - transform: rotate(180deg); -} - -.nitro-purse-classic .nitro-purse__content { - display: flex; - flex-direction: column; - gap: 5px; - padding: 6px; - overflow: hidden; - transform-origin: top; - transition: - max-height 0.58s cubic-bezier(0.16, 1, 0.3, 1), - opacity 0.38s ease-out, - transform 0.58s cubic-bezier(0.16, 1, 0.3, 1), - padding 0.4s ease-out; - max-height: 280px; - opacity: 1; - transform: translateY(0); - background: transparent; -} - -.nitro-purse-classic .nitro-purse__content.is-closed { - max-height: 0; - opacity: 0; - transform: translateY(-8px) scaleY(0.95); - padding-top: 0; - padding-bottom: 0; - pointer-events: none; -} - -.nitro-purse-classic .nitro-purse__summary, -.nitro-purse-classic .nitro-purse__seasonal { - transition: - opacity 0.32s ease-out, - transform 0.48s cubic-bezier(0.16, 1, 0.3, 1); -} - -.nitro-purse-classic .nitro-purse__summary { - transition-delay: 0.08s; -} - -.nitro-purse-classic .nitro-purse__seasonal { - transition-delay: 0.16s; -} - -.nitro-purse-classic .nitro-purse__content.is-closed .nitro-purse__summary, -.nitro-purse-classic .nitro-purse__content.is-closed .nitro-purse__seasonal { - opacity: 0; - transform: translateY(-6px); - transition-delay: 0s; -} - -.nitro-purse-classic .nitro-purse__summary { - display: grid; - grid-template-columns: minmax(0, 1fr) 30px 26px; - gap: 5px; - align-items: stretch; -} - -.nitro-purse-classic .nitro-purse__summary.is-no-hc { - grid-template-columns: minmax(0, 1fr) 26px; -} - -.nitro-purse-classic .nitro-purse__primary, -.nitro-purse-classic .nitro-purse__seasonal { - display: flex; - flex-direction: column; - gap: 3px; -} - -.nitro-purse-classic .nitro-purse .nitro-purse-button, -.nitro-purse-classic .nitro-purse-seasonal-currency { - min-height: 22px; - padding: 2px 0; - border: 0 !important; - border-radius: 0 !important; - background: transparent !important; - box-shadow: none !important; -} - -.nitro-purse-classic .nitro-purse .allcurrencypurse, -.nitro-purse-classic .nitro-purse-seasonal-currency { - position: relative; -} - -.nitro-purse-classic .nitro-purse .allcurrencypurse::after, -.nitro-purse-classic .nitro-purse-seasonal-currency::after { - content: ''; - position: absolute; - left: 0; - right: 0; - bottom: -1px; - height: 1px; - background: rgba(255, 255, 255, 0.08); -} - -.nitro-purse-classic .nitro-purse__primary > :last-child::after, -.nitro-purse-classic .nitro-purse__seasonal > :last-child::after { - display: none; -} - -.nitro-purse-classic .nitro-purse .allcurrencypurse .text-white { - font-size: 0.76rem; - font-weight: 700; - line-height: 1; - letter-spacing: 0.01em; - color: rgba(255, 255, 255, 0.88) !important; -} - -.nitro-purse-classic .nitro-purse .nitro-purse-button.currency--1 .text-white { - color: #7fdcff !important; -} - -.nitro-purse-classic .nitro-purse .nitro-purse-button.currency-0 .text-white { - color: #ffd76d !important; -} - -.nitro-purse-classic .nitro-purse .nitro-purse-button.currency-5 .text-white { - color: #df95ff !important; -} - -.nitro-purse-classic .nitro-purse-subscription { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 2px; - min-height: 62px; - cursor: pointer; - border-left: 1px solid rgba(255, 255, 255, 0.08); - border-right: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(255, 255, 255, 0.02); -} - -.nitro-purse-classic .nitro-purse-subscription__icon { - display: flex; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - opacity: 0.95; -} - -.nitro-purse-classic .nitro-purse-subscription__copy { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - gap: 1px; -} - -.nitro-purse-classic .nitro-purse-subscription__label { - font-size: 0.5rem; - font-weight: 700; - color: rgba(255, 255, 255, 0.62) !important; - letter-spacing: 0.08em; -} - -.nitro-purse-classic .nitro-purse-subscription__value { - font-size: 0.54rem; - font-weight: 700; - line-height: 1.05; - color: #ffffff !important; -} - -.nitro-purse-classic .nitro-purse__actions { - display: grid; - grid-template-columns: 1fr; - gap: 3px; -} - -.nitro-purse-classic .nitro-purse__action-button { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 20px; - padding: 0; - border: 1px solid rgba(7, 23, 31, 0.82); - border-radius: 7px; - color: rgba(255, 255, 255, 0.88); - background: rgba(255, 255, 255, 0.05); - box-shadow: none; - transition: background-color 0.18s ease, transform 0.18s ease; -} - -.nitro-purse-classic .nitro-purse__action-button:hover { - background: rgba(255, 255, 255, 0.1); - transform: translateY(-1px); -} - -.nitro-purse-classic .nitro-purse__action-button .nitro-icon { - transform: scale(0.82); -} - -.nitro-purse-classic .nitro-purse-seasonal-currency > div { - align-items: center; - gap: 6px; - padding: 0; -} - -.nitro-purse-classic .seasonal-row { - min-width: 0; -} - -.nitro-purse-classic .seasonal-text-padding, -.nitro-purse-classic .seasonal-amount { - display: flex; - align-items: center; - margin-left: 0; -} - -.nitro-purse-classic .seasonal-text { - min-width: 0; - font-size: 0.76rem; - font-weight: 700; - color: rgba(255, 255, 255, 0.76) !important; - line-height: 1; - letter-spacing: 0.01em; -} - -.nitro-purse-classic .seasonal-amount { - margin-left: auto; - white-space: nowrap; - flex: 0 0 auto; - font-size: 0.76rem; - font-weight: 700; - line-height: 1; - color: rgba(255, 255, 255, 0.96) !important; -} - -.nitro-purse-classic .seasonal-image-padding { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0; - border-radius: 0; - background: transparent; - flex: 0 0 auto; -} - -.nitro-purse-classic .seasonal-image { - display: block; - width: auto; - height: 14px; - object-fit: contain; -} - -@media (max-width: 640px) { - .nitro-purse-classic .nitro-purse-shell { - max-width: 100%; - } - - .nitro-purse-classic .nitro-purse-shell.is-closed { - max-width: 52px; - } - - .nitro-purse-classic .nitro-purse { - border-radius: 9px; - } - - .nitro-purse-classic .nitro-purse__content { - padding: 6px; - } - - .nitro-purse-classic .nitro-purse__summary { - grid-template-columns: minmax(0, 1fr) 28px 24px; - gap: 4px; - } - - .nitro-purse-classic .nitro-purse__summary.is-no-hc { - grid-template-columns: minmax(0, 1fr) 24px; - } - - .nitro-purse-classic .nitro-purse-subscription { - min-height: 58px; - } - - .nitro-purse-classic .nitro-purse .allcurrencypurse .text-white, - .nitro-purse-classic .seasonal-text, - .nitro-purse-classic .seasonal-amount { - font-size: 0.72rem; - } - - .nitro-purse-classic .nitro-purse__header-title { - font-size: 0.78rem; - } -} - -@media (max-width: 420px) { - .nitro-purse-classic .nitro-purse__summary { - grid-template-columns: minmax(0, 1fr) 26px 22px; - gap: 4px; - } - - .nitro-purse-classic .nitro-purse__summary.is-no-hc { - grid-template-columns: minmax(0, 1fr) 22px; - } - - .nitro-purse-classic .nitro-purse-subscription { - min-height: 54px; - } - - .nitro-purse-classic .nitro-purse-subscription__value { - font-size: 0.55rem; - } -}