diff --git a/src/components/purse/PurseView.tsx b/src/components/purse/PurseView.tsx index 32d2fe8..f673f1a 100644 --- a/src/components/purse/PurseView.tsx +++ b/src/components/purse/PurseView.tsx @@ -1,5 +1,5 @@ import { CreateLinkEvent } from '@nitrots/nitro-renderer'; -import { FC, useCallback, useMemo } from 'react'; +import { FC, useCallback, useMemo, useState } from 'react'; import { FaChartBar, FaCog, FaSignOutAlt } from 'react-icons/fa'; import { ClearRememberLogin, GetConfigurationValue, GetRememberLogin, LocalizeText } from '../../api'; import { Column, LayoutCurrencyIcon } from '../../common'; @@ -16,6 +16,13 @@ const localizeWithFallback = (key: string, fallback: string) => export const PurseView: FC<{}> = props => { const { purse = null, hcDisabled = false } = usePurse(); + const [ settingsMenuOpen, setSettingsMenuOpen ] = useState(false); + + const openSettingsSection = useCallback((section: string) => + { + CreateLinkEvent('user-settings/show/' + section); + setSettingsMenuOpen(false); + }, []); const displayedCurrencies = useMemo(() => GetConfigurationValue('system.currency.types', []), []); const currencyDisplayNumberShort = useMemo(() => GetConfigurationValue('currency.display.number.short', false), []); @@ -123,13 +130,21 @@ export const PurseView: FC<{}> = props => + { settingsMenuOpen && +
+ + + + + +
} { otherCurrencies.length > 0 &&
{ otherCurrencies.map(type => ) } diff --git a/src/components/user-settings/UserSettingsView.tsx b/src/components/user-settings/UserSettingsView.tsx index 4ed5dbd..7d4acf5 100644 --- a/src/components/user-settings/UserSettingsView.tsx +++ b/src/components/user-settings/UserSettingsView.tsx @@ -2,13 +2,24 @@ import { AddLinkEventTracker, CreateLinkEvent, ILinkEventTracker, NitroSettingsE import { FC, useEffect, useState } from 'react'; import { FaUserCog, FaVolumeDown, FaVolumeMute, FaVolumeUp } from 'react-icons/fa'; import { DispatchMainEvent, DispatchUiEvent, LocalizeText, SendMessageComposer } from '../../api'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; +import { Button, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; import { useCatalogPlaceMultipleItems, useCatalogSkipPurchaseConfirmation, useChatWindow, useMessageEvent } from '../../hooks'; import { classNames } from '../../layout'; +const localizeWithFallback = (key: string, fallback: string) => +{ + const text = LocalizeText(key); + return (text && text !== key) ? text : fallback; +}; + +// null = full window (legacy). 'audio' | 'chat' | 'other' = focused section +// opened from the purse gear dropdown. +type SettingsSection = null | 'audio' | 'chat' | 'other'; + export const UserSettingsView: FC<{}> = props => { const [ isVisible, setIsVisible ] = useState(false); + const [ section, setSection ] = useState(null); const [ userSettings, setUserSettings ] = useState(null); const [ catalogPlaceMultipleObjects, setCatalogPlaceMultipleObjects ] = useCatalogPlaceMultipleItems(); const [ catalogSkipPurchaseConfirmation, setCatalogSkipPurchaseConfirmation ] = useCatalogSkipPurchaseConfirmation(); @@ -100,12 +111,14 @@ export const UserSettingsView: FC<{}> = props => switch(parts[1]) { case 'show': + setSection((parts[2] as SettingsSection) || null); setIsVisible(true); return; case 'hide': setIsVisible(false); return; case 'toggle': + setSection((parts[2] as SettingsSection) || null); setIsVisible(prevValue => !prevValue); return; } @@ -127,81 +140,104 @@ export const UserSettingsView: FC<{}> = props => if(!isVisible || !userSettings) return null; + const showChat = (section === null || section === 'chat'); + const showOther = (section === null || section === 'other'); + const showAudio = (section === null || section === 'audio'); + const showAccountLink = (section === null); + + const headerText = (section === 'audio') + ? localizeWithFallback('widget.memenu.settings.volume', 'Audio settings') + : (section === 'chat') + ? localizeWithFallback('room.chat.settings.title', 'Chat settings') + : (section === 'other') + ? localizeWithFallback('memenu.settings.other', 'Other settings') + : LocalizeText('widget.memenu.settings.title'); + return ( - processAction('close_view') } /> + processAction('close_view') } /> -
-
- processAction('oldchat', event.target.checked) } /> - { LocalizeText('memenu.settings.chat.prefer.old.chat') } -
-
- processAction('room_invites', event.target.checked) } /> - { LocalizeText('memenu.settings.other.ignore.room.invites') } -
-
- processAction('camera_follow', event.target.checked) } /> - { LocalizeText('memenu.settings.other.disable.room.camera.follow') } -
-
- setCatalogPlaceMultipleObjects(event.target.checked) } /> - { LocalizeText('memenu.settings.other.place.multiple.objects') } -
-
- setCatalogSkipPurchaseConfirmation(event.target.checked) } /> - { LocalizeText('memenu.settings.other.skip.purchase.confirmation') } -
-
- setChatWindowEnabled(event.target.checked) } /> - { LocalizeText('memenu.settings.other.enable.chat.window') } -
-
-
- { LocalizeText('widget.memenu.settings.volume') } + { showChat &&
- { LocalizeText('widget.memenu.settings.volume.ui') }
- { (userSettings.volumeSystem === 0) && = 50) && 'text-muted', 'fa-icon') } /> } - { (userSettings.volumeSystem > 0) && = 50) && 'text-muted', 'fa-icon') } /> } - processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> - + processAction('oldchat', event.target.checked) } /> + { LocalizeText('memenu.settings.chat.prefer.old.chat') }
-
+
+ setChatWindowEnabled(event.target.checked) } /> + { LocalizeText('memenu.settings.other.enable.chat.window') } +
+
} + { showOther &&
- { LocalizeText('widget.memenu.settings.volume.furni') }
- { (userSettings.volumeFurni === 0) && = 50) && 'text-muted', 'fa-icon') } /> } - { (userSettings.volumeFurni > 0) && = 50) && 'text-muted', 'fa-icon') } /> } - processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> - + processAction('room_invites', event.target.checked) } /> + { LocalizeText('memenu.settings.other.ignore.room.invites') }
-
-
- { LocalizeText('widget.memenu.settings.volume.trax') }
- { (userSettings.volumeTrax === 0) && = 50) && 'text-muted', 'fa-icon') } /> } - { (userSettings.volumeTrax > 0) && = 50) && 'text-muted', 'fa-icon') } /> } - processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> - + processAction('camera_follow', event.target.checked) } /> + { LocalizeText('memenu.settings.other.disable.room.camera.follow') }
-
-
-
- -
+ } + { showAudio && +
+ { LocalizeText('widget.memenu.settings.volume') } +
+ { LocalizeText('widget.memenu.settings.volume.ui') } +
+ { (userSettings.volumeSystem === 0) && = 50) && 'text-muted', 'fa-icon') } /> } + { (userSettings.volumeSystem > 0) && = 50) && 'text-muted', 'fa-icon') } /> } + processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> + +
+
+
+ { LocalizeText('widget.memenu.settings.volume.furni') } +
+ { (userSettings.volumeFurni === 0) && = 50) && 'text-muted', 'fa-icon') } /> } + { (userSettings.volumeFurni > 0) && = 50) && 'text-muted', 'fa-icon') } /> } + processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> + +
+
+
+ { LocalizeText('widget.memenu.settings.volume.trax') } +
+ { (userSettings.volumeTrax === 0) && = 50) && 'text-muted', 'fa-icon') } /> } + { (userSettings.volumeTrax > 0) && = 50) && 'text-muted', 'fa-icon') } /> } + processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') } /> + +
+
+
} + { showAccountLink && +
+ +
} + { (section !== null) && +
+ +
} ); diff --git a/src/css/purse/PurseView.css b/src/css/purse/PurseView.css index b60fe8b..f6a1294 100644 --- a/src/css/purse/PurseView.css +++ b/src/css/purse/PurseView.css @@ -230,6 +230,45 @@ object-fit: contain; } +/* ---- Settings dropdown (gear menu) ---- */ +.nitro-purse-menu { + width: 100%; + max-width: 200px; + margin-top: 4px; + margin-left: auto; + display: flex; + flex-direction: column; + overflow: hidden; + border: 2px solid #41403c; + border-radius: 8px; + background: rgba(10, 10, 12, 0.92); + box-shadow: 0 8px 18px rgba(0, 0, 0, 0.3); + pointer-events: all; +} + +.nitro-purse-menu__item { + padding: 6px 10px; + text-align: left; + font-size: 0.78rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + background: transparent; + border: 0; + cursor: pointer; + transition: background 0.12s ease; +} + +.nitro-purse-menu__item:hover { + background: rgba(255, 255, 255, 0.08); +} + +.nitro-purse-menu__item--disabled, +.nitro-purse-menu__item--disabled:hover { + color: rgba(255, 255, 255, 0.35); + background: transparent; + cursor: default; +} + @media (max-width: 640px) { .nitro-purse { max-width: 100%;