this repo has no description

cid link blob url

handle.invalid c433fae1 2e806994

verified
+61 -27
+61 -27
src/components/json.tsx
··· 1 - import { isDid, isNsid, Nsid } from "@atcute/lexicons/syntax"; 1 + import { isCid, isDid, isNsid, Nsid } from "@atcute/lexicons/syntax"; 2 2 import { A, useNavigate, useParams } from "@solidjs/router"; 3 3 import { createEffect, createSignal, ErrorBoundary, For, Show } from "solid-js"; 4 4 import { setNotif } from "../layout"; ··· 15 15 mimeType: string; 16 16 } 17 17 18 - const JSONString = ({ data, isType }: { data: string; isType?: boolean }) => { 18 + const JSONString = (props: { 19 + data: string; 20 + isType?: boolean; 21 + isLink?: boolean; 22 + parentIsBlob?: boolean; 23 + }) => { 19 24 const navigate = useNavigate(); 25 + const params = useParams(); 20 26 21 27 const isURL = 22 28 URL.canParse ?? ··· 49 55 return ( 50 56 <span> 51 57 " 52 - <For each={data.split(/(\s)/)}> 58 + <For each={props.data.split(/(\s)/)}> 53 59 {(part) => ( 54 60 <> 55 61 {ATURI_RE.test(part) ? ··· 60 66 <A class="text-blue-400 hover:underline active:underline" href={`/at://${part}`}> 61 67 {part} 62 68 </A> 63 - : isNsid(part.split("#")[0]) && isType ? 69 + : isNsid(part.split("#")[0]) && props.isType ? 64 70 <button 65 71 type="button" 66 72 onClick={() => handleClick(part)} ··· 68 74 > 69 75 {part} 70 76 </button> 77 + : isCid(part) && props.isLink && props.parentIsBlob ? 78 + <A 79 + class="text-blue-400 hover:underline active:underline" 80 + rel="noopener" 81 + target="_blank" 82 + href={`https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${params.repo}&cid=${part}`} 83 + > 84 + {part} 85 + </A> 71 86 : ( 72 87 isURL(part) && 73 88 ["http:", "https:", "web+at:"].includes(new URL(part).protocol) && ··· 97 112 return <span>null</span>; 98 113 }; 99 114 100 - const JSONObject = ({ data, repo }: { data: { [x: string]: JSONType }; repo: string }) => { 115 + const JSONObject = (props: { 116 + data: { [x: string]: JSONType }; 117 + repo: string; 118 + parentIsBlob?: boolean; 119 + }) => { 101 120 const params = useParams(); 102 121 const [hide, setHide] = createSignal( 103 122 localStorage.hideMedia === "true" || params.rkey === undefined, ··· 106 125 createEffect(() => { 107 126 if (hideMedia()) setHide(hideMedia()); 108 127 }); 128 + 129 + const isBlob = props.data.$type === "blob"; 130 + const isBlobContext = isBlob || props.parentIsBlob; 109 131 110 132 const Obj = ({ key, value }: { key: string; value: JSONType }) => { 111 133 const [show, setShow] = createSignal(true); ··· 141 163 "invisible h-0": !show(), 142 164 }} 143 165 > 144 - <JSONValue data={value} repo={repo} isType={key === "$type" ? true : undefined} /> 166 + <JSONValue 167 + data={value} 168 + repo={props.repo} 169 + isType={key === "$type"} 170 + isLink={key === "$link"} 171 + parentIsBlob={isBlobContext} 172 + /> 145 173 </span> 146 174 </span> 147 175 ); 148 176 }; 149 177 150 178 const rawObj = ( 151 - <For each={Object.entries(data)}>{([key, value]) => <Obj key={key} value={value} />}</For> 179 + <For each={Object.entries(props.data)}>{([key, value]) => <Obj key={key} value={value} />}</For> 152 180 ); 153 181 154 - const blob: AtBlob = data as any; 182 + const blob: AtBlob = props.data as any; 155 183 156 184 if (blob.$type === "blob") { 157 185 return ( ··· 161 189 <Show when={blob.mimeType.startsWith("image/") && !hide()}> 162 190 <img 163 191 class="h-auto max-h-64 max-w-[16rem] object-contain" 164 - src={`https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${repo}&cid=${blob.ref.$link}`} 192 + src={`https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${props.repo}&cid=${blob.ref.$link}`} 165 193 /> 166 194 </Show> 167 195 <Show when={blob.mimeType === "video/mp4" && !hide()}> 168 196 <ErrorBoundary fallback={() => <span>Failed to load video</span>}> 169 - <VideoPlayer did={repo} cid={blob.ref.$link} /> 197 + <VideoPlayer did={props.repo} cid={blob.ref.$link} /> 170 198 </ErrorBoundary> 171 199 </Show> 172 200 <span ··· 187 215 </button> 188 216 </Tooltip> 189 217 </Show> 190 - <Tooltip text="Blob on PDS"> 191 - <a 192 - href={`https://${pds()}/xrpc/com.atproto.sync.getBlob?did=${repo}&cid=${blob.ref.$link}`} 193 - target="_blank" 194 - class={`${!hide() && (blob.mimeType.startsWith("image/") || blob.mimeType === "video/mp4") ? "-mb-1 -ml-0.5" : ""} flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600`} 195 - > 196 - <span class="iconify lucide--external-link text-base"></span> 197 - </a> 198 - </Tooltip> 199 218 </span> 200 219 </span> 201 220 </Show> ··· 207 226 return rawObj; 208 227 }; 209 228 210 - const JSONArray = ({ data, repo }: { data: JSONType[]; repo: string }) => { 229 + const JSONArray = (props: { data: JSONType[]; repo: string; parentIsBlob?: boolean }) => { 211 230 return ( 212 - <For each={data}> 231 + <For each={props.data}> 213 232 {(value, index) => ( 214 233 <span 215 234 classList={{ 216 235 "flex before:content-['-']": true, 217 - "mb-2": value === Object(value) && index() !== data.length - 1, 236 + "mb-2": value === Object(value) && index() !== props.data.length - 1, 218 237 }} 219 238 > 220 239 <span class="ml-[1ch] w-full"> 221 - <JSONValue data={value} repo={repo} /> 240 + <JSONValue data={value} repo={props.repo} parentIsBlob={props.parentIsBlob} /> 222 241 </span> 223 242 </span> 224 243 )} ··· 226 245 ); 227 246 }; 228 247 229 - export const JSONValue = (props: { data: JSONType; repo: string; isType?: boolean }) => { 248 + export const JSONValue = (props: { 249 + data: JSONType; 250 + repo: string; 251 + isType?: boolean; 252 + isLink?: boolean; 253 + parentIsBlob?: boolean; 254 + }) => { 230 255 const data = props.data; 231 - if (typeof data === "string") return <JSONString data={data} isType={props.isType} />; 256 + if (typeof data === "string") 257 + return ( 258 + <JSONString 259 + data={data} 260 + isType={props.isType} 261 + isLink={props.isLink} 262 + parentIsBlob={props.parentIsBlob} 263 + /> 264 + ); 232 265 if (typeof data === "number") return <JSONNumber data={data} />; 233 266 if (typeof data === "boolean") return <JSONBoolean data={data} />; 234 267 if (data === null) return <JSONNull />; 235 - if (Array.isArray(data)) return <JSONArray data={data} repo={props.repo} />; 236 - return <JSONObject data={data} repo={props.repo} />; 268 + if (Array.isArray(data)) 269 + return <JSONArray data={data} repo={props.repo} parentIsBlob={props.parentIsBlob} />; 270 + return <JSONObject data={data} repo={props.repo} parentIsBlob={props.parentIsBlob} />; 237 271 }; 238 272 239 273 export type JSONType = string | number | boolean | null | { [x: string]: JSONType } | JSONType[];