mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-26 07:11:50 +01:00
Merge pull request #540 from movie-web/rtl
Right-to-left support for translations
This commit is contained in:
commit
1ef2cf5b0e
2
.github/workflows/deploying.yml
vendored
2
.github/workflows/deploying.yml
vendored
@ -109,7 +109,6 @@ jobs:
|
|||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
- name: Upload release (PWA)
|
- name: Upload release (PWA)
|
||||||
id: upload-release-asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -120,7 +119,6 @@ jobs:
|
|||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|
||||||
- name: Upload Release (Normal)
|
- name: Upload Release (Normal)
|
||||||
id: upload-release-asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
80
index.html
80
index.html
@ -1,45 +1,41 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" dir="ltr">
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1.0, user-scalable=no" />
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="The place for your favourite movies & shows"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<head>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#120f1d" />
|
<meta name="viewport"
|
||||||
<meta name="msapplication-TileColor" content="#120f1d" />
|
content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#120f1d" />
|
<meta name="description" content="The place for your favourite movies & shows" />
|
||||||
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
<link
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap"
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#120f1d" />
|
||||||
rel="stylesheet"
|
<meta name="msapplication-TileColor" content="#120f1d" />
|
||||||
/>
|
<meta name="theme-color" content="#120f1d" />
|
||||||
|
|
||||||
<script src="/config.js"></script>
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<script src="https://cdn.jsdelivr.net/gh/movie-web/6C6F6C7A@8b821f445b83d51ef1b8f42c99b7346f6b47dce5/out.js"></script>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
|
|
||||||
<!-- prevent darkreader extension from messing with our already dark site -->
|
<script src="/config.js"></script>
|
||||||
<meta name="darkreader-lock" />
|
<script src="https://cdn.jsdelivr.net/gh/movie-web/6C6F6C7A@8b821f445b83d51ef1b8f42c99b7346f6b47dce5/out.js"></script>
|
||||||
|
|
||||||
<!-- disabling referrer can fix some provider problems -->
|
<!-- prevent darkreader extension from messing with our already dark site -->
|
||||||
<meta name="referrer" content="no-referrer" />
|
<meta name="darkreader-lock" />
|
||||||
|
|
||||||
<title>movie-web</title>
|
<!-- disabling referrer can fix some provider problems -->
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
|
||||||
{{#if opensearchEnabled }}
|
<title>movie-web</title>
|
||||||
<!-- OpenSearch -->
|
|
||||||
<link rel="search" type="application/opensearchdescription+xml" title="movie-web" href="/opensearch.xml">
|
|
||||||
|
|
||||||
<!-- Google Sitelinks -->
|
{{#if opensearchEnabled }}
|
||||||
<script type="application/ld+json">
|
<!-- OpenSearch -->
|
||||||
|
<link rel="search" type="application/opensearchdescription+xml" title="movie-web" href="/opensearch.xml">
|
||||||
|
|
||||||
|
<!-- Google Sitelinks -->
|
||||||
|
<script type="application/ld+json">
|
||||||
{
|
{
|
||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "WebSite",
|
"@type": "WebSite",
|
||||||
@ -54,11 +50,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<body>
|
||||||
<div id="root"></div>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script type="module" src="/src/index.tsx"></script>
|
<div id="root"></div>
|
||||||
</body>
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
</html>
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -46,6 +46,7 @@
|
|||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
"iso-639-1": "^3.1.0",
|
"iso-639-1": "^3.1.0",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"nanoid": "^5.0.4",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"ofetch": "^1.0.0",
|
"ofetch": "^1.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
@ -98,6 +99,8 @@
|
|||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"jsdom": "^21.1.0",
|
"jsdom": "^21.1.0",
|
||||||
"postcss": "^8.4.20",
|
"postcss": "^8.4.20",
|
||||||
|
"postcss-rtl": "^2.0.0",
|
||||||
|
"postcss-rtlcss": "^4.0.9",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.7",
|
"prettier-plugin-tailwindcss": "^0.1.7",
|
||||||
"tailwind-scrollbar": "^2.0.1",
|
"tailwind-scrollbar": "^2.0.1",
|
||||||
|
57
pnpm-lock.yaml
generated
57
pnpm-lock.yaml
generated
@ -71,6 +71,9 @@ dependencies:
|
|||||||
lodash.isequal:
|
lodash.isequal:
|
||||||
specifier: ^4.5.0
|
specifier: ^4.5.0
|
||||||
version: 4.5.0
|
version: 4.5.0
|
||||||
|
nanoid:
|
||||||
|
specifier: ^5.0.4
|
||||||
|
version: 5.0.4
|
||||||
node-forge:
|
node-forge:
|
||||||
specifier: ^1.3.1
|
specifier: ^1.3.1
|
||||||
version: 1.3.1
|
version: 1.3.1
|
||||||
@ -223,6 +226,12 @@ devDependencies:
|
|||||||
postcss:
|
postcss:
|
||||||
specifier: '>=8.4.31'
|
specifier: '>=8.4.31'
|
||||||
version: 8.4.31
|
version: 8.4.31
|
||||||
|
postcss-rtl:
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0(postcss@8.4.31)
|
||||||
|
postcss-rtlcss:
|
||||||
|
specifier: ^4.0.9
|
||||||
|
version: 4.0.9(postcss@8.4.31)
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^2.5.1
|
specifier: ^2.5.1
|
||||||
version: 2.8.8
|
version: 2.8.8
|
||||||
@ -4747,6 +4756,12 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/nanoid@5.0.4:
|
||||||
|
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
|
||||||
|
engines: {node: ^18 || >=20}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/natural-compare-lite@1.4.0:
|
/natural-compare-lite@1.4.0:
|
||||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -5070,6 +5085,26 @@ packages:
|
|||||||
postcss-selector-parser: 6.0.13
|
postcss-selector-parser: 6.0.13
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postcss-rtl@2.0.0(postcss@8.4.31):
|
||||||
|
resolution: {integrity: sha512-vFu78CvaGY9BafWRHNgDm6OjUxzRCWWCrp+KtnyXdgwibLwb/j5ls8Z/ubvOsk9B/Q2NLwSPrXRARKMaa9RBmA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
postcss: '>=8.4.31'
|
||||||
|
dependencies:
|
||||||
|
postcss: 8.4.31
|
||||||
|
rtlcss: 4.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/postcss-rtlcss@4.0.9(postcss@8.4.31):
|
||||||
|
resolution: {integrity: sha512-dCNKEf+FgTv+EA3XI8ysg2RnpS5s3/iZmU+9qpCNFxHU/BhK+4hz7jyCsCAfo0CLnDrMPtaQENhwb+EGm1wh7Q==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
postcss: '>=8.4.31'
|
||||||
|
dependencies:
|
||||||
|
postcss: 8.4.31
|
||||||
|
rtlcss: 4.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postcss-selector-parser@6.0.13:
|
/postcss-selector-parser@6.0.13:
|
||||||
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -5480,6 +5515,28 @@ packages:
|
|||||||
'@babel/runtime': 7.22.11
|
'@babel/runtime': 7.22.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/rtlcss@4.0.0:
|
||||||
|
resolution: {integrity: sha512-j6oypPP+mgFwDXL1JkLCtm6U/DQntMUqlv5SOhpgHhdIE+PmBcjrtAHIpXfbIup47kD5Sgja9JDsDF1NNOsBwQ==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
escalade: 3.1.1
|
||||||
|
picocolors: 1.0.0
|
||||||
|
postcss: 8.4.31
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/rtlcss@4.1.1:
|
||||||
|
resolution: {integrity: sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
escalade: 3.1.1
|
||||||
|
picocolors: 1.0.0
|
||||||
|
postcss: 8.4.31
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/run-parallel@1.2.0:
|
/run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -222,3 +222,12 @@ input[type=range].styled-slider.slider-progress::-ms-fill-lower {
|
|||||||
outline: 2px solid theme('colors.themePreview.primary');
|
outline: 2px solid theme('colors.themePreview.primary');
|
||||||
box-shadow: 0 0 10px theme('colors.themePreview.secondary');
|
box-shadow: 0 0 10px theme('colors.themePreview.secondary');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .transform {
|
||||||
|
/* Invert horizontal X offset on transform (Tailwind RTL plugin does the rest) */
|
||||||
|
transform: translate(calc(var(--tw-translate-x) * -1), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
|
||||||
|
}
|
||||||
|
[dir="ltr"] .transform {
|
||||||
|
/* default - otherwise it overwrites*/
|
||||||
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
|
||||||
|
}
|
||||||
|
@ -25,3 +25,6 @@ export const locales = {
|
|||||||
pirate,
|
pirate,
|
||||||
minion,
|
minion,
|
||||||
};
|
};
|
||||||
|
export type Locales = keyof typeof locales;
|
||||||
|
|
||||||
|
export const rtlLocales: Locales[] = [];
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ScrapeMedia } from "@movie-web/providers";
|
import { ScrapeMedia } from "@movie-web/providers";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import { ofetch } from "ofetch";
|
import { ofetch } from "ofetch";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ import { PlayerMeta } from "@/stores/player/slices/source";
|
|||||||
// for anybody who cares - these are anonymous metrics.
|
// for anybody who cares - these are anonymous metrics.
|
||||||
// They are just used for figuring out if providers are broken or not
|
// They are just used for figuring out if providers are broken or not
|
||||||
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers";
|
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers";
|
||||||
|
const batchId = () => nanoid(32);
|
||||||
|
|
||||||
export type ProviderMetric = {
|
export type ProviderMetric = {
|
||||||
tmdbId: string;
|
tmdbId: string;
|
||||||
@ -34,6 +36,7 @@ export async function reportProviders(items: ProviderMetric[]): Promise<void> {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
items,
|
items,
|
||||||
|
batchId: batchId(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import classNames from "classnames";
|
||||||
import { memo, useEffect, useRef } from "react";
|
import { memo, useEffect, useRef } from "react";
|
||||||
|
|
||||||
export enum Icons {
|
export enum Icons {
|
||||||
@ -152,10 +153,18 @@ export const Icon = memo((props: IconProps) => {
|
|||||||
return <ChromeCastButton />;
|
return <ChromeCastButton />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const flipClass =
|
||||||
|
props.icon === Icons.ARROW_LEFT ||
|
||||||
|
props.icon === Icons.ARROW_RIGHT ||
|
||||||
|
props.icon === Icons.CHEVRON_LEFT ||
|
||||||
|
props.icon === Icons.CHEVRON_RIGHT
|
||||||
|
? "rtl:-scale-x-100"
|
||||||
|
: "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} // eslint-disable-line react/no-danger
|
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} // eslint-disable-line react/no-danger
|
||||||
className={props.className}
|
className={classNames(props.className, flipClass)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -64,7 +64,7 @@ export function OverlayAnchorPosition(props: AnchorPositionProps) {
|
|||||||
transform: `translateX(${left}px) translateY(${top}px)`,
|
transform: `translateX(${left}px) translateY(${top}px)`,
|
||||||
}}
|
}}
|
||||||
className={classNames([
|
className={classNames([
|
||||||
"pointer-events-auto z-10 inline-block origin-top-left touch-none",
|
"[&>*]:pointer-events-auto z-10 flex dir-neutral:items-start justify-start dir-neutral:origin-top-left touch-none",
|
||||||
props.className,
|
props.className,
|
||||||
])}
|
])}
|
||||||
>
|
>
|
||||||
|
@ -9,8 +9,8 @@ export function EpisodeTitle() {
|
|||||||
if (meta?.type !== "show") return null;
|
if (meta?.type !== "show") return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex gap-3">
|
||||||
<span className="text-white font-medium mr-3">
|
<span className="text-white font-medium">
|
||||||
{t("media.episodeDisplay", {
|
{t("media.episodeDisplay", {
|
||||||
season: meta?.season?.number,
|
season: meta?.season?.number,
|
||||||
episode: meta?.episode?.number,
|
episode: meta?.episode?.number,
|
||||||
|
@ -119,7 +119,7 @@ export function ProgressBar() {
|
|||||||
}, [setDraggingTime, duration, dragPercentage]);
|
}, [setDraggingTime, duration, dragPercentage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full relative">
|
<div className="w-full relative" dir="ltr">
|
||||||
<div className="top-0 absolute inset-x-0">
|
<div className="top-0 absolute inset-x-0">
|
||||||
<div
|
<div
|
||||||
className="absolute bottom-0"
|
className="absolute bottom-0"
|
||||||
@ -158,7 +158,7 @@ export function ProgressBar() {
|
|||||||
|
|
||||||
{/* Actual progress bar */}
|
{/* Actual progress bar */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 left-0 h-full rounded-full bg-progress-filled flex justify-end items-center"
|
className="absolute top-0 dir-neutral:left-0 h-full rounded-full bg-progress-filled flex justify-end items-center"
|
||||||
style={{
|
style={{
|
||||||
width: `${
|
width: `${
|
||||||
Math.max(
|
Math.max(
|
||||||
|
@ -97,6 +97,7 @@ export function CaptionSetting(props: {
|
|||||||
onTouchStart={dragMouseDown}
|
onTouchStart={dragMouseDown}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
dir="ltr"
|
||||||
className={[
|
className={[
|
||||||
"relative w-full h-1 bg-video-context-slider bg-opacity-25 rounded-full transition-[height] duration-100 group-hover/progress:h-1.5",
|
"relative w-full h-1 bg-video-context-slider bg-opacity-25 rounded-full transition-[height] duration-100 group-hover/progress:h-1.5",
|
||||||
dragging ? "!h-1.5" : "",
|
dragging ? "!h-1.5" : "",
|
||||||
|
@ -27,7 +27,7 @@ export function BottomControls(props: {
|
|||||||
<div
|
<div
|
||||||
onMouseOver={() => setHoveringAnyControls(true)}
|
onMouseOver={() => setHoveringAnyControls(true)}
|
||||||
onMouseOut={() => setHoveringAnyControls(false)}
|
onMouseOut={() => setHoveringAnyControls(false)}
|
||||||
className="pointer-events-auto pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pb-3 mb-[env(safe-area-inset-bottom)] absolute bottom-0 w-full"
|
className="pointer-events-auto z-10 pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pb-3 mb-[env(safe-area-inset-bottom)] absolute bottom-0 w-full"
|
||||||
>
|
>
|
||||||
<Transition animation="slide-up" show={props.show}>
|
<Transition animation="slide-up" show={props.show}>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@ -22,20 +22,14 @@ export const VideoPlayerButton = forwardRef<
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => props.onClick?.(e.currentTarget as HTMLButtonElement)}
|
onClick={(e) => props.onClick?.(e.currentTarget as HTMLButtonElement)}
|
||||||
className={classNames([
|
className={classNames([
|
||||||
"tabbable p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-50 transition-transform duration-100 flex items-center",
|
"tabbable p-2 rounded-full hover:bg-video-buttonBackground hover:bg-opacity-50 transition-transform duration-100 flex items-center gap-3",
|
||||||
props.activeClass ??
|
props.activeClass ??
|
||||||
"active:scale-110 active:bg-opacity-75 active:text-white",
|
"active:scale-110 active:bg-opacity-75 active:text-white",
|
||||||
props.className ?? "",
|
props.className ?? "",
|
||||||
])}
|
])}
|
||||||
>
|
>
|
||||||
{props.icon && (
|
{props.icon && (
|
||||||
<Icon
|
<Icon className={props.iconSizeClass || "text-2xl"} icon={props.icon} />
|
||||||
className={classNames(
|
|
||||||
props.iconSizeClass || "text-2xl",
|
|
||||||
props.children ? "mr-3" : ""
|
|
||||||
)}
|
|
||||||
icon={props.icon}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{props.children}
|
{props.children}
|
||||||
</button>
|
</button>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbar {
|
[dir] .lightbar {
|
||||||
left: 50vw;
|
left: 50vw;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
@ -16,13 +16,14 @@
|
|||||||
width: 150vw;
|
width: 150vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbar {
|
[dir] .lightbar {
|
||||||
left: -25vw;
|
left: -25vw;
|
||||||
transform: initial;
|
transform: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbar {
|
[dir] .lightbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -31,7 +32,7 @@
|
|||||||
animation: boot var(--d) var(--animation) forwards;
|
animation: boot var(--d) var(--animation) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbar-visual {
|
[dir] .lightbar-visual {
|
||||||
left: 0;
|
left: 0;
|
||||||
--top: theme('colors.background.main');
|
--top: theme('colors.background.main');
|
||||||
--bottom: theme('colors.lightBar.light');
|
--bottom: theme('colors.lightBar.light');
|
||||||
@ -57,7 +58,6 @@
|
|||||||
|
|
||||||
@keyframes boot {
|
@keyframes boot {
|
||||||
from {
|
from {
|
||||||
|
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,4 +74,4 @@
|
|||||||
100% {
|
100% {
|
||||||
transform: rotate(180deg) translateZ(0px) translateY(400px) scaleX(1);
|
transform: rotate(180deg) translateZ(0px) translateY(400px) scaleX(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -88,7 +88,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
|||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden lg:flex justify-between">
|
<div className="hidden lg:flex justify-between" dir="ltr">
|
||||||
<Player.LeftSideControls>
|
<Player.LeftSideControls>
|
||||||
{status === playerStatus.PLAYING ? (
|
{status === playerStatus.PLAYING ? (
|
||||||
<>
|
<>
|
||||||
@ -130,6 +130,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
|||||||
</Player.BottomControls>
|
</Player.BottomControls>
|
||||||
|
|
||||||
<Player.VolumeChangedPopout />
|
<Player.VolumeChangedPopout />
|
||||||
|
|
||||||
<Player.NextEpisodeButton
|
<Player.NextEpisodeButton
|
||||||
controlsShowing={showTargets}
|
controlsShowing={showTargets}
|
||||||
onChange={props.onMetaChange}
|
onChange={props.onMetaChange}
|
||||||
|
@ -85,10 +85,14 @@ export function ScrapingPart(props: ScrapingProps) {
|
|||||||
currentProviderIndex = sourceOrder.length - 1;
|
currentProviderIndex = sourceOrder.length - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full relative" ref={containerRef}>
|
<div
|
||||||
|
className="h-full w-full relative dir-neutral:origin-top-left flex"
|
||||||
|
ref={containerRef}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"absolute transition-[transform,opacity] opacity-0": true,
|
"absolute transition-[transform,opacity] opacity-0 dir-neutral:left-0":
|
||||||
|
true,
|
||||||
"!opacity-100": renderedOnce,
|
"!opacity-100": renderedOnce,
|
||||||
})}
|
})}
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
|
@ -23,7 +23,7 @@ import { RegisterPage } from "@/pages/Register";
|
|||||||
import { SettingsPage } from "@/pages/Settings";
|
import { SettingsPage } from "@/pages/Settings";
|
||||||
import { Layout } from "@/setup/Layout";
|
import { Layout } from "@/setup/Layout";
|
||||||
import { useHistoryListener } from "@/stores/history";
|
import { useHistoryListener } from "@/stores/history";
|
||||||
import { useLanguageListener } from "@/stores/language";
|
import { LanguageProvider } from "@/stores/language";
|
||||||
|
|
||||||
function LegacyUrlView({ children }: { children: ReactElement }) {
|
function LegacyUrlView({ children }: { children: ReactElement }) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -61,10 +61,10 @@ function QuickSearch() {
|
|||||||
function App() {
|
function App() {
|
||||||
useHistoryListener();
|
useHistoryListener();
|
||||||
useOnlineListener();
|
useOnlineListener();
|
||||||
useLanguageListener();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
<LanguageProvider />
|
||||||
<Switch>
|
<Switch>
|
||||||
{/* functional routes */}
|
{/* functional routes */}
|
||||||
<Route exact path="/s/:query">
|
<Route exact path="/s/:query">
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { Helmet } from "react-helmet-async";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import { immer } from "zustand/middleware/immer";
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
|
import { rtlLocales } from "@/assets/languages";
|
||||||
import i18n from "@/setup/i18n";
|
import i18n from "@/setup/i18n";
|
||||||
|
|
||||||
export interface LanguageStore {
|
export interface LanguageStore {
|
||||||
@ -24,10 +26,18 @@ export const useLanguageStore = create(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export function useLanguageListener() {
|
export function LanguageProvider() {
|
||||||
const language = useLanguageStore((s) => s.language);
|
const language = useLanguageStore((s) => s.language);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
i18n.changeLanguage(language);
|
i18n.changeLanguage(language);
|
||||||
}, [language]);
|
}, [language]);
|
||||||
|
|
||||||
|
const isRtl = rtlLocales.includes(language as any);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Helmet>
|
||||||
|
<html dir={isRtl ? "rtl" : "ltr"} />
|
||||||
|
</Helmet>
|
||||||
|
);
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { allThemes, defaultTheme, safeThemeList } from "./themes";
|
import { allThemes, defaultTheme, safeThemeList } from "./themes";
|
||||||
import type { Config } from "tailwindcss";
|
import type { Config } from "tailwindcss";
|
||||||
|
import plugin from "tailwindcss/plugin";
|
||||||
|
|
||||||
const themer = require("tailwindcss-themer");
|
const themer = require("tailwindcss-themer");
|
||||||
|
|
||||||
@ -41,6 +42,9 @@ const config: Config = {
|
|||||||
...allThemes,
|
...allThemes,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
plugin(({ addVariant }) => {
|
||||||
|
addVariant("dir-neutral", "[dir] &");
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ import path from "path";
|
|||||||
import { handlebars } from "./plugins/handlebars";
|
import { handlebars } from "./plugins/handlebars";
|
||||||
import { loadEnv } from "vite";
|
import { loadEnv } from "vite";
|
||||||
|
|
||||||
|
import tailwind from "tailwindcss";
|
||||||
|
import rtl from "postcss-rtlcss";
|
||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const env = loadEnv(mode, process.cwd());
|
const env = loadEnv(mode, process.cwd());
|
||||||
return {
|
return {
|
||||||
@ -18,8 +21,8 @@ export default defineConfig(({ mode }) => {
|
|||||||
env.VITE_APP_DOMAIN +
|
env.VITE_APP_DOMAIN +
|
||||||
(env.VITE_NORMAL_ROUTER !== "true" ? "/#" : ""),
|
(env.VITE_NORMAL_ROUTER !== "true" ? "/#" : ""),
|
||||||
domain: env.VITE_APP_DOMAIN,
|
domain: env.VITE_APP_DOMAIN,
|
||||||
env
|
env,
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
react({
|
react({
|
||||||
babel: {
|
babel: {
|
||||||
@ -31,24 +34,24 @@ export default defineConfig(({ mode }) => {
|
|||||||
modules: false,
|
modules: false,
|
||||||
useBuiltIns: "entry",
|
useBuiltIns: "entry",
|
||||||
corejs: {
|
corejs: {
|
||||||
version: "3.29"
|
version: "3.29",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
disable: env.VITE_PWA_ENABLED !== "true",
|
disable: env.VITE_PWA_ENABLED !== "true",
|
||||||
registerType: "autoUpdate",
|
registerType: "autoUpdate",
|
||||||
workbox: {
|
workbox: {
|
||||||
maximumFileSizeToCacheInBytes: 4000000, // 4mb
|
maximumFileSizeToCacheInBytes: 4000000, // 4mb
|
||||||
globIgnores: ["**ping.txt**"]
|
globIgnores: ["**ping.txt**"],
|
||||||
},
|
},
|
||||||
includeAssets: [
|
includeAssets: [
|
||||||
"favicon.ico",
|
"favicon.ico",
|
||||||
"apple-touch-icon.png",
|
"apple-touch-icon.png",
|
||||||
"safari-pinned-tab.svg"
|
"safari-pinned-tab.svg",
|
||||||
],
|
],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: "movie-web",
|
name: "movie-web",
|
||||||
@ -63,48 +66,53 @@ export default defineConfig(({ mode }) => {
|
|||||||
src: "android-chrome-192x192.png",
|
src: "android-chrome-192x192.png",
|
||||||
sizes: "192x192",
|
sizes: "192x192",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "any"
|
purpose: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "android-chrome-512x512.png",
|
src: "android-chrome-512x512.png",
|
||||||
sizes: "512x512",
|
sizes: "512x512",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "any"
|
purpose: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "android-chrome-192x192.png",
|
src: "android-chrome-192x192.png",
|
||||||
sizes: "192x192",
|
sizes: "192x192",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "maskable"
|
purpose: "maskable",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "android-chrome-512x512.png",
|
src: "android-chrome-512x512.png",
|
||||||
sizes: "512x512",
|
sizes: "512x512",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "maskable"
|
purpose: "maskable",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
loadVersion(),
|
loadVersion(),
|
||||||
checker({
|
checker({
|
||||||
overlay: {
|
overlay: {
|
||||||
position: "tr"
|
position: "tr",
|
||||||
},
|
},
|
||||||
typescript: true, // check typescript build errors in dev server
|
typescript: true, // check typescript build errors in dev server
|
||||||
eslint: {
|
eslint: {
|
||||||
// check lint errors in dev server
|
// check lint errors in dev server
|
||||||
lintCommand: "eslint --ext .tsx,.ts src",
|
lintCommand: "eslint --ext .tsx,.ts src",
|
||||||
dev: {
|
dev: {
|
||||||
logLevel: ["error"]
|
logLevel: ["error"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [tailwind(), rtl()],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@ -112,12 +120,12 @@ export default defineConfig(({ mode }) => {
|
|||||||
"@sozialhelden/ietf-language-tags": path.resolve(
|
"@sozialhelden/ietf-language-tags": path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
"./node_modules/@sozialhelden/ietf-language-tags/dist/cjs"
|
"./node_modules/@sozialhelden/ietf-language-tags/dist/cjs"
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
test: {
|
test: {
|
||||||
environment: "jsdom"
|
environment: "jsdom",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user