mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-24 06:40:27 +01:00
Attempt 2 at scaling
Added some more logic for scaling to hopefully have a uniform state Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
This commit is contained in:
@@ -20,7 +20,10 @@ import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Animated,
|
||||
Dimensions,
|
||||
Easing,
|
||||
PixelRatio,
|
||||
Platform,
|
||||
ScrollView,
|
||||
View,
|
||||
} from "react-native";
|
||||
@@ -40,11 +43,12 @@ import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { getBackdropUrl } from "@/utils/jellyfin/image/getBackdropUrl";
|
||||
import { scaleSize } from "@/utils/scaleSize";
|
||||
|
||||
const HORIZONTAL_PADDING = 60;
|
||||
const TOP_PADDING = 100;
|
||||
const HORIZONTAL_PADDING = scaleSize(60);
|
||||
const TOP_PADDING = scaleSize(100);
|
||||
// Generous gap between sections for Apple TV+ aesthetic
|
||||
const SECTION_GAP = 24;
|
||||
const SECTION_GAP = scaleSize(10);
|
||||
|
||||
type InfiniteScrollingCollectionListSection = {
|
||||
type: "InfiniteScrollingCollectionList";
|
||||
@@ -79,6 +83,22 @@ export const Home = () => {
|
||||
const _invalidateCache = useInvalidatePlaybackProgressCache();
|
||||
const { showItemActions } = useTVItemActionModal();
|
||||
|
||||
// Log TV viewport dimensions for DPI scaling debug
|
||||
useEffect(() => {
|
||||
const w = Dimensions.get("window");
|
||||
const s = Dimensions.get("screen");
|
||||
console.log("========== TV DIMENSIONS ==========");
|
||||
console.log("Platform.OS:", Platform.OS, "isTV:", Platform.isTV);
|
||||
console.log("Window:", w.width, "x", w.height);
|
||||
console.log("Screen:", s.width, "x", s.height);
|
||||
console.log("PixelRatio:", PixelRatio.get());
|
||||
console.log(
|
||||
"scaleSize(210):",
|
||||
210 * Math.min(w.width / 1920, w.height / 1080),
|
||||
);
|
||||
console.log("====================================");
|
||||
}, []);
|
||||
|
||||
// Dynamic backdrop state with debounce
|
||||
const [focusedItem, setFocusedItem] = useState<BaseItemDto | null>(null);
|
||||
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
@@ -24,9 +24,10 @@ import { useScaledTVTypography } from "@/constants/TVTypography";
|
||||
import useRouter from "@/hooks/useAppRouter";
|
||||
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
|
||||
import { SortByOption, SortOrderOption } from "@/utils/atoms/filters";
|
||||
import { scaleSize } from "@/utils/scaleSize";
|
||||
|
||||
// Extra padding to accommodate scale animation (1.05x) and glow shadow
|
||||
const SCALE_PADDING = 20;
|
||||
const SCALE_PADDING = scaleSize(20);
|
||||
|
||||
interface Props extends ViewProps {
|
||||
title?: string | null;
|
||||
@@ -81,7 +82,7 @@ const TVSeeAllCard: React.FC<{
|
||||
style={{
|
||||
width,
|
||||
aspectRatio,
|
||||
borderRadius: 24,
|
||||
borderRadius: scaleSize(24),
|
||||
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
@@ -91,9 +92,9 @@ const TVSeeAllCard: React.FC<{
|
||||
>
|
||||
<Ionicons
|
||||
name='arrow-forward'
|
||||
size={32}
|
||||
size={scaleSize(32)}
|
||||
color='white'
|
||||
style={{ marginBottom: 8 }}
|
||||
style={{ marginBottom: scaleSize(8) }}
|
||||
/>
|
||||
<Text
|
||||
style={{
|
||||
@@ -250,7 +251,7 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
|
||||
fontSize: typography.heading,
|
||||
fontWeight: "700",
|
||||
color: "#FFFFFF",
|
||||
marginBottom: 20,
|
||||
marginBottom: scaleSize(20),
|
||||
marginLeft: sizes.padding.horizontal,
|
||||
letterSpacing: 0.5,
|
||||
}}
|
||||
@@ -286,8 +287,8 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
|
||||
backgroundColor: "#262626",
|
||||
width: itemWidth,
|
||||
aspectRatio: orientation === "horizontal" ? 16 / 9 : 10 / 15,
|
||||
borderRadius: 12,
|
||||
marginBottom: 8,
|
||||
borderRadius: scaleSize(12),
|
||||
marginBottom: scaleSize(8),
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
@@ -328,6 +329,9 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
|
||||
windowSize={5}
|
||||
removeClippedSubviews={false}
|
||||
maintainVisibleContentPosition={{ minIndexForVisible: 0 }}
|
||||
ListHeaderComponent={
|
||||
<View style={{ width: sizes.padding.horizontal }} />
|
||||
}
|
||||
style={{ overflow: "visible" }}
|
||||
contentInset={{
|
||||
left: sizes.padding.horizontal,
|
||||
@@ -342,6 +346,7 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: sizes.padding.horizontal,
|
||||
}}
|
||||
>
|
||||
{isFetchingNextPage && (
|
||||
@@ -350,7 +355,10 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
|
||||
marginLeft: itemWidth / 2,
|
||||
marginRight: ITEM_GAP,
|
||||
justifyContent: "center",
|
||||
height: orientation === "horizontal" ? 191 : 315,
|
||||
height:
|
||||
orientation === "horizontal"
|
||||
? scaleSize(191)
|
||||
: scaleSize(315),
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator size='small' color='white' />
|
||||
|
||||
@@ -19,10 +19,11 @@ import useRouter from "@/hooks/useAppRouter";
|
||||
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { scaleSize } from "@/utils/scaleSize";
|
||||
import { createStreamystatsApi } from "@/utils/streamystats/api";
|
||||
import type { StreamystatsWatchlist } from "@/utils/streamystats/types";
|
||||
|
||||
const SCALE_PADDING = 20;
|
||||
const SCALE_PADDING = scaleSize(20);
|
||||
|
||||
interface WatchlistSectionProps extends ViewProps {
|
||||
watchlist: StreamystatsWatchlist;
|
||||
@@ -168,8 +169,8 @@ const WatchlistSection: React.FC<WatchlistSectionProps> = ({
|
||||
backgroundColor: "#262626",
|
||||
width: posterSizes.poster,
|
||||
aspectRatio: 10 / 15,
|
||||
borderRadius: 12,
|
||||
marginBottom: 8,
|
||||
borderRadius: scaleSize(12),
|
||||
marginBottom: scaleSize(8),
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -286,12 +287,12 @@ export const StreamystatsPromotedWatchlists: React.FC<
|
||||
<View style={{ overflow: "visible" }} {...props}>
|
||||
<View
|
||||
style={{
|
||||
height: 16,
|
||||
width: 128,
|
||||
height: scaleSize(16),
|
||||
width: scaleSize(128),
|
||||
backgroundColor: "#262626",
|
||||
borderRadius: 4,
|
||||
borderRadius: scaleSize(4),
|
||||
marginLeft: SCALE_PADDING,
|
||||
marginBottom: 16,
|
||||
marginBottom: scaleSize(16),
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
@@ -309,8 +310,8 @@ export const StreamystatsPromotedWatchlists: React.FC<
|
||||
backgroundColor: "#262626",
|
||||
width: posterSizes.poster,
|
||||
aspectRatio: 10 / 15,
|
||||
borderRadius: 12,
|
||||
marginBottom: 8,
|
||||
borderRadius: scaleSize(12),
|
||||
marginBottom: scaleSize(8),
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -18,6 +18,7 @@ import useRouter from "@/hooks/useAppRouter";
|
||||
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
|
||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||
import { useSettings } from "@/utils/atoms/settings";
|
||||
import { scaleSize } from "@/utils/scaleSize";
|
||||
import { createStreamystatsApi } from "@/utils/streamystats/api";
|
||||
import type { StreamystatsRecommendationsIdsResponse } from "@/utils/streamystats/types";
|
||||
|
||||
@@ -220,8 +221,8 @@ export const StreamystatsRecommendations: React.FC<Props> = ({
|
||||
backgroundColor: "#262626",
|
||||
width: sizes.posters.poster,
|
||||
aspectRatio: 10 / 15,
|
||||
borderRadius: 12,
|
||||
marginBottom: 8,
|
||||
borderRadius: scaleSize(12),
|
||||
marginBottom: scaleSize(8),
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user