mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-03-01 23:42:22 +00:00
refactor: standardize Input component prop naming
Replaces `className` prop with `extraClassName` across Input and PasswordInput components for consistency. Updates PasswordInput to use numeric `topOffset` instead of string `topPosition` for better type safety and clearer intent. Adds uncontrolled mode support to PasswordInput with internal state management and optional `defaultShowPassword` prop. Removes unnecessary margin classes from various View components to clean up spacing.
This commit is contained in:
@@ -273,7 +273,7 @@ const Login: React.FC = () => {
|
||||
textContentType='oneTimeCode'
|
||||
clearButtonMode='while-editing'
|
||||
maxLength={500}
|
||||
extraClassName='mb-4'
|
||||
extraClassName=''
|
||||
/>
|
||||
|
||||
{/* Password */}
|
||||
@@ -286,15 +286,15 @@ const Login: React.FC = () => {
|
||||
placeholder={t("login.password_placeholder")}
|
||||
showPassword={showPassword}
|
||||
onShowPasswordChange={setShowPassword}
|
||||
topPosition='15'
|
||||
topOffset={15}
|
||||
layout='tv'
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View className='mt-4'>
|
||||
<View className=''>
|
||||
<Button onPress={handleLogin}>{t("login.login_button")}</Button>
|
||||
</View>
|
||||
<View className='mt-3'>
|
||||
<View className=''>
|
||||
<Button
|
||||
onPress={handleQuickConnect}
|
||||
className='bg-neutral-800 border border-neutral-700'
|
||||
@@ -348,7 +348,7 @@ const Login: React.FC = () => {
|
||||
</View>
|
||||
|
||||
{/* Lists stay full width but inside max width container */}
|
||||
<View className='mt-2'>
|
||||
<View className='mt-4'>
|
||||
<JellyfinServerDiscovery
|
||||
onServerSelect={async (server: any) => {
|
||||
setServerURL(server.address);
|
||||
@@ -402,10 +402,10 @@ const Login: React.FC = () => {
|
||||
textContentType='oneTimeCode'
|
||||
clearButtonMode='while-editing'
|
||||
maxLength={500}
|
||||
extraClassName='mb-4'
|
||||
extraClassName=''
|
||||
/>
|
||||
|
||||
<View className='relative mb-0.5'>
|
||||
<View className='relative'>
|
||||
<PasswordInput
|
||||
value={credentials.password}
|
||||
onChangeText={(text) =>
|
||||
@@ -414,7 +414,7 @@ const Login: React.FC = () => {
|
||||
placeholder={t("login.password_placeholder")}
|
||||
showPassword={showPassword}
|
||||
onShowPasswordChange={setShowPassword}
|
||||
topPosition='10'
|
||||
topOffset={12}
|
||||
layout='mobile'
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import type React from "react";
|
||||
import { useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import { Input } from "./common/Input";
|
||||
|
||||
@@ -10,7 +11,7 @@ type PasswordVisibilityControlled = {
|
||||
placeholder: string;
|
||||
showPassword: boolean;
|
||||
onShowPasswordChange: (show: boolean) => void;
|
||||
topPosition?: string;
|
||||
topOffset?: number;
|
||||
layout?: "tv" | "mobile";
|
||||
};
|
||||
|
||||
@@ -20,8 +21,9 @@ type PasswordVisibilityUncontrolled = {
|
||||
placeholder: string;
|
||||
showPassword?: never;
|
||||
onShowPasswordChange?: never;
|
||||
topPosition?: string;
|
||||
topOffset?: number;
|
||||
layout?: "tv" | "mobile";
|
||||
defaultShowPassword?: boolean;
|
||||
};
|
||||
|
||||
type PasswordInputProps =
|
||||
@@ -33,7 +35,7 @@ export const PasswordInput: React.FC<PasswordInputProps> = (props) => {
|
||||
value = "",
|
||||
onChangeText,
|
||||
placeholder,
|
||||
topPosition = "3.5",
|
||||
topOffset = 14, // Default 14px for mobile
|
||||
layout = "mobile",
|
||||
} = props;
|
||||
|
||||
@@ -41,26 +43,36 @@ export const PasswordInput: React.FC<PasswordInputProps> = (props) => {
|
||||
const isControlled =
|
||||
"showPassword" in props && "onShowPasswordChange" in props;
|
||||
|
||||
// For controlled mode, use the provided props
|
||||
// For uncontrolled mode, use internal state (but we need to handle this differently)
|
||||
// Internal state for uncontrolled mode
|
||||
const [internalShowPassword, setInternalShowPassword] = useState(() =>
|
||||
!isControlled && "defaultShowPassword" in props
|
||||
? ((props as PasswordVisibilityUncontrolled).defaultShowPassword ?? false)
|
||||
: false,
|
||||
);
|
||||
|
||||
// Use controlled value if available, otherwise use internal state
|
||||
const showPassword = isControlled
|
||||
? (props as PasswordVisibilityControlled).showPassword
|
||||
: false;
|
||||
: internalShowPassword;
|
||||
|
||||
const handleTogglePassword = () => {
|
||||
if (isControlled) {
|
||||
(props as PasswordVisibilityControlled).onShowPasswordChange(
|
||||
!showPassword,
|
||||
);
|
||||
} else {
|
||||
// For uncontrolled mode, toggle internal state
|
||||
setInternalShowPassword(!showPassword);
|
||||
}
|
||||
// For uncontrolled mode, we could add internal state handling here if needed
|
||||
};
|
||||
|
||||
// Generate top position with pixel precision
|
||||
// Generate top position style with validation
|
||||
const getTopStyle = () => {
|
||||
// Use pixel values directly
|
||||
const positionInPx = parseFloat(topPosition);
|
||||
return { top: positionInPx };
|
||||
if (typeof topOffset !== "number" || Number.isNaN(topOffset)) {
|
||||
console.warn(`Invalid topOffset value: ${topOffset}`);
|
||||
return { top: 14 }; // Default fallback (14px for mobile)
|
||||
}
|
||||
return { top: topOffset };
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -70,7 +82,7 @@ export const PasswordInput: React.FC<PasswordInputProps> = (props) => {
|
||||
onChangeText={onChangeText}
|
||||
value={value}
|
||||
secureTextEntry={!showPassword}
|
||||
className='pr-4'
|
||||
extraClassName='pr-4'
|
||||
/>
|
||||
<TouchableOpacity
|
||||
onPress={handleTogglePassword}
|
||||
|
||||
@@ -177,7 +177,7 @@ export const FilterSheet = <T,>({
|
||||
{showSearch && (
|
||||
<Input
|
||||
placeholder={t("search.search")}
|
||||
className='my-2 border-neutral-800 border'
|
||||
extraClassName='my-2 border-neutral-800 border'
|
||||
value={search}
|
||||
onChangeText={(text) => {
|
||||
setSearch(text);
|
||||
|
||||
@@ -131,7 +131,7 @@ export const JellyseerrSettings = () => {
|
||||
</Text>
|
||||
</View>
|
||||
<Input
|
||||
className='border border-neutral-800 mb-2'
|
||||
extraClassName='border border-neutral-800 mb-2'
|
||||
placeholder={t(
|
||||
"home.settings.plugins.jellyseerr.server_url_placeholder",
|
||||
)}
|
||||
@@ -161,7 +161,7 @@ export const JellyseerrSettings = () => {
|
||||
showPassword={showJellyseerrPassword}
|
||||
onShowPasswordChange={setShowJellyseerrPassword}
|
||||
layout='mobile'
|
||||
topPosition='11'
|
||||
topOffset={11}
|
||||
/>
|
||||
</View>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user