refactor: enhance LoginModal and StyledButton components for improved functionality

This commit is contained in:
zimplexing
2025-07-15 15:49:46 +08:00
parent bf99aee5f2
commit 8985781865
5 changed files with 140 additions and 127 deletions

2
.gitignore vendored
View File

@@ -23,5 +23,5 @@ expo-env.d.ts
web/**
.bmad-core
.kilocodemodes
.roomode
.roomodes
yarn-errors.log

View File

@@ -1,5 +1,5 @@
import React, { useState } from "react";
import { Modal, View, Text, TextInput, StyleSheet, ActivityIndicator } from "react-native";
import React, { useState, useRef } from "react";
import { Modal, View, TextInput, StyleSheet, ActivityIndicator } from "react-native";
import Toast from "react-native-toast-message";
import useAuthStore from "@/stores/authStore";
import { useSettingsStore } from "@/stores/settingsStore";
@@ -16,6 +16,8 @@ const LoginModal = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
const passwordInputRef = useRef<TextInput>(null);
const loginButtonRef = useRef<View>(null);
const handleLogin = async () => {
const isLocalStorage = serverConfig?.StorageType === "localstorage";
@@ -32,7 +34,7 @@ const LoginModal = () => {
hideLoginModal();
setUsername("");
setPassword("");
} catch (error) {
} catch {
Toast.show({ type: "error", text1: "登录失败", text2: "用户名或密码错误" });
} finally {
setIsLoading(false);
@@ -53,17 +55,28 @@ const LoginModal = () => {
value={username}
onChangeText={setUsername}
autoFocus
returnKeyType="next"
onSubmitEditing={() => passwordInputRef.current?.focus()}
/>
)}
<TextInput
ref={passwordInputRef}
style={styles.input}
placeholder="请输入密码"
placeholderTextColor="#888"
secureTextEntry
value={password}
onChangeText={setPassword}
returnKeyType="next"
onSubmitEditing={() => loginButtonRef.current?.focus()}
/>
<StyledButton text={isLoading ? "" : "登录"} onPress={handleLogin} disabled={isLoading} style={styles.button}>
<StyledButton
ref={loginButtonRef}
text={isLoading ? "" : "登录"}
onPress={handleLogin}
disabled={isLoading}
style={styles.button}
>
{isLoading && <ActivityIndicator color="#fff" />}
</StyledButton>
</ThemedView>

View File

@@ -1,5 +1,5 @@
import React from "react";
import { Animated, Pressable, StyleSheet, StyleProp, ViewStyle, PressableProps, TextStyle } from "react-native";
import React, { forwardRef } from "react";
import { Animated, Pressable, StyleSheet, StyleProp, ViewStyle, PressableProps, TextStyle, View } from "react-native";
import { ThemedText } from "./ThemedText";
import { Colors } from "@/constants/Colors";
import { useButtonAnimation } from "@/hooks/useAnimation";
@@ -13,133 +13,130 @@ interface StyledButtonProps extends PressableProps {
textStyle?: StyleProp<TextStyle>;
}
export const StyledButton: React.FC<StyledButtonProps> = ({
children,
text,
variant = "default",
isSelected = false,
style,
textStyle,
...rest
}) => {
const colorScheme = "dark";
const colors = Colors[colorScheme];
const [isFocused, setIsFocused] = React.useState(false);
const animationStyle = useButtonAnimation(isFocused);
export const StyledButton = forwardRef<View, StyledButtonProps>(
({ children, text, variant = "default", isSelected = false, style, textStyle, ...rest }, ref) => {
const colorScheme = "dark";
const colors = Colors[colorScheme];
const [isFocused, setIsFocused] = React.useState(false);
const animationStyle = useButtonAnimation(isFocused);
const variantStyles = {
default: StyleSheet.create({
const variantStyles = {
default: StyleSheet.create({
button: {
backgroundColor: colors.border,
},
text: {
color: colors.text,
},
selectedButton: {
backgroundColor: colors.tint,
},
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
},
selectedText: {
color: Colors.dark.text,
},
}),
primary: StyleSheet.create({
button: {
backgroundColor: "transparent",
},
text: {
color: colors.text,
},
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
},
selectedButton: {
backgroundColor: "rgba(0, 122, 255, 0.3)",
},
selectedText: {
color: colors.link,
},
}),
ghost: StyleSheet.create({
button: {
backgroundColor: "transparent",
},
text: {
color: colors.text,
},
focusedButton: {
backgroundColor: "rgba(119, 119, 119, 0.9)",
},
selectedButton: {},
selectedText: {},
}),
};
const styles = StyleSheet.create({
button: {
backgroundColor: colors.border,
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 8,
borderWidth: 2,
borderColor: "transparent",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
text: {
color: colors.text,
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
elevation: 5,
shadowColor: colors.link,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 1,
shadowRadius: 15,
},
selectedButton: {
backgroundColor: colors.tint,
},
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
text: {
fontSize: 16,
fontWeight: "500",
color: colors.text,
},
selectedText: {
color: Colors.dark.text,
},
}),
primary: StyleSheet.create({
button: {
backgroundColor: "transparent",
},
text: {
color: colors.text,
},
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
},
selectedButton: {
backgroundColor: "rgba(0, 122, 255, 0.3)",
},
selectedText: {
color: colors.link,
},
}),
ghost: StyleSheet.create({
button: {
backgroundColor: "transparent",
},
text: {
color: colors.text,
},
focusedButton: {
backgroundColor: "rgba(119, 119, 119, 0.9)",
},
selectedButton: {},
selectedText: {},
}),
};
});
const styles = StyleSheet.create({
button: {
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 8,
borderWidth: 2,
borderColor: "transparent",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
focusedButton: {
backgroundColor: colors.link,
borderColor: colors.background,
elevation: 5,
shadowColor: colors.link,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 1,
shadowRadius: 15,
},
selectedButton: {
backgroundColor: colors.tint,
},
text: {
fontSize: 16,
fontWeight: "500",
color: colors.text,
},
selectedText: {
color: Colors.dark.text,
},
});
return (
<Animated.View style={[animationStyle, style]}>
<Pressable
ref={ref}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
style={({ focused }) => [
styles.button,
variantStyles[variant].button,
isSelected && (variantStyles[variant].selectedButton ?? styles.selectedButton),
focused && (variantStyles[variant].focusedButton ?? styles.focusedButton),
]}
{...rest}
>
{text ? (
<ThemedText
style={[
styles.text,
variantStyles[variant].text,
isSelected && (variantStyles[variant].selectedText ?? styles.selectedText),
textStyle,
]}
>
{text}
</ThemedText>
) : (
children
)}
</Pressable>
</Animated.View>
);
}
);
return (
<Animated.View style={[animationStyle, style]}>
<Pressable
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
style={({ focused }) => [
styles.button,
variantStyles[variant].button,
isSelected && (variantStyles[variant].selectedButton ?? styles.selectedButton),
focused && (variantStyles[variant].focusedButton ?? styles.focusedButton),
]}
{...rest}
>
{text ? (
<ThemedText
style={[
styles.text,
variantStyles[variant].text,
isSelected && (variantStyles[variant].selectedText ?? styles.selectedText),
textStyle,
]}
>
{text}
</ThemedText>
) : (
children
)}
</Pressable>
</Animated.View>
);
};
StyledButton.displayName = "StyledButton";

View File

@@ -42,10 +42,13 @@ export interface SearchResult {
}
export interface Favorite {
cover: string;
title: string;
poster: string;
source_name: string;
save_time: number;
total_episodes: number;
search_title: string;
year: string;
}
export interface PlayRecord {

View File

@@ -82,7 +82,7 @@ export class FavoriteManager {
return (await api.getFavorites()) as Record<string, Favorite>;
}
static async save(source: string, id: string, item: Omit<Favorite, "save_time">): Promise<void> {
static async save(source: string, id: string, item: Favorite): Promise<void> {
const key = generateKey(source, id);
await api.addFavorite(key, item);
}
@@ -98,7 +98,7 @@ export class FavoriteManager {
return favorite !== null;
}
static async toggle(source: string, id: string, item: Omit<Favorite, "save_time">): Promise<boolean> {
static async toggle(source: string, id: string, item: Favorite): Promise<boolean> {
const isFav = await this.isFavorited(source, id);
if (isFav) {
await this.remove(source, id);