Skip to content

Commit 0106d05

Browse files
authored
Merge pull request #4722 from leather-wallet/release/recursive-inscriptions
Release/recursive inscriptions
2 parents 6c1a39d + b713c70 commit 0106d05

File tree

44 files changed

+414
-145
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+414
-145
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
"dependencies": {
123123
"@bitcoinerlab/secp256k1": "1.0.2",
124124
"@coinbase/cbpay-js": "1.0.2",
125-
"@dlc-link/dlc-tools": "1.0.9",
125+
"@dlc-link/dlc-tools": "1.1.1",
126126
"@fungible-systems/zone-file": "2.0.0",
127127
"@hirosystems/token-metadata-api-client": "1.1.0",
128128
"@ledgerhq/hw-transport-webusb": "6.27.19",

scripts/generate-manifest.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ const environmentIcons = {
3030
},
3131
};
3232

33+
const devCsp =
34+
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; frame-src https://ordinals.com/; frame-ancestors 'none';";
35+
36+
const prodCsp = `default-src 'none'; connect-src *; style-src 'unsafe-inline'; img-src 'self' data: https:; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-src https://ordinals.com/; frame-ancestors 'none';`;
37+
3338
const contentSecurityPolicyEnvironment = {
34-
development:
35-
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; frame-src 'none'; frame-ancestors 'none';",
36-
production: `default-src 'none'; connect-src *; style-src 'unsafe-inline'; img-src 'self' data: https:; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-src 'none'; frame-ancestors 'none';`,
39+
testing: prodCsp,
40+
development: devCsp,
41+
production: prodCsp,
3742
};
3843

3944
const defaultIconEnvironment = {

src/app/common/hooks/use-bitcoin-contracts.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,27 @@ export function useBitcoinContracts() {
207207
close();
208208
}
209209

210-
async function getAllSignedBitcoinContracts(): Promise<BitcoinContractListItem[] | undefined> {
210+
async function getAllActiveBitcoinContracts(): Promise<BitcoinContractListItem[] | undefined> {
211211
const bitcoinContractInterface = await getBitcoinContractInterface();
212212

213213
if (!bitcoinContractInterface) return;
214214

215+
await bitcoinContractInterface.periodic_check();
215216
const bitcoinContracts = await bitcoinContractInterface.get_contracts();
216-
const signedBitcoinContracts = bitcoinContracts.filter(
217-
(bitcoinContract: BitcoinContractListItem) => bitcoinContract.state === 'Signed'
218-
);
219217

220-
return signedBitcoinContracts;
218+
const stateOrder = ['Signed', 'Confirmed'];
219+
220+
const activeBitcoinContracts = bitcoinContracts
221+
.filter(
222+
(bitcoinContract: BitcoinContractListItem) =>
223+
bitcoinContract.state === 'Signed' || bitcoinContract.state === 'Confirmed'
224+
)
225+
.sort(
226+
(a: BitcoinContractListItem, b: BitcoinContractListItem) =>
227+
stateOrder.indexOf(a.state) - stateOrder.indexOf(b.state)
228+
);
229+
230+
return activeBitcoinContracts;
221231
}
222232

223233
function getTransactionDetails(txId: string, bitcoinCollateral: number) {
@@ -239,7 +249,7 @@ export function useBitcoinContracts() {
239249

240250
async function sumBitcoinContractCollateralAmounts(): Promise<Money> {
241251
let bitcoinContractsCollateralSum = 0;
242-
const bitcoinContracts = await getAllSignedBitcoinContracts();
252+
const bitcoinContracts = await getAllActiveBitcoinContracts();
243253
if (!bitcoinContracts) return createMoneyFromDecimal(0, 'BTC');
244254

245255
bitcoinContracts.forEach((bitcoinContract: BitcoinContractListItem) => {
@@ -313,7 +323,7 @@ export function useBitcoinContracts() {
313323
handleOffer,
314324
handleAccept,
315325
handleReject,
316-
getAllSignedBitcoinContracts,
326+
getAllActiveBitcoinContracts,
317327
sumBitcoinContractCollateralAmounts,
318328
sendRpcResponse,
319329
};

src/app/components/brc20-tokens-loader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import {
22
Brc20Token,
3-
useBrc20TokensQuery,
3+
useGetBrc20TokensQuery,
44
} from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query';
55

66
interface Brc20TokensLoaderProps {
77
children(brc20Tokens: Brc20Token[]): React.JSX.Element;
88
}
99
export function Brc20TokensLoader({ children }: Brc20TokensLoaderProps) {
10-
const { data: allBrc20TokensResponse } = useBrc20TokensQuery();
10+
const { data: allBrc20TokensResponse } = useGetBrc20TokensQuery();
1111
const brc20Tokens = allBrc20TokensResponse?.pages
1212
.flatMap(page => page.brc20Tokens)
1313
.filter(token => token.length > 0)

src/app/components/fees-row/components/fees-row.layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function FeesRowLayout(props: FeesRowLayoutProps) {
2323
const [_, meta] = useField('fee');
2424

2525
return (
26-
<HStack gap="space.04" width="100%" {...rest}>
26+
<HStack gap="space.04" width="100%" {...rest} flexWrap="wrap">
2727
<HStack alignItems="center" justifyContent="space-between" position="relative" width="100%">
2828
<HStack alignItems="center" width="100%">
2929
<Tooltip label={feesInfo} placement="bottom">
@@ -39,7 +39,7 @@ export function FeesRowLayout(props: FeesRowLayoutProps) {
3939
{feeField}
4040
</HStack>
4141
{isSponsored && <SponsoredLabel />}
42-
{!meta.error && fieldWarning && <WarningLabel>{fieldWarning}</WarningLabel>}
42+
{!meta.error && fieldWarning && <WarningLabel width="100%">{fieldWarning}</WarningLabel>}
4343
</HStack>
4444
);
4545
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ReactNode } from 'react';
2+
3+
import { AudioIcon } from '@app/ui/components/icons/audio-icon';
4+
5+
import { CollectibleItemLayout, CollectibleItemLayoutProps } from '../collectible-item.layout';
6+
import { CollectiblePlaceholderLayout } from './collectible-placeholder.layout';
7+
8+
interface CollectibleAudioProps extends Omit<CollectibleItemLayoutProps, 'children'> {
9+
icon: ReactNode;
10+
}
11+
export function CollectibleAudio({ icon, ...props }: CollectibleAudioProps) {
12+
return (
13+
<CollectibleItemLayout collectibleTypeIcon={icon} {...props}>
14+
<CollectiblePlaceholderLayout>
15+
<AudioIcon size="xl" />
16+
</CollectiblePlaceholderLayout>
17+
</CollectibleItemLayout>
18+
);
19+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ReactNode, useState } from 'react';
2+
3+
import { Iframe } from '@app/ui/components/iframe';
4+
5+
import { CollectibleItemLayout, CollectibleItemLayoutProps } from '../collectible-item.layout';
6+
import { ImageUnavailable } from '../image-unavailable';
7+
8+
interface CollectibleIframeProps extends Omit<CollectibleItemLayoutProps, 'children'> {
9+
icon: ReactNode;
10+
src: string;
11+
}
12+
export function CollectibleIframe({ icon, src, ...props }: CollectibleIframeProps) {
13+
const [isError, setIsError] = useState(false);
14+
15+
if (isError)
16+
return (
17+
<CollectibleItemLayout collectibleTypeIcon={icon} {...props}>
18+
<ImageUnavailable />
19+
</CollectibleItemLayout>
20+
);
21+
22+
return (
23+
<CollectibleItemLayout collectibleTypeIcon={icon} {...props}>
24+
<Iframe
25+
aspectRatio="1 / 1"
26+
height="100%"
27+
objectFit="cover"
28+
onError={() => setIsError(true)}
29+
src={src}
30+
width="100%"
31+
zIndex={99}
32+
/>
33+
</CollectibleItemLayout>
34+
);
35+
}

src/app/features/collectibles/components/_collectible-types/collectible-image.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { useState } from 'react';
1+
import { ReactNode, useState } from 'react';
22

33
import { CollectibleItemLayout, CollectibleItemLayoutProps } from '../collectible-item.layout';
44
import { ImageUnavailable } from '../image-unavailable';
55

66
interface CollectibleImageProps extends Omit<CollectibleItemLayoutProps, 'children'> {
77
alt?: string;
8-
icon: React.JSX.Element;
8+
icon: ReactNode;
99
src: string;
1010
}
1111
export function CollectibleImage(props: CollectibleImageProps) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Flex } from 'leather-styles/jsx';
2+
3+
import { HasChildren } from '@app/common/has-children';
4+
5+
export function CollectiblePlaceholderLayout({ children }: HasChildren) {
6+
return (
7+
<Flex
8+
alignItems="center"
9+
bg="accent.component-background-default"
10+
flexDirection="column"
11+
height="100%"
12+
justifyContent="center"
13+
textAlign="center"
14+
width="100%"
15+
>
16+
{children}
17+
</Flex>
18+
);
19+
}

src/app/features/collectibles/components/bitcoin/inscription.tsx

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { openInNewTab } from '@app/common/utils/open-in-new-tab';
77
import { convertInscriptionToSupportedInscriptionType } from '@app/query/bitcoin/ordinals/inscription.hooks';
88
import { OrdinalIcon } from '@app/ui/components/icons/ordinal-icon';
99

10+
import { CollectibleAudio } from '../_collectible-types/collectible-audio';
11+
import { CollectibleIframe } from '../_collectible-types/collectible-iframe';
1012
import { CollectibleImage } from '../_collectible-types/collectible-image';
1113
import { CollectibleOther } from '../_collectible-types/collectible-other';
1214
import { InscriptionText } from './inscription-text';
@@ -26,7 +28,32 @@ export function Inscription({ rawInscription }: InscriptionProps) {
2628
}
2729

2830
switch (inscription.type) {
29-
case 'image': {
31+
case 'audio':
32+
return (
33+
<CollectibleAudio
34+
icon={<OrdinalIcon size="lg" />}
35+
key={inscription.title}
36+
onClickCallToAction={() => openInNewTab(inscription.infoUrl)}
37+
onClickSend={() => openSendInscriptionModal()}
38+
subtitle="Ordinal inscription"
39+
title={`# ${inscription.number}`}
40+
/>
41+
);
42+
case 'html':
43+
case 'svg':
44+
case 'video':
45+
return (
46+
<CollectibleIframe
47+
icon={<OrdinalIcon size="lg" />}
48+
key={inscription.title}
49+
onClickCallToAction={() => openInNewTab(inscription.infoUrl)}
50+
onClickSend={() => openSendInscriptionModal()}
51+
src={inscription.src}
52+
subtitle="Ordinal inscription"
53+
title={`# ${inscription.number}`}
54+
/>
55+
);
56+
case 'image':
3057
return (
3158
<CollectibleImage
3259
icon={<OrdinalIcon size="lg" />}
@@ -38,8 +65,7 @@ export function Inscription({ rawInscription }: InscriptionProps) {
3865
title={`# ${inscription.number}`}
3966
/>
4067
);
41-
}
42-
case 'text': {
68+
case 'text':
4369
return (
4470
<InscriptionText
4571
contentSrc={inscription.contentSrc}
@@ -48,8 +74,7 @@ export function Inscription({ rawInscription }: InscriptionProps) {
4874
onClickSend={() => openSendInscriptionModal()}
4975
/>
5076
);
51-
}
52-
case 'other': {
77+
case 'other':
5378
return (
5479
<CollectibleOther
5580
key={inscription.title}
@@ -61,9 +86,7 @@ export function Inscription({ rawInscription }: InscriptionProps) {
6186
<OrdinalIcon />
6287
</CollectibleOther>
6388
);
64-
}
65-
default: {
89+
default:
6690
return null;
67-
}
6891
}
6992
}

0 commit comments

Comments
 (0)