Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"dev": "pnpm gen:api && vite",
"dev:only": "vite",
"gen:api": "node scripts/gen-api.mjs",
"build": "pnpm gen:api && tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
Expand Down
165 changes: 103 additions & 62 deletions admin/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,32 @@ import {Cable, Construction, Crown, NotepadText, Wrench, PhoneCall, LucideMenu,
import {UpdateBanner} from "./components/UpdateBanner";

const WS_URL = import.meta.env.DEV ? 'http://localhost:9001' : ''

export const App = () => {
const setSettings = useStore(state => state.setSettings);
const {t} = useTranslation()
const navigate = useNavigate()
const [sidebarOpen, setSidebarOpen] = useState<boolean>(true)
const updateStatus = useStore(state => state.updateStatus)
const version = updateStatus?.currentVersion ?? null

useEffect(() => {
fetch('/admin-auth/', {
method: 'POST'
}).then((value) => {
if (!value.ok) {
navigate('/login')
}
}).catch(() => {
navigate('/login')
})
fetch('/admin-auth/', {method: 'POST'}).then((value) => {
if (!value.ok) navigate('/login')
}).catch(() => navigate('/login'))
}, []);

useEffect(() => {
document.title = t('admin.page-title')

useStore.getState().setShowLoading(true);
const settingSocket = connect(`${WS_URL}/settings`, {
transports: ['websocket'],
});

const pluginsSocket = connect(`${WS_URL}/pluginfw/installer`, {
transports: ['websocket'],
})
const settingSocket = connect(`${WS_URL}/settings`, {transports: ['websocket']});
const pluginsSocket = connect(`${WS_URL}/pluginfw/installer`, {transports: ['websocket']})

pluginsSocket.on('connect', () => {
useStore.getState().setPluginsSocket(pluginsSocket);
});


settingSocket.on('connect', () => {
useStore.getState().setSettingsSocket(settingSocket);
useStore.getState().setShowLoading(false)
Expand All @@ -53,71 +44,121 @@ export const App = () => {
});

settingSocket.on('disconnect', (reason) => {
// The settingSocket.io client will automatically try to reconnect for all reasons other than "io
// server disconnect".
useStore.getState().setShowLoading(true)
if (reason === 'io server disconnect') {
settingSocket.connect();
}
if (reason === 'io server disconnect') settingSocket.connect();
});

settingSocket.on('settings', (settings) => {
/* Check whether the settings.json is authorized to be viewed */
if (settings.results === 'NOT_ALLOWED') {
console.log('Not allowed to view settings.json')
return;
}

/* Check to make sure the JSON is clean before proceeding */
if (isJSONClean(settings.results)) {
setSettings(settings.results);
} else {
alert('Invalid JSON');
}
if (isJSONClean(settings.results)) setSettings(settings.results);
else alert('Invalid JSON');
useStore.getState().setShowLoading(false);
});

settingSocket.on('saveprogress', (status) => {
console.log(status)
})
settingSocket.on('saveprogress', (status) => console.log(status))

return () => {
settingSocket.disconnect();
pluginsSocket.disconnect()
}
}, []);

return <div id="wrapper" className={`${sidebarOpen ? '': 'closed' }`}>
<LoadingScreen/>
<div className="menu">
<div className="inner-menu">
<span>
<Crown width={40} height={40}/>
<h1>Etherpad</h1>
</span>
<ul onClick={()=>{
if (window.innerWidth < 768) {
setSidebarOpen(false)
}
}}>
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
<li><NavLink to={"/pads"}><NotepadText/><Trans
i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li>
<li><NavLink to={"/shout"}><PhoneCall/>Communication</NavLink></li>
<li><NavLink to={"/update"}><Bell/><Trans i18nKey="update.page.title"/></NavLink></li>
</ul>
const closeOnMobile = () => {
if (window.innerWidth < 768) setSidebarOpen(false)
}

return (
<div id="wrapper" className={sidebarOpen ? '' : 'closed'}>
<LoadingScreen/>
<div className="menu">
<div className="inner-menu">
<div className="sidebar-top">
<button
className="sidebar-burger"
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-label="Toggle sidebar"
>
<LucideMenu size={20}/>
</button>
{sidebarOpen && (
<div className="sidebar-brand">
<div className="sidebar-brand-mark"><Crown size={20} strokeWidth={1.8}/></div>
<span className="sidebar-brand-name">Etherpad</span>
</div>
)}
</div>

<nav className="sidebar-nav" onClick={closeOnMobile}>
<NavLink
to="/plugins"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : t('admin_plugins')}
>
<span className="sidebar-nav-icon"><Cable size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label"><Trans i18nKey="admin_plugins"/></span>}
</NavLink>
<NavLink
to="/settings"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : t('admin_settings')}
>
<span className="sidebar-nav-icon"><Wrench size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label"><Trans i18nKey="admin_settings"/></span>}
</NavLink>
<NavLink
to="/help"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : t('admin_plugins_info')}
>
<span className="sidebar-nav-icon"><Construction size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label"><Trans i18nKey="admin_plugins_info"/></span>}
</NavLink>
<NavLink
to="/pads"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : undefined}
>
<span className="sidebar-nav-icon"><NotepadText size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label"><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></span>}
</NavLink>
<NavLink
to="/shout"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : 'Communication'}
>
<span className="sidebar-nav-icon"><PhoneCall size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label">Communication</span>}
</NavLink>
<NavLink
to="/update"
className={({isActive}) => `sidebar-nav-item${isActive ? ' is-active' : ''}`}
title={sidebarOpen ? undefined : t('update.page.title')}
>
<span className="sidebar-nav-icon"><Bell size={18}/></span>
{sidebarOpen && <span className="sidebar-nav-label"><Trans i18nKey="update.page.title"/></span>}
</NavLink>
</nav>

{sidebarOpen && (
<div className="sidebar-footer">
<div className="sidebar-footer-row">
<span className="sidebar-status-dot"/>
<span>{version ? `v${version}` : 'Etherpad'}</span>
</div>
</div>
)}
</div>
</div>

<div className="innerwrapper">
<UpdateBanner/>
<Outlet/>
</div>
</div>
<button id="icon-button" onClick={() => {
setSidebarOpen(!sidebarOpen)
}}><LucideMenu/></button>
<div className="innerwrapper">
<UpdateBanner/>
<Outlet/>
</div>
</div>
)
}

export default App
Loading
Loading