Compare commits

...

10 Commits

Author SHA1 Message Date
zisra fa4ef77a9e
Merge a9f6b518aa into f3dd80f42b 2024-04-23 15:08:29 +03:00
Jorrin f3dd80f42b
Merge pull request #1150 from movie-web/fix/#962
decrease amount of margin of edit button on small screens
2024-04-22 22:45:07 +02:00
William Oldham cfc74dfa78
Merge branch 'dev' into fix/#962 2024-04-22 21:15:23 +01:00
William Oldham 1a3144a872
Merge pull request #1153 from movie-web/feature/#754
Improve how to change seasons
2024-04-22 21:14:09 +01:00
Jorrin ae81832037 improve how to change seasons 2024-04-22 20:26:54 +02:00
Jorrin 3da8955607 decrease amount of margin on small screens 2024-04-22 19:12:18 +02:00
zisra a9f6b518aa
Merge branch 'movie-web:dev' into fix/1078 2024-04-05 13:46:26 -05:00
Isra 25452fa01a Fixes 2024-04-04 19:52:49 -05:00
Isra 7de093ef20 Large text 2024-04-04 19:45:22 -05:00
Isra 6fe063db4d open in app button 2024-04-04 19:25:15 -05:00
7 changed files with 89 additions and 11 deletions

View File

@ -156,7 +156,8 @@
},
"navigation": {
"banner": {
"offline": "Check your internet connection"
"offline": "Check your internet connection",
"mobile": "Open in app"
},
"menu": {
"about": "About us",
@ -278,7 +279,8 @@
"loadingError": "Error loading season",
"loadingList": "Loading...",
"loadingTitle": "Loading...",
"unairedEpisodes": "One or more episodes in this season have been disabled because they haven't been aired yet."
"unairedEpisodes": "One or more episodes in this season have been disabled because they haven't been aired yet.",
"seasons": "Seasons"
},
"playback": {
"speedLabel": "Playback speed",

View File

@ -65,6 +65,7 @@ export enum Icons {
CIRCLE_QUESTION = "circle_question",
BRUSH = "brush",
UPLOAD = "upload",
OPEN_IN_APP = "open_in_app",
}
export interface IconProps {
@ -136,6 +137,7 @@ const iconList: Record<Icons, string> = {
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>`,
brush: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M162.4 6c-1.5-3.6-5-6-8.9-6h-19c-3.9 0-7.5 2.4-8.9 6L104.9 57.7c-3.2 8-14.6 8-17.8 0L66.4 6c-1.5-3.6-5-6-8.9-6H48C21.5 0 0 21.5 0 48V224v22.4V256H9.6 374.4 384v-9.6V224 48c0-26.5-21.5-48-48-48H230.5c-3.9 0-7.5 2.4-8.9 6L200.9 57.7c-3.2 8-14.6 8-17.8 0L162.4 6zM0 288v32c0 35.3 28.7 64 64 64h64v64c0 35.3 28.7 64 64 64s64-28.7 64-64V384h64c35.3 0 64-28.7 64-64V288H0zM192 432a16 16 0 1 1 0 32 16 16 0 1 1 0-32z" fill="currentColor"/></svg>`,
upload: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path opacity="1" fill="currentColor" d="M320 480H64c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32H192V144c0 26.5 21.5 48 48 48H352V448c0 17.7-14.3 32-32 32zM240 160c-8.8 0-16-7.2-16-16V32.5c2.8 .7 5.4 2.1 7.4 4.2L347.3 152.6c2.1 2.1 3.5 4.6 4.2 7.4H240zM64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V163.9c0-12.7-5.1-24.9-14.1-33.9L254.1 14.1c-9-9-21.2-14.1-33.9-14.1H64zM208 278.6l52.7 52.7c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6l-80-80c-6.2-6.2-16.4-6.2-22.6 0l-80 80c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L176 278.6V400c0 8.8 7.2 16 16 16s16-7.2 16-16V278.6z"/></svg>`,
open_in_app: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc. --><path fill="currentColor" d="M304 41c0 10.9 4.3 21.3 12 29L362.1 116 207 271c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l155-155L442.1 196c7.7 7.7 18.1 12 29 12c22.6 0 41-18.3 41-41V40c0-22.1-17.9-40-40-40H345c-22.6 0-41 18.3-41 41zm57.9 7H464V150.1L361.9 48zM72 32C32.2 32 0 64.2 0 104V440c0 39.8 32.2 72 72 72H408c39.8 0 72-32.2 72-72V312c0-13.3-10.7-24-24-24s-24 10.7-24 24V440c0 13.3-10.7 24-24 24H72c-13.3 0-24-10.7-24-24V104c0-13.3 10.7-24 24-24H200c13.3 0 24-10.7 24-24s-10.7-24-24-24H72z"/></svg>`,
};
function ChromeCastButton() {

View File

@ -25,7 +25,7 @@ export function EditButton(props: EditButtonProps) {
>
<span ref={parent}>
{props.editing ? (
<span className="mx-4 whitespace-nowrap">
<span className="mx-2 sm:mx-4 whitespace-nowrap">
{t("home.mediaList.stopEditing")}
</span>
) : (

View File

@ -212,9 +212,16 @@ function EpisodesView({
return (
<Menu.CardWithScrollable>
<Menu.BackLink onClick={goBack}>
{loadingState?.value?.season.title ||
t("player.menus.episodes.loadingTitle")}
<Menu.BackLink
onClick={goBack}
rightSide={
<span>
{loadingState?.value?.season.title ||
t("player.menus.episodes.loadingTitle")}
</span>
}
>
{t("player.menus.episodes.seasons")}
</Menu.BackLink>
{content}
</Menu.CardWithScrollable>

View File

@ -24,7 +24,14 @@ export function useIsMobile(horizontal?: boolean) {
};
}, [horizontal]);
const userAgent = window.navigator.userAgent;
const hasMobileUserAgent =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
userAgent,
);
return {
isMobile,
hasMobileUserAgent,
};
}

View File

@ -1,21 +1,27 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Icon, Icons } from "@/components/Icon";
import { useIsMobile } from "@/hooks/useIsMobile";
import { useBannerStore, useRegisterBanner } from "@/stores/banner";
import { getMobileAppLink } from "@/utils/mobileAppLink";
import { usePlayerStore } from "../player/store";
export function Banner(props: {
children: React.ReactNode;
type: "error";
type: "error" | "open_app";
id: string;
}) {
const [ref] = useRegisterBanner<HTMLDivElement>(props.id);
const hideBanner = useBannerStore((s) => s.hideBanner);
const styles = {
error: "bg-[#C93957] text-white",
open_app: "bg-[#4169E1] text-white", // blue
};
const icons = {
error: Icons.CIRCLE_EXCLAMATION,
open_app: Icons.OPEN_IN_APP,
};
return (
@ -23,15 +29,15 @@ export function Banner(props: {
<div
className={[
styles[props.type],
"flex items-center justify-center p-1",
"flex items-center justify-center p-4",
].join(" ")}
>
<div className="flex items-center space-x-3">
<div className="flex items-center space-x-3 text-lg">
<Icon icon={icons[props.type]} />
<div>{props.children}</div>
</div>
<span
className="absolute right-4 hover:cursor-pointer"
className="absolute right-4 hover:cursor-pointer cursor-pointer"
onClick={() => hideBanner(props.id, true)}
>
<Icon icon={Icons.X} />
@ -41,12 +47,39 @@ export function Banner(props: {
);
}
export function LinkedBanner(props: {
href: string;
type: "error" | "open_app";
id: string;
children: React.ReactNode;
}) {
return (
<a href={props.href} rel="noreferrer">
<Banner type={props.type} id={props.id}>
{props.children}
</Banner>
</a>
);
}
export function BannerLocation(props: { location?: string }) {
const { t } = useTranslation();
const isOnline = useBannerStore((s) => s.isOnline);
const setLocation = useBannerStore((s) => s.setLocation);
const ignoredBannerIds = useBannerStore((s) => s.ignoredBannerIds);
const currentLocation = useBannerStore((s) => s.location);
const { hasMobileUserAgent } = useIsMobile();
const meta = usePlayerStore((s) => s.meta);
const [appLink, setAppLink] = useState("");
useEffect(() => {
if (meta) {
setAppLink(getMobileAppLink(meta));
} else {
setAppLink("");
}
}, [meta]);
const loc = props.location ?? null;
useEffect(() => {
@ -66,6 +99,15 @@ export function BannerLocation(props: { location?: string }) {
{t("navigation.banner.offline")}
</Banner>
) : null}
{hasMobileUserAgent && !ignoredBannerIds.includes("open_app") ? (
<LinkedBanner
id="open_app"
type="open_app"
href={meta ? appLink : "movieweb://"}
>
{t("navigation.banner.mobile")}
</LinkedBanner>
) : null}
</div>
);
}

View File

@ -0,0 +1,18 @@
import { PlayerMeta } from "@/stores/player/slices/source";
export function getMobileAppLink(meta: PlayerMeta) {
const url = new URL("movieweb://videoPlayer");
url.searchParams.set("type", meta.type === "movie" ? "movie" : "tv");
url.searchParams.set("id", meta.tmdbId);
const isShow = meta.type === "show";
if (isShow && meta.season?.number) {
url.searchParams.set("season", meta.season?.number.toString());
}
if (isShow && meta.episode?.number) {
url.searchParams.set("episode", meta.episode.number.toString());
}
return url.toString();
}