From 7dd0cee1864a4327faf65f43f2e707523ecdc668 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 28 Feb 2025 16:31:48 +0100 Subject: [PATCH] Add ROFL details page route --- src/app/pages/RoflAppDetailPage/index.tsx | 67 +++++++++++++++++++++++ src/app/utils/route-utils.ts | 26 +++++++++ src/locales/en/translation.json | 1 + src/oasis-nexus/api.ts | 5 ++ src/routes.tsx | 7 +++ 5 files changed, 106 insertions(+) create mode 100644 src/app/pages/RoflAppDetailPage/index.tsx diff --git a/src/app/pages/RoflAppDetailPage/index.tsx b/src/app/pages/RoflAppDetailPage/index.tsx new file mode 100644 index 000000000..33c35b8d2 --- /dev/null +++ b/src/app/pages/RoflAppDetailPage/index.tsx @@ -0,0 +1,67 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { RoflApp, Runtime, useGetRuntimeRoflAppsId } from '../../../oasis-nexus/api' +import { StyledDescriptionList } from '../../components/StyledDescriptionList' +import { PageLayout } from '../../components/PageLayout' +import { SubPageCard } from '../../components/SubPageCard' +import { useScreenSize } from '../../hooks/useScreensize' +import { AppErrors } from '../../../types/errors' +import { TextSkeleton } from '../../components/Skeleton' +import { useRequiredScopeParam } from '../../hooks/useScopeParam' +import { DashboardLink } from '../ParatimeDashboardPage/DashboardLink' +import { useParams } from 'react-router-dom' + +export const RoflAppDetailPage: FC = () => { + const { t } = useTranslation() + const scope = useRequiredScopeParam() + const id = useParams().id! + const { isLoading, data } = useGetRuntimeRoflAppsId(scope.network, scope.layer as Runtime, id) + const roflApp = data?.data + + if (!roflApp && !isLoading) { + throw AppErrors.NotFoundTxHash + } + + return ( + + + + + + ) +} + +export const RoflAppDetailView: FC<{ + isLoading?: boolean + app: RoflApp | undefined + detailsPage?: boolean + showLayer?: boolean +}> = ({ app, detailsPage, isLoading, showLayer }) => { + const { t } = useTranslation() + const { isMobile } = useScreenSize() + + if (isLoading) return + if (!app) return <> + + return ( + + {showLayer && ( + <> +
{t('common.paratime')}
+
+ +
+ + )} +
TODO
+
TODO
+ + {detailsPage && ( + <> +
TODO
+
TODO
+ + )} +
+ ) +} diff --git a/src/app/utils/route-utils.ts b/src/app/utils/route-utils.ts index 36653e5c7..60147f61b 100644 --- a/src/app/utils/route-utils.ts +++ b/src/app/utils/route-utils.ts @@ -271,6 +271,17 @@ const validateConsensusAddressParam = (address: string) => { return isValid } +const validateRoflAppIdParam = (id: string) => { + // TOOD: Use real validation once added to client SDK + const isValid = id.startsWith('rofl1') + + if (!isValid) { + throw new AppError(AppErrors.InvalidAddress) + } + + return isValid +} + const validateRuntimeAddressParam = (address: string) => { const isValid = isValidOasisAddress(address) || isValidEthAddress(address) if (!isValid) { @@ -310,6 +321,11 @@ export type AddressLoaderData = { searchTerm: string } +export type RoflAppLoaderData = { + id: string + searchTerm: string +} + const validateProposalIdParam = (proposalId: string) => { const isValid = isValidProposalId(proposalId) if (!isValid) { @@ -340,6 +356,16 @@ export const runtimeAddressParamLoader = } } +export const roflAppParamLoader = + (queryParam: string = 'id') => + ({ params, request }: LoaderFunctionArgs): RoflAppLoaderData => { + validateRoflAppIdParam(params[queryParam]!) + return { + id: params[queryParam]!, + searchTerm: getSearchTermFromRequest(request), + } + } + export const blockHeightParamLoader = async ({ params }: LoaderFunctionArgs) => { return validateBlockHeightParam(params.blockHeight!) } diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 8cb3d611d..6c2567c77 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -646,6 +646,7 @@ "emptyRoflAppsList": "No ROFL apps found.", "instances": "Instances", "listTitle": "ROFL Apps", + "header": "ROFL App", "tee": "TEE" }, "search": { diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 7d5288701..897a06350 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -141,6 +141,11 @@ declare module './generated/api' { export interface ValidatorAggStats { ticker: Ticker } + + export interface RoflApp { + network: Network + layer: Layer + } } export const isAccountEmpty = (account: RuntimeAccount | Account) => { diff --git a/src/routes.tsx b/src/routes.tsx index 95fd23fc9..6189df530 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -26,6 +26,7 @@ import { fixedLayer, RouteUtils, skipGraph, + roflAppParamLoader, } from './app/utils/route-utils' import { RoutingErrorPage } from './app/pages/RoutingErrorPage' import { ThemeByScope, withDefaultTheme } from './app/components/ThemeByScope' @@ -64,6 +65,7 @@ import { ConsensusAccountEventsCard } from './app/pages/ConsensusAccountDetailsP import { useConsensusAccountDetailsProps } from './app/pages/ConsensusAccountDetailsPage/hooks' import { ConsensusAccountTransactionsCard } from './app/pages/ConsensusAccountDetailsPage/ConsensusAccountTransactionsCard' import { RoflAppsPage } from './app/pages/RoflAppsPage' +import { RoflAppDetailPage } from 'app/pages/RoflAppDetailPage' import { FC, useEffect } from 'react' import { AnalyticsConsentProvider } from './app/components/AnalyticsConsent' @@ -355,6 +357,11 @@ export const routes: RouteObject[] = [ path: `rofl/app`, element: , }, + { + path: `rofl/app/:id`, + element: , + loader: roflAppParamLoader(), + }, ], }, ],