This commit is contained in:
frost768 2023-03-23 01:33:49 +03:00
commit 5edc99cdfe
19 changed files with 1512 additions and 1361 deletions

View File

@ -1,48 +0,0 @@
name: Annotate linting
permissions:
actions: read # download artifact
checks: write # annotate
# this is done as a seperate workflow so
# the annotater has access to write to checks (to annotate)
on:
workflow_run:
workflows: ["Linting and Testing"]
types:
- completed
jobs:
annotate:
name: Annotate linting
runs-on: ubuntu-latest
steps:
- name: Download linting report
uses: actions/github-script@v6
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "eslint_report.json"
})[0];
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
const fs = require('fs');
fs.writeFileSync('${{github.workspace}}/eslint_report.zip', Buffer.from(download.data));
- run: unzip eslint_report.zip
- name: Annotate linting
uses: ataylorme/eslint-annotate-action@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
report-json: "eslint_report.json"

View File

@ -25,15 +25,8 @@ jobs:
- name: Install Yarn packages
run: yarn install
- name: Run ESLint Report
run: yarn lint:report
# continue on error, so it still reports it in the next step
continue-on-error: true
- uses: actions/upload-artifact@v3
with:
name: eslint_report.json
path: eslint_report.json
- name: Run ESLint
run: yarn lint
building:
name: Build project

View File

@ -1,6 +1,6 @@
{
"name": "movie-web",
"version": "3.0.6",
"version": "3.0.8",
"private": true,
"homepage": "https://movie.squeezebox.dev",
"dependencies": {
@ -8,6 +8,7 @@
"@headlessui/react": "^1.5.0",
"@react-spring/web": "^9.7.1",
"@use-gesture/react": "^10.2.24",
"core-js": "^3.29.1",
"crypto-js": "^4.1.1",
"dompurify": "^3.0.1",
"fscreen": "^1.2.0",
@ -44,9 +45,8 @@
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
"defaults",
"chrome > 90"
],
"development": [
"last 1 chrome version",
@ -55,6 +55,9 @@
]
},
"devDependencies": {
"@babel/core": "^7.21.3",
"@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.21.0",
"@tailwindcss/line-clamp": "^0.4.2",
"@types/chromecast-caf-sender": "^1.0.5",
"@types/crypto-js": "^4.1.1",
@ -65,13 +68,14 @@
"@types/pako": "^2.0.0",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-router": "^5.1.18",
"@types/react-helmet": "^6.1.6",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.3.3",
"@types/react-stickynode": "^4.0.0",
"@types/react-transition-group": "^4.4.5",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"@vitejs/plugin-react-swc": "^3.0.0",
"@vitejs/plugin-react": "^3.1.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.10.0",
"eslint-config-airbnb": "19.0.4",
@ -94,7 +98,7 @@
"vite-plugin-package-version": "^1.0.2",
"vite-plugin-pwa": "^0.14.4",
"vitest": "^0.28.5",
"workbox-window": "^6.5.4",
"@types/react-helmet": "^6.1.6"
"workbox-build": "^6.5.4",
"workbox-window": "^6.5.4"
}
}

View File

@ -21,7 +21,7 @@ export type JWMediaResult = {
title: string;
poster?: string;
id: number;
original_release_year: number;
original_release_year?: number;
jw_entity_id: string;
object_type: JWContentTypes;
seasons?: JWSeasonShort[];
@ -67,7 +67,7 @@ export function formatJWMeta(
return {
title: media.title,
id: media.id.toString(),
year: media.original_release_year.toString(),
year: media.original_release_year?.toString(),
poster: media.poster
? `${JW_IMAGE_BASE}${media.poster.replace("{profile}", "s166")}`
: undefined,

View File

@ -24,7 +24,7 @@ export type MWSeasonWithEpisodeMeta = {
type MWMediaMetaBase = {
title: string;
id: string;
year: string;
year?: string;
poster?: string;
};

View File

@ -33,6 +33,9 @@ function MediaCardContent({
const canLink = linkable && !closable;
const dotListContent = [t(`media.${media.type}`)];
if (media.year) dotListContent.push(media.year);
return (
<div
className={`group -m-3 mb-2 rounded-xl bg-denim-300 bg-opacity-0 transition-colors duration-100 ${
@ -115,10 +118,7 @@ function MediaCardContent({
<h1 className="mb-1 max-h-[4.5rem] text-ellipsis break-words font-bold text-white line-clamp-3">
<span>{media.title}</span>
</h1>
<DotList
className="text-xs"
content={[t(`media.${media.type}`), media.year]}
/>
<DotList className="text-xs" content={dotListContent} />
</article>
</div>
);

View File

@ -1,6 +1,8 @@
import React, { ReactNode, Suspense } from "react";
import "core-js/stable";
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
import { conf } from "@/setup/config";
import { registerSW } from "virtual:pwa-register";
@ -21,9 +23,7 @@ if (key) {
}
initializeChromecast();
registerSW({
onNeedRefresh() {
window.location.reload();
},
immediate: true,
});
const LazyLoadedApp = React.lazy(async () => {

View File

@ -1,4 +1,4 @@
export const APP_VERSION = import.meta.env.PACKAGE_VERSION;
export const DISCORD_LINK = "https://discord.gg/Jhqt4Xzpfb";
export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
export const APP_VERSION = "3.0.6";
export const GA_ID = "G-44YVXRL61C";

View File

@ -99,7 +99,7 @@ function buildStorageObject<T>(store: InternalStoreData): StoreRet<T> {
localStorage.setItem(key, JSON.stringify(withVersion));
if (!storeCallbacks[key]) storeCallbacks[key] = [];
storeCallbacks[key].forEach((v) => v(structuredClone(data)));
storeCallbacks[key].forEach((v) => v(window.structuredClone(data)));
}
return {

View File

@ -39,7 +39,7 @@ function VideoPlayerBaseWithState(props: VideoPlayerBaseProps) {
<div
ref={ref}
className={[
"is-video-player popout-location relative h-full w-full select-none bg-black",
"is-video-player popout-location relative h-full w-full select-none overflow-hidden bg-black",
props.includeSafeArea || videoInterface.isFullscreen
? "[border-left:env(safe-area-inset-left)_solid_transparent] [border-right:env(safe-area-inset-right)_solid_transparent]"
: "",

View File

@ -1,4 +1,4 @@
import { Icon, Icons } from "@/components/Icon";
import { Icons } from "@/components/Icon";
import { useTranslation } from "react-i18next";
import { PopoutListAction } from "../../popouts/PopoutUtils";
import { QualityDisplayAction } from "./QualityDisplayAction";

View File

@ -41,7 +41,7 @@ function VideoElement(props: Props) {
autoPlay={props.autoPlay}
muted={mediaPlaying.volume === 0}
playsInline
className="absolute inset-0 z-0 h-full w-full"
className="z-0 h-full w-full"
/>
);
}

View File

@ -2,7 +2,8 @@ import { MWMediaMeta } from "@/backend/metadata/types";
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
import { Link } from "@/components/text/Link";
import { conf } from "@/setup/config";
import { Component, ReactNode } from "react";
import { Component } from "react";
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
import { Trans } from "react-i18next";
import { VideoPlayerHeader } from "./VideoPlayerHeader";

View File

@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { useParams } from "react-router-dom";
import { Icon, Icons } from "@/components/Icon";
import { useLoading } from "@/hooks/useLoading";

View File

@ -43,6 +43,8 @@ export function ScrollToActive(props: ScrollToActiveProps) {
const ref = createRef<HTMLDivElement>();
const inited = useRef<boolean>(false);
const SAFE_OFFSET = 30;
// Scroll to "active" child on first load (AKA mount except React dumb)
useEffect(() => {
if (inited.current) return;
@ -61,27 +63,31 @@ export function ScrollToActive(props: ScrollToActiveProps) {
wrapper?.querySelector(".active");
if (wrapper && active) {
let activeYPositionCentered = 0;
const setActiveYPositionCentered = () => {
activeYPositionCentered =
active.getBoundingClientRect().top -
wrapper.getBoundingClientRect().top +
active.offsetHeight / 2;
let wrapperHeight = 0;
let activePos = 0;
let activeHeight = 0;
let wrapperScroll = 0;
const getCoords = () => {
const activeRect = active.getBoundingClientRect();
const wrapperRect = wrapper.getBoundingClientRect();
wrapperHeight = wrapperRect.height;
activeHeight = activeRect.height;
activePos = activeRect.top - wrapperRect.top + wrapper.scrollTop;
wrapperScroll = wrapper.scrollTop;
};
setActiveYPositionCentered();
getCoords();
if (activeYPositionCentered >= wrapper.offsetHeight / 2) {
// Check if the active element is below the vertical center line, then scroll it into center
const isVisible =
activePos + activeHeight <
wrapperScroll + wrapperHeight - SAFE_OFFSET ||
activePos > wrapperScroll + SAFE_OFFSET;
if (isVisible) {
const activeMiddlePos = activePos + activeHeight / 2; // pos of middle of active element
const viewMiddle = wrapperHeight / 2; // half of the available height
const pos = activeMiddlePos - viewMiddle;
wrapper.scrollTo({
top: activeYPositionCentered - wrapper.offsetHeight / 2,
});
}
setActiveYPositionCentered();
if (activeYPositionCentered > wrapper.offsetHeight / 2) {
// If the element is over the vertical center line, scroll to the end
wrapper.scrollTo({
top: wrapper.scrollHeight,
top: pos,
});
}
}

View File

@ -50,7 +50,7 @@ export function VideoTesterView() {
if (video) {
return (
<div className="fixed top-0 left-0 h-full w-screen">
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
<Helmet>
<html data-full="true" />
</Helmet>

View File

@ -132,7 +132,7 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) {
}
return (
<div className="fixed top-0 left-0 h-full w-screen">
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
<Helmet>
<html data-full="true" />
</Helmet>

View File

@ -1,5 +1,5 @@
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react-swc";
import react from "@vitejs/plugin-react";
import loadVersion from "vite-plugin-package-version";
import { VitePWA } from "vite-plugin-pwa";
import checker from "vite-plugin-checker";
@ -7,10 +7,25 @@ import path from "path";
export default defineConfig({
plugins: [
react(),
react({
babel: {
presets: [
"@babel/preset-typescript",
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "entry",
corejs: {
version: "3.29",
},
},
],
],
},
}),
VitePWA({
registerType: "autoUpdate",
injectRegister: "inline",
workbox: {
globIgnores: ["**ping.txt**"],
},

2690
yarn.lock

File diff suppressed because it is too large Load Diff