mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-30 16:17:41 +01:00
commit
9b4da96fd4
7 changed files with 63 additions and 26 deletions
|
@ -83,6 +83,7 @@
|
||||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||||
"@typescript-eslint/parser": "^5.13.0",
|
"@typescript-eslint/parser": "^5.13.0",
|
||||||
"@vitejs/plugin-react": "^3.1.0",
|
"@vitejs/plugin-react": "^3.1.0",
|
||||||
|
"type-fest": "^4.3.3",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.10.0",
|
"eslint": "^8.10.0",
|
||||||
|
@ -103,7 +104,6 @@
|
||||||
"tailwind-scrollbar": "^2.0.1",
|
"tailwind-scrollbar": "^2.0.1",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
"tailwindcss-themer": "^3.1.0",
|
"tailwindcss-themer": "^3.1.0",
|
||||||
"type-fest": "^4.3.3",
|
|
||||||
"typescript": "^4.6.4",
|
"typescript": "^4.6.4",
|
||||||
"vite": "^4.4.12",
|
"vite": "^4.4.12",
|
||||||
"vite-plugin-checker": "^0.5.6",
|
"vite-plugin-checker": "^0.5.6",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"deviceNameLabel": "Device name",
|
"deviceNameLabel": "Device name",
|
||||||
"deviceNamePlaceholder": "Personal phone",
|
"deviceNamePlaceholder": "Personal phone",
|
||||||
"hasAccount": "Already have an account? <0>Login here.</0>",
|
"hasAccount": "Already have an account? <0>Login here.</0>",
|
||||||
"createAccount": "Dont have an account yet? <0>Create an account.</0>",
|
"createAccount": "Don't have an account yet? <0>Create an account.</0>",
|
||||||
"register": {
|
"register": {
|
||||||
"information": {
|
"information": {
|
||||||
"title": "Account information",
|
"title": "Account information",
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
"generate": {
|
"generate": {
|
||||||
"title": "Your passphrase",
|
"title": "Your passphrase",
|
||||||
"next": "I have saved my passphrase",
|
"next": "I have saved my passphrase",
|
||||||
"description": "Your passphase acts as your username and password. Make sure to keep it safe as you will need to enter it to login to your account"
|
"description": "Your passphrase acts as your username and password. Make sure to keep it safe as you will need to enter it to login to your account"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"title": "Do you trust this server?",
|
"title": "Do you trust this server?",
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
"items": {
|
"items": {
|
||||||
"pending": "Checking for videos...",
|
"pending": "Checking for videos...",
|
||||||
"notFound": "Doesn't have the video",
|
"notFound": "Doesn't have the video",
|
||||||
"failure": "Error occured"
|
"failure": "Error occurred"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playbackError": {
|
"playbackError": {
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
"errorNetwork": "Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.",
|
"errorNetwork": "Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.",
|
||||||
"errorDecode": "Despite having previously been determined to be usable, an error occurred while trying to decode the media resource, resulting in an error.",
|
"errorDecode": "Despite having previously been determined to be usable, an error occurred while trying to decode the media resource, resulting in an error.",
|
||||||
"errorNotSupported": "The media or media provider object is not supported.",
|
"errorNotSupported": "The media or media provider object is not supported.",
|
||||||
"errorGenericMedia": "Unknown media error occured."
|
"errorGenericMedia": "Unknown media error occurred."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
@ -230,7 +230,7 @@
|
||||||
},
|
},
|
||||||
"night": {
|
"night": {
|
||||||
"default": "What would you like to watch tonight?",
|
"default": "What would you like to watch tonight?",
|
||||||
"extra": ["Tired? I hear The Excorcist is good."]
|
"extra": ["Tired? I hear The Exorcist is good."]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
|
@ -276,6 +276,7 @@
|
||||||
"register": "Sync to cloud",
|
"register": "Sync to cloud",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"about": "About us",
|
"about": "About us",
|
||||||
|
"donation": "Donate",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
"logout": "Log out"
|
"logout": "Log out"
|
||||||
}
|
}
|
||||||
|
@ -398,7 +399,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"tagline": "Watch your favorite shows and movies with this open source streaming app.",
|
"tagline": "Watch your favourite shows and movies with this open source streaming app.",
|
||||||
"links": {
|
"links": {
|
||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
"dmca": "DMCA",
|
"dmca": "DMCA",
|
||||||
|
|
|
@ -59,6 +59,8 @@ export enum Icons {
|
||||||
MENU = "menu",
|
MENU = "menu",
|
||||||
LOCK = "lock",
|
LOCK = "lock",
|
||||||
UNLOCK = "unlock",
|
UNLOCK = "unlock",
|
||||||
|
DONATION = "donation",
|
||||||
|
CIRCLE_QUESTION = "circle_question",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IconProps {
|
export interface IconProps {
|
||||||
|
@ -125,6 +127,8 @@ const iconList: Record<Icons, string> = {
|
||||||
menu: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>`,
|
menu: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>`,
|
||||||
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-lock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
|
lock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-lock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>`,
|
||||||
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-unlock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>`,
|
unlock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-unlock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path></svg>`,
|
||||||
|
donation: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M163.9 136.9c-29.4-29.8-29.4-78.2 0-108s77-29.8 106.4 0l17.7 18 17.7-18c29.4-29.8 77-29.8 106.4 0s29.4 78.2 0 108L310.5 240.1c-6.2 6.3-14.3 9.4-22.5 9.4s-16.3-3.1-22.5-9.4L163.9 136.9zM568.2 336.3c13.1 17.8 9.3 42.8-8.5 55.9L433.1 485.5c-23.4 17.2-51.6 26.5-80.7 26.5H192 32c-17.7 0-32-14.3-32-32V416c0-17.7 14.3-32 32-32H68.8l44.9-36c22.7-18.2 50.9-28 80-28H272h16 64c17.7 0 32 14.3 32 32s-14.3 32-32 32H288 272c-8.8 0-16 7.2-16 16s7.2 16 16 16H392.6l119.7-88.2c17.8-13.1 42.8-9.3 55.9 8.5zM193.6 384l0 0-.9 0c.3 0 .6 0 .9 0z"/></svg>`,
|
||||||
|
circle_question: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChromeCastButton() {
|
function ChromeCastButton() {
|
||||||
|
|
|
@ -139,9 +139,12 @@ export function LinksDropdown(props: { children: React.ReactNode }) {
|
||||||
<DropdownLink href="/settings" icon={Icons.SETTINGS}>
|
<DropdownLink href="/settings" icon={Icons.SETTINGS}>
|
||||||
{t("navigation.menu.settings")}
|
{t("navigation.menu.settings")}
|
||||||
</DropdownLink>
|
</DropdownLink>
|
||||||
<DropdownLink href="/about" icon={Icons.EPISODES}>
|
<DropdownLink href="/about" icon={Icons.CIRCLE_QUESTION}>
|
||||||
{t("navigation.menu.about")}
|
{t("navigation.menu.about")}
|
||||||
</DropdownLink>
|
</DropdownLink>
|
||||||
|
<DropdownLink href={conf().DONATION_LINK} icon={Icons.DONATION}>
|
||||||
|
{t("navigation.menu.donation")}
|
||||||
|
</DropdownLink>
|
||||||
{deviceName ? (
|
{deviceName ? (
|
||||||
<DropdownLink
|
<DropdownLink
|
||||||
className="!text-type-danger opacity-75 hover:opacity-100"
|
className="!text-type-danger opacity-75 hover:opacity-100"
|
||||||
|
@ -160,7 +163,7 @@ export function LinksDropdown(props: { children: React.ReactNode }) {
|
||||||
<CircleDropdownLink href={conf().GITHUB_LINK} icon={Icons.GITHUB} />
|
<CircleDropdownLink href={conf().GITHUB_LINK} icon={Icons.GITHUB} />
|
||||||
<CircleDropdownLink
|
<CircleDropdownLink
|
||||||
href={conf().DONATION_LINK}
|
href={conf().DONATION_LINK}
|
||||||
icon={Icons.COINS}
|
icon={Icons.DONATION}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { useCallback } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
import type { RequireExactlyOne } from "type-fest";
|
||||||
|
|
||||||
import { Icon, Icons } from "@/components/Icon";
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
import { BrandPill } from "@/components/layout/BrandPill";
|
import { BrandPill } from "@/components/layout/BrandPill";
|
||||||
|
@ -7,19 +9,33 @@ import { WideContainer } from "@/components/layout/WideContainer";
|
||||||
import { shouldHaveDmcaPage } from "@/pages/Dmca";
|
import { shouldHaveDmcaPage } from "@/pages/Dmca";
|
||||||
import { conf } from "@/setup/config";
|
import { conf } from "@/setup/config";
|
||||||
|
|
||||||
function FooterLink(props: {
|
// to and href are mutually exclusive
|
||||||
href?: string;
|
type FooterLinkProps = RequireExactlyOne<
|
||||||
onClick?: () => void;
|
{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
icon: Icons;
|
icon: Icons;
|
||||||
}) {
|
to: string;
|
||||||
|
href: string;
|
||||||
|
},
|
||||||
|
"to" | "href"
|
||||||
|
>;
|
||||||
|
|
||||||
|
function FooterLink(props: FooterLinkProps) {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const navigateTo = useCallback(() => {
|
||||||
|
if (!props.to) return;
|
||||||
|
|
||||||
|
history.push(props.to);
|
||||||
|
}, [history, props.to]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={props.href ?? "#"}
|
href={props.href}
|
||||||
target="_blank"
|
target={props.href ? "_blank" : undefined}
|
||||||
className="tabbable rounded py-2 px-3 inline-flex items-center space-x-3 transition-colors duration-200 hover:text-type-emphasis"
|
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
onClick={props.onClick}
|
className="tabbable rounded py-2 px-3 inline-flex cursor-pointer items-center space-x-3 transition-colors duration-200 hover:text-type-emphasis"
|
||||||
|
onClick={props.to ? navigateTo : undefined}
|
||||||
>
|
>
|
||||||
<Icon icon={props.icon} className="text-2xl" />
|
<Icon icon={props.icon} className="text-2xl" />
|
||||||
<span className="font-medium">{props.children}</span>
|
<span className="font-medium">{props.children}</span>
|
||||||
|
@ -29,12 +45,11 @@ function FooterLink(props: {
|
||||||
|
|
||||||
function Dmca() {
|
function Dmca() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
if (!shouldHaveDmcaPage()) return null;
|
if (!shouldHaveDmcaPage()) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FooterLink icon={Icons.DRAGON} onClick={() => history.push("/dmca")}>
|
<FooterLink to="/dmca" icon={Icons.DRAGON}>
|
||||||
{t("footer.links.dmca")}
|
{t("footer.links.dmca")}
|
||||||
</FooterLink>
|
</FooterLink>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { useCallback, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Sticky from "react-sticky-el";
|
import Sticky from "react-sticky-el";
|
||||||
|
import { useWindowSize } from "react-use";
|
||||||
|
|
||||||
import { SearchBarInput } from "@/components/form/SearchBar";
|
import { SearchBarInput } from "@/components/form/SearchBar";
|
||||||
import { ThinContainer } from "@/components/layout/ThinContainer";
|
import { ThinContainer } from "@/components/layout/ThinContainer";
|
||||||
|
@ -29,6 +30,20 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
|
||||||
[setShowBg, setIsSticky]
|
[setShowBg, setIsSticky]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { width: windowWidth } = useWindowSize();
|
||||||
|
|
||||||
|
const topSpacing = 16;
|
||||||
|
const [stickyOffset, setStickyOffset] = useState(topSpacing);
|
||||||
|
useEffect(() => {
|
||||||
|
if (windowWidth > 1200) {
|
||||||
|
// On large screens the bar goes inline with the nav elements
|
||||||
|
setStickyOffset(topSpacing);
|
||||||
|
} else {
|
||||||
|
// On smaller screens the bar goes below the nav elements
|
||||||
|
setStickyOffset(topSpacing + 60);
|
||||||
|
}
|
||||||
|
}, [windowWidth]);
|
||||||
|
|
||||||
let time = "night";
|
let time = "night";
|
||||||
const hour = new Date().getHours();
|
const hour = new Date().getHours();
|
||||||
if (hour < 12) time = "morning";
|
if (hour < 12) time = "morning";
|
||||||
|
@ -47,9 +62,9 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
|
||||||
</div>
|
</div>
|
||||||
<div className="relative h-20 z-30">
|
<div className="relative h-20 z-30">
|
||||||
<Sticky
|
<Sticky
|
||||||
topOffset={-16 + bannerSize}
|
topOffset={stickyOffset * -1 + bannerSize}
|
||||||
stickyStyle={{
|
stickyStyle={{
|
||||||
paddingTop: `${16 + bannerSize}px`,
|
paddingTop: `${stickyOffset + bannerSize}px`,
|
||||||
}}
|
}}
|
||||||
onFixedToggle={stickStateChanged}
|
onFixedToggle={stickStateChanged}
|
||||||
>
|
>
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default defineConfig(({ mode }) => {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
disable: process.env.VITE_PWA_ENABLED !== "yes",
|
disable: env.VITE_PWA_ENABLED !== "yes",
|
||||||
registerType: "autoUpdate",
|
registerType: "autoUpdate",
|
||||||
workbox: {
|
workbox: {
|
||||||
maximumFileSizeToCacheInBytes: 4000000, // 4mb
|
maximumFileSizeToCacheInBytes: 4000000, // 4mb
|
||||||
|
@ -57,7 +57,6 @@ export default defineConfig(({ mode }) => {
|
||||||
theme_color: "#120f1d",
|
theme_color: "#120f1d",
|
||||||
background_color: "#120f1d",
|
background_color: "#120f1d",
|
||||||
display: "standalone",
|
display: "standalone",
|
||||||
orientation: "portrait-primary",
|
|
||||||
start_url: "/",
|
start_url: "/",
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue