quintodrome/ui/src/App.jsx
Deluan Quintão 6dd98e0bed
feat(ui): add configuration tab in About dialog (#4142)
* Flatten config endpoint and improve About dialog

* add config resource

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(ui): replace `==` with `===`

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): add environment variables

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): add sensitive value redaction

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): more translations

Signed-off-by: Deluan <deluan@navidrome.org>

* address PR comments

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): add configuration export feature in About dialog

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): translate development flags section header

Signed-off-by: Deluan <deluan@navidrome.org>

* refactor

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(api): refactor routes for keepalive and insights endpoints

Signed-off-by: Deluan <deluan@navidrome.org>

* lint

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(ui): enhance string escaping in formatTomlValue function

Updated the formatTomlValue function to properly escape backslashes in addition to quotes. Added new test cases to ensure correct handling of strings containing both backslashes and quotes.

Signed-off-by: Deluan <deluan@navidrome.org>

* feat(ui): adjust dialog size

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2025-05-30 21:07:08 -04:00

165 lines
4.5 KiB
JavaScript

import ReactGA from 'react-ga'
import { Provider } from 'react-redux'
import { createHashHistory } from 'history'
import { Admin as RAAdmin, Resource } from 'react-admin'
import { HotKeys } from 'react-hotkeys'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
import { Layout, Login, Logout } from './layout'
import transcoding from './transcoding'
import player from './player'
import user from './user'
import song from './song'
import album from './album'
import artist from './artist'
import playlist from './playlist'
import radio from './radio'
import share from './share'
import { Player } from './audioplayer'
import customRoutes from './routes'
import {
themeReducer,
addToPlaylistDialogReducer,
expandInfoDialogReducer,
listenBrainzTokenDialogReducer,
saveQueueDialogReducer,
playerReducer,
albumViewReducer,
activityReducer,
settingsReducer,
replayGainReducer,
downloadMenuDialogReducer,
shareDialogReducer,
} from './reducers'
import createAdminStore from './store/createAdminStore'
import { i18nProvider } from './i18n'
import config, { shareInfo } from './config'
import { keyMap } from './hotkeys'
import useChangeThemeColor from './useChangeThemeColor'
import SharePlayer from './share/SharePlayer'
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
import missing from './missing/index.js'
const history = createHashHistory()
if (config.gaTrackingId) {
ReactGA.initialize(config.gaTrackingId)
history.listen((location) => {
ReactGA.pageview(location.pathname)
})
ReactGA.pageview(window.location.pathname)
}
const adminStore = createAdminStore({
authProvider,
dataProvider,
history,
customReducers: {
player: playerReducer,
albumView: albumViewReducer,
theme: themeReducer,
addToPlaylistDialog: addToPlaylistDialogReducer,
downloadMenuDialog: downloadMenuDialogReducer,
expandInfoDialog: expandInfoDialogReducer,
listenBrainzTokenDialog: listenBrainzTokenDialogReducer,
saveQueueDialog: saveQueueDialogReducer,
shareDialog: shareDialogReducer,
activity: activityReducer,
settings: settingsReducer,
replayGain: replayGainReducer,
},
})
const App = () => (
<Provider store={adminStore}>
<Admin />
</Provider>
)
const Admin = (props) => {
useChangeThemeColor()
/* eslint-disable react/jsx-key */
return (
<RAAdmin
disableTelemetry
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
customRoutes={customRoutes}
history={history}
layout={Layout}
loginPage={Login}
logoutButton={Logout}
{...props}
>
{(permissions) => [
<Resource name="album" {...album} options={{ subMenu: 'albumList' }} />,
<Resource name="artist" {...artist} />,
<Resource name="song" {...song} />,
<Resource
name="radio"
{...(permissions === 'admin' ? radio.admin : radio.all)}
/>,
config.enableSharing && <Resource name="share" {...share} />,
<Resource
name="playlist"
{...playlist}
options={{ subMenu: 'playlist' }}
/>,
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />,
<Resource
name="player"
{...player}
options={{ subMenu: 'settings' }}
/>,
permissions === 'admin' ? (
<Resource
name="transcoding"
{...transcoding}
options={{ subMenu: 'settings' }}
/>
) : (
<Resource name="transcoding" />
),
permissions === 'admin' ? (
<Resource
name="missing"
{...missing}
options={{ subMenu: 'settings' }}
/>
) : null,
<Resource name="translation" />,
<Resource name="genre" />,
<Resource name="tag" />,
<Resource name="playlistTrack" />,
<Resource name="keepalive" />,
<Resource name="insights" />,
permissions === 'admin' && config.devUIShowConfig ? (
<Resource name="config" />
) : null,
<Player />,
]}
</RAAdmin>
)
/* eslint-enable react/jsx-key */
}
const AppWithHotkeys = () => {
let language = localStorage.getItem('locale') || 'en'
document.documentElement.lang = language
if (config.enableSharing && shareInfo) {
return <SharePlayer />
}
return (
<HotKeys keyMap={keyMap}>
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</HotKeys>
)
}
export default AppWithHotkeys