1- import React from 'react' ;
2- import { Routes , Route , Navigate } from 'react-router-dom' ;
1+ import React , { useState , useEffect , useMemo } from 'react' ;
2+ import { useLocation , useNavigate } from 'react-router-dom' ;
33import { PageTitle } from '@app/components/common/PageTitle/PageTitle' ;
4- import SettingsNavigation from './SettingsNavigation' ;
4+ import styled from 'styled-components' ;
5+ import {
6+ FilterOutlined ,
7+ PictureOutlined ,
8+ ApiOutlined ,
9+ RobotOutlined ,
10+ TwitterOutlined ,
11+ InfoCircleOutlined ,
12+ WalletOutlined ,
13+ GlobalOutlined ,
14+ DatabaseOutlined ,
15+ DownOutlined ,
16+ RightOutlined
17+ } from '@ant-design/icons' ;
518import ImageModerationSettings from './ImageModerationSettings' ;
619import ContentFilterSettings from './ContentFilterSettings' ;
720import NestFeederSettings from './NestFeederSettings' ;
@@ -12,27 +25,181 @@ import RelayInfoSettings from './RelayInfoSettings';
1225import QueryCacheSettings from './QueryCacheSettings' ;
1326import XNostrSettings from './XNostrSettings' ;
1427
28+ const SettingsContainer = styled . div `
29+ width: 100%;
30+ max-height: calc(100vh - 150px);
31+ overflow-y: auto;
32+ padding-right: 10px;
33+ ` ;
34+
35+ const SettingSection = styled . div `
36+ margin-bottom: 1rem;
37+ border-radius: 8px;
38+ overflow: hidden;
39+ background-color: rgba(0, 0, 0, 0.2);
40+ ` ;
41+
42+ const SectionHeader = styled . div < { $isActive : boolean } > `
43+ display: flex;
44+ align-items: center;
45+ padding: 16px;
46+ cursor: pointer;
47+ background-color: rgba(0, 0, 0, 0.3);
48+ transition: background-color 0.3s;
49+
50+ &:hover {
51+ background-color: rgba(0, 0, 0, 0.4);
52+ }
53+
54+ ${ props => props . $isActive && `
55+ background-color: rgba(0, 0, 0, 0.5);
56+ ` }
57+ ` ;
58+
59+ const SectionIcon = styled . span `
60+ margin-right: 12px;
61+ font-size: 16px;
62+ display: flex;
63+ align-items: center;
64+ ` ;
65+
66+ const SectionTitle = styled . span `
67+ flex: 1;
68+ font-size: 16px;
69+ ` ;
70+
71+ const SectionContent = styled . div < { $isVisible : boolean } > `
72+ display: ${ props => props . $isVisible ? 'block' : 'none' } ;
73+ padding: 0;
74+ transition: all 0.3s;
75+ ` ;
76+
77+ interface SettingItem {
78+ key : string ;
79+ path : string ;
80+ label : string ;
81+ icon : React . ReactNode ;
82+ component : React . ReactNode ;
83+ }
84+
1585const SettingsPage : React . FC = ( ) => {
86+ const location = useLocation ( ) ;
87+ const navigate = useNavigate ( ) ;
88+ const [ activeKey , setActiveKey ] = useState < string | undefined > ( undefined ) ;
89+
90+ const settingItems : SettingItem [ ] = useMemo ( ( ) => [
91+ {
92+ key : 'image_moderation' ,
93+ path : '/settings/image-moderation' ,
94+ label : 'Image Moderation' ,
95+ icon : < PictureOutlined /> ,
96+ component : < ImageModerationSettings />
97+ } ,
98+ {
99+ key : 'content_filter' ,
100+ path : '/settings/content-filter' ,
101+ label : 'Content Filter' ,
102+ icon : < FilterOutlined /> ,
103+ component : < ContentFilterSettings />
104+ } ,
105+ {
106+ key : 'nest_feeder' ,
107+ path : '/settings/nest-feeder' ,
108+ label : 'Nest Feeder' ,
109+ icon : < ApiOutlined /> ,
110+ component : < NestFeederSettings />
111+ } ,
112+ {
113+ key : 'ollama' ,
114+ path : '/settings/ollama' ,
115+ label : 'Ollama' ,
116+ icon : < RobotOutlined /> ,
117+ component : < OllamaSettings />
118+ } ,
119+ {
120+ key : 'xnostr' ,
121+ path : '/settings/xnostr' ,
122+ label : 'XNostr' ,
123+ icon : < TwitterOutlined /> ,
124+ component : < XNostrSettings />
125+ } ,
126+ {
127+ key : 'relay_info' ,
128+ path : '/settings/relay-info' ,
129+ label : 'Relay Info' ,
130+ icon : < InfoCircleOutlined /> ,
131+ component : < RelayInfoSettings />
132+ } ,
133+ {
134+ key : 'wallet' ,
135+ path : '/settings/wallet' ,
136+ label : 'Wallet' ,
137+ icon : < WalletOutlined /> ,
138+ component : < WalletSettings />
139+ } ,
140+ {
141+ key : 'general' ,
142+ path : '/settings/general' ,
143+ label : 'General' ,
144+ icon : < GlobalOutlined /> ,
145+ component : < GeneralSettings />
146+ } ,
147+ {
148+ key : 'query_cache' ,
149+ path : '/settings/query-cache' ,
150+ label : 'Query Cache' ,
151+ icon : < DatabaseOutlined /> ,
152+ component : < QueryCacheSettings />
153+ }
154+ ] , [ ] ) ;
155+
156+ // Set active key based on current path
157+ useEffect ( ( ) => {
158+ const path = location . pathname ;
159+ const item = settingItems . find ( item => item . path === path ) ;
160+
161+ if ( item ) {
162+ setActiveKey ( item . key ) ;
163+ } else if ( path === '/settings' || path === '/settings/' ) {
164+ // Don't navigate to any settings by default
165+ setActiveKey ( undefined ) ;
166+ }
167+ } , [ location . pathname , navigate , settingItems ] ) ;
168+
169+ const handleSectionClick = ( item : SettingItem ) => {
170+ if ( activeKey === item . key ) {
171+ // If clicking the active section, close it
172+ setActiveKey ( undefined ) ;
173+ navigate ( '/settings' ) ;
174+ } else {
175+ // Otherwise, open the clicked section
176+ setActiveKey ( item . key ) ;
177+ navigate ( item . path ) ;
178+ }
179+ } ;
180+
16181 return (
17182 < >
18183 < PageTitle > Advanced Settings</ PageTitle >
19184
20- < SettingsNavigation />
21-
22- < Routes >
23- < Route path = "image-moderation" element = { < ImageModerationSettings /> } />
24- < Route path = "content-filter" element = { < ContentFilterSettings /> } />
25- < Route path = "nest-feeder" element = { < NestFeederSettings /> } />
26- < Route path = "ollama" element = { < OllamaSettings /> } />
27- < Route path = "xnostr" element = { < XNostrSettings /> } />
28- < Route path = "relay-info" element = { < RelayInfoSettings /> } />
29- < Route path = "wallet" element = { < WalletSettings /> } />
30- < Route path = "general" element = { < GeneralSettings /> } />
31- < Route path = "query-cache" element = { < QueryCacheSettings /> } />
32-
33- { /* Redirect to general settings by default */ }
34- < Route path = "/" element = { < Navigate to = "general" replace /> } />
35- </ Routes >
185+ < SettingsContainer >
186+ { settingItems . map ( item => (
187+ < SettingSection key = { item . key } >
188+ < SectionHeader
189+ $isActive = { activeKey === item . key }
190+ onClick = { ( ) => handleSectionClick ( item ) }
191+ >
192+ < SectionIcon > { item . icon } </ SectionIcon >
193+ < SectionTitle > { item . label } </ SectionTitle >
194+ { activeKey === item . key ? < DownOutlined /> : < RightOutlined /> }
195+ </ SectionHeader >
196+
197+ < SectionContent $isVisible = { activeKey === item . key } >
198+ { item . component }
199+ </ SectionContent >
200+ </ SettingSection >
201+ ) ) }
202+ </ SettingsContainer >
36203 </ >
37204 ) ;
38205} ;
0 commit comments