diff --git a/.env.template b/.env.template deleted file mode 100644 index a8b8d7ab..00000000 --- a/.env.template +++ /dev/null @@ -1 +0,0 @@ -HELIUS_KEY = "" \ No newline at end of file diff --git a/src/lib/components/providers/token-provider.svelte b/src/lib/components/providers/token-provider.svelte index 286171b5..30e16f93 100644 --- a/src/lib/components/providers/token-provider.svelte +++ b/src/lib/components/providers/token-provider.svelte @@ -44,7 +44,7 @@ }, }; - const metadata: UITokenMetadata = { + export const metadata: UITokenMetadata = { address: "", attributes: [], collectionKey: "", @@ -118,6 +118,12 @@ data?.offChainMetadata?.metadata?.name || data?.legacyMetadata?.name || data?.onChainMetadata?.metadata?.data.name; + metadata.files = data?.offChainMetadata?.metadata?.properties?.files; + // Checking all files to see if a video exists + metadata.video_uri = + data?.offChainMetadata?.metadata?.properties?.files?.find( + (file: any) => file.type.startsWith("video/") + )?.uri; } $: tokenIsLoading = address !== SOL && $token.isLoading; diff --git a/src/lib/trpc/routes/transactions.ts b/src/lib/trpc/routes/transactions.ts index b60a2305..61b1fc99 100644 --- a/src/lib/trpc/routes/transactions.ts +++ b/src/lib/trpc/routes/transactions.ts @@ -37,7 +37,7 @@ export const transactions = t.procedure z.object({ actionType: z.string(), amount: z.number(), - from: z.string(), + from: z.string().nullable().optional(), fromName: z.string().optional(), received: z.string().optional(), sent: z.string().optional(), diff --git a/src/lib/types.ts b/src/lib/types.ts index fea78951..0c278405 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -39,6 +39,12 @@ export interface UITokenMetadataCreators { share: number; verified: boolean; } + +export interface FileProperties { + type: string; + uri: string; +} + export interface UITokenMetadata { address: string; image: string; @@ -60,6 +66,8 @@ export interface UITokenMetadata { tree?: string; seq?: number; leafId?: number; + files?: FileProperties[]; + video_uri?: string; } export type Icon = keyof typeof IconPaths; diff --git a/src/lib/util/get-mime-type.ts b/src/lib/util/get-mime-type.ts new file mode 100644 index 00000000..00b17848 --- /dev/null +++ b/src/lib/util/get-mime-type.ts @@ -0,0 +1,17 @@ +/* eslint-disable no-console */ +const getMimeType = async (url: string) => { + try { + const response = await fetch(url, { method: "HEAD" }); + if (!response.ok) { + console.error( + `Failed to fetch MIME type: ${response.status} ${response.statusText}` + ); + return null; + } + return response.headers.get("Content-Type"); + } catch (error: any) { + return null; + } +}; + +export default getMimeType; diff --git a/src/lib/util/stores/metadata.ts b/src/lib/util/stores/metadata.ts new file mode 100644 index 00000000..ae427d68 --- /dev/null +++ b/src/lib/util/stores/metadata.ts @@ -0,0 +1,4 @@ +import type { UITokenMetadata } from "$lib/types"; +import { writable } from "svelte/store"; + +export const metadataStore = writable(null); diff --git a/src/lib/xray/lib/parser/types.ts b/src/lib/xray/lib/parser/types.ts index d31a7a7b..98ed05a9 100644 --- a/src/lib/xray/lib/parser/types.ts +++ b/src/lib/xray/lib/parser/types.ts @@ -1,5 +1,5 @@ -import { EnrichedTransaction, Source, TransactionType } from "helius-sdk"; - +import type { EnrichedTransaction } from "helius-sdk"; +import { Source, TransactionType } from "helius-sdk"; import * as parser from "./parsers"; export const SOL = "So11111111111111111111111111111111111111112"; @@ -138,7 +138,7 @@ export type ProtonParser = ( export interface ProtonTransactionAction { actionType: ProtonActionType; - from: string; + from: string | null; to: string; sent?: string; received?: string; diff --git a/src/routes/account/[account]/concurrent-merkle-tree/+page.svelte b/src/routes/account/[account]/concurrent-merkle-tree/+page.svelte index 21f385d5..379fe6ae 100644 --- a/src/routes/account/[account]/concurrent-merkle-tree/+page.svelte +++ b/src/routes/account/[account]/concurrent-merkle-tree/+page.svelte @@ -238,7 +238,7 @@

- {($cmt.data.rightMostIndex).toLocaleString()} + {$cmt.data.rightMostIndex.toLocaleString()}

diff --git a/src/routes/token/[token]/+page.svelte b/src/routes/token/[token]/+page.svelte index 5de5e557..cecc1d2f 100644 --- a/src/routes/token/[token]/+page.svelte +++ b/src/routes/token/[token]/+page.svelte @@ -11,7 +11,7 @@ } .img { - max-height: 25vh; + max-height: 55vh; } @@ -31,12 +31,51 @@ import CopyButton from "$lib/components/copy-button.svelte"; import TokenProvider from "$lib/components/providers/token-provider.svelte"; + import getMimeType from "$lib/util/get-mime-type"; + import { metadataStore } from "$lib/util/stores/metadata"; + import type { UITokenMetadata } from "$lib/types"; + const address = $page.params.token; + + let metadata: UITokenMetadata; + + let mediaUrl: string | null = null; + let mediaType: string | null = null; + + const setMedia = async (metadata: UITokenMetadata) => { + if (metadata.image) { + const mimeType = await getMimeType(metadata.image); + if (mimeType && mimeType.startsWith("video/")) { + mediaUrl = metadata.image; + mediaType = "video"; + return; + } + + if (metadata.video_uri) { + mediaUrl = metadata.video_uri; + mediaType = "video"; + return; + } + + if (metadata.image) { + mediaUrl = metadata.image; + mediaType = "image"; + } + } + }; + + metadataStore.subscribe((metadata) => { + if (metadata) setMedia(metadata); + }); + + $: if (metadata) { + metadataStore.set(metadata); + } {#if tokenIsLoading} @@ -71,12 +110,29 @@ class="flex flex-col items-center justify-center" in:fade={{ delay: 100, duration: 800 }} > - token symbol + {#if mediaType === "video"} + +