feat: new icons, lazy loading for video

This commit is contained in:
florian 2023-09-12 15:40:15 +02:00
parent d63404482b
commit d172a7b4da
13 changed files with 161 additions and 95 deletions

10
package-lock.json generated
View File

@ -15,6 +15,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-lazy-load": "^4.0.1",
"react-router-dom": "^6.15.0",
"react-swipeable": "^7.0.1"
},
@ -4647,6 +4648,15 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/react-lazy-load": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-4.0.1.tgz",
"integrity": "sha512-TnXRr79X9rlC9UcmO6iyS28rOPHrgkHIP4+b8yZPfs1tw6k/Rp2DmFY8R20BqWR45ZWkpT+4dqV1f+yci+1ozg==",
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",

View File

@ -19,6 +19,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-lazy-load": "^4.0.1",
"react-router-dom": "^6.15.0",
"react-swipeable": "^7.0.1"
},

View File

@ -37,6 +37,10 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
[images, activeImageIdx]
);
useEffect(() => {
setState({ ...state, showNavButtons: false });
return () => setState({ ...state, showNavButtons: true });
}, []);
const activeProfile = currentImage?.author !== undefined ? getProfile(currentImage?.author) : undefined;
const { nav, currentSettings } = useNav();
@ -126,7 +130,7 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
currentImage.post.wasZapped = true;
};
const imageWidth = useMemo(() => size.width && size.width > 1600 ? 1600 : 800, [size.width])
const imageWidth = useMemo(() => (size.width && size.width > 1600 ? 1600 : 800), [size.width]);
const nextImageProxyUrl = nextImage?.url && createImgProxyUrl(nextImage?.url, imageWidth, -1);
const currentImageProxyUrl = currentImage?.url && createImgProxyUrl(currentImage?.url, imageWidth, -1);
@ -137,12 +141,19 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
return (
<div className="details">
<CloseButton onClick={() => setActiveImageIdx(undefined)}></CloseButton>
{nextImage && !isVideo(nextImage.url) && <img src={nextImageProxyUrl} loading='eager' style={{ display: 'none' }} />}
{nextImage && isVideo(nextImage.url) && <video src={nextImage?.url} preload='true' style={{ display: 'none' }} />}
<div className="details-contents" style={
{ backgroundImage: `url(${!isVideo(currentImage.url) ? currentImageProxyUrl : ''})` }
}>
{isVideo(currentImage.url) ? <video className="detail-image" src={currentImage?.url} autoPlay loop muted playsInline></video> : <img className="detail-image" src={currentImageProxyUrl} loading='eager'></img>}
{nextImage && !isVideo(nextImage.url) && (
<img src={nextImageProxyUrl} loading="eager" style={{ display: 'none' }} />
)}
{nextImage && isVideo(nextImage.url) && <video src={nextImage?.url} preload="true" style={{ display: 'none' }} />}
<div
className="details-contents"
style={{ backgroundImage: `url(${!isVideo(currentImage.url) ? currentImageProxyUrl : ''})` }}
>
{isVideo(currentImage.url) ? (
<video className="detail-image" src={currentImage?.url} autoPlay loop muted playsInline></video>
) : (
<img className="detail-image" src={currentImageProxyUrl} loading="eager"></img>
)}
<div className="detail-description">
<DetailsAuthor
profile={activeProfile}
@ -150,7 +161,8 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
setActiveImageIdx={setActiveImageIdx}
></DetailsAuthor>
<div className="details-text">{currentImage?.content}</div>
{currentImage?.content && <div className="details-text">{currentImage?.content}</div>}
{state.userNPub && (
<div className="details-actions">
<div className={`heart ${heartState}`} onClick={() => currentImage && heartClick(currentImage)}>
@ -163,6 +175,7 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
)}
</div>
)}
{currentImage.tags.length > 0 && (
<div>
{uniq(currentImage?.tags).map(t => (
<>
@ -178,6 +191,7 @@ const DetailsView = ({ images, activeImageIdx, setActiveImageIdx }: DetailsViewP
</>
))}
</div>
)}
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
import { MouseEventHandler, SyntheticEvent, useState } from 'react';
import { NostrImage, createImgProxyUrl, isVideo } from '../nostrImageDownload';
import LazyLoad from 'react-lazy-load';
interface GridImageProps {
image: NostrImage;
@ -11,7 +12,9 @@ const GridImage = ({ image, onClick }: GridImageProps) => {
const mediaIsVideo = isVideo(image.url);
return mediaIsVideo ? (
return (
<LazyLoad height={200}>
{mediaIsVideo ? (
<video
className={`image ${loaded ? 'show' : ''}`}
data-node-id={image.noteId}
@ -36,6 +39,8 @@ const GridImage = ({ image, onClick }: GridImageProps) => {
onClick={onClick}
src={createImgProxyUrl(image.url)}
></img>
)}
</LazyLoad>
);
};

View File

@ -7,6 +7,7 @@ import { Settings } from '../../utils/useNav';
import { useNDK } from '@nostr-dev-kit/ndk-react';
import AuthorProfile from '../AuthorProfile';
import { useSwipeable } from 'react-swipeable';
import LazyLoad from 'react-lazy-load';
type GridViewProps = {
settings: Settings;

View File

@ -1,6 +1,10 @@
const IconFullScreen = () => (
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512" fill="white">
<path d="M32 32C14.3 32 0 46.3 0 64v96c0 17.7 14.3 32 32 32s32-14.3 32-32V96h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H64V352zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H320zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H320c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V352z" />
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"
/>
</svg>
);

View File

@ -1,6 +1,14 @@
const IconGrid = () => (
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512">
<path d="M128 136c0-22.1-17.9-40-40-40L40 96C17.9 96 0 113.9 0 136l0 48c0 22.1 17.9 40 40 40H88c22.1 0 40-17.9 40-40l0-48zm0 192c0-22.1-17.9-40-40-40H40c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40H88c22.1 0 40-17.9 40-40V328zm32-192v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V136c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM288 328c0-22.1-17.9-40-40-40H200c-22.1 0-40 17.9-40 40l0 48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V328zm32-192v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V136c0-22.1-17.9-40-40-40l-48 0c-22.1 0-40 17.9-40 40zM448 328c0-22.1-17.9-40-40-40H360c-22.1 0-40 17.9-40 40v48c0 22.1 17.9 40 40 40h48c22.1 0 40-17.9 40-40V328z" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z" />
</svg>
);

View File

@ -1,6 +1,10 @@
const IconPlay = () => (
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512">
<path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z" />
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="2 2 20 20" stroke-width="1.5" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z"
/>
</svg>
);

View File

@ -1,6 +1,15 @@
const IconSettings = () => (
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512">
<path d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z" />
<svg
id="gear"
viewBox="0 0 20 22"
fill="none"
stroke-width="1.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M7.39504 18.3711L7.97949 19.6856C8.15323 20.0768 8.43676 20.4093 8.79571 20.6426C9.15466 20.8759 9.5736 21.0001 10.0017 21C10.4298 21.0001 10.8488 20.8759 11.2077 20.6426C11.5667 20.4093 11.8502 20.0768 12.0239 19.6856L12.6084 18.3711C12.8164 17.9047 13.1664 17.5159 13.6084 17.26C14.0532 17.0034 14.5677 16.8941 15.0784 16.9478L16.5084 17.1C16.934 17.145 17.3636 17.0656 17.7451 16.8713C18.1265 16.6771 18.4434 16.3763 18.6573 16.0056C18.8714 15.635 18.9735 15.2103 18.951 14.7829C18.9285 14.3555 18.7825 13.9438 18.5306 13.5978L17.6839 12.4344C17.3825 12.0171 17.2214 11.5148 17.2239 11C17.2238 10.4866 17.3864 9.98635 17.6884 9.57111L18.535 8.40778C18.7869 8.06175 18.933 7.65007 18.9554 7.22267C18.9779 6.79528 18.8759 6.37054 18.6617 6C18.4478 5.62923 18.1309 5.32849 17.7495 5.13423C17.3681 4.93997 16.9385 4.86053 16.5128 4.90556L15.0828 5.05778C14.5722 5.11141 14.0576 5.00212 13.6128 4.74556C13.1699 4.48825 12.8199 4.09736 12.6128 3.62889L12.0239 2.31444C11.8502 1.92317 11.5667 1.59072 11.2077 1.3574C10.8488 1.12408 10.4298 0.99993 10.0017 1C9.5736 0.99993 9.15466 1.12408 8.79571 1.3574C8.43676 1.59072 8.15323 1.92317 7.97949 2.31444L7.39504 3.62889C7.18797 4.09736 6.83792 4.48825 6.39504 4.74556C5.95026 5.00212 5.43571 5.11141 4.92504 5.05778L3.4906 4.90556C3.06493 4.86053 2.63534 4.93997 2.25391 5.13423C1.87249 5.32849 1.55561 5.62923 1.34171 6C1.12753 6.37054 1.02549 6.79528 1.04798 7.22267C1.07046 7.65007 1.2165 8.06175 1.46838 8.40778L2.31504 9.57111C2.61698 9.98635 2.77958 10.4866 2.77949 11C2.77958 11.5134 2.61698 12.0137 2.31504 12.4289L1.46838 13.5922C1.2165 13.9382 1.07046 14.3499 1.04798 14.7773C1.02549 15.2047 1.12753 15.6295 1.34171 16C1.55582 16.3706 1.87274 16.6712 2.25411 16.8654C2.63548 17.0596 3.06496 17.1392 3.4906 17.0944L4.9206 16.9422C5.43127 16.8886 5.94581 16.9979 6.3906 17.2544C6.83513 17.511 7.18681 17.902 7.39504 18.3711Z" />
<path d="M9.99992 14C11.6568 14 12.9999 12.6569 12.9999 11C12.9999 9.34315 11.6568 8 9.99992 8C8.34307 8 6.99992 9.34315 6.99992 11C6.99992 12.6569 8.34307 14 9.99992 14Z" />
</svg>
);

View File

@ -0,0 +1,19 @@
const IconUser = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
/>
</svg>
);
};
export default IconUser;

View File

@ -119,37 +119,22 @@
}
.top-controls button svg {
fill: white;
opacity: 0.8;
height: 1.8em;
width: 2em;
transform: fill 0.5s ease-in-out;
height: 24px;
width: 24px;
border-radius: 0px;
padding: 0px;
}
.top-controls button {
background-color: none;
padding: 0.5em;
background-color: transparent;
vertical-align: middle;
border-radius: 12px;
margin-left: 6px;
}
.top-controls button.login {
background-color: white;
display: inline;
}
.top-controls button:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, 0.4);
}
.top-controls button.login:hover {
background-color: white;
}
.top-controls button:hover svg {
opacity: 1;
width: 48px;
height: 48px;
}
.top-controls img.profile {
@ -178,7 +163,7 @@
.bottom-controls button svg {
width: 24px;
height: 24px;
fill: white;
color: white;
}
@ -196,6 +181,7 @@
animation-name: hidePanel;
animation-duration: 0.5s;
animation-timing-function: ease-in;
display: none; /* disabled for now */
}
.bottomPanel:hover {

View File

@ -28,6 +28,7 @@ import useNav from '../utils/useNav';
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useGlobalState } from '../utils/globalState';
import useAutoLogin from '../utils/useAutoLogin';
import IconUser from './Icons/IconUser';
type AlbyNostr = typeof window.nostr & { enabled: boolean };
@ -232,12 +233,13 @@ const SlideShow = () => {
<img className="profile" onClick={onLogout} src={createImgProxyUrl(currentUserProfile.image, 80, 80)} />
)
) : (
<button onClick={onLogin} className="btn btn-primary login">
Login
<button onClick={onLogin} className="login">
<IconUser></IconUser>
</button>
)}
</div>
{state.showNavButtons && (
<div className="bottom-controls">
<button onClick={() => setShowGrid(g => !g)} title={showGrid ? 'Play random slideshow (G)' : 'view grid (G)'}>
{showGrid ? <IconPlay /> : <IconGrid />}
@ -253,6 +255,7 @@ const SlideShow = () => {
</button>
)}
</div>
)}
{showGrid ? (
<GridView images={images.current} settings={settings}></GridView>

View File

@ -5,9 +5,11 @@ import React, { createContext, useContext, useReducer } from 'react';
interface GlobalState {
userNPub?: string;
profile?: NDKUserProfile;
showNavButtons: boolean;
}
const initialState: GlobalState = {
userNPub: undefined,
showNavButtons: true
};
type GlobalStateType = [GlobalState, React.Dispatch<Partial<GlobalState>>];