Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
sharunkumar committed Nov 21, 2024
2 parents 98d5467 + d98d39e commit fe6c31a
Show file tree
Hide file tree
Showing 24 changed files with 267 additions and 160 deletions.
15 changes: 14 additions & 1 deletion .github/workflows/build_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down
33 changes: 26 additions & 7 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,42 +1,61 @@
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
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}

- name: Build and push
uses: docker/build-push-action@v6
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions src/features/media/InlineMedia.tsx
Original file line number Diff line number Diff line change
@@ -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<MediaProps, "ref"> & {
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 (
<MediaPlaceholder
className={className}
style={baseStyle}
state={buildPlaceholderState()}
defaultAspectRatio={defaultAspectRatio}
>
<Media
{...props}
src={src}
className={cx(MEDIA_EL_CLASSNAME, mediaClassName)}
style={buildStyle()}
ref={mediaRef}
onError={() => {
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 }));
}}
/>
</MediaPlaceholder>
);
}
Original file line number Diff line number Diff line change
@@ -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 "./GalleryMedia";
import GalleryMedia, { GalleryMediaProps } from "./gallery/GalleryMedia";

export interface MediaProps
extends Omit<GalleryMediaProps & PlayerProps, "src" | "ref"> {
extends Omit<GalleryMediaProps & VideoProps, "src" | "ref"> {
src: string;

ref?: React.RefObject<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ 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;
aspect-ratio: ${({ defaultAspectRatio }) => defaultAspectRatio};
position: relative;
${StyledPostMedia} {
.${MEDIA_EL_CLASSNAME} {
position: absolute;
top: 0;
left: 0;
Expand All @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/features/media/external/redgifs/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
File renamed without changes.
11 changes: 11 additions & 0 deletions src/features/media/useAspectRatio.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<ComponentRef<typeof Media>>(null);
const resizeObserverRef = useRef<ResizeObserver | undefined>();

Expand All @@ -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
Expand Down
19 changes: 16 additions & 3 deletions src/features/media/video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<PlayerProps, "ref"> {
ref: React.RefObject<HTMLVideoElement | undefined>;
shouldPortal?: boolean;
}

export default function Video({ src, ref, ...props }: VideoProps) {
export default function Video(props: VideoProps) {
const VideoComponent = props.shouldPortal ? PortaledVideo : UnportaledVideo;

return <VideoComponent {...props} />;
}

function UnportaledVideo(props: VideoProps) {
return (
<Player {...props} ref={props.ref as React.RefObject<HTMLVideoElement>} />
);
}

function PortaledVideo({ src, ref, ...props }: VideoProps) {
const portalNode = useVideoPortalNode(src);

useImperativeHandle(
Expand All @@ -22,8 +35,8 @@ export default function Video({ src, ref, ...props }: VideoProps) {
<div style={props.style} className={props.className}>
{portalNode ? (
<portals.OutPortal<typeof Player>
node={portalNode}
{...props}
node={portalNode}
src={src}
/>
) : undefined}
Expand Down
2 changes: 1 addition & 1 deletion src/features/post/inFeed/compact/CompactFeedPostMedia.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand Down
Loading

0 comments on commit fe6c31a

Please sign in to comment.