message list image context menu

This commit is contained in:
Michael Peters 2021-12-25 19:21:15 -06:00
parent 54536c1b03
commit 916c3b2f24
4 changed files with 37 additions and 20 deletions

View File

@ -1,7 +1,8 @@
import moment from 'moment'; import moment from 'moment';
import React, { FC, useCallback, useMemo } from 'react'; import React, { FC, MouseEvent, useCallback, useMemo, useState } from 'react';
import { Member, Message } from '../../../data-types'; import { Member, Message } from '../../../data-types';
import CombinedGuild from '../../../guild-combined'; import CombinedGuild from '../../../guild-combined';
import ImageContextMenu from '../../context-menus/context-menu-image';
import ImageOverlay from '../../overlays/overlay-image'; import ImageOverlay from '../../overlays/overlay-image';
import ElementsUtil from '../../require/elements-util'; import ElementsUtil from '../../require/elements-util';
import GuildSubscriptions from '../../require/guild-subscriptions'; import GuildSubscriptions from '../../require/guild-subscriptions';
@ -50,16 +51,38 @@ interface PreviewImageElementProps {
const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageElementProps) => { const PreviewImageElement: FC<PreviewImageElementProps> = (props: PreviewImageElementProps) => {
const { guild, previewWidth, previewHeight, resourcePreviewId, resourceId, resourceName } = props; const { guild, previewWidth, previewHeight, resourcePreviewId, resourceId, resourceName } = props;
const [ imgSrc ] = GuildSubscriptions.useSoftImageSrcResourceSubscription(guild, resourcePreviewId);
// TODO: Handle resourceError
const [ previewImgSrc, previewResource, previewResourceError ] = GuildSubscriptions.useSoftImageSrcResourceSubscription(guild, resourcePreviewId);
const [ relativeToPos, setRelativeToPos ] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
const [ contextMenu, toggleContextMenu ] = ReactHelper.useContextMenu((close: () => void) => {
if (!previewResource) return null;
return (
<ImageContextMenu
relativeToPos={relativeToPos} close={close}
resourceName={resourceName} resourceBuff={previewResource.data} isPreview={true}
/>
)
}, [ previewResource, relativeToPos, resourceName ]);
const openImageOverlay = useCallback(() => { const openImageOverlay = useCallback(() => {
// Note: document here isn't 100% guaranteed (I think) but we should be getting rid of this eventually anyway // Note: document here isn't 100% guaranteed (I think) but we should be getting rid of this eventually anyway
ElementsUtil.presentReactOverlay(document, <ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} />); ElementsUtil.presentReactOverlay(document, <ImageOverlay guild={guild} resourceId={resourceId} resourceName={resourceName} />);
}, [ guild, resourceId, resourceName ]); }, [ guild, resourceId, resourceName ]);
const onContextMenu = useCallback((event: MouseEvent<HTMLImageElement>) => {
setRelativeToPos({ x: event.clientX, y: event.clientY });
toggleContextMenu();
}, [ toggleContextMenu ]);
return ( return (
<div className="content image" style={{ width: previewWidth, height: previewHeight }} onClick={openImageOverlay}> <div className="content image" style={{ width: previewWidth, height: previewHeight }}>
<img src={imgSrc} alt={resourceName}/> <img
src={previewImgSrc} alt={resourceName}
onClick={openImageOverlay} onContextMenu={onContextMenu}
/>
{contextMenu}
</div> </div>
); );
} }

View File

@ -24,13 +24,7 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
// TODO: Handle errors // TODO: Handle errors
const rootRef = useRef<HTMLDivElement>(null); const rootRef = useRef<HTMLDivElement>(null);
const [ resource, resourceError ] = GuildSubscriptions.useResourceSubscription(guild, resourceId); const [ imgSrc, resource, resourceError ] = GuildSubscriptions.useSoftImageSrcResourceSubscription(guild, resourceId);
// Note: We want this customization here since we need the resource buffer.
const [ resourceImgSrc ] = ReactHelper.useOneTimeAsyncAction(
async () => await ElementsUtil.getImageSrcFromBufferFailSoftly(resource?.data ?? null),
'./img/loading.svg',
[ guild, resource ]
);
const [ relativeToPos, setRelativeToPos ] = useState<{ x: number, y: number }>({ x: 0, y: 0 }); const [ relativeToPos, setRelativeToPos ] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
@ -54,7 +48,7 @@ const ImageOverlay: FC<ImageOverlayProps> = (props: ImageOverlayProps) => {
return ( return (
<Overlay childRootRef={rootRef}> <Overlay childRootRef={rootRef}>
<div ref={rootRef} className="content popup-image"> <div ref={rootRef} className="content popup-image">
<img src={resourceImgSrc} alt={resourceName} title={resourceName} onContextMenu={onContextMenu}></img> <img src={imgSrc} alt={resourceName} title={resourceName} onContextMenu={onContextMenu}></img>
<div className="download"> <div className="download">
<div className="info"> <div className="info">
<div className="name">{resourceName}</div> <div className="name">{resourceName}</div>

View File

@ -574,20 +574,20 @@ export default class GuildSubscriptions {
}, fetchResourceFunc); }, fetchResourceFunc);
} }
static useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null): [ imgSrc: string ] { static useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null): [ imgSrc: string, resource: Resource | null, fetchError: unknown | null ] {
const [ value, fetchError ] = GuildSubscriptions.useResourceSubscription(guild, resourceId); const [ resource, fetchError ] = GuildSubscriptions.useResourceSubscription(guild, resourceId);
const [ imgSrc ] = ReactHelper.useOneTimeAsyncAction( const [ imgSrc ] = ReactHelper.useOneTimeAsyncAction(
async () => { async () => {
if (fetchError) return './img/error.png'; if (fetchError) return './img/error.png';
if (!value) return './img/loading.svg'; if (!resource) return './img/loading.svg';
return await ElementsUtil.getImageSrcFromBufferFailSoftly(value.data); return await ElementsUtil.getImageSrcFromBufferFailSoftly(resource.data);
}, },
'./img/loading.svg', './img/loading.svg',
[ value, fetchError ] [ resource, fetchError ]
); );
return [ imgSrc ]; return [ imgSrc, resource, fetchError ];
} }
static useChannelsSubscription(guild: CombinedGuild) { static useChannelsSubscription(guild: CombinedGuild) {

View File

@ -413,7 +413,7 @@ export default class ReactHelper {
}, []); }, []);
const contextMenu = useMemo(() => { const contextMenu = useMemo(() => {
return createContextMenu(close); return createContextMenu(close);
}, createContextMenuDeps); }, [ close, ...createContextMenuDeps ]);
const toggle = useCallback(() => { const toggle = useCallback(() => {
setIsOpen(oldIsOpen => !!contextMenu && !oldIsOpen); setIsOpen(oldIsOpen => !!contextMenu && !oldIsOpen);
@ -421,7 +421,7 @@ export default class ReactHelper {
const open = useCallback(() => { const open = useCallback(() => {
setIsOpen(!!contextMenu); setIsOpen(!!contextMenu);
}, [ contextMenu ]); }, [ contextMenu ]);
return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ]; return [ isOpen ? contextMenu : null, toggle, close, open, isOpen ];
} }
} }