Skip to content

Commit 97249a0

Browse files
authored
Merge pull request #1840 from oasisprotocol/mz/metaPolicy
Create ROFL app metadata and policy cards
2 parents 239f9c4 + 7f93be0 commit 97249a0

File tree

6 files changed

+282
-1
lines changed

6 files changed

+282
-1
lines changed

.changelog/1840.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Create ROFL app metadata and policy cards
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import Box from '@mui/material/Box'
4+
import Typography from '@mui/material/Typography'
5+
import { styled } from '@mui/material/styles'
6+
import DataObjectIcon from '@mui/icons-material/DataObject'
7+
import { COLORS } from '../../../styles/theme/colors'
8+
9+
const StyledBox = styled(Box)(({ theme }) => ({
10+
display: 'flex',
11+
flexDirection: 'column',
12+
justifyContent: 'center',
13+
alignItems: 'center',
14+
[theme.breakpoints.down('sm')]: {
15+
minHeight: '150px',
16+
},
17+
[theme.breakpoints.up('sm')]: {
18+
minHeight: '200px',
19+
},
20+
}))
21+
22+
export const EmptyStateCard: FC = () => {
23+
const { t } = useTranslation()
24+
25+
return (
26+
<StyledBox gap={3}>
27+
<DataObjectIcon sx={{ color: COLORS.grayMedium, fontSize: 40, opacity: 0.5 }} />
28+
<Typography
29+
sx={{
30+
color: COLORS.grayMedium,
31+
fontWeight: 700,
32+
textAlign: 'center',
33+
opacity: 0.5,
34+
}}
35+
>
36+
{t('rofl.noData')}
37+
</Typography>
38+
</StyledBox>
39+
)
40+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { FC, ReactNode } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { styled } from '@mui/material/styles'
4+
import Grid from '@mui/material/Grid'
5+
import Tooltip from '@mui/material/Tooltip'
6+
import InfoIcon from '@mui/icons-material/Info'
7+
import { COLORS } from '../../../styles/theme/colors'
8+
9+
export const StyledGrid = styled(Grid)(({ theme }) => ({
10+
display: 'flex',
11+
gap: 3,
12+
alignContent: 'center',
13+
borderBottom: 'solid 1px #F4F5F7',
14+
paddingTop: theme.spacing(3),
15+
paddingBottom: theme.spacing(3),
16+
}))
17+
18+
type GridRowProps = {
19+
children?: ReactNode
20+
label: string
21+
tooltip?: ReactNode
22+
}
23+
24+
export const GridRow: FC<GridRowProps> = ({ label, children, tooltip }) => {
25+
const { t } = useTranslation()
26+
27+
return (
28+
<>
29+
<StyledGrid item xs={12} md={5}>
30+
{label}:
31+
{tooltip && (
32+
<Tooltip title={tooltip} placement="top">
33+
<InfoIcon htmlColor={COLORS.brandDark} fontSize="small" />
34+
</Tooltip>
35+
)}
36+
</StyledGrid>
37+
<StyledGrid item xs={12} md={7}>
38+
{children ? <strong>{children}</strong> : t('common.missing')}
39+
</StyledGrid>
40+
</>
41+
)
42+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { FC } from 'react'
2+
import { Trans, useTranslation } from 'react-i18next'
3+
import { styled } from '@mui/material/styles'
4+
import Box from '@mui/material/Box'
5+
import Card from '@mui/material/Card'
6+
import CardHeader from '@mui/material/CardHeader'
7+
import CardContent from '@mui/material/CardContent'
8+
import Grid from '@mui/material/Grid'
9+
import Link from '@mui/material/Link'
10+
import Typography from '@mui/material/Typography'
11+
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
12+
import { RoflAppMetadata } from '../../../oasis-nexus/api'
13+
import { COLORS } from '../../../styles/theme/colors'
14+
import { EmptyStateCard } from './EmptyStateCard'
15+
import { GridRow } from './GridRow'
16+
17+
export const StyledLink = styled(Link)(() => ({
18+
display: 'inline-flex',
19+
alignItems: 'center',
20+
wordBreak: 'break-all',
21+
gap: 5,
22+
}))
23+
24+
type MetaDataCardProps = {
25+
isFetched: boolean
26+
metadata: RoflAppMetadata | undefined
27+
}
28+
29+
// Nexus is not parsing ROFL app metadata, but we can expect props with net.oasis.rofl prefix
30+
// and object to be similar to this:
31+
// https://github.com/oasisprotocol/cli/blob/bb1a57c13ce7bed635e5ce4306cba8ec33c9a651/build/rofl/manifest.go#L185
32+
33+
export const MetaDataCard: FC<MetaDataCardProps> = ({ isFetched, metadata }) => {
34+
const { t } = useTranslation()
35+
36+
return (
37+
<Card sx={{ flex: 1 }}>
38+
<CardHeader
39+
titleTypographyProps={{ variant: 'h1' }}
40+
disableTypography
41+
component="h3"
42+
title={
43+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 5 }}>
44+
<Box sx={{ flex: 1, whiteSpace: 'nowrap' }}>{t('rofl.metadata')}</Box>
45+
<Typography component="span" sx={{ fontSize: '12px', color: COLORS.grayDark }}>
46+
{t('rofl.metadataInfo')}
47+
</Typography>
48+
</Box>
49+
}
50+
/>
51+
<CardContent>
52+
{isFetched && !metadata && <EmptyStateCard />}
53+
{metadata && (
54+
<>
55+
<Grid container spacing={4}>
56+
<GridRow label={t('rofl.roflName')}>{metadata['net.oasis.rofl.name']}</GridRow>
57+
<GridRow label={t('rofl.description')}>{metadata['net.oasis.rofl.description']}</GridRow>
58+
<GridRow label={t('rofl.author')}>{metadata['net.oasis.rofl.author']}</GridRow>
59+
<GridRow label={t('rofl.license')}>{metadata['net.oasis.rofl.license']}</GridRow>
60+
<GridRow
61+
label={t('rofl.repositoryUrl')}
62+
tooltip={
63+
<Trans
64+
i18nKey="rofl.verifyCommand"
65+
t={t}
66+
components={{
67+
Command: (
68+
<Typography variant="mono" component="span">
69+
oasis rofl build --verify
70+
</Typography>
71+
),
72+
}}
73+
/>
74+
}
75+
>
76+
{metadata['net.oasis.rofl.repository'] ? (
77+
<StyledLink
78+
href={metadata['net.oasis.rofl.repository']}
79+
rel="noopener noreferrer"
80+
target="_blank"
81+
>
82+
{metadata['net.oasis.rofl.repository']} <OpenInNewIcon sx={{ fontSize: 20 }} />
83+
</StyledLink>
84+
) : undefined}
85+
</GridRow>
86+
</Grid>
87+
</>
88+
)}
89+
</CardContent>
90+
</Card>
91+
)
92+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import Card from '@mui/material/Card'
4+
import CardHeader from '@mui/material/CardHeader'
5+
import CardContent from '@mui/material/CardContent'
6+
import Grid from '@mui/material/Grid'
7+
import Typography from '@mui/material/Typography'
8+
import { Layer, RoflAppPolicy, useGetRuntimeRoflAppsIdTransactions } from '../../../oasis-nexus/api'
9+
import { TransactionLink } from '../../components/Transactions/TransactionLink'
10+
import { EmptyStateCard } from './EmptyStateCard'
11+
import { GridRow } from './GridRow'
12+
13+
type PolicyCardProps = {
14+
id: string
15+
isFetched: boolean
16+
network: any
17+
policy: RoflAppPolicy | undefined
18+
}
19+
20+
// Nexus is not parsing ROFL app policy, but we can expect object to be similar to this:
21+
// https://github.com/oasisprotocol/oasis-sdk/blame/main/tests/runtimes/components-ronl/src/lib.rs#L120
22+
23+
// feePolicy can be 1 for InstancePays and 2 for EndorsingNodePays
24+
// https://github.com/oasisprotocol/oasis-sdk/blob/41480106d585debd33391cb0dfcad32d2f3cdc9d/runtime-sdk/src/modules/rofl/policy.rs#L48
25+
26+
export const PolicyCard: FC<PolicyCardProps> = ({ id, isFetched, network, policy }) => {
27+
const { t } = useTranslation()
28+
const { data } = useGetRuntimeRoflAppsIdTransactions(network, Layer.sapphire, id, {
29+
limit: 1,
30+
method: 'rofl.Update' as unknown as string[],
31+
})
32+
const transaction = data?.data.transactions[0]
33+
const feePolicy =
34+
policy?.fees === 1 ? t('rofl.instancePays') : policy?.fees === 2 ? t('rofl.endorsingNodePays') : undefined
35+
36+
return (
37+
<Card sx={{ flex: 1 }}>
38+
<CardHeader disableTypography component="h3" title={t('rofl.policy')} />
39+
<CardContent>
40+
{isFetched && !policy && <EmptyStateCard />}
41+
{policy && (
42+
<>
43+
<Grid container spacing={4}>
44+
<GridRow label={t('rofl.validity')}>
45+
{policy.quotes?.pcs?.tcb_validity_period
46+
? t('rofl.validityPeriodDays', { value: policy.quotes?.pcs?.tcb_validity_period })
47+
: undefined}
48+
</GridRow>
49+
<GridRow label={t('rofl.evaluation')}>
50+
{policy.quotes?.pcs?.min_tcb_evaluation_data_number ? (
51+
<>
52+
{policy.quotes?.pcs?.min_tcb_evaluation_data_number}
53+
<Typography component="span" sx={{ pl: 2 }}>
54+
{t('rofl.min')}
55+
</Typography>
56+
</>
57+
) : undefined}
58+
</GridRow>
59+
<GridRow label={t('rofl.feePolicy')}>{feePolicy}</GridRow>
60+
<GridRow label={t('rofl.update')}>
61+
{transaction ? (
62+
<>
63+
{t('common.formattedDateTime', {
64+
value: transaction.timestamp,
65+
formatParams: {
66+
timestamp: {
67+
year: 'numeric',
68+
month: 'long',
69+
day: 'numeric',
70+
} satisfies Intl.DateTimeFormatOptions,
71+
},
72+
})}
73+
<br />
74+
<TransactionLink
75+
scope={transaction}
76+
alwaysTrim
77+
hash={transaction.eth_hash || transaction.hash}
78+
/>
79+
</>
80+
) : undefined}
81+
</GridRow>
82+
</Grid>
83+
</>
84+
)}
85+
</CardContent>
86+
</Card>
87+
)
88+
}

src/locales/en/translation.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,25 @@
669669
"nameNotProvided": "Name not provided",
670670
"nodeId": "Node ID",
671671
"expirationEpoch": "Expiration epoch",
672-
"emptyInstancesList": "No ROFL app instances found."
672+
"emptyInstancesList": "No ROFL app instances found.",
673+
"noData": "No data available",
674+
"metadata": "Meta data",
675+
"metadataInfo": "This info provided by deployer, Oasis does not verify this information.",
676+
"roflName": "ROFL name",
677+
"description": "Description",
678+
"author": "Author",
679+
"license": "License",
680+
"repositoryUrl": "Repository URL",
681+
"verifyCommand": "Run <Command /> to verify.",
682+
"policy": "Policy",
683+
"instancePays": "Instance pays",
684+
"endorsingNodePays": "Endorsing node pays",
685+
"validity": "Validity period",
686+
"evaluation": "TCB data evaluation",
687+
"feePolicy": "Fee policy",
688+
"validityPeriodDays": "{{value}} days",
689+
"min": "(minimum)",
690+
"update": "Update"
673691
},
674692
"search": {
675693
"placeholder": "Address, Block, Contract, Transaction hash, Token name, etc.",

0 commit comments

Comments
 (0)