mirror of
https://github.com/pezkuwichain/pezkuwi-apps.git
synced 2026-11-01 06:09:49 +00:00
133 lines
3.5 KiB
TypeScript
133 lines
3.5 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/apps authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { Route } from '@pezkuwi/apps-routing/types';
|
|
|
|
import React, { Suspense, useMemo } from 'react';
|
|
import { useLocation } from 'react-router-dom';
|
|
|
|
import createRoutes from '@pezkuwi/apps-routing';
|
|
import { ErrorBoundary, Spinner, styled } from '@pezkuwi/react-components';
|
|
import { useApi, useQueue } from '@pezkuwi/react-hooks';
|
|
import { TabsCtx } from '@pezkuwi/react-hooks/ctx/Tabs';
|
|
|
|
import { findMissingApis } from '../endpoint.js';
|
|
import { useTranslation } from '../translate.js';
|
|
import BrowserCheckAlert from './BrowserCheckAlert.js';
|
|
import NotFound from './NotFound.js';
|
|
import Status from './Status.js';
|
|
|
|
interface Props {
|
|
className?: string;
|
|
}
|
|
|
|
const NOT_FOUND: Route = {
|
|
Component: NotFound,
|
|
display: {},
|
|
group: 'settings',
|
|
icon: 'times',
|
|
isIgnored: false,
|
|
name: 'unknown',
|
|
text: 'Unknown'
|
|
};
|
|
|
|
function Content ({ className }: Props): React.ReactElement<Props> {
|
|
const location = useLocation();
|
|
const { t } = useTranslation();
|
|
const { api, isApiConnected, isApiReady, isDevelopment } = useApi();
|
|
const { queueAction } = useQueue();
|
|
|
|
const { Component, display: { needsApi, needsApiCheck, needsApiInstances }, icon, name, text } = useMemo(
|
|
(): Route => {
|
|
const app = location.pathname.slice(1) || '';
|
|
|
|
return createRoutes(t).find((r) =>
|
|
r &&
|
|
app.startsWith(r.name) &&
|
|
(isDevelopment || !r.display.isDevelopment)
|
|
) || NOT_FOUND;
|
|
},
|
|
[isDevelopment, location, t]
|
|
);
|
|
|
|
const missingApis = useMemo(
|
|
() => needsApi
|
|
? isApiReady && isApiConnected
|
|
? findMissingApis(api, needsApi, needsApiInstances, needsApiCheck)
|
|
: null
|
|
: [],
|
|
[api, isApiConnected, isApiReady, needsApi, needsApiCheck, needsApiInstances]
|
|
);
|
|
|
|
return (
|
|
<StyledDiv className={className}>
|
|
{!missingApis
|
|
? (
|
|
<div className='connecting'>
|
|
<BrowserCheckAlert />
|
|
<Spinner label={t('Initializing connection')} />
|
|
</div>
|
|
)
|
|
: (
|
|
<>
|
|
<Suspense fallback='...'>
|
|
<ErrorBoundary trigger={name}>
|
|
<TabsCtx.Provider value={{ icon, text }}>
|
|
{missingApis.length
|
|
? (
|
|
<NotFound
|
|
basePath={`/${name}`}
|
|
location={location}
|
|
missingApis={missingApis}
|
|
onStatusChange={queueAction}
|
|
/>
|
|
)
|
|
: (
|
|
<Component
|
|
basePath={`/${name}`}
|
|
location={location}
|
|
onStatusChange={queueAction}
|
|
/>
|
|
)
|
|
}
|
|
</TabsCtx.Provider>
|
|
</ErrorBoundary>
|
|
</Suspense>
|
|
<Status />
|
|
</>
|
|
)
|
|
}
|
|
</StyledDiv>
|
|
);
|
|
}
|
|
|
|
const StyledDiv = styled.div`
|
|
flex-grow: 1;
|
|
overflow: hidden auto;
|
|
padding: 0 0 1rem 0;
|
|
position: relative;
|
|
width: 100%;
|
|
|
|
.connecting {
|
|
padding: 3.5rem 0;
|
|
}
|
|
|
|
& main > *:not(header):not(.hasOwnMaxWidth) {
|
|
max-width: var(--width-full);
|
|
margin-right: auto;
|
|
margin-left: auto;
|
|
width: 100%;
|
|
padding: 0 1.5rem;
|
|
|
|
@media only screen and (max-width: 1100px) {
|
|
padding: 0 1rem;
|
|
}
|
|
|
|
@media only screen and (max-width: 800px) {
|
|
padding: 0 0.75rem;
|
|
}
|
|
}
|
|
`;
|
|
|
|
export default React.memo(Content);
|