From 8665596ea95bb6c5dcbb0da50bdcf187e5d93eea Mon Sep 17 00:00:00 2001 From: evavirseda Date: Wed, 29 Nov 2023 15:50:16 +0100 Subject: [PATCH 1/6] feat: add getIpfsUri function --- .../shared/lib/core/nfts/utils/getIpfsUri.ts | 149 ++++++++++++++++++ packages/shared/lib/core/nfts/utils/index.ts | 5 +- 2 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 packages/shared/lib/core/nfts/utils/getIpfsUri.ts diff --git a/packages/shared/lib/core/nfts/utils/getIpfsUri.ts b/packages/shared/lib/core/nfts/utils/getIpfsUri.ts new file mode 100644 index 00000000000..b313f87a2cd --- /dev/null +++ b/packages/shared/lib/core/nfts/utils/getIpfsUri.ts @@ -0,0 +1,149 @@ +interface IIpfsLink { + Hash: string + Name: string + Size: number + Target: string + Type: number + Mode?: string + Mtime?: number + MtimeNsecs?: number +} + +interface IIPfsEntry { + readonly type: 'dir' | 'file' + readonly cid: string + readonly name: string + readonly path: string + mode?: number + mtime?: { + secs: number + nsecs?: number + } + size: number +} + +interface IIpfsObject { + Hash: string + Links: IIpfsLink[] +} + +enum typeOfLink { + Dir = 'dir', + File = 'file', +} + +const IPFS_ENDPOINT = 'https://ipfs.io' +const IPFS_PATH = '/api/v0/ls' +const IPFS_PREFIX = '/ipfs/' + +export async function getIpfsUri(link: { path?: string; hash: string }): Promise { + try { + const slicedLink = link.hash.slice('https://ipfs.io/'.length) + const ipfsEntry = await ls(slicedLink) + if (ipfsEntry) { + if (ipfsEntry.type === 'dir') { + const path = `${link.path ?? ''}/${ipfsEntry.name}` + return await getIpfsUri({ hash: link.hash, path }) + } + return `${link.hash}/${encodeURIComponent(ipfsEntry.name)}` + } + } catch (error) { + console.error('error', error) + } +} + +async function ls(path: string): Promise { + let ipfsEntry: IIPfsEntry | undefined + + try { + const baseUrl = IPFS_ENDPOINT + const method = 'get' + const payload = undefined + let headers = {} + const timeout = undefined + + headers ??= {} + + let controller: AbortController | undefined + let timerId: NodeJS.Timeout | undefined + + if (timeout !== undefined) { + controller = new AbortController() + timerId = setTimeout(() => { + if (controller) { + controller.abort() + } + }, timeout) + } + + try { + if (path.includes('ipfs')) { + const response = await fetch(`${baseUrl}${IPFS_PATH}?arg=/${path}`, { + method, + headers, + body: payload ? JSON.stringify(payload) : undefined, + signal: controller ? controller.signal : undefined, + }) + const lsResponse = (await response.json()) as { Objects: IIpfsObject[] } + const result = lsResponse.Objects[0] + if (result) { + const links = result.Links + if (links.length > 0) { + ipfsEntry = mapLinkToIpfsEntry(links[0], path) + } + } + } + } catch (error) { + console.error('error', error) + } finally { + if (timerId) { + clearTimeout(timerId) + } + } + } catch (error) { + console.error('error', error) + } + + return ipfsEntry +} + +function mapLinkToIpfsEntry(link: IIpfsLink, path: string): IIPfsEntry { + const hash = link.Hash.startsWith(IPFS_PREFIX) ? link.Hash.slice(IPFS_PREFIX.length) : link.Hash + const entry: IIPfsEntry = { + name: link.Name, + path: path + (link.Name ? `/${link.Name}` : ''), + size: link.Size, + cid: hash, + type: typeOf(link), + } + if (link.Mode) { + entry.mode = Number.parseInt(link.Mode, 8) + } + + if (link.Mtime !== undefined && link.Mtime !== null) { + entry.mtime = { + secs: link.Mtime, + } + + if (link.MtimeNsecs !== undefined && link.MtimeNsecs !== null) { + entry.mtime.nsecs = link.MtimeNsecs + } + } + + return entry +} + +function typeOf(link: IIpfsLink): typeOfLink { + switch (link.Type) { + case 1: + case 5: { + return typeOfLink.Dir + } + case 2: { + return typeOfLink.File + } + default: { + return typeOfLink.File + } + } +} diff --git a/packages/shared/lib/core/nfts/utils/index.ts b/packages/shared/lib/core/nfts/utils/index.ts index ea8bd888dc2..250a101e79b 100644 --- a/packages/shared/lib/core/nfts/utils/index.ts +++ b/packages/shared/lib/core/nfts/utils/index.ts @@ -2,9 +2,10 @@ export * from './buildNftFromNftOutput' export * from './checkIfNftShouldBeDownloaded' export * from './composeUrlFromNftUri' export * from './convertAndFormatNftMetadata' -export * from './getSpendableStatusFromUnspentNftOutput' export * from './fetchWithTimeout' +export * from './getIpfsUri' +export * from './getParentMimeType' +export * from './getSpendableStatusFromUnspentNftOutput' export * from './isNftOwnedByAnyAccount' export * from './parseNftMetadata' export * from './rewriteIpfsUri' -export * from './getParentMimeType' From 885abaedc079630a7c6182b870555f5f8f027ce0 Mon Sep 17 00:00:00 2001 From: evavirseda Date: Wed, 29 Nov 2023 15:52:26 +0100 Subject: [PATCH 2/6] feat: display ipfs media --- packages/shared/components/MediaDisplay.svelte | 2 +- packages/shared/components/NftImageOrIconBox.svelte | 7 +++---- .../lib/core/nfts/actions/downloadNextNftInQueue.ts | 10 +++++++++- .../core/nfts/actions/updateNftInAllAccountNfts.ts | 12 +++++++++--- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/shared/components/MediaDisplay.svelte b/packages/shared/components/MediaDisplay.svelte index 8df73db2c8a..d08e9111b20 100644 --- a/packages/shared/components/MediaDisplay.svelte +++ b/packages/shared/components/MediaDisplay.svelte @@ -31,7 +31,7 @@
- {#if htmlTag === ParentMimeType.Image} + {#if htmlTag === ParentMimeType.Image || htmlTag === ParentMimeType.Text} {:else if htmlTag === ParentMimeType.Video}