diff --git a/ts/components/Lightbox.tsx b/ts/components/Lightbox.tsx index 23b6b7282a1..cc30176c867 100644 --- a/ts/components/Lightbox.tsx +++ b/ts/components/Lightbox.tsx @@ -22,6 +22,7 @@ import { IMAGE_PNG, isImage, isVideo } from '../types/MIME'; import { LocalizerType } from '../types/Util'; import { MediaItemType, MessageAttributesType } from '../types/MediaItem'; import { formatDuration } from '../util/formatDuration'; +import { useRestoreFocus } from '../util/hooks/useRestoreFocus'; export type PropsType = { children?: ReactNode; @@ -55,20 +56,13 @@ export function Lightbox({ initialSelectedIndex ); - const [previousFocus, setPreviousFocus] = useState(); const [videoElement, setVideoElement] = useState( null ); const [videoTime, setVideoTime] = useState(); const [zoomed, setZoomed] = useState(false); const containerRef = useRef(null); - const focusRef = useRef(null); - - const restorePreviousFocus = useCallback(() => { - if (previousFocus && previousFocus.focus) { - previousFocus.focus(); - } - }, [previousFocus]); + const [focusRef] = useRestoreFocus(); const onPrevious = useCallback(() => { setSelectedIndex(prevSelectedIndex => Math.max(prevSelectedIndex - 1, 0)); @@ -167,18 +161,6 @@ export function Lightbox({ }; }, []); - useEffect(() => { - if (!previousFocus) { - setPreviousFocus(document.activeElement as HTMLElement); - } - }, [previousFocus]); - - useEffect(() => { - return () => { - restorePreviousFocus(); - }; - }, [restorePreviousFocus]); - useEffect(() => { const useCapture = true; document.addEventListener('keydown', onKeyDown, useCapture); @@ -191,10 +173,6 @@ export function Lightbox({ useEffect(() => { playVideo(); - if (focusRef && focusRef.current) { - focusRef.current.focus(); - } - if (videoElement && isViewOnce) { videoElement.addEventListener('timeupdate', onTimeUpdate); diff --git a/ts/components/Modal.tsx b/ts/components/Modal.tsx index 2174d7facd8..919ede18263 100644 --- a/ts/components/Modal.tsx +++ b/ts/components/Modal.tsx @@ -56,6 +56,9 @@ export function Modal({ return ( + {/* We don't want the click event to propagate to its container node. */} + {/* eslint-disable jsx-a11y/no-static-element-interactions */} + {/* eslint-disable jsx-a11y/click-events-have-key-events */}
{ + event.stopPropagation(); + }} > + {/* eslint-enable jsx-a11y/no-static-element-interactions */} + {/* eslint-enable jsx-a11y/click-events-have-key-events */} {hasHeader && (
{hasXButton && ( diff --git a/ts/components/conversation/AttachmentList.tsx b/ts/components/conversation/AttachmentList.tsx index 1a69e443007..9fb200e072f 100644 --- a/ts/components/conversation/AttachmentList.tsx +++ b/ts/components/conversation/AttachmentList.tsx @@ -66,7 +66,7 @@ export const AttachmentList = ({ const isImage = isImageAttachment(attachment); const isVideo = isVideoAttachment(attachment); - if (isImage || isVideo) { + if (isImage || isVideo || attachment.pending) { const clickCallback = attachments.length > 1 ? onClickAttachment : undefined; diff --git a/ts/linkPreviews/shouldUseFullSizeLinkPreviewImage.ts b/ts/linkPreviews/shouldUseFullSizeLinkPreviewImage.ts index c5238572457..0c50706536b 100644 --- a/ts/linkPreviews/shouldUseFullSizeLinkPreviewImage.ts +++ b/ts/linkPreviews/shouldUseFullSizeLinkPreviewImage.ts @@ -10,7 +10,7 @@ export function shouldUseFullSizeLinkPreviewImage({ isStickerPack, image, }: Readonly): boolean { - if (isStickerPack || !isImageAttachment(image)) { + if (isStickerPack || !image || !isImageAttachment(image)) { return false; } diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index b2ba5e6f98d..36342016ced 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -626,9 +626,7 @@ export function isImage(attachments?: Array): boolean { ); } -export function isImageAttachment( - attachment?: AttachmentType -): attachment is AttachmentType { +export function isImageAttachment(attachment?: AttachmentType): boolean { return Boolean( attachment && attachment.contentType && @@ -640,7 +638,9 @@ export function canBeTranscoded( attachment?: AttachmentType ): attachment is AttachmentType { return Boolean( - isImageAttachment(attachment) && !MIME.isGif(attachment.contentType) + attachment && + isImageAttachment(attachment) && + !MIME.isGif(attachment.contentType) ); } diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 6618fca0274..03337fdbdbf 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -13295,13 +13295,6 @@ "reasonCategory": "usageTrusted", "updated": "2021-08-23T18:39:37.081Z" }, - { - "rule": "React-useRef", - "path": "ts/components/Lightbox.tsx", - "line": " const focusRef = useRef(null);", - "reasonCategory": "usageTrusted", - "updated": "2021-08-23T18:39:37.081Z" - }, { "rule": "React-createRef", "path": "ts/components/MainHeader.js", diff --git a/ts/views/conversation_view.ts b/ts/views/conversation_view.ts index 64a5be9229d..023d3ec3851 100644 --- a/ts/views/conversation_view.ts +++ b/ts/views/conversation_view.ts @@ -3084,6 +3084,11 @@ export class ConversationView extends window.Backbone.View { }; }; + if (this.lightboxView) { + this.lightboxView.remove(); + this.lightboxView = undefined; + } + this.lightboxView = new Whisper.ReactWrapperView({ className: 'lightbox-wrapper', Component: window.Signal.Components.Lightbox, @@ -3206,6 +3211,11 @@ export class ConversationView extends window.Backbone.View { mediaItem.attachment.path === selectedMediaItem.attachment.path ); + if (this.lightboxView) { + this.lightboxView.remove(); + this.lightboxView = undefined; + } + this.lightboxView = new Whisper.ReactWrapperView({ className: 'lightbox-wrapper', Component: window.Signal.Components.Lightbox,