chore: updated usage of tv scaling, alert text fix

Sweep across a few pages to ensure they use the scaling factors now
Added a plugin to fix the alert text on android tv

Signed-off-by: Lance Chant <13349722+lancechant@users.noreply.github.com>
This commit is contained in:
Lance Chant
2026-05-22 15:00:13 +02:00
parent 5fd8e40c44
commit 03f17a758f
47 changed files with 528 additions and 389 deletions

View File

@@ -136,6 +136,7 @@
"expo-web-browser", "expo-web-browser",
["./plugins/with-runtime-framework-headers.js"], ["./plugins/with-runtime-framework-headers.js"],
["./plugins/withChangeNativeAndroidTextToWhite.js"], ["./plugins/withChangeNativeAndroidTextToWhite.js"],
["./plugins/withAndroidAlertColors.js"],
["./plugins/withAndroidManifest.js"], ["./plugins/withAndroidManifest.js"],
["./plugins/withTrustLocalCerts.js"], ["./plugins/withTrustLocalCerts.js"],
["./plugins/withGradleProperties.js"], ["./plugins/withGradleProperties.js"],

View File

@@ -15,6 +15,7 @@ import {
View, View,
} from "react-native"; } from "react-native";
import { useHaptic } from "@/hooks/useHaptic"; import { useHaptic } from "@/hooks/useHaptic";
import { scaleSize } from "@/utils/scaleSize";
import { Loader } from "./Loader"; import { Loader } from "./Loader";
const getColorClasses = ( const getColorClasses = (
@@ -135,16 +136,26 @@ export const Button: React.FC<PropsWithChildren<ButtonProps>> = ({
shadowColor: "#ffffff", shadowColor: "#ffffff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.5 : 0, shadowOpacity: focused ? 0.5 : 0,
shadowRadius: focused ? 10 : 0, shadowRadius: focused ? scaleSize(10) : 0,
elevation: focused ? 12 : 0, // Android glow elevation: focused ? 12 : 0, // Android glow
}} }}
> >
<View <View
className={`rounded-2xl py-5 items-center justify-center style={{
${colorClasses} borderRadius: scaleSize(16),
${className}`} paddingVertical: scaleSize(14),
alignItems: "center",
justifyContent: "center",
}}
className={`${colorClasses} ${className}`}
> >
<Text className={`${textColorClass} text-xl font-bold`}> <Text
style={{
fontSize: scaleSize(20),
fontWeight: "bold",
}}
className={textColorClass}
>
{children} {children}
</Text> </Text>
</View> </View>

View File

@@ -3,6 +3,7 @@ import React, { useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Animated, Easing, Pressable, View } from "react-native"; import { Animated, Easing, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { scaleSize } from "@/utils/scaleSize";
import type { SavedServerAccount } from "@/utils/secureCredentials"; import type { SavedServerAccount } from "@/utils/secureCredentials";
interface TVAccountCardProps { interface TVAccountCardProps {
@@ -97,9 +98,9 @@ export const TVAccountCard: React.FC<TVAccountCardProps> = ({
backgroundColor: isFocused ? "#2a2a2a" : "#262626", backgroundColor: isFocused ? "#2a2a2a" : "#262626",
borderWidth: 2, borderWidth: 2,
borderColor: isFocused ? "#FFFFFF" : "transparent", borderColor: isFocused ? "#FFFFFF" : "transparent",
borderRadius: 16, borderRadius: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
paddingVertical: 20, paddingVertical: scaleSize(20),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
}} }}
@@ -107,23 +108,23 @@ export const TVAccountCard: React.FC<TVAccountCardProps> = ({
{/* Avatar */} {/* Avatar */}
<View <View
style={{ style={{
width: 56, width: scaleSize(56),
height: 56, height: scaleSize(56),
backgroundColor: "#404040", backgroundColor: "#404040",
borderRadius: 28, borderRadius: scaleSize(28),
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
marginRight: 20, marginRight: scaleSize(20),
}} }}
> >
<Ionicons name='person' size={28} color='white' /> <Ionicons name='person' size={scaleSize(28)} color='white' />
</View> </View>
{/* Account Info */} {/* Account Info */}
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text <Text
style={{ style={{
fontSize: 22, fontSize: scaleSize(22),
fontWeight: "600", fontWeight: "600",
color: "#FFFFFF", color: "#FFFFFF",
}} }}
@@ -132,9 +133,9 @@ export const TVAccountCard: React.FC<TVAccountCardProps> = ({
</Text> </Text>
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: "#9CA3AF", color: "#9CA3AF",
marginTop: 4, marginTop: scaleSize(4),
}} }}
> >
{getSecurityText()} {getSecurityText()}
@@ -142,7 +143,11 @@ export const TVAccountCard: React.FC<TVAccountCardProps> = ({
</View> </View>
{/* Security Icon */} {/* Security Icon */}
<Ionicons name={getSecurityIcon()} size={24} color='#fff' /> <Ionicons
name={getSecurityIcon()}
size={scaleSize(24)}
color='#fff'
/>
</View> </View>
</Animated.View> </Animated.View>
</Pressable> </Pressable>

View File

@@ -4,6 +4,7 @@ import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
export interface TVAddIconProps { export interface TVAddIconProps {
label: string; label: string;
@@ -33,24 +34,24 @@ export const TVAddIcon = React.forwardRef<View, TVAddIconProps>(
animatedStyle, animatedStyle,
{ {
alignItems: "center", alignItems: "center",
width: 160, width: scaleSize(160),
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.5 : 0, shadowOpacity: focused ? 0.5 : 0,
shadowRadius: focused ? 16 : 0, shadowRadius: focused ? scaleSize(16) : 0,
}, },
]} ]}
> >
<View <View
style={{ style={{
width: 140, width: scaleSize(140),
height: 140, height: scaleSize(140),
borderRadius: 70, borderRadius: scaleSize(70),
backgroundColor: focused backgroundColor: focused
? "rgba(255,255,255,0.15)" ? "rgba(255,255,255,0.15)"
: "rgba(255,255,255,0.05)", : "rgba(255,255,255,0.05)",
marginBottom: 14, marginBottom: scaleSize(14),
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: focused ? "#fff" : "rgba(255,255,255,0.3)", borderColor: focused ? "#fff" : "rgba(255,255,255,0.3)",
borderStyle: "dashed", borderStyle: "dashed",
justifyContent: "center", justifyContent: "center",
@@ -59,7 +60,7 @@ export const TVAddIcon = React.forwardRef<View, TVAddIconProps>(
> >
<Ionicons <Ionicons
name='add' name='add'
size={56} size={scaleSize(56)}
color={focused ? "#fff" : "rgba(255,255,255,0.5)"} color={focused ? "#fff" : "rgba(255,255,255,0.5)"}
/> />
</View> </View>

View File

@@ -5,6 +5,7 @@ import { Button } from "@/components/Button";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { useTVBackPress } from "@/hooks/useTVBackPress"; import { useTVBackPress } from "@/hooks/useTVBackPress";
import { scaleSize } from "@/utils/scaleSize";
import { TVInput } from "./TVInput"; import { TVInput } from "./TVInput";
interface TVAddServerFormProps { interface TVAddServerFormProps {
@@ -48,7 +49,7 @@ export const TVAddServerForm: React.FC<TVAddServerFormProps> = ({
flexGrow: 1, flexGrow: 1,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingVertical: 60, paddingVertical: scaleSize(60),
}} }}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
@@ -56,7 +57,7 @@ export const TVAddServerForm: React.FC<TVAddServerFormProps> = ({
style={{ style={{
width: "100%", width: "100%",
maxWidth: 800, maxWidth: 800,
paddingHorizontal: 60, paddingHorizontal: scaleSize(60),
}} }}
> >
{/* Title */} {/* Title */}
@@ -66,15 +67,20 @@ export const TVAddServerForm: React.FC<TVAddServerFormProps> = ({
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
textAlign: "left", textAlign: "left",
marginBottom: 24, marginBottom: scaleSize(24),
paddingHorizontal: 8, paddingHorizontal: scaleSize(8),
}} }}
> >
{t("server.enter_url_to_jellyfin_server")} {t("server.enter_url_to_jellyfin_server")}
</Text> </Text>
{/* Server URL Input */} {/* Server URL Input */}
<View style={{ marginBottom: 24, paddingHorizontal: 8 }}> <View
style={{
marginBottom: scaleSize(24),
paddingHorizontal: scaleSize(8),
}}
>
<TVInput <TVInput
placeholder={t("server.server_url_placeholder")} placeholder={t("server.server_url_placeholder")}
value={serverURL} value={serverURL}
@@ -89,7 +95,7 @@ export const TVAddServerForm: React.FC<TVAddServerFormProps> = ({
</View> </View>
{/* Connect Button */} {/* Connect Button */}
<View style={{ marginBottom: 24 }}> <View style={{ marginBottom: scaleSize(24) }}>
<Button <Button
onPress={handleConnect} onPress={handleConnect}
loading={loading} loading={loading}

View File

@@ -5,6 +5,7 @@ import { Button } from "@/components/Button";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { useTVBackPress } from "@/hooks/useTVBackPress"; import { useTVBackPress } from "@/hooks/useTVBackPress";
import { scaleSize } from "@/utils/scaleSize";
import { TVInput } from "./TVInput"; import { TVInput } from "./TVInput";
import { TVSaveAccountToggle } from "./TVSaveAccountToggle"; import { TVSaveAccountToggle } from "./TVSaveAccountToggle";
@@ -61,7 +62,7 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
flexGrow: 1, flexGrow: 1,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingVertical: 60, paddingVertical: scaleSize(60),
}} }}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
@@ -69,7 +70,7 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
style={{ style={{
width: "100%", width: "100%",
maxWidth: 800, maxWidth: 800,
paddingHorizontal: 60, paddingHorizontal: scaleSize(60),
}} }}
> >
{/* Title */} {/* Title */}
@@ -78,7 +79,7 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
fontSize: typography.title, fontSize: typography.title,
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
marginBottom: 8, marginBottom: scaleSize(8),
}} }}
> >
{serverName ? ( {serverName ? (
@@ -94,14 +95,19 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#9CA3AF", color: "#9CA3AF",
marginBottom: 40, marginBottom: scaleSize(40),
}} }}
> >
{serverAddress} {serverAddress}
</Text> </Text>
{/* Username Input */} {/* Username Input */}
<View style={{ marginBottom: 24, paddingHorizontal: 8 }}> <View
style={{
marginBottom: scaleSize(24),
paddingHorizontal: scaleSize(8),
}}
>
<TVInput <TVInput
placeholder={t("login.username_placeholder")} placeholder={t("login.username_placeholder")}
value={credentials.username} value={credentials.username}
@@ -118,7 +124,12 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
</View> </View>
{/* Password Input */} {/* Password Input */}
<View style={{ marginBottom: 32, paddingHorizontal: 8 }}> <View
style={{
marginBottom: scaleSize(32),
paddingHorizontal: scaleSize(8),
}}
>
<TVInput <TVInput
placeholder={t("login.password_placeholder")} placeholder={t("login.password_placeholder")}
value={credentials.password} value={credentials.password}
@@ -134,7 +145,12 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
</View> </View>
{/* Save Account Toggle */} {/* Save Account Toggle */}
<View style={{ marginBottom: 40, paddingHorizontal: 8 }}> <View
style={{
marginBottom: scaleSize(40),
paddingHorizontal: scaleSize(8),
}}
>
<TVSaveAccountToggle <TVSaveAccountToggle
value={saveAccount} value={saveAccount}
onValueChange={setSaveAccount} onValueChange={setSaveAccount}
@@ -144,7 +160,7 @@ export const TVAddUserForm: React.FC<TVAddUserFormProps> = ({
</View> </View>
{/* Login Button */} {/* Login Button */}
<View style={{ marginBottom: 16 }}> <View style={{ marginBottom: scaleSize(16) }}>
<Button <Button
onPress={handleLogin} onPress={handleLogin}
loading={loading} loading={loading}

View File

@@ -4,6 +4,7 @@ import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
export interface TVBackIconProps { export interface TVBackIconProps {
label: string; label: string;
@@ -33,24 +34,24 @@ export const TVBackIcon = React.forwardRef<View, TVBackIconProps>(
animatedStyle, animatedStyle,
{ {
alignItems: "center", alignItems: "center",
width: 160, width: scaleSize(160),
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.5 : 0, shadowOpacity: focused ? 0.5 : 0,
shadowRadius: focused ? 16 : 0, shadowRadius: focused ? scaleSize(16) : 0,
}, },
]} ]}
> >
<View <View
style={{ style={{
width: 140, width: scaleSize(140),
height: 140, height: scaleSize(140),
borderRadius: 70, borderRadius: scaleSize(70),
backgroundColor: focused backgroundColor: focused
? "rgba(255,255,255,0.15)" ? "rgba(255,255,255,0.15)"
: "rgba(255,255,255,0.05)", : "rgba(255,255,255,0.05)",
marginBottom: 14, marginBottom: scaleSize(14),
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: focused ? "#fff" : "rgba(255,255,255,0.3)", borderColor: focused ? "#fff" : "rgba(255,255,255,0.3)",
borderStyle: "dashed", borderStyle: "dashed",
justifyContent: "center", justifyContent: "center",
@@ -59,7 +60,7 @@ export const TVBackIcon = React.forwardRef<View, TVBackIconProps>(
> >
<Ionicons <Ionicons
name='arrow-back' name='arrow-back'
size={56} size={scaleSize(56)}
color={focused ? "#fff" : "rgba(255,255,255,0.5)"} color={focused ? "#fff" : "rgba(255,255,255,0.5)"}
/> />
</View> </View>

View File

@@ -6,6 +6,7 @@ import {
TextInput, TextInput,
type TextInputProps, type TextInputProps,
} from "react-native"; } from "react-native";
import { scaleSize } from "@/utils/scaleSize";
interface TVInputProps extends TextInputProps { interface TVInputProps extends TextInputProps {
label?: string; label?: string;
@@ -58,7 +59,7 @@ export const TVInput: React.FC<TVInputProps> = ({
<Animated.View <Animated.View
style={{ style={{
transform: [{ scale }], transform: [{ scale }],
borderRadius: 12, borderRadius: scaleSize(12),
backgroundColor: isFocused backgroundColor: isFocused
? "rgba(255,255,255,0.15)" ? "rgba(255,255,255,0.15)"
: "rgba(255,255,255,0.08)", : "rgba(255,255,255,0.08)",
@@ -73,10 +74,10 @@ export const TVInput: React.FC<TVInputProps> = ({
allowFontScaling={false} allowFontScaling={false}
style={[ style={[
{ {
height: 64, height: scaleSize(64),
fontSize: 22, fontSize: scaleSize(22),
color: "#FFFFFF", color: "#FFFFFF",
paddingHorizontal: 20, paddingHorizontal: scaleSize(20),
}, },
style, style,
]} ]}

View File

@@ -15,6 +15,7 @@ import {
type PairingCredentials, type PairingCredentials,
startPairingListener, startPairingListener,
} from "@/utils/pairingService"; } from "@/utils/pairingService";
import { scaleSize } from "@/utils/scaleSize";
import { import {
type AccountSecurityType, type AccountSecurityType,
getPreviousServers, getPreviousServers,
@@ -700,17 +701,17 @@ export const TVLogin: React.FC = () => {
> >
<Text <Text
style={{ style={{
fontSize: 24, fontSize: scaleSize(24),
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
marginBottom: 12, marginBottom: scaleSize(12),
}} }}
> >
{t("pairing.logging_in")} {t("pairing.logging_in")}
</Text> </Text>
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: "#9CA3AF", color: "#9CA3AF",
}} }}
> >

View File

@@ -12,6 +12,7 @@ import {
View, View,
} from "react-native"; } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { scaleSize } from "@/utils/scaleSize";
import { verifyAccountPIN } from "@/utils/secureCredentials"; import { verifyAccountPIN } from "@/utils/secureCredentials";
interface TVPINEntryModalProps { interface TVPINEntryModalProps {
@@ -130,15 +131,15 @@ const ForgotPINLink: React.FC<{
<Animated.View <Animated.View
style={{ style={{
transform: [{ scale }], transform: [{ scale }],
paddingHorizontal: 16, paddingHorizontal: scaleSize(16),
paddingVertical: 10, paddingVertical: scaleSize(10),
borderRadius: 8, borderRadius: scaleSize(8),
backgroundColor: focused ? "rgba(255,255,255,0.15)" : "transparent", backgroundColor: focused ? "rgba(255,255,255,0.15)" : "transparent",
}} }}
> >
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: focused ? "#fff" : "rgba(255,255,255,0.5)", color: focused ? "#fff" : "rgba(255,255,255,0.5)",
}} }}
> >
@@ -417,36 +418,36 @@ const styles = StyleSheet.create({
maxWidth: 400, maxWidth: 400,
}, },
blurContainer: { blurContainer: {
borderRadius: 24, borderRadius: scaleSize(24),
overflow: "hidden", overflow: "hidden",
}, },
content: { content: {
padding: 40, padding: scaleSize(40),
alignItems: "center", alignItems: "center",
}, },
title: { title: {
fontSize: 28, fontSize: scaleSize(28),
fontWeight: "bold", fontWeight: "bold",
color: "#fff", color: "#fff",
marginBottom: 8, marginBottom: scaleSize(8),
textAlign: "center", textAlign: "center",
}, },
subtitle: { subtitle: {
fontSize: 18, fontSize: scaleSize(18),
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
marginBottom: 32, marginBottom: scaleSize(32),
textAlign: "center", textAlign: "center",
}, },
pinDotsContainer: { pinDotsContainer: {
flexDirection: "row", flexDirection: "row",
gap: 16, gap: scaleSize(16),
marginBottom: 32, marginBottom: scaleSize(32),
}, },
pinDot: { pinDot: {
width: 20, width: scaleSize(20),
height: 20, height: scaleSize(20),
borderRadius: 10, borderRadius: scaleSize(10),
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: "rgba(255,255,255,0.4)", borderColor: "rgba(255,255,255,0.4)",
backgroundColor: "transparent", backgroundColor: "transparent",
}, },
@@ -459,26 +460,26 @@ const styles = StyleSheet.create({
backgroundColor: "#ef4444", backgroundColor: "#ef4444",
}, },
numberPad: { numberPad: {
gap: 12, gap: scaleSize(12),
marginBottom: 24, marginBottom: scaleSize(24),
}, },
numberRow: { numberRow: {
flexDirection: "row", flexDirection: "row",
gap: 12, gap: scaleSize(12),
}, },
numberButton: { numberButton: {
width: 72, width: scaleSize(72),
height: 72, height: scaleSize(72),
borderRadius: 36, borderRadius: scaleSize(36),
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
}, },
numberButtonPlaceholder: { numberButtonPlaceholder: {
width: 72, width: scaleSize(72),
height: 72, height: scaleSize(72),
}, },
numberText: { numberText: {
fontSize: 28, fontSize: scaleSize(28),
fontWeight: "600", fontWeight: "600",
}, },
forgotContainer: { forgotContainer: {

View File

@@ -14,6 +14,7 @@ import {
} from "react-native"; } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv"; import { useTVFocusAnimation } from "@/components/tv";
import { scaleSize } from "@/utils/scaleSize";
interface TVPasswordEntryModalProps { interface TVPasswordEntryModalProps {
visible: boolean; visible: boolean;
@@ -51,14 +52,14 @@ const TVSubmitButton: React.FC<{
: isDisabled : isDisabled
? "#4a4a4a" ? "#4a4a4a"
: "rgba(255,255,255,0.15)", : "rgba(255,255,255,0.15)",
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
paddingVertical: 14, paddingVertical: scaleSize(14),
borderRadius: 10, borderRadius: scaleSize(10),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
gap: 8, gap: scaleSize(8),
minWidth: 120, minWidth: scaleSize(120),
opacity: isDisabled ? 0.5 : 1, opacity: isDisabled ? 0.5 : 1,
}, },
]} ]}
@@ -69,12 +70,12 @@ const TVSubmitButton: React.FC<{
<> <>
<Ionicons <Ionicons
name='log-in-outline' name='log-in-outline'
size={20} size={scaleSize(20)}
color={focused ? "#000" : "#fff"} color={focused ? "#000" : "#fff"}
/> />
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: focused ? "#000" : "#fff", color: focused ? "#000" : "#fff",
fontWeight: "600", fontWeight: "600",
}} }}
@@ -121,11 +122,11 @@ const TVPasswordInput: React.FC<{
animatedStyle, animatedStyle,
{ {
backgroundColor: "#1F2937", backgroundColor: "#1F2937",
borderRadius: 12, borderRadius: scaleSize(12),
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: focused ? "#fff" : "#374151", borderColor: focused ? "#fff" : "#374151",
paddingHorizontal: 16, paddingHorizontal: scaleSize(16),
paddingVertical: 14, paddingVertical: scaleSize(14),
}, },
]} ]}
> >
@@ -140,7 +141,7 @@ const TVPasswordInput: React.FC<{
autoCorrect={false} autoCorrect={false}
style={{ style={{
color: "#fff", color: "#fff",
fontSize: 18, fontSize: scaleSize(18),
}} }}
onSubmitEditing={onSubmitEditing} onSubmitEditing={onSubmitEditing}
returnKeyType='done' returnKeyType='done'
@@ -299,45 +300,45 @@ const styles = StyleSheet.create({
width: "100%", width: "100%",
}, },
blurContainer: { blurContainer: {
borderTopLeftRadius: 24, borderTopLeftRadius: scaleSize(24),
borderTopRightRadius: 24, borderTopRightRadius: scaleSize(24),
overflow: "hidden", overflow: "hidden",
}, },
content: { content: {
paddingTop: 24, paddingTop: scaleSize(24),
paddingBottom: 50, paddingBottom: scaleSize(50),
overflow: "visible", overflow: "visible",
}, },
header: { header: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
marginBottom: 24, marginBottom: scaleSize(24),
}, },
title: { title: {
fontSize: 28, fontSize: scaleSize(28),
fontWeight: "bold", fontWeight: "bold",
color: "#fff", color: "#fff",
marginBottom: 4, marginBottom: scaleSize(4),
}, },
subtitle: { subtitle: {
fontSize: 16, fontSize: scaleSize(16),
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
}, },
inputContainer: { inputContainer: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
marginBottom: 20, marginBottom: scaleSize(20),
}, },
inputLabel: { inputLabel: {
fontSize: 14, fontSize: scaleSize(14),
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
marginBottom: 8, marginBottom: scaleSize(8),
}, },
errorText: { errorText: {
color: "#ef4444", color: "#ef4444",
fontSize: 14, fontSize: scaleSize(14),
marginTop: 8, marginTop: scaleSize(8),
}, },
buttonContainer: { buttonContainer: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
alignItems: "flex-start", alignItems: "flex-start",
}, },
}); });

View File

@@ -57,7 +57,7 @@ export const TVQRCodeDisplay: React.FC<TVQRCodeDisplayProps> = ({
alignItems: "center", alignItems: "center",
paddingVertical: sectionPadding, paddingVertical: sectionPadding,
paddingHorizontal: cardPadding, paddingHorizontal: cardPadding,
borderRadius: 16, borderRadius: scaleSize(16),
backgroundColor: "rgba(255, 255, 255, 0.05)", backgroundColor: "rgba(255, 255, 255, 0.05)",
}} }}
> >
@@ -66,7 +66,7 @@ export const TVQRCodeDisplay: React.FC<TVQRCodeDisplayProps> = ({
fontSize: typography.heading, fontSize: typography.heading,
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
marginBottom: 8, marginBottom: scaleSize(8),
}} }}
> >
{t("pairing.waiting_for_phone")} {t("pairing.waiting_for_phone")}
@@ -75,7 +75,7 @@ export const TVQRCodeDisplay: React.FC<TVQRCodeDisplayProps> = ({
<View <View
style={{ style={{
padding: cardPadding, padding: cardPadding,
borderRadius: 12, borderRadius: scaleSize(12),
backgroundColor: "#FFFFFF", backgroundColor: "#FFFFFF",
}} }}
> >

View File

@@ -14,6 +14,7 @@ import {
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { TVPinInput, type TVPinInputRef } from "@/components/inputs/TVPinInput"; import { TVPinInput, type TVPinInputRef } from "@/components/inputs/TVPinInput";
import { TVOptionCard, useTVFocusAnimation } from "@/components/tv"; import { TVOptionCard, useTVFocusAnimation } from "@/components/tv";
import { scaleSize } from "@/utils/scaleSize";
import type { AccountSecurityType } from "@/utils/secureCredentials"; import type { AccountSecurityType } from "@/utils/secureCredentials";
interface TVSaveAccountModalProps { interface TVSaveAccountModalProps {
@@ -79,24 +80,24 @@ const TVSaveButton: React.FC<{
: disabled : disabled
? "#4a4a4a" ? "#4a4a4a"
: "rgba(255,255,255,0.15)", : "rgba(255,255,255,0.15)",
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
paddingVertical: 14, paddingVertical: scaleSize(14),
borderRadius: 10, borderRadius: scaleSize(10),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
gap: 8, gap: scaleSize(8),
opacity: disabled ? 0.5 : 1, opacity: disabled ? 0.5 : 1,
}, },
]} ]}
> >
<Ionicons <Ionicons
name='checkmark' name='checkmark'
size={20} size={scaleSize(20)}
color={focused ? "#000" : "#fff"} color={focused ? "#000" : "#fff"}
/> />
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: focused ? "#000" : "#fff", color: focused ? "#000" : "#fff",
fontWeight: "600", fontWeight: "600",
}} }}
@@ -129,23 +130,23 @@ const TVBackButton: React.FC<{
animatedStyle, animatedStyle,
{ {
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)", backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)",
paddingHorizontal: 20, paddingHorizontal: scaleSize(20),
paddingVertical: 12, paddingVertical: scaleSize(12),
borderRadius: 10, borderRadius: scaleSize(10),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
gap: 8, gap: scaleSize(8),
}, },
]} ]}
> >
<Ionicons <Ionicons
name='chevron-back' name='chevron-back'
size={20} size={scaleSize(20)}
color={focused ? "#000" : "rgba(255,255,255,0.8)"} color={focused ? "#000" : "rgba(255,255,255,0.8)"}
/> />
<Text <Text
style={{ style={{
fontSize: 16, fontSize: scaleSize(16),
color: focused ? "#000" : "rgba(255,255,255,0.8)", color: focused ? "#000" : "rgba(255,255,255,0.8)",
fontWeight: "500", fontWeight: "500",
}} }}
@@ -378,35 +379,35 @@ const styles = StyleSheet.create({
width: "100%", width: "100%",
}, },
blurContainer: { blurContainer: {
borderTopLeftRadius: 24, borderTopLeftRadius: scaleSize(24),
borderTopRightRadius: 24, borderTopRightRadius: scaleSize(24),
overflow: "hidden", overflow: "hidden",
}, },
content: { content: {
paddingTop: 24, paddingTop: scaleSize(24),
paddingBottom: 50, paddingBottom: scaleSize(50),
overflow: "visible", overflow: "visible",
}, },
header: { header: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
marginBottom: 20, marginBottom: scaleSize(20),
}, },
title: { title: {
fontSize: 28, fontSize: scaleSize(28),
fontWeight: "bold", fontWeight: "bold",
color: "#fff", color: "#fff",
marginBottom: 4, marginBottom: scaleSize(4),
}, },
subtitle: { subtitle: {
fontSize: 16, fontSize: scaleSize(16),
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
}, },
sectionTitle: { sectionTitle: {
fontSize: 16, fontSize: scaleSize(16),
fontWeight: "500", fontWeight: "500",
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
marginBottom: 16, marginBottom: scaleSize(16),
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
}, },
@@ -414,26 +415,26 @@ const styles = StyleSheet.create({
overflow: "visible", overflow: "visible",
}, },
scrollContent: { scrollContent: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
paddingVertical: 10, paddingVertical: scaleSize(10),
gap: 12, gap: scaleSize(12),
}, },
buttonRow: { buttonRow: {
marginTop: 20, marginTop: scaleSize(20),
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
flexDirection: "row", flexDirection: "row",
gap: 16, gap: scaleSize(16),
alignItems: "center", alignItems: "center",
}, },
pinContainer: { pinContainer: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
alignItems: "center", alignItems: "center",
marginBottom: 10, marginBottom: scaleSize(10),
}, },
errorText: { errorText: {
color: "#ef4444", color: "#ef4444",
fontSize: 14, fontSize: scaleSize(14),
marginTop: 12, marginTop: scaleSize(12),
textAlign: "center", textAlign: "center",
}, },
}); });

View File

@@ -1,6 +1,7 @@
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import { Animated, Easing, Pressable, View } from "react-native"; import { Animated, Easing, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { scaleSize } from "@/utils/scaleSize";
interface TVSaveAccountToggleProps { interface TVSaveAccountToggleProps {
value: boolean; value: boolean;
@@ -74,9 +75,9 @@ export const TVSaveAccountToggle: React.FC<TVSaveAccountToggleProps> = ({
backgroundColor: isFocused ? "#2a2a2a" : "#1a1a1a", backgroundColor: isFocused ? "#2a2a2a" : "#1a1a1a",
borderWidth: 2, borderWidth: 2,
borderColor: isFocused ? "#FFFFFF" : "transparent", borderColor: isFocused ? "#FFFFFF" : "transparent",
borderRadius: 16, borderRadius: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
paddingVertical: 20, paddingVertical: scaleSize(20),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -84,7 +85,7 @@ export const TVSaveAccountToggle: React.FC<TVSaveAccountToggleProps> = ({
> >
<Text <Text
style={{ style={{
fontSize: 20, fontSize: scaleSize(20),
color: "#FFFFFF", color: "#FFFFFF",
}} }}
> >
@@ -93,19 +94,19 @@ export const TVSaveAccountToggle: React.FC<TVSaveAccountToggleProps> = ({
<View <View
pointerEvents='none' pointerEvents='none'
style={{ style={{
width: 60, width: scaleSize(60),
height: 34, height: scaleSize(34),
borderRadius: 17, borderRadius: scaleSize(17),
backgroundColor: value ? "#fff" : "#3f3f46", backgroundColor: value ? "#fff" : "#3f3f46",
justifyContent: "center", justifyContent: "center",
paddingHorizontal: 3, paddingHorizontal: scaleSize(3),
}} }}
> >
<View <View
style={{ style={{
width: 28, width: scaleSize(28),
height: 28, height: scaleSize(28),
borderRadius: 14, borderRadius: scaleSize(14),
backgroundColor: value ? "#000" : "#fff", backgroundColor: value ? "#000" : "#fff",
alignSelf: value ? "flex-end" : "flex-start", alignSelf: value ? "flex-end" : "flex-start",
}} }}

View File

@@ -4,6 +4,7 @@ import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
// Sci-fi gradient color pairs (from, to) - cyberpunk/neon vibes // Sci-fi gradient color pairs (from, to) - cyberpunk/neon vibes
const SERVER_GRADIENTS: [string, string][] = [ const SERVER_GRADIENTS: [string, string][] = [
@@ -131,22 +132,22 @@ export const TVServerIcon = React.forwardRef<View, TVServerIconProps>(
animatedStyle, animatedStyle,
{ {
alignItems: "center", alignItems: "center",
width: 160, width: scaleSize(160),
shadowColor: gradientStart, shadowColor: gradientStart,
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.7 : 0, shadowOpacity: focused ? 0.7 : 0,
shadowRadius: focused ? 24 : 0, shadowRadius: focused ? scaleSize(24) : 0,
}, },
]} ]}
> >
<View <View
style={{ style={{
width: 140, width: scaleSize(140),
height: 140, height: scaleSize(140),
borderRadius: 70, borderRadius: scaleSize(70),
overflow: "hidden", overflow: "hidden",
marginBottom: 14, marginBottom: scaleSize(14),
borderWidth: focused ? 3 : 0, borderWidth: focused ? scaleSize(3) : 0,
borderColor: "#fff", borderColor: "#fff",
}} }}
> >
@@ -164,7 +165,7 @@ export const TVServerIcon = React.forwardRef<View, TVServerIconProps>(
> >
<Text <Text
style={{ style={{
fontSize: 48, fontSize: scaleSize(48),
fontWeight: "bold", fontWeight: "bold",
color: "#fff", color: "#fff",
textShadowColor: "rgba(0,0,0,0.3)", textShadowColor: "rgba(0,0,0,0.3)",
@@ -183,9 +184,9 @@ export const TVServerIcon = React.forwardRef<View, TVServerIconProps>(
fontWeight: "600", fontWeight: "600",
color: focused ? "#fff" : "rgba(255,255,255,0.9)", color: focused ? "#fff" : "rgba(255,255,255,0.9)",
textAlign: "center", textAlign: "center",
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
numberOfLines={2} numberOfLines={3}
> >
{displayName} {displayName}
</Text> </Text>
@@ -199,7 +200,7 @@ export const TVServerIcon = React.forwardRef<View, TVServerIconProps>(
: "rgba(255,255,255,0.5)", : "rgba(255,255,255,0.5)",
textAlign: "center", textAlign: "center",
}} }}
numberOfLines={1} numberOfLines={3}
> >
{address.replace(/^https?:\/\//, "")} {address.replace(/^https?:\/\//, "")}
</Text> </Text>

View File

@@ -5,6 +5,7 @@ import { Alert, ScrollView, View } from "react-native";
import { useMMKVString } from "react-native-mmkv"; import { useMMKVString } from "react-native-mmkv";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import type { SavedServer } from "@/utils/secureCredentials"; import type { SavedServer } from "@/utils/secureCredentials";
import { TVAddIcon } from "./TVAddIcon"; import { TVAddIcon } from "./TVAddIcon";
import { TVServerIcon } from "./TVServerIcon"; import { TVServerIcon } from "./TVServerIcon";
@@ -56,7 +57,7 @@ export const TVServerSelectionScreen: React.FC<
flexGrow: 1, flexGrow: 1,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingVertical: 60, paddingVertical: scaleSize(60),
}} }}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
@@ -64,14 +65,14 @@ export const TVServerSelectionScreen: React.FC<
style={{ style={{
width: "100%", width: "100%",
alignItems: "center", alignItems: "center",
paddingHorizontal: 60, paddingHorizontal: scaleSize(60),
}} }}
> >
{/* Logo */} {/* Logo */}
<View style={{ alignItems: "center", marginBottom: 16 }}> <View style={{ alignItems: "center", marginBottom: scaleSize(16) }}>
<Image <Image
source={require("@/assets/images/icon-ios-plain.png")} source={require("@/assets/images/icon-ios-plain.png")}
style={{ width: 150, height: 150 }} style={{ width: scaleSize(150), height: scaleSize(150) }}
contentFit='contain' contentFit='contain'
/> />
</View> </View>
@@ -83,7 +84,7 @@ export const TVServerSelectionScreen: React.FC<
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
textAlign: "center", textAlign: "center",
marginBottom: 8, marginBottom: scaleSize(8),
}} }}
> >
Streamyfin Streamyfin
@@ -93,7 +94,7 @@ export const TVServerSelectionScreen: React.FC<
fontSize: typography.body, fontSize: typography.body,
color: "#9CA3AF", color: "#9CA3AF",
textAlign: "center", textAlign: "center",
marginBottom: 48, marginBottom: scaleSize(48),
}} }}
> >
{hasServers {hasServers
@@ -106,8 +107,8 @@ export const TVServerSelectionScreen: React.FC<
horizontal horizontal
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
contentContainerStyle={{ contentContainerStyle={{
paddingHorizontal: 20, paddingHorizontal: scaleSize(20),
gap: 24, gap: scaleSize(24),
}} }}
style={{ overflow: "visible" }} style={{ overflow: "visible" }}
> >

View File

@@ -6,6 +6,7 @@ import { Text } from "@/components/common/Text";
import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "@/components/tv/hooks/useTVFocusAnimation";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { getUserImageUrl } from "@/utils/jellyfin/image/getUserImageUrl"; import { getUserImageUrl } from "@/utils/jellyfin/image/getUserImageUrl";
import { scaleSize } from "@/utils/scaleSize";
import type { AccountSecurityType } from "@/utils/secureCredentials"; import type { AccountSecurityType } from "@/utils/secureCredentials";
export interface TVUserIconProps { export interface TVUserIconProps {
@@ -76,27 +77,27 @@ export const TVUserIcon = React.forwardRef<View, TVUserIconProps>(
animatedStyle, animatedStyle,
{ {
alignItems: "center", alignItems: "center",
width: 160, width: scaleSize(160),
overflow: "visible", overflow: "visible",
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.5 : 0, shadowOpacity: focused ? 0.5 : 0,
shadowRadius: focused ? 16 : 0, shadowRadius: focused ? scaleSize(16) : 0,
}, },
]} ]}
> >
<View style={{ position: "relative" }}> <View style={{ position: "relative" }}>
<View <View
style={{ style={{
width: 140, width: scaleSize(140),
height: 140, height: scaleSize(140),
borderRadius: 70, borderRadius: scaleSize(70),
overflow: "hidden", overflow: "hidden",
backgroundColor: focused backgroundColor: focused
? "rgba(255,255,255,0.2)" ? "rgba(255,255,255,0.2)"
: "rgba(255,255,255,0.1)", : "rgba(255,255,255,0.1)",
marginBottom: 14, marginBottom: scaleSize(14),
borderWidth: focused ? 3 : 0, borderWidth: focused ? scaleSize(3) : 0,
borderColor: "#fff", borderColor: "#fff",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
@@ -105,14 +106,14 @@ export const TVUserIcon = React.forwardRef<View, TVUserIconProps>(
{imageUrl ? ( {imageUrl ? (
<Image <Image
source={{ uri: imageUrl }} source={{ uri: imageUrl }}
style={{ width: 140, height: 140 }} style={{ width: scaleSize(140), height: scaleSize(140) }}
contentFit='cover' contentFit='cover'
onError={() => setImageError(true)} onError={() => setImageError(true)}
/> />
) : ( ) : (
<Ionicons <Ionicons
name='person' name='person'
size={56} size={scaleSize(56)}
color={ color={
focused ? "rgba(255,255,255,0.6)" : "rgba(255,255,255,0.4)" focused ? "rgba(255,255,255,0.6)" : "rgba(255,255,255,0.4)"
} }
@@ -125,21 +126,25 @@ export const TVUserIcon = React.forwardRef<View, TVUserIconProps>(
<View <View
style={{ style={{
position: "absolute", position: "absolute",
bottom: 10, bottom: scaleSize(10),
right: 10, right: scaleSize(10),
width: 32, width: scaleSize(32),
height: 32, height: scaleSize(32),
borderRadius: 16, borderRadius: scaleSize(16),
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.9)", backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.9)",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
shadowColor: "#000", shadowColor: "#000",
shadowOffset: { width: 0, height: 2 }, shadowOffset: { width: 0, height: scaleSize(2) },
shadowOpacity: 0.3, shadowOpacity: 0.3,
shadowRadius: 4, shadowRadius: scaleSize(4),
}} }}
> >
<Ionicons name={getSecurityIcon()} size={16} color='#000' /> <Ionicons
name={getSecurityIcon()}
size={scaleSize(16)}
color='#000'
/>
</View> </View>
)} )}
</View> </View>

View File

@@ -4,6 +4,7 @@ import { ScrollView, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { useTVBackPress } from "@/hooks/useTVBackPress"; import { useTVBackPress } from "@/hooks/useTVBackPress";
import { scaleSize } from "@/utils/scaleSize";
import type { import type {
SavedServer, SavedServer,
SavedServerAccount, SavedServerAccount,
@@ -47,7 +48,7 @@ export const TVUserSelectionScreen: React.FC<TVUserSelectionScreenProps> = ({
flexGrow: 1, flexGrow: 1,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingVertical: 60, paddingVertical: scaleSize(60),
}} }}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
@@ -55,18 +56,18 @@ export const TVUserSelectionScreen: React.FC<TVUserSelectionScreenProps> = ({
style={{ style={{
width: "100%", width: "100%",
alignItems: "center", alignItems: "center",
paddingHorizontal: 60, paddingHorizontal: scaleSize(60),
}} }}
> >
{/* Server Info Header */} {/* Server Info Header */}
<View style={{ marginBottom: 48, alignItems: "center" }}> <View style={{ marginBottom: scaleSize(48), alignItems: "center" }}>
<Text <Text
style={{ style={{
fontSize: typography.title, fontSize: typography.title,
fontWeight: "bold", fontWeight: "bold",
color: "#FFFFFF", color: "#FFFFFF",
textAlign: "center", textAlign: "center",
marginBottom: 8, marginBottom: scaleSize(8),
}} }}
> >
{server.name || server.address} {server.name || server.address}
@@ -87,7 +88,7 @@ export const TVUserSelectionScreen: React.FC<TVUserSelectionScreenProps> = ({
fontSize: typography.body, fontSize: typography.body,
color: "#6B7280", color: "#6B7280",
textAlign: "center", textAlign: "center",
marginTop: 16, marginTop: scaleSize(16),
}} }}
> >
{hasAccounts {hasAccounts
@@ -101,8 +102,8 @@ export const TVUserSelectionScreen: React.FC<TVUserSelectionScreenProps> = ({
horizontal horizontal
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
contentContainerStyle={{ contentContainerStyle={{
paddingHorizontal: 20, paddingHorizontal: scaleSize(20),
gap: 24, gap: scaleSize(24),
}} }}
style={{ overflow: "visible" }} style={{ overflow: "visible" }}
> >

View File

@@ -4,6 +4,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVActorCardProps { export interface TVActorCardProps {
@@ -40,23 +41,23 @@ export const TVActorCard = React.forwardRef<View, TVActorCardProps>(
animatedStyle, animatedStyle,
{ {
alignItems: "center", alignItems: "center",
width: 160, width: scaleSize(160),
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: focused ? 0.5 : 0, shadowOpacity: focused ? 0.5 : 0,
shadowRadius: focused ? 16 : 0, shadowRadius: focused ? scaleSize(16) : 0,
}, },
]} ]}
> >
<View <View
style={{ style={{
width: 140, width: scaleSize(140),
height: 140, height: scaleSize(140),
borderRadius: 70, borderRadius: scaleSize(70),
overflow: "hidden", overflow: "hidden",
backgroundColor: "rgba(255,255,255,0.1)", backgroundColor: "rgba(255,255,255,0.1)",
marginBottom: 14, marginBottom: scaleSize(14),
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: focused ? "#FFFFFF" : "transparent", borderColor: focused ? "#FFFFFF" : "transparent",
}} }}
> >
@@ -76,7 +77,7 @@ export const TVActorCard = React.forwardRef<View, TVActorCardProps>(
> >
<Ionicons <Ionicons
name='person' name='person'
size={56} size={scaleSize(56)}
color='rgba(255,255,255,0.4)' color='rgba(255,255,255,0.4)'
/> />
</View> </View>
@@ -89,9 +90,9 @@ export const TVActorCard = React.forwardRef<View, TVActorCardProps>(
fontWeight: "600", fontWeight: "600",
color: focused ? "#fff" : "rgba(255,255,255,0.9)", color: focused ? "#fff" : "rgba(255,255,255,0.9)",
textAlign: "center", textAlign: "center",
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
numberOfLines={1} numberOfLines={2}
> >
{person.Name} {person.Name}
</Text> </Text>
@@ -105,7 +106,7 @@ export const TVActorCard = React.forwardRef<View, TVActorCardProps>(
: "rgba(255,255,255,0.5)", : "rgba(255,255,255,0.5)",
textAlign: "center", textAlign: "center",
}} }}
numberOfLines={1} numberOfLines={2}
> >
{person.Role} {person.Role}
</Text> </Text>

View File

@@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import { Animated, Pressable, View, type ViewStyle } from "react-native"; import { Animated, Pressable, View, type ViewStyle } from "react-native";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVButtonProps { export interface TVButtonProps {
@@ -98,13 +99,13 @@ export const TVButton: React.FC<TVButtonProps> = ({
backgroundColor: buttonStyles.backgroundColor, backgroundColor: buttonStyles.backgroundColor,
borderWidth: buttonStyles.borderWidth, borderWidth: buttonStyles.borderWidth,
borderColor: buttonStyles.borderColor, borderColor: buttonStyles.borderColor,
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 18, paddingVertical: scaleSize(18),
paddingHorizontal: square ? 18 : 32, paddingHorizontal: square ? scaleSize(18) : scaleSize(32),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
minWidth: square ? undefined : 180, minWidth: square ? undefined : scaleSize(180),
}} }}
> >
{children} {children}

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable } from "react-native"; import { Animated, Pressable } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVCancelButtonProps { export interface TVCancelButtonProps {
@@ -33,18 +34,18 @@ export const TVCancelButton: React.FC<TVCancelButtonProps> = ({
animatedStyle, animatedStyle,
{ {
backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)", backgroundColor: focused ? "#fff" : "rgba(255,255,255,0.15)",
paddingHorizontal: 20, paddingHorizontal: scaleSize(20),
paddingVertical: 12, paddingVertical: scaleSize(12),
borderRadius: 10, borderRadius: scaleSize(10),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
gap: 8, gap: scaleSize(8),
}, },
]} ]}
> >
<Ionicons <Ionicons
name='close' name='close'
size={20} size={scaleSize(20)}
color={focused ? "#000" : "rgba(255,255,255,0.8)"} color={focused ? "#000" : "rgba(255,255,255,0.8)"}
/> />
<Text <Text

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { View } from "react-native"; import { View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
export interface TVCastCrewTextProps { export interface TVCastCrewTextProps {
director?: BaseItemPerson | null; director?: BaseItemPerson | null;
@@ -22,18 +23,18 @@ export const TVCastCrewText: React.FC<TVCastCrewTextProps> = React.memo(
} }
return ( return (
<View style={{ marginBottom: 32 }}> <View style={{ marginBottom: scaleSize(32) }}>
<Text <Text
style={{ style={{
fontSize: typography.heading, fontSize: typography.heading,
fontWeight: "600", fontWeight: "600",
color: "#FFFFFF", color: "#FFFFFF",
marginBottom: 16, marginBottom: scaleSize(16),
}} }}
> >
{t("item_card.cast_and_crew")} {t("item_card.cast_and_crew")}
</Text> </Text>
<View style={{ flexDirection: "row", gap: 40 }}> <View style={{ flexDirection: "row", gap: scaleSize(40) }}>
{director && ( {director && (
<View> <View>
<Text <Text
@@ -42,7 +43,7 @@ export const TVCastCrewText: React.FC<TVCastCrewTextProps> = React.memo(
color: "#6B7280", color: "#6B7280",
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
> >
{t("item_card.director")} {t("item_card.director")}
@@ -60,7 +61,7 @@ export const TVCastCrewText: React.FC<TVCastCrewTextProps> = React.memo(
color: "#6B7280", color: "#6B7280",
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
> >
{t("item_card.cast")} {t("item_card.cast")}

View File

@@ -6,6 +6,7 @@ import {
StyleSheet, StyleSheet,
type View, type View,
} from "react-native"; } from "react-native";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVControlButtonProps { export interface TVControlButtonProps {
@@ -63,7 +64,7 @@ export const TVControlButton: FC<TVControlButtonProps> = ({
}, },
]} ]}
> >
<Ionicons name={icon} size={size} color='#fff' /> <Ionicons name={icon} size={scaleSize(size)} color='#fff' />
</RNAnimated.View> </RNAnimated.View>
</Pressable> </Pressable>
); );
@@ -71,10 +72,10 @@ export const TVControlButton: FC<TVControlButtonProps> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
button: { button: {
width: 64, width: scaleSize(64),
height: 64, height: scaleSize(64),
borderRadius: 32, borderRadius: scaleSize(32),
borderWidth: 2, borderWidth: scaleSize(2),
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
}, },

View File

@@ -2,6 +2,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVFilterButtonProps { export interface TVFilterButtonProps {
@@ -42,13 +43,13 @@ export const TVFilterButton: React.FC<TVFilterButtonProps> = ({
: hasActiveFilter : hasActiveFilter
? "rgba(255, 255, 255, 0.25)" ? "rgba(255, 255, 255, 0.25)"
: "rgba(255,255,255,0.1)", : "rgba(255,255,255,0.1)",
borderRadius: 10, borderRadius: scaleSize(10),
paddingVertical: 10, paddingVertical: scaleSize(10),
paddingHorizontal: 16, paddingHorizontal: scaleSize(16),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
gap: 8, gap: scaleSize(8),
borderWidth: hasActiveFilter && !focused ? 1 : 0, borderWidth: hasActiveFilter && !focused ? scaleSize(1) : 0,
borderColor: "rgba(255, 255, 255, 0.4)", borderColor: "rgba(255, 255, 255, 0.4)",
}} }}
> >

View File

@@ -8,6 +8,7 @@ import {
} from "react-native"; } from "react-native";
import type { SharedValue } from "react-native-reanimated"; import type { SharedValue } from "react-native-reanimated";
import ReanimatedModule, { useAnimatedStyle } from "react-native-reanimated"; import ReanimatedModule, { useAnimatedStyle } from "react-native-reanimated";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
const ReanimatedView = ReanimatedModule.View; const ReanimatedView = ReanimatedModule.View;
@@ -35,7 +36,7 @@ export interface TVFocusableProgressBarProps {
style?: ViewStyle; style?: ViewStyle;
} }
const PROGRESS_BAR_HEIGHT = 14; const PROGRESS_BAR_HEIGHT = scaleSize(14);
export const TVFocusableProgressBar: React.FC<TVFocusableProgressBarProps> = export const TVFocusableProgressBar: React.FC<TVFocusableProgressBarProps> =
React.memo( React.memo(
@@ -124,21 +125,21 @@ export const TVFocusableProgressBar: React.FC<TVFocusableProgressBarProps> =
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pressableContainer: { pressableContainer: {
// Add padding for focus scale animation to not clip // Add padding for focus scale animation to not clip
paddingVertical: 8, paddingVertical: scaleSize(8),
paddingHorizontal: 4, paddingHorizontal: scaleSize(4),
}, },
animatedContainer: { animatedContainer: {
height: PROGRESS_BAR_HEIGHT + 8, height: PROGRESS_BAR_HEIGHT + scaleSize(8),
justifyContent: "center", justifyContent: "center",
borderRadius: 12, borderRadius: scaleSize(12),
paddingHorizontal: 4, paddingHorizontal: scaleSize(4),
}, },
animatedContainerFocused: { animatedContainerFocused: {
// Subtle glow effect when focused // Subtle glow effect when focused
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.5, shadowOpacity: 0.5,
shadowRadius: 12, shadowRadius: scaleSize(12),
}, },
progressTrackWrapper: { progressTrackWrapper: {
position: "relative", position: "relative",
@@ -147,7 +148,7 @@ const styles = StyleSheet.create({
progressTrack: { progressTrack: {
height: PROGRESS_BAR_HEIGHT, height: PROGRESS_BAR_HEIGHT,
backgroundColor: "rgba(255,255,255,0.2)", backgroundColor: "rgba(255,255,255,0.2)",
borderRadius: 8, borderRadius: scaleSize(8),
overflow: "hidden", overflow: "hidden",
}, },
progressTrackFocused: { progressTrackFocused: {
@@ -160,7 +161,7 @@ const styles = StyleSheet.create({
left: 0, left: 0,
height: "100%", height: "100%",
backgroundColor: "rgba(255,255,255,0.3)", backgroundColor: "rgba(255,255,255,0.3)",
borderRadius: 8, borderRadius: scaleSize(8),
}, },
progressFill: { progressFill: {
position: "absolute", position: "absolute",
@@ -168,7 +169,7 @@ const styles = StyleSheet.create({
left: 0, left: 0,
height: "100%", height: "100%",
backgroundColor: "#fff", backgroundColor: "#fff",
borderRadius: 8, borderRadius: scaleSize(8),
}, },
chapterMarkersContainer: { chapterMarkersContainer: {
position: "absolute", position: "absolute",
@@ -179,11 +180,11 @@ const styles = StyleSheet.create({
}, },
chapterMarker: { chapterMarker: {
position: "absolute", position: "absolute",
width: 2, width: scaleSize(2),
height: PROGRESS_BAR_HEIGHT + 5, height: PROGRESS_BAR_HEIGHT + scaleSize(5),
bottom: 0, bottom: 0,
backgroundColor: "rgba(255, 255, 255, 0.6)", backgroundColor: "rgba(255, 255, 255, 0.6)",
borderRadius: 1, borderRadius: scaleSize(1),
transform: [{ translateX: -1 }], transform: [{ translateX: -scaleSize(1) }],
}, },
}); });

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, StyleSheet, View } from "react-native"; import { Animated, Pressable, StyleSheet, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVLanguageCardProps { export interface TVLanguageCardProps {
@@ -63,7 +64,7 @@ export const TVLanguageCard = React.forwardRef<View, TVLanguageCardProps>(
<View style={styles.checkmark}> <View style={styles.checkmark}>
<Ionicons <Ionicons
name='checkmark' name='checkmark'
size={16} size={scaleSize(16)}
color='rgba(255,255,255,0.8)' color='rgba(255,255,255,0.8)'
/> />
</View> </View>
@@ -77,12 +78,12 @@ export const TVLanguageCard = React.forwardRef<View, TVLanguageCardProps>(
const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) => const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
StyleSheet.create({ StyleSheet.create({
languageCard: { languageCard: {
width: 120, width: scaleSize(120),
height: 60, height: scaleSize(60),
borderRadius: 12, borderRadius: scaleSize(12),
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingHorizontal: 12, paddingHorizontal: scaleSize(12),
}, },
languageCardText: { languageCardText: {
fontSize: typography.callout, fontSize: typography.callout,
@@ -94,7 +95,7 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
}, },
checkmark: { checkmark: {
position: "absolute", position: "absolute",
top: 8, top: scaleSize(8),
right: 8, right: scaleSize(8),
}, },
}); });

View File

@@ -4,6 +4,7 @@ import { View } from "react-native";
import { Badge } from "@/components/Badge"; import { Badge } from "@/components/Badge";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
export interface TVMetadataBadgesProps { export interface TVMetadataBadgesProps {
year?: number | null; year?: number | null;
@@ -22,8 +23,8 @@ export const TVMetadataBadges: React.FC<TVMetadataBadgesProps> = React.memo(
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
flexWrap: "wrap", flexWrap: "wrap",
gap: 16, gap: scaleSize(16),
marginBottom: 24, marginBottom: scaleSize(24),
}} }}
> >
{year != null && ( {year != null && (

View File

@@ -23,6 +23,7 @@ import Animated, {
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl"; import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVNextEpisodeCountdownProps { export interface TVNextEpisodeCountdownProps {
@@ -198,17 +199,17 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
StyleSheet.create({ StyleSheet.create({
container: { container: {
position: "absolute", position: "absolute",
right: 80, right: scaleSize(80),
zIndex: 100, zIndex: 100,
}, },
focusedCard: { focusedCard: {
shadowColor: "#fff", shadowColor: "#fff",
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.6, shadowOpacity: 0.6,
shadowRadius: 16, shadowRadius: scaleSize(16),
}, },
blur: { blur: {
borderRadius: 16, borderRadius: scaleSize(16),
overflow: "hidden", overflow: "hidden",
}, },
innerContainer: { innerContainer: {
@@ -216,31 +217,31 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
alignItems: "stretch", alignItems: "stretch",
}, },
thumbnail: { thumbnail: {
width: 180, width: scaleSize(180),
backgroundColor: "rgba(0,0,0,0.3)", backgroundColor: "rgba(0,0,0,0.3)",
}, },
content: { content: {
padding: 16, padding: scaleSize(16),
justifyContent: "center", justifyContent: "center",
width: 280, width: scaleSize(280),
}, },
label: { label: {
fontSize: typography.callout, fontSize: typography.callout,
color: "rgba(255,255,255,0.5)", color: "rgba(255,255,255,0.5)",
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
marginBottom: 4, marginBottom: scaleSize(4),
}, },
seriesName: { seriesName: {
fontSize: typography.callout, fontSize: typography.callout,
color: "rgba(255,255,255,0.7)", color: "rgba(255,255,255,0.7)",
marginBottom: 2, marginBottom: scaleSize(2),
}, },
episodeInfo: { episodeInfo: {
fontSize: typography.body, fontSize: typography.body,
color: "#fff", color: "#fff",
fontWeight: "600", fontWeight: "600",
marginBottom: 12, marginBottom: scaleSize(12),
}, },
progressContainer: { progressContainer: {
height: 4, height: 4,

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVOptionCardProps { export interface TVOptionCardProps {
@@ -49,12 +50,14 @@ export const TVOptionCard = React.forwardRef<View, TVOptionCardProps>(
backgroundColor: focused backgroundColor: focused
? "#fff" ? "#fff"
: selected : selected
? "rgba(255,255,255,0.2)" ? "rgba(255,255,255,0.15)"
: "rgba(255,255,255,0.08)", : "rgba(255,255,255,0.08)",
borderRadius: 14, borderRadius: scaleSize(14),
borderWidth: focused ? 0 : selected ? scaleSize(2) : 0,
borderColor: "rgba(255,255,255,0.6)",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingHorizontal: 12, paddingHorizontal: scaleSize(12),
}, },
]} ]}
> >

View File

@@ -10,6 +10,7 @@ import {
} from "react-native"; } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { TVCancelButton } from "./TVCancelButton"; import { TVCancelButton } from "./TVCancelButton";
import { TVOptionCard } from "./TVOptionCard"; import { TVOptionCard } from "./TVOptionCard";
@@ -170,21 +171,21 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
width: "100%", width: "100%",
}, },
blurContainer: { blurContainer: {
borderTopLeftRadius: 24, borderTopLeftRadius: scaleSize(24),
borderTopRightRadius: 24, borderTopRightRadius: scaleSize(24),
overflow: "hidden", overflow: "hidden",
}, },
content: { content: {
paddingTop: 24, paddingTop: scaleSize(24),
paddingBottom: 50, paddingBottom: scaleSize(50),
overflow: "visible", overflow: "visible",
}, },
title: { title: {
fontSize: typography.callout, fontSize: typography.callout,
fontWeight: "500", fontWeight: "500",
color: "rgba(255,255,255,0.6)", color: "rgba(255,255,255,0.6)",
marginBottom: 16, marginBottom: scaleSize(16),
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
}, },
@@ -192,13 +193,13 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
overflow: "visible", overflow: "visible",
}, },
scrollContent: { scrollContent: {
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
paddingVertical: 20, paddingVertical: scaleSize(20),
gap: 12, gap: scaleSize(12),
}, },
cancelButtonContainer: { cancelButtonContainer: {
marginTop: 16, marginTop: scaleSize(16),
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
alignItems: "flex-start", alignItems: "flex-start",
}, },
}); });

View File

@@ -9,6 +9,7 @@ import {
GlassPosterView, GlassPosterView,
isGlassEffectAvailable, isGlassEffectAvailable,
} from "@/modules/glass-poster"; } from "@/modules/glass-poster";
import { scaleSize } from "@/utils/scaleSize";
export interface TVSeriesSeasonCardProps { export interface TVSeriesSeasonCardProps {
title: string; title: string;
@@ -66,10 +67,10 @@ export const TVSeriesSeasonCard: React.FC<TVSeriesSeasonCardProps> = ({
style={{ style={{
width: sizes.posters.poster, width: sizes.posters.poster,
aspectRatio: 10 / 15, aspectRatio: 10 / 15,
borderRadius: 24, borderRadius: scaleSize(24),
overflow: "hidden", overflow: "hidden",
backgroundColor: "rgba(255,255,255,0.1)", backgroundColor: "rgba(255,255,255,0.1)",
borderWidth: 2, borderWidth: scaleSize(2),
borderColor: focused ? "#FFFFFF" : "transparent", borderColor: focused ? "#FFFFFF" : "transparent",
}} }}
> >
@@ -87,7 +88,11 @@ export const TVSeriesSeasonCard: React.FC<TVSeriesSeasonCardProps> = ({
alignItems: "center", alignItems: "center",
}} }}
> >
<Ionicons name='film' size={56} color='rgba(255,255,255,0.4)' /> <Ionicons
name='film'
size={scaleSize(56)}
color='rgba(255,255,255,0.4)'
/>
</View> </View>
)} )}
</View> </View>
@@ -129,7 +134,7 @@ export const TVSeriesSeasonCard: React.FC<TVSeriesSeasonCardProps> = ({
</Animated.View> </Animated.View>
</Pressable> </Pressable>
<View style={{ marginTop: 12 }}> <View style={{ marginTop: scaleSize(12) }}>
<Text <Text
style={{ style={{
fontSize: typography.body, fontSize: typography.body,
@@ -145,7 +150,7 @@ export const TVSeriesSeasonCard: React.FC<TVSeriesSeasonCardProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#9CA3AF", color: "#9CA3AF",
marginTop: 4, marginTop: scaleSize(4),
}} }}
numberOfLines={1} numberOfLines={1}
> >

View File

@@ -16,6 +16,7 @@ import Animated, {
withTiming, withTiming,
} from "react-native-reanimated"; } from "react-native-reanimated";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVSkipSegmentCardProps { export interface TVSkipSegmentCardProps {
@@ -102,7 +103,7 @@ export const TVSkipSegmentCard: FC<TVSkipSegmentCardProps> = ({
}, },
]} ]}
> >
<Ionicons name='play-forward' size={20} color='#fff' /> <Ionicons name='play-forward' size={scaleSize(20)} color='#fff' />
<Text style={styles.label}>{labelText}</Text> <Text style={styles.label}>{labelText}</Text>
</RNAnimated.View> </RNAnimated.View>
</Pressable> </Pressable>
@@ -119,20 +120,20 @@ export const TVSkipSegmentCard: FC<TVSkipSegmentCardProps> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
position: "absolute", position: "absolute",
right: 80, right: scaleSize(80),
zIndex: 100, zIndex: 100,
}, },
button: { button: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
paddingVertical: 10, paddingVertical: scaleSize(10),
paddingHorizontal: 18, paddingHorizontal: scaleSize(18),
borderRadius: 12, borderRadius: scaleSize(12),
borderWidth: 2, borderWidth: scaleSize(2),
gap: 8, gap: scaleSize(8),
}, },
label: { label: {
fontSize: 20, fontSize: scaleSize(20),
color: "#fff", color: "#fff",
fontWeight: "600", fontWeight: "600",
}, },

View File

@@ -10,6 +10,7 @@ import {
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import type { SubtitleSearchResult } from "@/hooks/useRemoteSubtitles"; import type { SubtitleSearchResult } from "@/hooks/useRemoteSubtitles";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVSubtitleResultCardProps { export interface TVSubtitleResultCardProps {
@@ -202,18 +203,18 @@ export const TVSubtitleResultCard = React.forwardRef<
const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) => const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
StyleSheet.create({ StyleSheet.create({
resultCard: { resultCard: {
width: 220, width: scaleSize(220),
minHeight: 120, minHeight: scaleSize(120),
borderRadius: 14, borderRadius: scaleSize(14),
padding: 14, padding: scaleSize(14),
borderWidth: 1, borderWidth: scaleSize(1),
}, },
providerBadge: { providerBadge: {
alignSelf: "flex-start", alignSelf: "flex-start",
paddingHorizontal: 8, paddingHorizontal: scaleSize(8),
paddingVertical: 3, paddingVertical: scaleSize(3),
borderRadius: 6, borderRadius: scaleSize(6),
marginBottom: 8, marginBottom: scaleSize(8),
}, },
providerText: { providerText: {
fontSize: typography.callout, fontSize: typography.callout,
@@ -224,14 +225,14 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
resultName: { resultName: {
fontSize: typography.callout, fontSize: typography.callout,
fontWeight: "500", fontWeight: "500",
marginBottom: 8, marginBottom: scaleSize(8),
lineHeight: 18, lineHeight: scaleSize(18),
}, },
resultMeta: { resultMeta: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
gap: 12, gap: scaleSize(12),
marginBottom: 8, marginBottom: scaleSize(8),
}, },
resultMetaText: { resultMetaText: {
fontSize: typography.callout, fontSize: typography.callout,
@@ -248,13 +249,13 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
}, },
flagsContainer: { flagsContainer: {
flexDirection: "row", flexDirection: "row",
gap: 6, gap: scaleSize(6),
flexWrap: "wrap", flexWrap: "wrap",
}, },
flag: { flag: {
paddingHorizontal: 6, paddingHorizontal: scaleSize(6),
paddingVertical: 2, paddingVertical: scaleSize(2),
borderRadius: 4, borderRadius: scaleSize(4),
}, },
flagText: { flagText: {
fontSize: typography.callout, fontSize: typography.callout,
@@ -264,7 +265,7 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
downloadingOverlay: { downloadingOverlay: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFillObject,
backgroundColor: "rgba(0,0,0,0.5)", backgroundColor: "rgba(0,0,0,0.5)",
borderRadius: 14, borderRadius: scaleSize(14),
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
}, },

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { View } from "react-native"; import { View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
export interface TVTechnicalDetailsProps { export interface TVTechnicalDetailsProps {
mediaStreams: MediaStream[]; mediaStreams: MediaStream[];
@@ -22,18 +23,18 @@ export const TVTechnicalDetails: React.FC<TVTechnicalDetailsProps> = React.memo(
} }
return ( return (
<View style={{ marginBottom: 32 }}> <View style={{ marginBottom: scaleSize(32) }}>
<Text <Text
style={{ style={{
fontSize: typography.heading, fontSize: typography.heading,
fontWeight: "600", fontWeight: "600",
color: "#FFFFFF", color: "#FFFFFF",
marginBottom: 20, marginBottom: scaleSize(20),
}} }}
> >
{t("item_card.technical_details")} {t("item_card.technical_details")}
</Text> </Text>
<View style={{ flexDirection: "row", gap: 40 }}> <View style={{ flexDirection: "row", gap: scaleSize(40) }}>
{videoStream && ( {videoStream && (
<View> <View>
<Text <Text
@@ -42,7 +43,7 @@ export const TVTechnicalDetails: React.FC<TVTechnicalDetailsProps> = React.memo(
color: "#6B7280", color: "#6B7280",
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
> >
{t("common.video")} {t("common.video")}
@@ -61,7 +62,7 @@ export const TVTechnicalDetails: React.FC<TVTechnicalDetailsProps> = React.memo(
color: "#6B7280", color: "#6B7280",
textTransform: "uppercase", textTransform: "uppercase",
letterSpacing: 1, letterSpacing: 1,
marginBottom: 4, marginBottom: scaleSize(4),
}} }}
> >
{t("common.audio")} {t("common.audio")}

View File

@@ -2,6 +2,7 @@ import { Ionicons } from "@expo/vector-icons";
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import { Animated, Easing, Pressable, View } from "react-native"; import { Animated, Easing, Pressable, View } from "react-native";
import { AnimatedEqualizer } from "@/components/music/AnimatedEqualizer"; import { AnimatedEqualizer } from "@/components/music/AnimatedEqualizer";
import { scaleSize } from "@/utils/scaleSize";
interface TVThemeMusicIndicatorProps { interface TVThemeMusicIndicatorProps {
isPlaying: boolean; isPlaying: boolean;
@@ -51,16 +52,16 @@ export const TVThemeMusicIndicator: React.FC<TVThemeMusicIndicatorProps> = ({
backgroundColor: focused backgroundColor: focused
? "rgba(255,255,255,0.25)" ? "rgba(255,255,255,0.25)"
: "rgba(255,255,255,0.1)", : "rgba(255,255,255,0.1)",
borderRadius: 12, borderRadius: scaleSize(12),
padding: 12, padding: scaleSize(12),
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
width: 48, width: scaleSize(48),
height: 48, height: scaleSize(48),
}} }}
> >
{isMuted ? ( {isMuted ? (
<Ionicons name='volume-mute' size={22} color='#FFFFFF' /> <Ionicons name='volume-mute' size={scaleSize(22)} color='#FFFFFF' />
) : ( ) : (
<View style={{ marginRight: 0 }}> <View style={{ marginRight: 0 }}>
<AnimatedEqualizer <AnimatedEqualizer

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, StyleSheet, View } from "react-native"; import { Animated, Pressable, StyleSheet, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
export interface TVTrackCardProps { export interface TVTrackCardProps {
@@ -68,7 +69,7 @@ export const TVTrackCard = React.forwardRef<View, TVTrackCardProps>(
<View style={styles.checkmark}> <View style={styles.checkmark}>
<Ionicons <Ionicons
name='checkmark' name='checkmark'
size={16} size={scaleSize(16)}
color='rgba(255,255,255,0.8)' color='rgba(255,255,255,0.8)'
/> />
</View> </View>
@@ -82,12 +83,12 @@ export const TVTrackCard = React.forwardRef<View, TVTrackCardProps>(
const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) => const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
StyleSheet.create({ StyleSheet.create({
trackCard: { trackCard: {
width: 180, width: scaleSize(180),
height: 80, height: scaleSize(80),
borderRadius: 14, borderRadius: scaleSize(14),
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
paddingHorizontal: 12, paddingHorizontal: scaleSize(12),
}, },
trackCardText: { trackCardText: {
fontSize: typography.callout, fontSize: typography.callout,
@@ -99,7 +100,7 @@ const createStyles = (typography: ReturnType<typeof useScaledTVTypography>) =>
}, },
checkmark: { checkmark: {
position: "absolute", position: "absolute",
top: 8, top: scaleSize(8),
right: 8, right: scaleSize(8),
}, },
}); });

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import type { AccountSecurityType } from "@/utils/secureCredentials"; import type { AccountSecurityType } from "@/utils/secureCredentials";
import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "./hooks/useTVFocusAnimation";
@@ -89,29 +90,33 @@ export const TVUserCard = React.forwardRef<View, TVUserCardProps>(
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
backgroundColor: getBackgroundColor(), backgroundColor: getBackgroundColor(),
borderRadius: 14, borderRadius: scaleSize(14),
paddingHorizontal: 16, paddingHorizontal: scaleSize(16),
paddingVertical: 14, paddingVertical: scaleSize(14),
gap: 14, gap: scaleSize(14),
}, },
]} ]}
> >
{/* User Avatar */} {/* User Avatar */}
<View <View
style={{ style={{
width: 44, width: scaleSize(44),
height: 44, height: scaleSize(44),
backgroundColor: isCurrent backgroundColor: isCurrent
? "rgba(255,255,255,0.08)" ? "rgba(255,255,255,0.08)"
: focused : focused
? "rgba(0,0,0,0.1)" ? "rgba(0,0,0,0.1)"
: "rgba(255,255,255,0.15)", : "rgba(255,255,255,0.15)",
borderRadius: 22, borderRadius: scaleSize(22),
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
}} }}
> >
<Ionicons name='person' size={24} color={getTextColor()} /> <Ionicons
name='person'
size={scaleSize(24)}
color={getTextColor()}
/>
</View> </View>
{/* Text column */} {/* Text column */}
@@ -153,7 +158,7 @@ export const TVUserCard = React.forwardRef<View, TVUserCardProps>(
> >
<Ionicons <Ionicons
name={getSecurityIcon()} name={getSecurityIcon()}
size={12} size={scaleSize(12)}
color={getSecondaryColor()} color={getSecondaryColor()}
/> />
<Text <Text

View File

@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVLogoutButtonProps { export interface TVLogoutButtonProps {
@@ -41,9 +42,9 @@ export const TVLogoutButton: React.FC<TVLogoutButtonProps> = ({
<View <View
style={{ style={{
backgroundColor: focused ? "#ef4444" : "rgba(239, 68, 68, 0.8)", backgroundColor: focused ? "#ef4444" : "rgba(239, 68, 68, 0.8)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 18, paddingVertical: scaleSize(18),
paddingHorizontal: 48, paddingHorizontal: scaleSize(48),
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
}} }}

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVSettingsOptionButtonProps { export interface TVSettingsOptionButtonProps {
@@ -40,10 +41,10 @@ export const TVSettingsOptionButton: React.FC<TVSettingsOptionButtonProps> = ({
backgroundColor: focused backgroundColor: focused
? "rgba(255, 255, 255, 0.15)" ? "rgba(255, 255, 255, 0.15)"
: "rgba(255, 255, 255, 0.05)", : "rgba(255, 255, 255, 0.05)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 16, paddingVertical: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
marginBottom: 8, marginBottom: scaleSize(8),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -59,12 +60,16 @@ export const TVSettingsOptionButton: React.FC<TVSettingsOptionButtonProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#9CA3AF", color: "#9CA3AF",
marginRight: 12, marginRight: scaleSize(12),
}} }}
> >
{value} {value}
</Text> </Text>
<Ionicons name='chevron-forward' size={20} color='#6B7280' /> <Ionicons
name='chevron-forward'
size={scaleSize(20)}
color='#6B7280'
/>
</View> </View>
</Animated.View> </Animated.View>
</Pressable> </Pressable>

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVSettingsRowProps { export interface TVSettingsRowProps {
@@ -42,10 +43,10 @@ export const TVSettingsRow: React.FC<TVSettingsRowProps> = ({
backgroundColor: focused backgroundColor: focused
? "rgba(255, 255, 255, 0.15)" ? "rgba(255, 255, 255, 0.15)"
: "rgba(255, 255, 255, 0.05)", : "rgba(255, 255, 255, 0.05)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 16, paddingVertical: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
marginBottom: 8, marginBottom: scaleSize(8),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -60,13 +61,17 @@ export const TVSettingsRow: React.FC<TVSettingsRowProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#9CA3AF", color: "#9CA3AF",
marginRight: showChevron ? 12 : 0, marginRight: showChevron ? scaleSize(12) : 0,
}} }}
> >
{value} {value}
</Text> </Text>
{showChevron && ( {showChevron && (
<Ionicons name='chevron-forward' size={20} color='#6B7280' /> <Ionicons
name='chevron-forward'
size={scaleSize(20)}
color='#6B7280'
/>
)} )}
</View> </View>
</Animated.View> </Animated.View>

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVSettingsStepperProps { export interface TVSettingsStepperProps {
@@ -38,10 +39,10 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
labelAnim.focused || minusAnim.focused || plusAnim.focused labelAnim.focused || minusAnim.focused || plusAnim.focused
? "rgba(255, 255, 255, 0.15)" ? "rgba(255, 255, 255, 0.15)"
: "rgba(255, 255, 255, 0.05)", : "rgba(255, 255, 255, 0.05)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 16, paddingVertical: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
marginBottom: 8, marginBottom: scaleSize(8),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -72,9 +73,9 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
style={[ style={[
minusAnim.animatedStyle, minusAnim.animatedStyle,
{ {
width: 40, width: scaleSize(40),
height: 40, height: scaleSize(40),
borderRadius: 10, borderRadius: scaleSize(10),
backgroundColor: minusAnim.focused ? "#FFFFFF" : "#4B5563", backgroundColor: minusAnim.focused ? "#FFFFFF" : "#4B5563",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
@@ -83,7 +84,7 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
> >
<Ionicons <Ionicons
name='remove' name='remove'
size={24} size={scaleSize(24)}
color={minusAnim.focused ? "#000000" : "#FFFFFF"} color={minusAnim.focused ? "#000000" : "#FFFFFF"}
/> />
</Animated.View> </Animated.View>
@@ -92,9 +93,9 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#FFFFFF", color: "#FFFFFF",
minWidth: 60, minWidth: scaleSize(60),
textAlign: "center", textAlign: "center",
marginHorizontal: 16, marginHorizontal: scaleSize(16),
}} }}
> >
{displayValue} {displayValue}
@@ -110,9 +111,9 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
style={[ style={[
plusAnim.animatedStyle, plusAnim.animatedStyle,
{ {
width: 40, width: scaleSize(40),
height: 40, height: scaleSize(40),
borderRadius: 10, borderRadius: scaleSize(10),
backgroundColor: plusAnim.focused ? "#FFFFFF" : "#4B5563", backgroundColor: plusAnim.focused ? "#FFFFFF" : "#4B5563",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
@@ -121,7 +122,7 @@ export const TVSettingsStepper: React.FC<TVSettingsStepperProps> = ({
> >
<Ionicons <Ionicons
name='add' name='add'
size={24} size={scaleSize(24)}
color={plusAnim.focused ? "#000000" : "#FFFFFF"} color={plusAnim.focused ? "#000000" : "#FFFFFF"}
/> />
</Animated.View> </Animated.View>

View File

@@ -2,6 +2,7 @@ import React, { useRef } from "react";
import { Animated, Pressable, TextInput } from "react-native"; import { Animated, Pressable, TextInput } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVSettingsTextInputProps { export interface TVSettingsTextInputProps {
@@ -48,10 +49,10 @@ export const TVSettingsTextInput: React.FC<TVSettingsTextInputProps> = ({
backgroundColor: focused backgroundColor: focused
? "rgba(255, 255, 255, 0.15)" ? "rgba(255, 255, 255, 0.15)"
: "rgba(255, 255, 255, 0.05)", : "rgba(255, 255, 255, 0.05)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 16, paddingVertical: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
marginBottom: 8, marginBottom: scaleSize(8),
}, },
]} ]}
> >
@@ -59,7 +60,7 @@ export const TVSettingsTextInput: React.FC<TVSettingsTextInputProps> = ({
style={{ style={{
fontSize: typography.callout, fontSize: typography.callout,
color: "#9CA3AF", color: "#9CA3AF",
marginBottom: 8, marginBottom: scaleSize(8),
}} }}
> >
{label} {label}
@@ -78,10 +79,10 @@ export const TVSettingsTextInput: React.FC<TVSettingsTextInputProps> = ({
fontSize: typography.body, fontSize: typography.body,
color: "#FFFFFF", color: "#FFFFFF",
backgroundColor: "rgba(255, 255, 255, 0.05)", backgroundColor: "rgba(255, 255, 255, 0.05)",
borderRadius: 8, borderRadius: scaleSize(8),
paddingVertical: 12, paddingVertical: scaleSize(12),
paddingHorizontal: 16, paddingHorizontal: scaleSize(16),
borderWidth: focused ? 2 : 1, borderWidth: focused ? scaleSize(2) : scaleSize(1),
borderColor: focused ? "#FFFFFF" : "#4B5563", borderColor: focused ? "#FFFFFF" : "#4B5563",
}} }}
/> />

View File

@@ -2,6 +2,7 @@ import React from "react";
import { Animated, Pressable, View } from "react-native"; import { Animated, Pressable, View } from "react-native";
import { Text } from "@/components/common/Text"; import { Text } from "@/components/common/Text";
import { useScaledTVTypography } from "@/constants/TVTypography"; import { useScaledTVTypography } from "@/constants/TVTypography";
import { scaleSize } from "@/utils/scaleSize";
import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation"; import { useTVFocusAnimation } from "../hooks/useTVFocusAnimation";
export interface TVSettingsToggleProps { export interface TVSettingsToggleProps {
@@ -39,10 +40,10 @@ export const TVSettingsToggle: React.FC<TVSettingsToggleProps> = ({
backgroundColor: focused backgroundColor: focused
? "rgba(255, 255, 255, 0.15)" ? "rgba(255, 255, 255, 0.15)"
: "rgba(255, 255, 255, 0.05)", : "rgba(255, 255, 255, 0.05)",
borderRadius: 12, borderRadius: scaleSize(12),
paddingVertical: 16, paddingVertical: scaleSize(16),
paddingHorizontal: 24, paddingHorizontal: scaleSize(24),
marginBottom: 8, marginBottom: scaleSize(8),
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
@@ -54,19 +55,19 @@ export const TVSettingsToggle: React.FC<TVSettingsToggleProps> = ({
</Text> </Text>
<View <View
style={{ style={{
width: 56, width: scaleSize(56),
height: 32, height: scaleSize(32),
borderRadius: 16, borderRadius: scaleSize(16),
backgroundColor: value ? "#FFFFFF" : "#4B5563", backgroundColor: value ? "#FFFFFF" : "#4B5563",
justifyContent: "center", justifyContent: "center",
paddingHorizontal: 2, paddingHorizontal: scaleSize(2),
}} }}
> >
<View <View
style={{ style={{
width: 28, width: scaleSize(28),
height: 28, height: scaleSize(28),
borderRadius: 14, borderRadius: scaleSize(14),
backgroundColor: value ? "#000000" : "#FFFFFF", backgroundColor: value ? "#000000" : "#FFFFFF",
alignSelf: value ? "flex-end" : "flex-start", alignSelf: value ? "flex-end" : "flex-start",
}} }}

View File

@@ -79,7 +79,7 @@ export const TVAnimation = {
* Applied to poster sizes and gaps. * Applied to poster sizes and gaps.
*/ */
const sizeScaleMultipliers: Record<TVTypographyScale, number> = { const sizeScaleMultipliers: Record<TVTypographyScale, number> = {
[TVTypographyScale.Small]: 0.8, [TVTypographyScale.Small]: 0.9,
[TVTypographyScale.Default]: 1.0, [TVTypographyScale.Default]: 1.0,
[TVTypographyScale.Large]: 1.1, [TVTypographyScale.Large]: 1.1,
[TVTypographyScale.ExtraLarge]: 1.2, [TVTypographyScale.ExtraLarge]: 1.2,

View File

@@ -37,10 +37,10 @@ export type TVTypographyKey = keyof typeof TVTypography;
// ============================================================================= // =============================================================================
const scaleMultipliers: Record<TVTypographyScale, number> = { const scaleMultipliers: Record<TVTypographyScale, number> = {
[TVTypographyScale.Small]: 0.8, [TVTypographyScale.Small]: 0.85,
[TVTypographyScale.Default]: 1.0, [TVTypographyScale.Default]: 1.0,
[TVTypographyScale.Large]: 1.1, [TVTypographyScale.Large]: 1.2,
[TVTypographyScale.ExtraLarge]: 1.2, [TVTypographyScale.ExtraLarge]: 1.4,
}; };
// ============================================================================= // =============================================================================

View File

@@ -251,6 +251,7 @@ internal object TvRecommendationsPublisher {
imageUrl.takeIf { it.isNotBlank() }?.let { imageUrl.takeIf { it.isNotBlank() }?.let {
val imageUri = Uri.parse(it) val imageUri = Uri.parse(it)
builder.setPosterArtUri(imageUri)
builder.setThumbnailUri(imageUri) builder.setThumbnailUri(imageUri)
} }

View File

@@ -0,0 +1,40 @@
const {
withAndroidColors,
withAndroidColorsNight,
} = require("expo/config-plugins");
const withAndroidAlertColors = (config) => {
const setColor = (colorsList, name, value) => {
const existingColor = colorsList.find(
(item) => item.$ && item.$.name === name,
);
if (existingColor) {
existingColor._ = value;
} else {
colorsList.push({
$: { name },
_: value,
});
}
};
config = withAndroidColors(config, (config) => {
const colors = config.modResults;
const colorsList = colors.resources.color || [];
setColor(colorsList, "colorPrimary", "#000000");
colors.resources.color = colorsList;
return config;
});
config = withAndroidColorsNight(config, (config) => {
const colors = config.modResults;
const colorsList = colors.resources.color || [];
setColor(colorsList, "colorPrimary", "#FFFFFF");
colors.resources.color = colorsList;
return config;
});
return config;
};
module.exports = withAndroidAlertColors;