Files
5chan/src/components/settings-modal/settings-modal.tsx
T

169 lines
6.6 KiB
TypeScript
Raw Normal View History

2026-02-24 20:41:47 +08:00
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import styles from './settings-modal.module.css';
import AccountSettings from './account-settings';
import CryptoAddressSetting from './crypto-address-setting';
2024-05-15 21:20:14 +02:00
import CryptoWalletsSetting from './crypto-wallets-setting';
import InterfaceSettings from './interface-settings';
import MediaHostingSettings from './media-hosting-settings';
import AdvancedSettings from './advanced-settings';
2025-02-05 15:20:07 +01:00
import SubscriptionsSetting from './subscriptions-setting';
2024-05-10 17:03:33 +02:00
2026-02-24 20:41:47 +08:00
const allSectionIds = ['interface-settings', 'media-hosting-settings', 'account-settings', 'subscriptions-settings', 'advanced-settings'];
const hashToSection = (hash: string): string | null => {
if (hash === 'crypto-address-settings' || hash === 'crypto-wallet-settings') return 'account-settings';
if (allSectionIds.includes(hash)) return hash;
return null;
};
const SettingsModal = () => {
const { t } = useTranslation();
const location = useLocation();
const navigate = useNavigate();
const hash = location.hash.slice(1);
2025-01-31 12:31:38 +01:00
const closeModal = useCallback(() => {
const newPath = location.pathname.replace(/\/settings$/, '');
navigate(newPath);
2025-01-31 12:31:38 +01:00
}, [location.pathname, navigate]);
2025-01-30 22:05:54 +01:00
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
closeModal();
}
};
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('keydown', handleEscape);
};
}, [closeModal]);
2026-02-24 20:41:47 +08:00
const [expandedSections, setExpandedSections] = useState<Set<string>>(() => {
const section = hashToSection(hash);
return section ? new Set([section]) : new Set();
});
2024-05-16 22:00:22 +02:00
2026-02-24 20:41:47 +08:00
const showInterfaceSettings = expandedSections.has('interface-settings');
const showMediaHostingSettings = expandedSections.has('media-hosting-settings');
const showAccountSettings = expandedSections.has('account-settings');
const showSubscriptionsSettings = expandedSections.has('subscriptions-settings');
const showAdvancedSettings = expandedSections.has('advanced-settings');
2026-02-24 20:41:47 +08:00
const allExpanded = useMemo(() => allSectionIds.every((id) => expandedSections.has(id)), [expandedSections]);
2026-02-24 20:41:47 +08:00
const basePath = location.pathname;
2026-02-24 20:41:47 +08:00
useEffect(() => {
const section = hashToSection(hash);
if (section && !expandedSections.has(section)) {
setExpandedSections((prev) => new Set(prev).add(section));
}
2026-02-24 20:41:47 +08:00
}, [hash]); // eslint-disable-line react-hooks/exhaustive-deps
const handleCategoryClick = (categoryId: string) => {
setExpandedSections((prev) => {
const next = new Set(prev);
const isOpening = !next.has(categoryId);
if (isOpening) {
next.add(categoryId);
} else {
next.delete(categoryId);
}
if (isOpening) {
navigate(`${basePath}#${categoryId}`, { replace: true });
} else if (next.size === 1) {
const remaining = next.values().next().value;
navigate(`${basePath}#${remaining}`, { replace: true });
} else {
navigate(basePath, { replace: true });
}
return next;
});
};
2024-05-16 22:00:22 +02:00
const handleExpandAll = () => {
2026-02-24 20:41:47 +08:00
if (allExpanded) {
setExpandedSections(new Set());
navigate(basePath, { replace: true });
} else {
setExpandedSections(new Set(allSectionIds));
navigate(basePath, { replace: true });
}
2024-05-16 22:00:22 +02:00
};
2024-05-10 17:03:33 +02:00
const handleKeyDown = (handler: () => void) => (e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handler();
}
};
return (
<>
<div className={styles.overlay} role='button' tabIndex={0} onClick={closeModal} onKeyDown={handleKeyDown(closeModal)} />
<div className={styles.settingsModal}>
<div className={styles.header}>
<span className={styles.title}>{t('settings')}</span>
<span className={styles.closeButton} role='button' tabIndex={0} title='close' onClick={closeModal} onKeyDown={handleKeyDown(closeModal)} />
</div>
2024-05-16 22:00:22 +02:00
<div className={styles.expandAllSettings}>
[
<span role='button' tabIndex={0} onClick={handleExpandAll} onKeyDown={handleKeyDown(handleExpandAll)}>
2026-02-24 20:41:47 +08:00
{allExpanded ? t('collapse_all_settings') : t('expand_all_settings')}
</span>
]
2024-05-16 22:00:22 +02:00
</div>
<div id='interface-settings' className={`${styles.setting} ${styles.category}`}>
2026-02-24 20:41:47 +08:00
<label onClick={() => handleCategoryClick('interface-settings')}>
<span className={showInterfaceSettings ? styles.hideButton : styles.showButton} />
2024-05-31 16:51:22 +02:00
{t('interface')}
</label>
</div>
{showInterfaceSettings && <InterfaceSettings />}
<div id='media-hosting-settings' className={`${styles.setting} ${styles.category}`}>
2026-02-24 20:41:47 +08:00
<label onClick={() => handleCategoryClick('media-hosting-settings')}>
<span className={showMediaHostingSettings ? styles.hideButton : styles.showButton} />
{t('media_hosting')}
</label>
</div>
{showMediaHostingSettings && <MediaHostingSettings />}
<div id='account-settings' className={`${styles.setting} ${styles.category}`}>
2026-02-24 20:41:47 +08:00
<label onClick={() => handleCategoryClick('account-settings')}>
2024-05-10 17:03:33 +02:00
<span className={showAccountSettings ? styles.hideButton : styles.showButton} />
{t('bitsocial_account')}
2024-05-10 17:03:33 +02:00
</label>
</div>
{showAccountSettings && (
<>
<AccountSettings />
<div className={styles.subSectionHeader}>{t('crypto_address')}</div>
<CryptoAddressSetting />
<div className={styles.subSectionHeader}>{t('crypto_wallets')}</div>
<CryptoWalletsSetting />
</>
)}
2025-02-05 15:20:07 +01:00
<div id='subscriptions-settings' className={`${styles.setting} ${styles.category}`}>
2026-02-24 20:41:47 +08:00
<label onClick={() => handleCategoryClick('subscriptions-settings')}>
2025-02-05 15:20:07 +01:00
<span className={showSubscriptionsSettings ? styles.hideButton : styles.showButton} />
{t('board_subscriptions')}
2025-02-05 15:20:07 +01:00
</label>
</div>
{showSubscriptionsSettings && <SubscriptionsSetting />}
<div id='advanced-settings' className={`${styles.setting} ${styles.category}`}>
2026-02-24 20:41:47 +08:00
<label onClick={() => handleCategoryClick('advanced-settings')}>
<span className={showAdvancedSettings ? styles.hideButton : styles.showButton} />
{t('advanced_settings')}
2024-06-18 11:50:17 +02:00
</label>
</div>
{showAdvancedSettings && <AdvancedSettings />}
</div>
</>
);
};
2024-05-09 16:04:24 +02:00
export default SettingsModal;