feat: support initialHidden in PreviewedLink

This commit is contained in:
Shusui MOYATANI
2024-01-08 01:38:53 +09:00
parent ce125bc643
commit 805d401632
5 changed files with 82 additions and 40 deletions

View File

@@ -1,14 +1,15 @@
import { Component, JSX, Switch, Match, createEffect, Show } from 'solid-js';
import { Component, JSX, Switch, Match, createSignal, createEffect, Show } from 'solid-js';
import LazyLoad from '@/components/utils/LazyLoad';
import SafeLink from '@/components/utils/SafeLink';
import useConfig from '@/core/useConfig';
import { useOgp } from '@/utils/ogp';
import { useTranslation } from '@/i18n/useTranslation';
import { useOgp, isOgpUrl } from '@/utils/ogp';
import { isTwitterUrl, parseYouTubeVideoUrl } from '@/utils/url';
type PreviewdLinkProps = {
class?: string;
href: string;
url: string;
initialHidden: boolean;
children?: JSX.Element;
};
@@ -29,13 +30,13 @@ const youtubeUrl = (videoId: string): string => {
return iframeUrl.href;
};
const TwitterEmbed: Component<{ class?: string; href: string }> = (props) => {
const TwitterEmbed: Component<{ url: string }> = (props) => {
let twitterRef: HTMLQuoteElement | undefined;
const { getColorTheme } = useConfig();
createEffect(() => {
if (isTwitterUrl(props.href)) {
if (isTwitterUrl(props.url)) {
window.twttr?.widgets?.load(twitterRef);
}
});
@@ -51,12 +52,12 @@ const TwitterEmbed: Component<{ class?: string; href: string }> = (props) => {
return (
<blockquote ref={twitterRef} class="twitter-tweet" data-theme={dataTheme()}>
<a
class={props.class}
href={twitterUrl(props.href)}
class="text-link underline"
href={twitterUrl(props.url)}
target="_blank"
rel="noreferrer noopener"
>
{twitterUrl(props.href)}
{twitterUrl(props.url)}
</a>
</blockquote>
);
@@ -68,7 +69,7 @@ const OgpEmbed: Component<{ class?: string; url: string }> = (props) => {
}));
return (
<Show when={ogp()} fallback={<SafeLink class={props.class} href={props.url} />} keyed>
<Show when={ogp()} fallback={<SafeLink class="text-link underline" href={props.url} />} keyed>
{(ogpProps) => (
<SafeLink href={props.url}>
<div class="my-2 rounded-lg border border-border transition-colors hover:bg-bg-tertiary">
@@ -89,39 +90,75 @@ const OgpEmbed: Component<{ class?: string; url: string }> = (props) => {
);
};
type ClickToShowProps = {
initialHidden: boolean;
url: string;
children: JSX.Element;
};
const ClickToShow: Component<ClickToShowProps> = (props) => {
const i18n = useTranslation();
const [hidden, setHidden] = createSignal(props.initialHidden);
return (
<Show
when={!hidden()}
fallback={
<div>
<button
class="flex flex-col items-center rounded bg-bg-tertiary p-3 text-xs text-fg-secondary hover:shadow"
onClick={() => setHidden(false)}
>
{i18n()('post.showPreview')}
</button>
<SafeLink class="text-link underline" href={props.url} />
</div>
}
>
{props.children}
</Show>
);
};
const PreviewedLink: Component<PreviewdLinkProps> = (props) => {
const { config } = useConfig();
return (
<Switch fallback={<SafeLink class={props.class} href={props.href} />}>
<Match when={config().embedding.twitter && isTwitterUrl(props.href)}>
<TwitterEmbed class={props.class} href={props.href} />
<Switch fallback={<SafeLink class="text-link underline" href={props.url} />}>
<Match when={config().embedding.twitter && isTwitterUrl(props.url)}>
<ClickToShow url={props.url} initialHidden={props.initialHidden}>
<TwitterEmbed url={props.url} />
</ClickToShow>
</Match>
<Match when={config().embedding.youtube && parseYouTubeVideoUrl(props.href)} keyed>
<Match when={config().embedding.youtube && parseYouTubeVideoUrl(props.url)} keyed>
{({ videoId }) => (
<LazyLoad
fallback={
<div class="aspect-video max-w-full">
<SafeLink href={props.href} />
</div>
}
>
{() => (
<div class="my-2 aspect-video w-full">
<iframe
loading="lazy"
title="YouTube"
class="my-2 h-full w-full"
src={youtubeUrl(videoId)}
allowfullscreen
/>
</div>
)}
</LazyLoad>
<ClickToShow url={props.url} initialHidden={props.initialHidden}>
<LazyLoad
fallback={
<div class="aspect-video max-w-full">
<SafeLink href={props.url} />
</div>
}
>
{() => (
<div class="my-2 aspect-video w-full">
<iframe
loading="lazy"
title="YouTube"
class="my-2 h-full w-full"
src={youtubeUrl(videoId)}
allowfullscreen
/>
</div>
)}
</LazyLoad>
</ClickToShow>
)}
</Match>
<Match when={config().embedding.ogp}>
<LazyLoad>{() => <OgpEmbed class={props.class} url={props.href} />}</LazyLoad>
<Match when={config().embedding.ogp && isOgpUrl(props.url)}>
<ClickToShow url={props.url} initialHidden={props.initialHidden}>
<LazyLoad>{() => <OgpEmbed url={props.url} />}</LazyLoad>
</ClickToShow>
</Match>
</Switch>
);

View File

@@ -64,7 +64,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
</button>
);
}
return <PreviewedLink class="text-link underline" href={item.content} />;
return <PreviewedLink url={item.content} initialHidden={initialHidden()} />;
}
if (item.type === 'TagReferenceResolved') {
if (item.reference == null) {

View File

@@ -108,6 +108,7 @@ export default {
failedToDelete: 'Failed to delete',
showImage: 'Show image',
showVideo: 'Show video',
showPreview: 'Show preview',
showOverflow: 'Read more',
hideOverflow: 'Hide',
download: 'Download',

View File

@@ -104,6 +104,7 @@ export default {
failedToDelete: 'すべてのリレーで削除に失敗しました',
showImage: '画像を表示する',
showVideo: '動画を表示する',
showPreview: 'プレビューを表示する',
showOverflow: '続きを読む',
hideOverflow: '隠す',
download: 'ダウンロード',

View File

@@ -39,13 +39,16 @@ export const parseOgp = (text: string): OgpContent | null => {
return parseOgpFromDOM(doc);
};
export const fetchOgpContent = async (urlString: string): Promise<OgpContent | null> => {
export const isOgpUrl = (urlString: string): boolean => {
const allowList = ['www3.nhk.or.jp'];
const url = new URL(urlString);
if (!allowList.includes(url.host)) return null;
return allowList.includes(url.host);
};
const res = await fetch(url, { headers: { Accept: 'text/html' } });
export const fetchOgpContent = async (urlString: string): Promise<OgpContent | null> => {
if (!isOgpUrl(urlString)) return null;
const res = await fetch(urlString, { headers: { Accept: 'text/html' } });
const text = await res.text();
return parseOgp(text);
};