Skip to content

Commit

Permalink
Merge pull request #577 from plebbit/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
plebeius-eth authored Oct 19, 2024
2 parents b7cbe98 + e9f7f1b commit 25dad82
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 30 deletions.
19 changes: 8 additions & 11 deletions src/hooks/use-fetch-gif-first-frame.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { useEffect, useState } from 'react';
import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js';

const GIF_FRAME_CACHE_KEY = 'gifFrameCache';
const gifFrameDb = localForageLru.createInstance({ name: 'plebchanGifFrames', size: 500 });

const getCachedGifFrame = (url: string): string | null => {
const cache = JSON.parse(localStorage.getItem(GIF_FRAME_CACHE_KEY) || '{}');
return cache[url] || null;
const getCachedGifFrame = async (url: string): Promise<string | null> => {
return await gifFrameDb.getItem(url);
};

const setCachedGifFrame = (url: string, frameUrl: string): void => {
const cache = JSON.parse(localStorage.getItem(GIF_FRAME_CACHE_KEY) || '{}');
cache[url] = frameUrl;
localStorage.setItem(GIF_FRAME_CACHE_KEY, JSON.stringify(cache));
const setCachedGifFrame = async (url: string, frameUrl: string): Promise<void> => {
await gifFrameDb.setItem(url, frameUrl);
};

export const fetchImage = (url: string): Promise<ArrayBuffer> => {
Expand Down Expand Up @@ -75,7 +73,7 @@ const useFetchGifFirstFrame = (url: string | undefined) => {

const fetchFrame = async () => {
try {
const cachedFrame = getCachedGifFrame(url);
const cachedFrame = await getCachedGifFrame(url);
if (cachedFrame) {
if (isActive) setFrameUrl(cachedFrame);
return;
Expand All @@ -85,7 +83,7 @@ const useFetchGifFirstFrame = (url: string | undefined) => {
const objectUrl = URL.createObjectURL(blob);
if (isActive) {
setFrameUrl(objectUrl);
setCachedGifFrame(url, objectUrl);
await setCachedGifFrame(url, objectUrl);
}
} catch (error) {
console.error('Failed to load GIF frame:', error);
Expand All @@ -95,7 +93,6 @@ const useFetchGifFirstFrame = (url: string | undefined) => {

fetchFrame();

// Cleanup function to avoid setting state on unmounted component
return () => {
isActive = false;
};
Expand Down
64 changes: 45 additions & 19 deletions src/lib/utils/media-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import localForageLru from '@plebbit/plebbit-react-hooks/dist/lib/localforage-lru/index.js';
import { Comment } from '@plebbit/plebbit-react-hooks';
import extName from 'ext-name';
import { canEmbed } from '../../components/embed';
Expand Down Expand Up @@ -88,7 +89,7 @@ export const getLinkMediaInfo = memoize(
}

try {
mime = extName(new URL(link).pathname.toLowerCase().replace('/', ''))[0]?.mime;
mime = extName(url.pathname.toLowerCase().replace('/', ''))[0]?.mime;
if (mime) {
if (mime.startsWith('image')) {
type = mime === 'image/gif' ? 'gif' : 'image';
Expand Down Expand Up @@ -119,14 +120,41 @@ export const getLinkMediaInfo = memoize(
const fetchWebpageThumbnail = async (url: string): Promise<string | undefined> => {
try {
let html: string;
const MAX_HTML_SIZE = 1024 * 1024;
const TIMEOUT = 5000;

if (Capacitor.isNativePlatform()) {
// in the native app, the Capacitor HTTP plugin is used to fetch the thumbnail
const response = await CapacitorHttp.get({ url });
html = response.data;
const response = await CapacitorHttp.get({
url,
readTimeout: TIMEOUT,
connectTimeout: TIMEOUT,
responseType: 'text',
headers: { Accept: 'text/html', Range: `bytes=0-${MAX_HTML_SIZE - 1}` },
});
html = response.data.slice(0, MAX_HTML_SIZE);
} else {
// some sites have CORS access, from which the thumbnail can be fetched client-side, which is helpful if subplebbit.settings.fetchThumbnailUrls is false
const response = await fetch(url);
html = await response.text();
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT);

const response = await fetch(url, {
signal: controller.signal,
headers: { Accept: 'text/html' },
});

clearTimeout(timeoutId);

if (!response.ok) throw new Error('Network response was not ok');

const reader = response.body?.getReader();
let result = '';
while (true) {
const { done, value } = await reader!.read();
if (done || result.length >= MAX_HTML_SIZE) break;
result += new TextDecoder().decode(value);
}
html = result.slice(0, MAX_HTML_SIZE);
}

const parser = new DOMParser();
Expand Down Expand Up @@ -201,29 +229,27 @@ export const getMediaDimensions = (commentMediaInfo: CommentMediaInfo | undefine
return '';
};

const thumbnailUrlsDb = localForageLru.createInstance({ name: 'plebchanThumbnailUrls', size: 500 });

export const getCachedThumbnail = async (url: string): Promise<string | null> => {
return await thumbnailUrlsDb.getItem(url);
};

export const setCachedThumbnail = async (url: string, thumbnail: string): Promise<void> => {
await thumbnailUrlsDb.setItem(url, thumbnail);
};

export const fetchWebpageThumbnailIfNeeded = async (commentMediaInfo: CommentMediaInfo): Promise<CommentMediaInfo> => {
if (commentMediaInfo.type === 'webpage' && !commentMediaInfo.thumbnail) {
const cachedThumbnail = getCachedThumbnail(commentMediaInfo.url);
const cachedThumbnail = await getCachedThumbnail(commentMediaInfo.url);
if (cachedThumbnail) {
return { ...commentMediaInfo, thumbnail: cachedThumbnail };
}
const thumbnail = await fetchWebpageThumbnail(commentMediaInfo.url);
if (thumbnail) {
setCachedThumbnail(commentMediaInfo.url, thumbnail);
await setCachedThumbnail(commentMediaInfo.url, thumbnail);
}
return { ...commentMediaInfo, thumbnail };
}
return commentMediaInfo;
};
const THUMBNAIL_CACHE_KEY = 'webpageThumbnailCache';

export const getCachedThumbnail = (url: string): string | null => {
const cache = JSON.parse(localStorage.getItem(THUMBNAIL_CACHE_KEY) || '{}');
return cache[url] || null;
};

export const setCachedThumbnail = (url: string, thumbnail: string): void => {
const cache = JSON.parse(localStorage.getItem(THUMBNAIL_CACHE_KEY) || '{}');
cache[url] = thumbnail;
localStorage.setItem(THUMBNAIL_CACHE_KEY, JSON.stringify(cache));
};
1 change: 1 addition & 0 deletions src/views/board/board.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
}

.footer a, .newerPostsButton {
cursor: pointer;
text-decoration: var(--button-text-decoration);
color: var(--button-desktop-text-color);
}
Expand Down

0 comments on commit 25dad82

Please sign in to comment.