Skip to content

Commit 9bd8919

Browse files
committed
feat: Optimize UI
1 parent 1f20362 commit 9bd8919

39 files changed

+371
-205
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"@radix-ui/react-popover": "^1.0.6",
5050
"@radix-ui/react-select": "^1.2.2",
5151
"@radix-ui/react-slot": "^1.1.0",
52-
"@radix-ui/react-switch": "^1.0.3",
52+
"@radix-ui/react-switch": "^1.1.0",
5353
"@radix-ui/react-tabs": "^1.0.4",
5454
"@radix-ui/react-toggle": "^1.0.3",
5555
"@reduxjs/toolkit": "^1.9.5",
@@ -70,7 +70,6 @@
7070
"@types/react-collapse": "^5.0.0",
7171
"@types/react-dom": "^18.2.7",
7272
"@types/react-helmet": "^6.1.6",
73-
"@types/react-router-dom": "^5.1.5",
7473
"@types/react-virtualized": "^9.21.22",
7574
"@types/semver": "^7.5.0",
7675
"@types/uuid": "^9.0.2",

public/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon-152x152.png" sizes="152x152">
1313
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon-180x180.png" sizes="180x180">
1414
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
15+
<meta name="mobile-web-app-capable" content="yes">
16+
<meta name="mobile-web-app-status-bar-style" content="dark-content">
1517
<meta name="apple-mobile-web-app-capable" content="yes">
1618
<meta name="apple-mobile-web-app-status-bar-style" content="dark-content">
1719
<meta name="format-detection" content="telephone=no">

src/App.tsx

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,14 @@
1-
import React, {
2-
lazy,
3-
Suspense,
4-
useCallback,
5-
useEffect,
6-
useRef,
7-
useState,
8-
} from 'react'
1+
import React, { useCallback, useEffect, useRef, useState } from 'react'
92
import { Toaster, toast } from 'react-hot-toast'
103
import { useTranslation } from 'react-i18next'
11-
import {
12-
Navigate,
13-
Route,
14-
Routes,
15-
useLocation,
16-
useNavigate,
17-
} from 'react-router-dom'
4+
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
185
import { SWRConfig } from 'swr'
196

20-
import FullLoading from '@/components/FullLoading'
217
import NetworkErrorModal from '@/components/NetworkErrorModal'
228
import NewVersionAlert from '@/components/NewVersionAlert'
239
import PageLayout from '@/components/PageLayout'
2410
import RunInSurge from '@/components/RunInSurge'
2511
import useTrafficUpdater from '@/hooks/useTrafficUpdater'
26-
import HomePage from '@/pages/Home'
27-
import { LandingPage } from '@/pages/Landing'
2812
import {
2913
usePlatformVersion,
3014
useAppDispatch,
@@ -36,17 +20,6 @@ import { profileActions } from '@/store/slices/profile'
3620
import { isRunInSurge } from '@/utils'
3721
import { httpClient } from '@/utils/fetcher'
3822

39-
const PoliciesPage = lazy(() => import('@/pages/Policies'))
40-
const RequestsPage = lazy(() => import('@/pages/Requests'))
41-
const TrafficPage = lazy(() => import('@/pages/Traffic'))
42-
const ModulesPage = lazy(() => import('@/pages/Modules'))
43-
const ScriptingPage = lazy(() => import('@/pages/Scripting'))
44-
const EvaluatePage = lazy(() => import('@/pages/Scripting/Evaluate'))
45-
const DnsPage = lazy(() => import('@/pages/Dns'))
46-
const DevicesPage = lazy(() => import('@/pages/Devices'))
47-
const CurrentProfilePage = lazy(() => import('@/pages/Profiles/Current'))
48-
const ManageProfilesPage = lazy(() => import('@/pages/Profiles/Manage'))
49-
5023
const App: React.FC = () => {
5124
const { t } = useTranslation()
5225
const [isNetworkModalOpen, setIsNetworkModalOpen] = useState(false)
@@ -136,23 +109,7 @@ const App: React.FC = () => {
136109
</RunInSurge>
137110

138111
<PageLayout>
139-
<Suspense fallback={<FullLoading />}>
140-
<Routes>
141-
<Route path="/" element={<LandingPage />} />
142-
<Route path="/home" element={<HomePage />} />
143-
<Route path="/policies" element={<PoliciesPage />} />
144-
<Route path="/requests" element={<RequestsPage />} />
145-
<Route path="/traffic" element={<TrafficPage />} />
146-
<Route path="/modules" element={<ModulesPage />} />
147-
<Route path="/scripting" element={<ScriptingPage />} />
148-
<Route path="/scripting/evaluate" element={<EvaluatePage />} />
149-
<Route path="/dns" element={<DnsPage />} />
150-
<Route path="/devices" element={<DevicesPage />} />
151-
<Route path="/profiles" element={<ManageProfilesPage />} />
152-
<Route path="/profiles/current" element={<CurrentProfilePage />} />
153-
<Route path="*" element={<Navigate to="/" replace />} />
154-
</Routes>
155-
</Suspense>
112+
<Outlet />
156113
</PageLayout>
157114
</SWRConfig>
158115
)

src/AppContainer.tsx

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,23 @@
1-
import React, { ReactNode, Suspense } from 'react'
1+
import React, { ReactNode } from 'react'
22
import { HelmetProvider } from 'react-helmet-async'
33
import { Provider as ReduxProvider } from 'react-redux'
4-
import {
5-
BrowserRouter,
6-
BrowserRouterProps,
7-
HashRouter,
8-
HashRouterProps,
9-
} from 'react-router-dom'
104

115
import Bootstrap from '@/bootstrap'
126
import { ThemeProvider } from '@/components/ThemeProvider'
137
import { UIProvider } from '@/components/UIProvider'
148
import { store } from '@/store'
159

16-
const ReactRouter: React.FC<BrowserRouterProps | HashRouterProps> = (args) => {
17-
return process.env.REACT_APP_HASH_ROUTER === 'true' ? (
18-
<HashRouter {...(args as HashRouterProps)}>{args.children}</HashRouter>
19-
) : (
20-
<BrowserRouter {...(args as BrowserRouterProps)}>
21-
{args.children}
22-
</BrowserRouter>
23-
)
24-
}
25-
2610
const AppContainer: React.FC<{ children: ReactNode }> = ({ children }) => {
2711
return (
28-
<Suspense fallback={<div />}>
29-
<ReduxProvider store={store}>
30-
<HelmetProvider>
31-
<ReactRouter>
32-
<ThemeProvider>
33-
<UIProvider>
34-
<Bootstrap>{children}</Bootstrap>
35-
</UIProvider>
36-
</ThemeProvider>
37-
</ReactRouter>
38-
</HelmetProvider>
39-
</ReduxProvider>
40-
</Suspense>
12+
<ReduxProvider store={store}>
13+
<HelmetProvider>
14+
<ThemeProvider>
15+
<UIProvider>
16+
<Bootstrap>{children}</Bootstrap>
17+
</UIProvider>
18+
</ThemeProvider>
19+
</HelmetProvider>
20+
</ReduxProvider>
4121
)
4222
}
4323

src/components/Data/DataRowMain.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ export const DataRowMain = ({
3939
return (
4040
<div
4141
className={cn(
42-
'flex items-center justify-between px-3 py-3 md:px-5 md:py-4 leading-normal text-sm',
42+
'flex items-center justify-between px-3 py-3 md:px-5 md:py-4 leading-normal text-sm gap-3',
4343
isClickable &&
44-
'gap-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-black/20',
44+
'cursor-pointer hover:bg-gray-100 dark:hover:bg-black/20',
4545
destructive && 'text-destructive dark:text-red-500',
4646
disabled && 'cursor-not-allowed text-gray-400 dark:text-gray-600',
4747
responsiveFont && 'lg:text-base',

src/components/ErrorBoundary.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react'
2+
import { useRouteError } from 'react-router-dom'
3+
4+
export default function ErrorBoundary() {
5+
const error = useRouteError()
6+
7+
console.error(error)
8+
9+
return <div>Dang!</div>
10+
}
11+
12+
ErrorBoundary.displayName = 'ErrorBoundary'
13+
14+
export { ErrorBoundary }

src/components/FixedFullscreenContainer.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ const FixedFullscreenContainer: React.FC<{
1515

1616
return (
1717
<div
18-
className={cn(
19-
'absolute top-0 right-0 bottom-0 left-0 h-full overflow-hidden',
20-
)}
18+
className={cn('absolute top-0 bottom-0 h-full overflow-hidden')}
2119
css={[
2220
offsetBottom &&
2321
css`
2422
padding-bottom: env(safe-area-inset-bottom);
2523
`,
24+
css`
25+
left: 1px;
26+
right: 1px;
27+
`,
2628
]}
2729
>
2830
<div className="w-full h-full flex flex-col">{props.children}</div>

src/components/FullLoading/index.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React from 'react'
2-
import tw from 'twin.macro'
3-
4-
const FullLoadingWrapper = tw.div`fixed top-0 right-0 bottom-0 left-0 flex items-center justify-center`
52

63
const FullLoading: React.FC = () => {
7-
return <FullLoadingWrapper></FullLoadingWrapper>
4+
return (
5+
<div className="fixed top-0 right-0 bottom-0 left-0 flex items-center justify-center"></div>
6+
)
87
}
98

109
export default FullLoading

src/components/PageLayout/index.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import React from 'react'
22

3+
import FixedFullscreenContainer from '@/components/FixedFullscreenContainer'
4+
import PageContainer from '@/components/PageContainer'
5+
import { useRouteOptions } from '@/router'
36
import { cn } from '@/utils/shadcn'
47

58
const PageLayout: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
69
children,
710
className,
811
...props
912
}) => {
13+
const routeOptions = useRouteOptions()
14+
const isFullscreen = routeOptions?.fullscreen ?? false
15+
const isBottomSafeAreaShown = routeOptions?.bottomSafeArea ?? true
16+
1017
return (
1118
<div
1219
className={cn(
@@ -17,7 +24,13 @@ const PageLayout: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
1724
>
1825
<div className="flex flex-1 relative max-w-4xl w-full bg-background">
1926
<div className="w-full border-l border-r border-gray-200 dark:border-gray-800">
20-
{children}
27+
{isFullscreen ? (
28+
<FixedFullscreenContainer offsetBottom={isBottomSafeAreaShown}>
29+
{children}
30+
</FixedFullscreenContainer>
31+
) : (
32+
<PageContainer>{children}</PageContainer>
33+
)}
2134
</div>
2235
</div>
2336
</div>

src/components/VerticalSafeArea.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { css } from '@emotion/react'
44
export const BottomSafeArea = () => {
55
return (
66
<div
7-
className="flex flex-1 sm:hidden"
7+
className="flex sm:hidden"
88
css={css`
99
height: 0;
1010
padding-bottom: env(safe-area-inset-bottom, 0px);

src/i18n/en/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"completed": "Completed",
100100
"rule_evaluating": "Rule Evaluating",
101101
"dns_lookup": "DNS Lookup",
102+
"hostname": "Hostname",
102103
"establishing_connection": "Establishing Connection",
103104
"general_tab": "General",
104105
"request_tab": "Request",

src/i18n/zh/translation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"completed": "已完成",
100100
"rule_evaluating": "解析规则",
101101
"dns_lookup": "解析域名",
102+
"hostname": "主机名",
102103
"establishing_connection": "建立连接",
103104
"general_tab": "一般",
104105
"request_tab": "请求",

src/index.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@ import './styles/shadcn.css'
66
import './styles/global.css'
77

88
import SWUpdateNotification from '@/components/SWUpdateNotification'
9+
import { RouterProvider } from '@/router'
910

10-
import App from './App'
11-
import AppContainer from './AppContainer'
11+
import routes from './routes'
1212
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
1313
import './i18n'
1414

1515
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
1616

1717
root.render(
1818
<React.StrictMode>
19-
<AppContainer>
20-
<App />
21-
</AppContainer>
19+
<RouterProvider value={routes}></RouterProvider>
2220
</React.StrictMode>,
2321
)
2422

src/pages/Devices/components/DeviceSettingsModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Checkbox } from '@/components/ui/checkbox'
1111
import {
1212
Dialog,
1313
DialogContent,
14+
DialogFooter,
1415
DialogHeader,
1516
DialogTitle,
1617
} from '@/components/ui/dialog'
@@ -23,6 +24,7 @@ import {
2324
FormMessage,
2425
} from '@/components/ui/form'
2526
import { Input } from '@/components/ui/input'
27+
import { Switch } from '@/components/ui/switch'
2628
import { DHCPDevice } from '@/types'
2729
import fetcher from '@/utils/fetcher'
2830

@@ -176,7 +178,7 @@ const DeviceSettingsModal = ({
176178
render={({ field }) => (
177179
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
178180
<FormControl>
179-
<Checkbox
181+
<Switch
180182
disabled={isLoading}
181183
checked={field.value}
182184
onCheckedChange={(checked) => field.onChange(checked)}
@@ -187,15 +189,15 @@ const DeviceSettingsModal = ({
187189
)}
188190
/>
189191

190-
<div>
192+
<DialogFooter>
191193
<Button
192194
isLoading={isLoading}
193195
type="submit"
194196
loadingLabel={t('common.is_loading')}
195197
>
196198
{t('common.save')}
197199
</Button>
198-
</div>
200+
</DialogFooter>
199201
</form>
200202
</Form>
201203
</DialogContent>

src/pages/Devices/index.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ import { useTranslation } from 'react-i18next'
33
import useSWR from 'swr'
44

55
import { ListCell, ListFullHeightCell } from '@/components/ListCell'
6-
import PageContainer from '@/components/PageContainer'
76
import PageTitle from '@/components/PageTitle'
87
import { DevicesResult } from '@/types'
98
import fetcher from '@/utils/fetcher'
109
import withProfile from '@/utils/with-profile'
1110

1211
import DeviceItem from './components/DeviceItem'
1312

14-
const Page = (): JSX.Element => {
13+
const ComponentBase = (): JSX.Element => {
1514
const { t } = useTranslation()
1615
const [isAutoRefresh, setIsAutoRefresh] = useState<boolean>(false)
1716
const { data: devices } = useSWR<DevicesResult>('/devices', fetcher, {
@@ -31,7 +30,7 @@ const Page = (): JSX.Element => {
3130
)
3231

3332
return (
34-
<PageContainer>
33+
<>
3534
<PageTitle
3635
title={t('home.device_management')}
3736
hasAutoRefresh={true}
@@ -48,8 +47,12 @@ const Page = (): JSX.Element => {
4847
deviceList
4948
)}
5049
</div>
51-
</PageContainer>
50+
</>
5251
)
5352
}
5453

55-
export default withProfile(Page)
54+
export const Component = withProfile(ComponentBase)
55+
56+
Component.displayName = 'DevicesPage'
57+
58+
export { ErrorBoundary } from '@/components/ErrorBoundary'

0 commit comments

Comments
 (0)