From 7314a9559f26d0896e0436510eac39d0c26d1b57 Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Sun, 17 Nov 2024 22:59:52 -0600 Subject: [PATCH 1/6] feat: loading/error states for markdown images (#1733) --- src/features/media/InlineMedia.tsx | 90 ++++++++++++++++++ src/features/media/{gallery => }/Media.tsx | 2 +- .../large => }/media/MediaPlaceholder.tsx | 11 +-- .../external/redgifs/LargeFeedRedgifMedia.tsx | 2 +- .../media/external/redgifs/helpers.ts | 2 +- .../inFeed/large => media}/imageSlice.ts | 0 src/features/media/useAspectRatio.ts | 11 +++ .../large => media}/useMediaLoadObserver.ts | 13 ++- .../inFeed/compact/CompactFeedPostMedia.tsx | 2 +- .../post/inFeed/large/media/BlurOverlay.tsx | 13 ++- .../inFeed/large/media/LargeFeedMedia.tsx | 92 +++---------------- .../inFeed/large/media/LargeFeedPostMedia.tsx | 14 ++- src/features/post/inFeed/usePostSrc.ts | 3 +- src/features/shared/markdown/Markdown.tsx | 4 - src/features/shared/markdown/MarkdownImg.tsx | 46 +++++----- src/store.tsx | 2 +- 16 files changed, 175 insertions(+), 132 deletions(-) create mode 100644 src/features/media/InlineMedia.tsx rename src/features/media/{gallery => }/Media.tsx (92%) rename src/features/{post/inFeed/large => }/media/MediaPlaceholder.tsx (87%) rename src/features/{post/inFeed/large => media}/imageSlice.ts (100%) create mode 100644 src/features/media/useAspectRatio.ts rename src/features/{post/inFeed/large => media}/useMediaLoadObserver.ts (86%) diff --git a/src/features/media/InlineMedia.tsx b/src/features/media/InlineMedia.tsx new file mode 100644 index 0000000000..9370f05927 --- /dev/null +++ b/src/features/media/InlineMedia.tsx @@ -0,0 +1,90 @@ +import { cx } from "@linaria/core"; +import { CSSProperties } from "react"; + +import Media, { MediaProps } from "#/features/media/Media"; +import useLatch from "#/helpers/useLatch"; +import { useAppDispatch } from "#/store"; + +import MediaPlaceholder from "./MediaPlaceholder"; +import { IMAGE_FAILED, imageFailed, imageLoaded } from "./imageSlice"; +import { isLoadedAspectRatio } from "./useAspectRatio"; +import useMediaLoadObserver, { + getTargetDimensions, +} from "./useMediaLoadObserver"; + +export type InlineMediaProps = Omit & { + defaultAspectRatio?: number; + mediaClassName?: string; +}; + +export const MEDIA_EL_CLASSNAME = "media"; + +export default function InlineMedia({ + src, + className, + style: baseStyle, + defaultAspectRatio, + mediaClassName, + ...props +}: InlineMediaProps) { + const dispatch = useAppDispatch(); + const [mediaRef, currentAspectRatio] = useMediaLoadObserver(src); + + /** + * Cross posts have different image thumbnail url when loaded, so prevent resizing by latching + * + * If the new image is different size (or errors), it will be properly updated then + * (IMAGE_FAILED is truthy) + */ + const aspectRatio = useLatch(currentAspectRatio); + + function buildPlaceholderState() { + if (aspectRatio === IMAGE_FAILED) return "error"; + if (!aspectRatio) return "loading"; + + return "loaded"; + } + + function buildStyle(): CSSProperties { + if (!aspectRatio || aspectRatio === IMAGE_FAILED) return { opacity: 0 }; + + return { aspectRatio }; + } + + return ( + + { + if (src) dispatch(imageFailed(src)); + }} + // useMediaLoadObserver fires if image is partially loaded. + // but sometimes a Safari quirk doesn't fire the resize handler. + // this catches those edge cases. + // + // TLDR Image loading should still work with this function commented out! + onLoad={(event) => { + if (!src) return; + if (isLoadedAspectRatio(aspectRatio)) return; + + const dimensions = getTargetDimensions( + event.target as HTMLImageElement, + ); + if (!dimensions) return; + const { width, height } = dimensions; + + dispatch(imageLoaded({ src, aspectRatio: width / height })); + }} + /> + + ); +} diff --git a/src/features/media/gallery/Media.tsx b/src/features/media/Media.tsx similarity index 92% rename from src/features/media/gallery/Media.tsx rename to src/features/media/Media.tsx index d207cc9622..282fece520 100644 --- a/src/features/media/gallery/Media.tsx +++ b/src/features/media/Media.tsx @@ -4,7 +4,7 @@ import { PlayerProps } from "#/features/media/video/Player"; import Video from "#/features/media/video/Video"; import { isUrlVideo } from "#/helpers/url"; -import GalleryMedia, { GalleryMediaProps } from "./GalleryMedia"; +import GalleryMedia, { GalleryMediaProps } from "./gallery/GalleryMedia"; export interface MediaProps extends Omit { diff --git a/src/features/post/inFeed/large/media/MediaPlaceholder.tsx b/src/features/media/MediaPlaceholder.tsx similarity index 87% rename from src/features/post/inFeed/large/media/MediaPlaceholder.tsx rename to src/features/media/MediaPlaceholder.tsx index 39d400fe11..4d33e63dd0 100644 --- a/src/features/post/inFeed/large/media/MediaPlaceholder.tsx +++ b/src/features/media/MediaPlaceholder.tsx @@ -4,13 +4,11 @@ import { styled } from "@linaria/react"; import { imageOutline } from "ionicons/icons"; import { HTMLAttributes } from "react"; -import { StyledPostMedia } from "./LargeFeedMedia"; +import { MEDIA_EL_CLASSNAME } from "./InlineMedia"; -const PlaceholderContainer = styled.div<{ defaultAspectRatio: number }>` +const PlaceholderContainer = styled.span<{ defaultAspectRatio: number }>` display: flex; - background: var(--lightroom-bg); - &.not-loaded { align-items: center; justify-content: center; @@ -18,7 +16,7 @@ const PlaceholderContainer = styled.div<{ defaultAspectRatio: number }>` aspect-ratio: ${({ defaultAspectRatio }) => defaultAspectRatio}; position: relative; - ${StyledPostMedia} { + .${MEDIA_EL_CLASSNAME} { position: absolute; top: 0; left: 0; @@ -31,8 +29,9 @@ const LoadingIonIcon = styled(IonIcon)` font-size: 24px; `; -const Error = styled.div` +const Error = styled.span` opacity: 0.5; + padding: 16px; `; type State = "loading" | "loaded" | "error" | "custom"; diff --git a/src/features/media/external/redgifs/LargeFeedRedgifMedia.tsx b/src/features/media/external/redgifs/LargeFeedRedgifMedia.tsx index e6274366f5..9fff902ef7 100644 --- a/src/features/media/external/redgifs/LargeFeedRedgifMedia.tsx +++ b/src/features/media/external/redgifs/LargeFeedRedgifMedia.tsx @@ -3,8 +3,8 @@ import { css } from "@linaria/core"; import { styled } from "@linaria/react"; import { ComponentProps, useEffect } from "react"; +import MediaPlaceholder from "#/features/media/MediaPlaceholder"; import LargeFeedMedia from "#/features/post/inFeed/large/media/LargeFeedMedia"; -import MediaPlaceholder from "#/features/post/inFeed/large/media/MediaPlaceholder"; import { setEmbedExternalMedia } from "#/features/settings/settingsSlice"; import { stopIonicTapClick } from "#/helpers/ionic"; import { useAppDispatch, useAppSelector } from "#/store"; diff --git a/src/features/media/external/redgifs/helpers.ts b/src/features/media/external/redgifs/helpers.ts index e984a4baa6..216bfbb8f5 100644 --- a/src/features/media/external/redgifs/helpers.ts +++ b/src/features/media/external/redgifs/helpers.ts @@ -4,7 +4,7 @@ export const redgifUrlRegex = /^https?:\/\/(?:www\.|v3\.)?redgifs.com\/watch\/([a-z]+)/; /** - * Uncomment `return true` in dev to test with following command + * Uncomment `return true` in dev to test with following command to disable CORS for testing * * ``` * /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --disable-site-isolation-trials --disable-web-security --user-data-dir="~/tmp" diff --git a/src/features/post/inFeed/large/imageSlice.ts b/src/features/media/imageSlice.ts similarity index 100% rename from src/features/post/inFeed/large/imageSlice.ts rename to src/features/media/imageSlice.ts diff --git a/src/features/media/useAspectRatio.ts b/src/features/media/useAspectRatio.ts new file mode 100644 index 0000000000..20de9541d0 --- /dev/null +++ b/src/features/media/useAspectRatio.ts @@ -0,0 +1,11 @@ +import { useAppSelector } from "#/store"; + +export default function useAspectRatio(src: string | undefined) { + return useAppSelector((state) => + src ? state.image.loadedBySrc[src] : undefined, + ); +} + +export function isLoadedAspectRatio(aspectRatio: number | undefined) { + return !!aspectRatio && aspectRatio > 0; +} diff --git a/src/features/post/inFeed/large/useMediaLoadObserver.ts b/src/features/media/useMediaLoadObserver.ts similarity index 86% rename from src/features/post/inFeed/large/useMediaLoadObserver.ts rename to src/features/media/useMediaLoadObserver.ts index 5d1819e641..3325e7b54f 100644 --- a/src/features/post/inFeed/large/useMediaLoadObserver.ts +++ b/src/features/media/useMediaLoadObserver.ts @@ -1,15 +1,14 @@ import { ComponentRef, useEffect, useRef } from "react"; -import type Media from "#/features/media/gallery/Media"; -import { useAppDispatch, useAppSelector } from "#/store"; +import type Media from "#/features/media/Media"; +import { imageLoaded } from "#/features/media/imageSlice"; +import { useAppDispatch } from "#/store"; -import { imageLoaded } from "./imageSlice"; +import useAspectRatio, { isLoadedAspectRatio } from "./useAspectRatio"; export default function useMediaLoadObserver(src: string | undefined) { const dispatch = useAppDispatch(); - const aspectRatio = useAppSelector((state) => - src ? state.image.loadedBySrc[src] : undefined, - ); + const aspectRatio = useAspectRatio(src); const mediaRef = useRef>(null); const resizeObserverRef = useRef(); @@ -19,7 +18,7 @@ export default function useMediaLoadObserver(src: string | undefined) { function setupObserver() { if (destroyed) return; - if (aspectRatio && aspectRatio > 0) return; + if (isLoadedAspectRatio(aspectRatio)) return; if (!src) return; if (!mediaRef.current) { // react-reverse-portal refs can take some time to setup. Try again on next paint diff --git a/src/features/post/inFeed/compact/CompactFeedPostMedia.tsx b/src/features/post/inFeed/compact/CompactFeedPostMedia.tsx index 27079eb27e..3874244b78 100644 --- a/src/features/post/inFeed/compact/CompactFeedPostMedia.tsx +++ b/src/features/post/inFeed/compact/CompactFeedPostMedia.tsx @@ -1,7 +1,7 @@ import { PostView } from "lemmy-js-client"; import { ComponentProps } from "react"; -import Media from "#/features/media/gallery/Media"; +import Media from "#/features/media/Media"; import usePostSrc from "../usePostSrc"; diff --git a/src/features/post/inFeed/large/media/BlurOverlay.tsx b/src/features/post/inFeed/large/media/BlurOverlay.tsx index 1e86227a4d..3cdb565311 100644 --- a/src/features/post/inFeed/large/media/BlurOverlay.tsx +++ b/src/features/post/inFeed/large/media/BlurOverlay.tsx @@ -1,6 +1,10 @@ import { css } from "@linaria/core"; import { styled } from "@linaria/react"; +import useAspectRatio, { + isLoadedAspectRatio, +} from "#/features/media/useAspectRatio"; + import BlurOverlayMessage from "./BlurOverlayMessage"; const BlurContainer = styled.div` @@ -15,10 +19,15 @@ const blurCss = css` `; interface BlurOverlayProps extends React.PropsWithChildren { - blur: boolean; + src: string; } -export default function BlurOverlay({ blur, children }: BlurOverlayProps) { +export default function BlurOverlay({ src, children }: BlurOverlayProps) { + const aspectRatio = useAspectRatio(src); + + // Only blur if image is displayed (loaded) + const blur = !!isLoadedAspectRatio(aspectRatio); + return (
{children}
diff --git a/src/features/post/inFeed/large/media/LargeFeedMedia.tsx b/src/features/post/inFeed/large/media/LargeFeedMedia.tsx index 4de7c3333e..5ffa02a392 100644 --- a/src/features/post/inFeed/large/media/LargeFeedMedia.tsx +++ b/src/features/post/inFeed/large/media/LargeFeedMedia.tsx @@ -1,18 +1,10 @@ -import { styled } from "@linaria/react"; -import { CSSProperties } from "react"; +import { css } from "@linaria/core"; -import Media, { MediaProps } from "#/features/media/gallery/Media"; -import useLatch from "#/helpers/useLatch"; -import { useAppDispatch } from "#/store"; +import InlineMedia, { InlineMediaProps } from "#/features/media/InlineMedia"; -import { IMAGE_FAILED, imageFailed, imageLoaded } from "../imageSlice"; -import useMediaLoadObserver, { - getTargetDimensions, -} from "../useMediaLoadObserver"; import BlurOverlay from "./BlurOverlay"; -import MediaPlaceholder from "./MediaPlaceholder"; -export const StyledPostMedia = styled(Media)` +export const fullWidthPostStyles = css` display: flex; width: 100%; max-width: none; @@ -23,81 +15,19 @@ export const StyledPostMedia = styled(Media)` min-height: 0; `; +interface LargeFeedMediaProps extends InlineMediaProps { + blur?: boolean; +} + export default function LargeFeedMedia({ - src, blur, - className, - style: baseStyle, - defaultAspectRatio, ...props -}: Omit & { - blur?: boolean; - defaultAspectRatio?: number; -}) { - const dispatch = useAppDispatch(); - const [mediaRef, currentAspectRatio] = useMediaLoadObserver(src); - - /** - * Cross posts have different image thumbnail url when loaded, so prevent resizing by latching - * - * If the new image is different size (or errors), it will be properly updated then - * (IMAGE_FAILED is truthy) - */ - const aspectRatio = useLatch(currentAspectRatio); - - function buildPlaceholderState() { - if (aspectRatio === IMAGE_FAILED) return "error"; - if (!aspectRatio) return "loading"; - - return "loaded"; - } - - function buildStyle(): CSSProperties { - if (!aspectRatio || aspectRatio === IMAGE_FAILED) return { opacity: 0 }; - - return { aspectRatio }; - } - - const loaded = !!aspectRatio && aspectRatio > 0; - +}: LargeFeedMediaProps) { const contents = ( - - { - if (src) dispatch(imageFailed(src)); - }} - // useMediaLoadObserver fires if image is partially loaded. - // but sometimes a Safari quirk doesn't fire the resize handler. - // this catches those edge cases. - // - // TLDR Image loading should still work with this function commented out! - onLoad={(event) => { - if (!src) return; - if (loaded) return; - - const dimensions = getTargetDimensions( - event.target as HTMLImageElement, - ); - if (!dimensions) return; - const { width, height } = dimensions; - - dispatch(imageLoaded({ src, aspectRatio: width / height })); - }} - /> - + ); - if (!blur) return contents; // optimization + if (!blur) return contents; - return {contents}; + return {contents}; } diff --git a/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx b/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx index be55073b60..eeaf175108 100644 --- a/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx +++ b/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx @@ -1,3 +1,4 @@ +import { css } from "@linaria/core"; import { PostView } from "lemmy-js-client"; import { ComponentProps } from "react"; @@ -7,6 +8,10 @@ import { isRedgif } from "#/features/media/external/redgifs/helpers"; import usePostSrc from "../../usePostSrc"; import LargeFeedMedia from "./LargeFeedMedia"; +const lightboxStyles = css` + background: var(--lightroom-bg); +`; + export default function LargeFeedPostMedia( props: Omit, "src"> & { post: PostView; @@ -19,12 +24,19 @@ export default function LargeFeedPostMedia( ); if (src) return ( - + ); } diff --git a/src/features/post/inFeed/usePostSrc.ts b/src/features/post/inFeed/usePostSrc.ts index a79bb9ba5d..d279360936 100644 --- a/src/features/post/inFeed/usePostSrc.ts +++ b/src/features/post/inFeed/usePostSrc.ts @@ -1,12 +1,11 @@ import { PostView } from "lemmy-js-client"; +import { IMAGE_FAILED } from "#/features/media/imageSlice"; import { findLoneImage } from "#/helpers/markdown"; import { findUrlMediaType } from "#/helpers/url"; import useSupported from "#/helpers/useSupported"; import { useAppSelector } from "#/store"; -import { IMAGE_FAILED } from "./large/imageSlice"; - export default function usePostSrc(post: PostView): string | undefined { const thumbnailIsFullsize = useSupported("Fullsize thumbnails"); diff --git a/src/features/shared/markdown/Markdown.tsx b/src/features/shared/markdown/Markdown.tsx index 169dc31d3b..f874639f6c 100644 --- a/src/features/shared/markdown/Markdown.tsx +++ b/src/features/shared/markdown/Markdown.tsx @@ -65,10 +65,6 @@ const markdownCss = css` margin: 0; } } - - img { - vertical-align: middle; - } `; // TODO - remove never when upgrading to rehypeHighlight v7 diff --git a/src/features/shared/markdown/MarkdownImg.tsx b/src/features/shared/markdown/MarkdownImg.tsx index a46df6d8f2..62a3592aba 100644 --- a/src/features/shared/markdown/MarkdownImg.tsx +++ b/src/features/shared/markdown/MarkdownImg.tsx @@ -1,16 +1,22 @@ import { css, cx } from "@linaria/core"; -import { useMemo } from "react"; -import GalleryMedia, { - GalleryMediaProps, -} from "#/features/media/gallery/GalleryMedia"; -import Player from "#/features/media/video/Player"; -import { isUrlVideo } from "#/helpers/url"; +import InlineMedia from "#/features/media/InlineMedia"; +import { GalleryMediaProps } from "#/features/media/gallery/GalleryMedia"; const smallStyles = css` max-height: 200px; `; +const mediaStyles = css` + display: inline-flex; + vertical-align: middle; + + &.not-loaded { + height: 200px; + border: 1px solid rgba(var(--ion-color-dark-rgb), 0.15); + } +`; + interface MarkdownImgProps extends Omit { /** * Restrict height of media within comments (unrestricted in post body) @@ -18,29 +24,21 @@ interface MarkdownImgProps extends Omit { small?: boolean; } -export default function MarkdownImg({ small, ...props }: MarkdownImgProps) { +export default function MarkdownImg({ + small, + src, + ...props +}: MarkdownImgProps) { const sharedStyles = small ? smallStyles : undefined; - const isVideo = useMemo( - () => props.src && isUrlVideo(props.src, undefined), - [props.src], - ); - if (isVideo) - return ( - - ); + if (!src) return; return ( - ); diff --git a/src/store.tsx b/src/store.tsx index e259ffeccd..b324603487 100644 --- a/src/store.tsx +++ b/src/store.tsx @@ -26,10 +26,10 @@ import instancesSlice, { getInstances, } from "./features/instances/instancesSlice"; import redgifsSlice from "./features/media/external/redgifs/redgifsSlice"; +import imageSlice from "./features/media/imageSlice"; import migrationSlice from "./features/migrate/migrationSlice"; import modSlice from "./features/moderation/modSlice"; import postAppearanceSlice from "./features/post/appearance/appearanceSlice"; -import imageSlice from "./features/post/inFeed/large/imageSlice"; import thumbnailSlice from "./features/post/link/thumbnail/thumbnailSlice"; import postSlice from "./features/post/postSlice"; import resolveSlice from "./features/resolve/resolveSlice"; From 6e6b0c9ff6cfaf68c31f913181b6257814e4103b Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Mon, 18 Nov 2024 17:34:47 -0600 Subject: [PATCH 2/6] fix: crash when viewing comments with inline videos (#1736) --- src/features/media/video/Video.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/media/video/Video.tsx b/src/features/media/video/Video.tsx index dfdc848a62..85da22bdf5 100644 --- a/src/features/media/video/Video.tsx +++ b/src/features/media/video/Video.tsx @@ -22,8 +22,8 @@ export default function Video({ src, ref, ...props }: VideoProps) {
{portalNode ? ( - node={portalNode} {...props} + node={portalNode} src={src} /> ) : undefined} From 7f14f15b7576268b1fec3b9eaff384e920fd625a Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Mon, 18 Nov 2024 20:42:27 -0600 Subject: [PATCH 3/6] fix: comments should not portal videos nor have controls (#1737) --- src/features/media/Media.tsx | 5 ++--- src/features/media/video/Video.tsx | 17 +++++++++++++++-- .../inFeed/large/media/LargeFeedPostMedia.tsx | 2 ++ src/features/shared/markdown/MarkdownImg.tsx | 2 ++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/features/media/Media.tsx b/src/features/media/Media.tsx index 282fece520..cdc52c0439 100644 --- a/src/features/media/Media.tsx +++ b/src/features/media/Media.tsx @@ -1,13 +1,12 @@ import { ComponentProps, ComponentRef } from "react"; -import { PlayerProps } from "#/features/media/video/Player"; -import Video from "#/features/media/video/Video"; +import Video, { VideoProps } from "#/features/media/video/Video"; import { isUrlVideo } from "#/helpers/url"; import GalleryMedia, { GalleryMediaProps } from "./gallery/GalleryMedia"; export interface MediaProps - extends Omit { + extends Omit { src: string; ref?: React.RefObject< diff --git a/src/features/media/video/Video.tsx b/src/features/media/video/Video.tsx index 85da22bdf5..e6f18de1e4 100644 --- a/src/features/media/video/Video.tsx +++ b/src/features/media/video/Video.tsx @@ -2,14 +2,27 @@ import { useImperativeHandle } from "react"; import * as portals from "react-reverse-portal"; import type { PlayerProps } from "./Player"; -import type Player from "./Player"; +import Player from "./Player"; import { useVideoPortalNode } from "./VideoPortalProvider"; export interface VideoProps extends Omit { ref: React.RefObject; + shouldPortal?: boolean; } -export default function Video({ src, ref, ...props }: VideoProps) { +export default function Video(props: VideoProps) { + const VideoComponent = props.shouldPortal ? PortaledVideo : UnportaledVideo; + + return ; +} + +function UnportaledVideo(props: VideoProps) { + return ( + } /> + ); +} + +function PortaledVideo({ src, ref, ...props }: VideoProps) { const portalNode = useVideoPortalNode(src); useImperativeHandle( diff --git a/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx b/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx index eeaf175108..e93ec8db9e 100644 --- a/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx +++ b/src/features/post/inFeed/large/media/LargeFeedPostMedia.tsx @@ -25,6 +25,7 @@ export default function LargeFeedPostMedia( url={props.post.post.url} alt={props.post.post.alt_text} autoPlay={!props.blur} + shouldPortal {...props} /> ); @@ -37,6 +38,7 @@ export default function LargeFeedPostMedia( autoPlay={!props.blur} alt={props.post.post.alt_text} className={lightboxStyles} + shouldPortal /> ); } diff --git a/src/features/shared/markdown/MarkdownImg.tsx b/src/features/shared/markdown/MarkdownImg.tsx index 62a3592aba..4d20fbbccf 100644 --- a/src/features/shared/markdown/MarkdownImg.tsx +++ b/src/features/shared/markdown/MarkdownImg.tsx @@ -40,6 +40,8 @@ export default function MarkdownImg({ mediaClassName={cx(sharedStyles, props.className)} className={mediaStyles} animationType="zoom" + progress={false} + volume={false} /> ); } From fe3a5b3512558e4b0ce1488d09fc1edcff7c3edb Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Wed, 20 Nov 2024 21:47:13 -0600 Subject: [PATCH 4/6] fix: allow tagging while logged out (#1740) --- src/routes/pages/settings/SettingsPage.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/routes/pages/settings/SettingsPage.tsx b/src/routes/pages/settings/SettingsPage.tsx index 5a0b47f323..7431052666 100644 --- a/src/routes/pages/settings/SettingsPage.tsx +++ b/src/routes/pages/settings/SettingsPage.tsx @@ -206,14 +206,12 @@ export default function SettingsPage() { )} - {currentHandle && ( - - - - - User Tags - - )} + + + + + User Tags + Date: Wed, 20 Nov 2024 22:10:29 -0600 Subject: [PATCH 5/6] fix: docker build not showing version number (#1739) --- .github/workflows/build_release.yml | 15 ++++++++- .github/workflows/docker.yml | 33 +++++++++++++++---- Dockerfile | 7 +++- .../pages/settings/about/AppDetails.tsx | 2 +- .../pages/settings/about/AppVersionInfo.tsx | 20 ++++++++--- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 30530bf830..55208364db 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -17,6 +17,14 @@ concurrency: group: release jobs: + build_docker: + uses: ./.github/workflows/docker.yml + with: + is_main_build: ${{ inputs.is_main_build }} + permissions: + contents: read + packages: write + build_web: runs-on: ubuntu-latest steps: @@ -223,7 +231,12 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} create_release: - needs: [build_web, build_ios, build_android_play, build_android] + needs: + - build_web + - build_ios + - build_android_play + - build_android + - build_docker if: inputs.is_main_build != true runs-on: ubuntu-latest diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8554100eba..5486cccd53 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,35 +1,53 @@ name: docker on: - push: - branches: - - main - - release/* - tags: - - "*" + workflow_call: + inputs: + is_main_build: + type: boolean + required: true + pull_request: branches: - main - - release/* + jobs: docker: runs-on: ubuntu-latest + permissions: contents: read packages: write + steps: - name: Checkout uses: actions/checkout@v4 + + - name: Download bumped version artifacts + if: inputs.is_main_build + uses: actions/download-artifact@v4 + with: + name: release-data + + # If not main build, create .env file with APP_GIT_REF + - name: Create .env file + if: inputs.is_main_build != true + run: | + echo "APP_GIT_REF=${{ github.sha }}" >> .env + - name: Docker meta id: metal uses: docker/metadata-action@v5 with: images: | ghcr.io/${{ github.repository }} + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' uses: docker/login-action@v3 @@ -37,6 +55,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} + - name: Build and push uses: docker/build-push-action@v6 with: diff --git a/Dockerfile b/Dockerfile index 880f43fe14..c59d3e7147 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,9 +20,14 @@ FROM base AS builder RUN apk add --no-cache git # Prepare build deps -COPY package.json pnpm-lock.yaml .npmrc ./ +# Copy .env conditionally https://stackoverflow.com/a/31532674/1319878 +COPY package.json pnpm-lock.yaml .npmrc .en[v] ./ COPY patches ./patches +# Add APP_VERSION to .env if it doesn't already exist +RUN grep -q 'APP_VERSION=' .env || \ + echo "APP_VERSION=`node -p \"require('./package.json').version\"`" >> .env + RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile # Copy all source files diff --git a/src/routes/pages/settings/about/AppDetails.tsx b/src/routes/pages/settings/about/AppDetails.tsx index 308584a7f2..fb62e07ae1 100644 --- a/src/routes/pages/settings/about/AppDetails.tsx +++ b/src/routes/pages/settings/about/AppDetails.tsx @@ -28,7 +28,7 @@ export default function AppDetails() {
- Voyager + Voyager
diff --git a/src/routes/pages/settings/about/AppVersionInfo.tsx b/src/routes/pages/settings/about/AppVersionInfo.tsx index 808590b7c0..61544fb691 100644 --- a/src/routes/pages/settings/about/AppVersionInfo.tsx +++ b/src/routes/pages/settings/about/AppVersionInfo.tsx @@ -3,7 +3,7 @@ import React from "react"; interface AppVersionInfoProps { betaAs?: React.ElementType; - betaPrefix?: string; + verbose?: boolean; } export default function AppVersionInfo({ @@ -20,14 +20,26 @@ export default function AppVersionInfo({ ); } -function BetaInfo({ betaPrefix }: AppVersionInfoProps) { +function BetaInfo({ verbose }: AppVersionInfoProps) { if (import.meta.env.DEV) return Development; - // If the app version is different from the git ref (tag), it's a pre-release + if (!import.meta.env.APP_GIT_REF) return; + + // e.g. pull request build + if (!import.meta.env.APP_BUILD) { + return ( + + {import.meta.env.APP_GIT_REF.slice(0, 7)} + + ); + } + + // e.g. beta release + // if the app version is different from the git ref (tag) if (import.meta.env.APP_GIT_REF !== import.meta.env.APP_VERSION) return ( - {betaPrefix && `${betaPrefix} – `}[{import.meta.env.APP_BUILD}]{" "} + {verbose && `Beta Track – `}[{import.meta.env.APP_BUILD}]{" "} {import.meta.env.APP_GIT_REF.slice(0, 7)} ); From d98d39eb1d12e55f54379be0f3574df4a902890e Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Wed, 20 Nov 2024 22:13:59 -0600 Subject: [PATCH 6/6] ci: add permission for release script --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c7121d438..01ffaad7b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,6 +114,7 @@ jobs: secrets: inherit permissions: contents: write # needed for create_release, even though it won't be called + packages: write # docker release push_release: needs: [bump_src, app_build, app_version]