Skip to content

Commit

Permalink
Merge Release 2.13.1
Browse files Browse the repository at this point in the history
  • Loading branch information
sharunkumar committed Jul 28, 2024
2 parents 8523139 + a56d17a commit 5e83cca
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 20 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "app.vger.voyager"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 255
versionName "2.13.0"
versionCode 256
versionName "2.13.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand Down
4 changes: 2 additions & 2 deletions ios/App/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.13.0</string>
<string>2.13.1</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>255</string>
<string>256</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "voyager",
"description": "A progressive webapp Lemmy client",
"private": true,
"version": "2.13.0",
"version": "2.13.1",
"type": "module",
"packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a",
"scripts": {
Expand Down
13 changes: 11 additions & 2 deletions src/features/media/gallery/Media.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PostView } from "lemmy-js-client";
import { findLoneImage } from "../../../helpers/markdown";
import { isUrlMedia, isUrlVideo } from "../../../helpers/url";
import { findUrlMediaType, isUrlVideo } from "../../../helpers/url";
import { PlayerProps } from "../video/Player";
import { ComponentProps, ComponentRef, forwardRef, memo, useMemo } from "react";
import GalleryMedia, { GalleryMediaProps } from "./GalleryMedia";
Expand Down Expand Up @@ -41,8 +41,17 @@ export default memo(Media);
export function getPostMedia(
post: PostView,
): [string] | [string, string] | undefined {
if (post.post.url && isUrlMedia(post.post.url)) {
const urlType = post.post.url && findUrlMediaType(post.post.url);

if (post.post.url && urlType) {
const thumbnailType =
post.post.thumbnail_url && findUrlMediaType(post.post.thumbnail_url);

if (post.post.thumbnail_url) {
// Sometimes Lemmy will cache the video, sometimes the thumbnail will be a still frame of the video
if (urlType === "video" && thumbnailType === "image")
return [post.post.url];

return [post.post.thumbnail_url, post.post.url];
}

Expand Down
4 changes: 2 additions & 2 deletions src/features/post/useIsPostUrlMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback } from "react";
import { useAppSelector } from "../../store";
import { PostView } from "lemmy-js-client";
import { isRedgif } from "../media/external/redgifs/helpers";
import { isUrlMedia } from "../../helpers/url";
import { findUrlMediaType } from "../../helpers/url";

export default function useIsPostUrlMedia() {
const embedExternalMedia = useAppSelector(
Expand All @@ -19,7 +19,7 @@ export default function useIsPostUrlMedia() {
if (isRedgif(url)) return true;
}

return isUrlMedia(url);
return !!findUrlMediaType(url);
},
[embedExternalMedia],
);
Expand Down
37 changes: 37 additions & 0 deletions src/helpers/gif.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// https://gist.github.com/zakirt/faa4a58cec5a7505b10e3686a226f285
export function determineIsAnimatedGif(file: File): Promise<boolean> {
return new Promise((resolve, reject) => {
const HEADER_LEN = 6;
const LOGICAL_SCREEN_DESC_LEN = 7;
const fileReader = new FileReader();

fileReader.onload = function () {
const buffer = fileReader.result as ArrayBuffer;
const dv = new DataView(buffer, HEADER_LEN + LOGICAL_SCREEN_DESC_LEN - 3);
let offset = 0;
const globalColorTable = dv.getUint8(0);
let globalColorTableSize = 0;

if (globalColorTable & 0x80) {
globalColorTableSize = 3 * Math.pow(2, (globalColorTable & 0x7) + 1);
}

offset = 3 + globalColorTableSize;
const extensionIntroducer = dv.getUint8(offset);
const graphicsControlLabel = dv.getUint8(offset + 1);
let delayTime = 0;

if (extensionIntroducer & 0x21 && graphicsControlLabel & 0xf9) {
delayTime = dv.getUint16(offset + 4);
}

resolve(delayTime !== 0);
};

fileReader.onerror = function (error) {
reject(error);
};

fileReader.readAsArrayBuffer(file);
});
}
16 changes: 12 additions & 4 deletions src/helpers/imageCompress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Source: https://stackoverflow.com/a/44849182/1319878
*/

import { determineIsAnimatedGif } from "./gif";

function imgToCanvas(
img: HTMLImageElement,
rawWidth: number,
Expand All @@ -26,11 +28,17 @@ export async function reduceFileSize(
maxHeight: number,
quality = 0.7,
): Promise<Blob | File> {
if (file.size <= acceptFileSize) {
return file;
}

if (file.type === "image/gif" && (await determineIsAnimatedGif(file))) {
// If its animated, our compression code won't work (will only keep the first frame)
// So just bail and hope the Lemmy instance allows the filesize of the upload
return file;
}

return new Promise((resolve) => {
if (file.size <= acceptFileSize) {
resolve(file);
return;
}
const img = new Image();

img.addEventListener("error", function () {
Expand Down
41 changes: 34 additions & 7 deletions src/helpers/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,35 @@ export function getPathname(url: string): string | undefined {
}
}

const imageExtensions = ["jpeg", "png", "gif", "jpg", "webp", "jxl"];
export function parseUrl(url: string): URL | undefined {
try {
return new URL(url);
} catch {
return;
}
}

export function getPotentialImageProxyPathname(
url: string,
): string | undefined {
const parsedURL = parseUrl(url);

if (!parsedURL) return;

if (parsedURL.pathname === "/api/v3/image_proxy") {
const actualImageURL = parsedURL.searchParams.get("url");

if (!actualImageURL) return;
return getPathname(actualImageURL);
}

return parsedURL.pathname;
}

const imageExtensions = ["jpeg", "png", "gif", "jpg", "webp", "jxl", "avif"];

export function isUrlImage(url: string): boolean {
const pathname = getPathname(url);
const pathname = getPotentialImageProxyPathname(url);

if (!pathname) return false;

Expand All @@ -38,10 +63,10 @@ export function isUrlImage(url: string): boolean {
);
}

const animatedImageExtensions = ["gif", "webp", "jxl"];
const animatedImageExtensions = ["gif", "webp", "jxl", "avif"];

export function isUrlPotentialAnimatedImage(url: string): boolean {
const pathname = getPathname(url);
const pathname = getPotentialImageProxyPathname(url);

if (!pathname) return false;

Expand All @@ -53,16 +78,18 @@ export function isUrlPotentialAnimatedImage(url: string): boolean {
const videoExtensions = ["mp4", "webm", "gifv"];

export function isUrlVideo(url: string): boolean {
const pathname = getPathname(url);
const pathname = getPotentialImageProxyPathname(url);
if (!pathname) return false;

return videoExtensions.some((extension) =>
pathname.endsWith(`.${extension}`),
);
}

export function isUrlMedia(url: string): boolean {
return isUrlImage(url) || isUrlVideo(url);
export function findUrlMediaType(url: string): "video" | "image" | undefined {
if (isUrlImage(url)) return "image";

if (isUrlVideo(url)) return "video";
}

// https://github.com/miguelmota/is-valid-hostname
Expand Down

0 comments on commit 5e83cca

Please sign in to comment.