feat: render embeds from link preview

closes #693
This commit is contained in:
kieran 2024-04-12 12:59:40 +01:00
parent 5292b8880e
commit 8d306ce466
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
6 changed files with 55 additions and 15 deletions

View File

@ -0,0 +1,34 @@
import { useState } from "react";
import Icon from "../Icons/Icon";
import { ProxyImg } from "../ProxyImg";
export default function GenericPlayer({ url, poster }: { url: string; poster: string }) {
const [play, setPlay] = useState(false);
if (!play) {
return (
<div
className="relative aspect-video"
onClick={e => {
e.preventDefault();
e.stopPropagation();
setPlay(true);
}}>
<ProxyImg className="absolute" src={poster} />
<div className="absolute w-full h-full opacity-0 hover:opacity-100 hover:bg-black/30 flex items-center justify-center transition">
<Icon name="play-square-outline" size={50} />
</div>
</div>
);
}
return (
<iframe
className="aspect-video w-full"
src={url}
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen={true}
/>
);
}

View File

@ -38,7 +38,9 @@
font-size: 12px;
}
.link-preview-image {
.link-preview-container img,
.link-preview-container video,
.link-preview-container iframe {
margin: 0 0 15px 0 !important;
border-radius: 0 !important;
background-image: var(--img-url);

View File

@ -1,12 +1,14 @@
import "./LinkPreview.css";
import { CSSProperties, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { LRUCache } from "typescript-lru-cache";
import { MediaElement } from "@/Components/Embed/MediaElement";
import Spinner from "@/Components/Icons/Spinner";
import { LinkPreviewData, NostrServices } from "@/External/NostrServices";
import useImgProxy from "@/Hooks/useImgProxy";
import { ProxyImg } from "../ProxyImg";
import GenericPlayer from "./GenericPlayer";
async function fetchUrlPreviewInfo(url: string) {
const api = new NostrServices("https://nostr.api.v0l.io");
@ -23,7 +25,6 @@ const cache = new LRUCache<string, LinkPreviewData>({
const LinkPreview = ({ url }: { url: string }) => {
const [preview, setPreview] = useState<LinkPreviewData | null>(cache.get(url));
const { proxy } = useImgProxy();
useEffect(() => {
(async () => {
@ -59,6 +60,9 @@ const LinkPreview = ({ url }: { url: string }) => {
if (link && videoType.startsWith("video/")) {
return <MediaElement url={link} mime={videoType} />;
}
if (link && videoType.startsWith("text/html") && preview?.image) {
return <GenericPlayer url={link} poster={preview?.image} />;
}
}
if (type?.startsWith("image")) {
const urlTags = ["og:image:secure_url", "og:image:url", "og:image"];
@ -69,9 +73,7 @@ const LinkPreview = ({ url }: { url: string }) => {
}
}
if (preview?.image) {
const backgroundImage = preview?.image ? `url(${proxy(preview?.image)})` : "";
const style = { "--img-url": backgroundImage } as CSSProperties;
return <div className="link-preview-image" style={style}></div>;
return <ProxyImg src={preview?.image} />;
}
return null;
}

View File

@ -4,13 +4,7 @@ const TwitchEmbed = ({ link }: { link: string }) => {
const args = `?channel=${channel}&parent=${window.location.hostname}&muted=true`;
return (
<>
<iframe
src={`https://player.twitch.tv/${args}`}
className="w-max"
allowFullScreen={true}
// eslint-disable-next-line react/no-unknown-property
credentialless=""
/>
<iframe src={`https://player.twitch.tv/${args}`} className="w-max" allowFullScreen={true} />
<a href={link} target="_blank" rel="noreferrer" onClick={e => e.stopPropagation()} className="ext">
{link}
</a>

View File

@ -83,7 +83,7 @@ export function Note(props: NoteProps) {
<div className="body" onClick={e => goToEvent(e, ev)}>
<NoteText {...props} translated={translated} showTranslation={showTranslation} />
{translated && <TranslationInfo translated={translated} setShowTranslation={setShowTranslation} />}
{ev.kind === EventKind.Polls && <Poll ev={ev} />}
{ev.kind === EventKind.Polls && <Poll ev={ev} zaps={[]} />}
{optionsMerged.showFooter && (
<div className="mt-4">
<NoteFooter ev={ev} replyCount={props.threadChains?.get(chainKey(ev))?.length} />

View File

@ -380,17 +380,25 @@ export class Query extends EventEmitter<QueryEvents> {
}
#canSendQuery(c: Connection, q: BuiltRawReqFilter) {
// query is not for this relay
if (q.relay && q.relay !== c.Address) {
return false;
}
// cannot send unless relay is tagged on ephemeral relay connection
if (!q.relay && c.Ephemeral) {
this.#log("Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relay, c);
return false;
}
// search not supported, cant send
if (q.filters.some(a => a.search) && !c.supportsNip(Nips.Search)) {
this.#log("Cant send REQ to non-search relay", c.Address);
return false;
}
// query already closed, cant send
if (this.canRemove()) {
this.#log("Cant send REQ when query is closed", this.id, q);
return false;
}
return true;
}