fix themes modal, Add English to the trip information tab
This commit is contained in:
@@ -5,17 +5,19 @@ import CrewListTable from "@/components/tripInfo/CrewListTable";
|
|||||||
import FishingToolsTable from "@/components/tripInfo/FishingToolsList";
|
import FishingToolsTable from "@/components/tripInfo/FishingToolsList";
|
||||||
import NetListTable from "@/components/tripInfo/NetListTable";
|
import NetListTable from "@/components/tripInfo/NetListTable";
|
||||||
import TripCostTable from "@/components/tripInfo/TripCostTable";
|
import TripCostTable from "@/components/tripInfo/TripCostTable";
|
||||||
|
import { useI18n } from "@/hooks/use-i18n";
|
||||||
import { useThemeContext } from "@/hooks/use-theme-context";
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
||||||
import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
import { Platform, ScrollView, StyleSheet, Text, View } from "react-native";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function TripInfoScreen() {
|
export default function TripInfoScreen() {
|
||||||
|
const { t } = useI18n();
|
||||||
const { colors } = useThemeContext();
|
const { colors } = useThemeContext();
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.safeArea} edges={["top", "left", "right"]}>
|
<SafeAreaView style={styles.safeArea} edges={["top", "left", "right"]}>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={[styles.titleText, { color: colors.text }]}>
|
<Text style={[styles.titleText, { color: colors.text }]}>
|
||||||
Thông Tin Chuyến Đi
|
{t("trip.infoTrip")}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={styles.buttonWrapper}>
|
<View style={styles.buttonWrapper}>
|
||||||
<ButtonCreateNewHaulOrTrip />
|
<ButtonCreateNewHaulOrTrip />
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { useI18n } from "@/hooks/use-i18n";
|
|||||||
import {
|
import {
|
||||||
ColorScheme as ThemeColorScheme,
|
ColorScheme as ThemeColorScheme,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
useThemeContext,
|
||||||
} from "@/hooks/use-theme-context";
|
} from "@/hooks/use-theme-context";
|
||||||
import { showErrorToast, showWarningToast } from "@/services/toast_service";
|
import { showErrorToast, showWarningToast } from "@/services/toast_service";
|
||||||
import {
|
import {
|
||||||
@@ -44,6 +45,7 @@ export default function LoginScreen() {
|
|||||||
const [isShowingQRScanner, setIsShowingQRScanner] = useState(false);
|
const [isShowingQRScanner, setIsShowingQRScanner] = useState(false);
|
||||||
const { t, setLocale, locale } = useI18n();
|
const { t, setLocale, locale } = useI18n();
|
||||||
const { colors, colorScheme } = useTheme();
|
const { colors, colorScheme } = useTheme();
|
||||||
|
const { setThemeMode } = useThemeContext();
|
||||||
const styles = useMemo(
|
const styles = useMemo(
|
||||||
() => createStyles(colors, colorScheme),
|
() => createStyles(colors, colorScheme),
|
||||||
[colors, colorScheme]
|
[colors, colorScheme]
|
||||||
@@ -312,6 +314,10 @@ export default function LoginScreen() {
|
|||||||
inactiveBackgroundColor={colors.surface}
|
inactiveBackgroundColor={colors.surface}
|
||||||
inactiveOverlayColor={colors.textSecondary}
|
inactiveOverlayColor={colors.textSecondary}
|
||||||
activeOverlayColor={colors.background}
|
activeOverlayColor={colors.background}
|
||||||
|
value={colorScheme === "light"}
|
||||||
|
onChange={(val) => {
|
||||||
|
setThemeMode(val ? "light" : "dark");
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
||||||
import { AntDesign } from "@expo/vector-icons";
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
@@ -93,6 +94,12 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
|
|
||||||
const sz = sizeMap[size];
|
const sz = sizeMap[size];
|
||||||
|
|
||||||
|
// Theme colors from context (consistent with other components)
|
||||||
|
const { colors } = useThemeContext();
|
||||||
|
const selectBackgroundColor = disabled
|
||||||
|
? colors.backgroundSecondary
|
||||||
|
: colors.surface;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.wrapper}>
|
<View style={styles.wrapper}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -101,7 +108,8 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
{
|
{
|
||||||
height: sz.height,
|
height: sz.height,
|
||||||
paddingHorizontal: sz.paddingHorizontal,
|
paddingHorizontal: sz.paddingHorizontal,
|
||||||
opacity: disabled ? 0.6 : 1,
|
backgroundColor: selectBackgroundColor,
|
||||||
|
borderColor: disabled ? colors.border : colors.primary,
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
@@ -112,14 +120,18 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
>
|
>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<ActivityIndicator size="small" color="#4ecdc4" />
|
<ActivityIndicator size="small" color={colors.primary} />
|
||||||
) : (
|
) : (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.text,
|
styles.text,
|
||||||
{
|
{
|
||||||
fontSize: sz.fontSize,
|
fontSize: sz.fontSize,
|
||||||
color: selectedValue ? "#111" : "#999",
|
color: disabled
|
||||||
|
? colors.textSecondary
|
||||||
|
: selectedValue
|
||||||
|
? colors.text
|
||||||
|
: colors.textSecondary,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
@@ -131,24 +143,41 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
<View style={styles.suffix}>
|
<View style={styles.suffix}>
|
||||||
{allowClear && selectedValue && !loading ? (
|
{allowClear && selectedValue && !loading ? (
|
||||||
<TouchableOpacity onPress={handleClear} style={styles.icon}>
|
<TouchableOpacity onPress={handleClear} style={styles.icon}>
|
||||||
<AntDesign name="close" size={16} color="#999" />
|
<AntDesign name="close" size={16} color={colors.textSecondary} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : null}
|
) : null}
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name={isOpen ? "up" : "down"}
|
name={isOpen ? "up" : "down"}
|
||||||
size={14}
|
size={14}
|
||||||
color="#999"
|
color={colors.textSecondary}
|
||||||
style={styles.arrow}
|
style={styles.arrow}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<View style={[styles.dropdown, { top: containerHeight }]}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.dropdown,
|
||||||
|
{
|
||||||
|
top: containerHeight,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
borderColor: colors.border,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
{showSearch && (
|
{showSearch && (
|
||||||
<TextInput
|
<TextInput
|
||||||
style={styles.searchInput}
|
style={[
|
||||||
|
styles.searchInput,
|
||||||
|
{
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
borderColor: colors.border,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
]}
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
|
placeholderTextColor={colors.textSecondary}
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChangeText={setSearchText}
|
onChangeText={setSearchText}
|
||||||
autoFocus
|
autoFocus
|
||||||
@@ -160,8 +189,13 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
key={item.value}
|
key={item.value}
|
||||||
style={[
|
style={[
|
||||||
styles.option,
|
styles.option,
|
||||||
|
{
|
||||||
|
borderBottomColor: colors.separator,
|
||||||
|
},
|
||||||
item.disabled && styles.optionDisabled,
|
item.disabled && styles.optionDisabled,
|
||||||
selectedValue === item.value && styles.optionSelected,
|
selectedValue === item.value && {
|
||||||
|
backgroundColor: colors.primary + "20", // Add transparency to primary color
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
onPress={() => !item.disabled && handleSelect(item.value)}
|
onPress={() => !item.disabled && handleSelect(item.value)}
|
||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
@@ -169,14 +203,22 @@ const Select: React.FC<SelectProps> = ({
|
|||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.optionText,
|
styles.optionText,
|
||||||
item.disabled && styles.optionTextDisabled,
|
{
|
||||||
selectedValue === item.value && styles.optionTextSelected,
|
color: colors.text,
|
||||||
|
},
|
||||||
|
item.disabled && {
|
||||||
|
color: colors.textSecondary,
|
||||||
|
},
|
||||||
|
selectedValue === item.value && {
|
||||||
|
color: colors.primary,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</Text>
|
</Text>
|
||||||
{selectedValue === item.value && (
|
{selectedValue === item.value && (
|
||||||
<AntDesign name="check" size={16} color="#4ecdc4" />
|
<AntDesign name="check" size={16} color={colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
@@ -193,9 +235,7 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#e6e6e6",
|
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: "#fff",
|
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
@@ -204,7 +244,7 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
color: "#111",
|
// Color is set dynamically via theme
|
||||||
},
|
},
|
||||||
suffix: {
|
suffix: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -220,9 +260,7 @@ const styles = StyleSheet.create({
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
backgroundColor: "#fff",
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#e6e6e6",
|
|
||||||
borderTopWidth: 0,
|
borderTopWidth: 0,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
borderBottomLeftRadius: 8,
|
borderBottomLeftRadius: 8,
|
||||||
@@ -236,7 +274,6 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
searchInput: {
|
searchInput: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#e6e6e6",
|
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
padding: 8,
|
padding: 8,
|
||||||
margin: 8,
|
margin: 8,
|
||||||
@@ -247,7 +284,6 @@ const styles = StyleSheet.create({
|
|||||||
option: {
|
option: {
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: "#f0f0f0",
|
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@@ -255,20 +291,11 @@ const styles = StyleSheet.create({
|
|||||||
optionDisabled: {
|
optionDisabled: {
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
},
|
},
|
||||||
optionSelected: {
|
// optionSelected is handled dynamically via inline styles
|
||||||
backgroundColor: "#f6ffed",
|
|
||||||
},
|
|
||||||
optionText: {
|
optionText: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: "#111",
|
|
||||||
},
|
|
||||||
optionTextDisabled: {
|
|
||||||
color: "#999",
|
|
||||||
},
|
|
||||||
optionTextSelected: {
|
|
||||||
color: "#4ecdc4",
|
|
||||||
fontWeight: "600",
|
|
||||||
},
|
},
|
||||||
|
// optionTextDisabled and optionTextSelected are handled dynamically via inline styles
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Select;
|
export default Select;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Text, View } from "react-native";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
|
import { useAppTheme } from "@/hooks/use-app-theme";
|
||||||
|
import { View } from "react-native";
|
||||||
|
|
||||||
interface DescriptionProps {
|
interface DescriptionProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -8,10 +10,15 @@ export const Description = ({
|
|||||||
title = "",
|
title = "",
|
||||||
description = "",
|
description = "",
|
||||||
}: DescriptionProps) => {
|
}: DescriptionProps) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
return (
|
return (
|
||||||
<View className="flex-row gap-2 ">
|
<View className="flex-row gap-2 ">
|
||||||
<Text className="opacity-50 text-lg">{title}:</Text>
|
<ThemedText
|
||||||
<Text className="text-lg">{description}</Text>
|
style={{ color: colors.textSecondary, fontSize: 16 }}
|
||||||
|
>
|
||||||
|
{title}:
|
||||||
|
</ThemedText>
|
||||||
|
<ThemedText style={{ fontSize: 16 }}>{description}</ThemedText>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useAppTheme } from "@/hooks/use-app-theme";
|
||||||
import { useI18n } from "@/hooks/use-i18n";
|
import { useI18n } from "@/hooks/use-i18n";
|
||||||
import { convertToDMS, kmhToKnot } from "@/utils/geom";
|
import { convertToDMS, kmhToKnot } from "@/utils/geom";
|
||||||
import { MaterialIcons } from "@expo/vector-icons";
|
import { MaterialIcons } from "@expo/vector-icons";
|
||||||
@@ -15,6 +16,8 @@ const GPSInfoPanel = ({ gpsData }: GPSInfoPanelProps) => {
|
|||||||
const translateY = useRef(new Animated.Value(0)).current;
|
const translateY = useRef(new Animated.Value(0)).current;
|
||||||
const blockBottom = useRef(new Animated.Value(0)).current;
|
const blockBottom = useRef(new Animated.Value(0)).current;
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { colors, styles } = useAppTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Animated.timing(translateY, {
|
Animated.timing(translateY, {
|
||||||
toValue: isExpanded ? 0 : 200, // Dịch chuyển xuống 200px khi thu gọn
|
toValue: isExpanded ? 0 : 200, // Dịch chuyển xuống 200px khi thu gọn
|
||||||
@@ -44,45 +47,35 @@ const GPSInfoPanel = ({ gpsData }: GPSInfoPanelProps) => {
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: blockBottom,
|
bottom: blockBottom,
|
||||||
left: 5,
|
left: 5,
|
||||||
// width: 48,
|
|
||||||
// height: 48,
|
|
||||||
// backgroundColor: "blue",
|
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
zIndex: 30,
|
zIndex: 30,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonCreateNewHaulOrTrip gpsData={gpsData} />
|
<ButtonCreateNewHaulOrTrip gpsData={gpsData} />
|
||||||
{/* <TouchableOpacity
|
|
||||||
onPress={() => {
|
|
||||||
// showInfoToast("oad");
|
|
||||||
showWarningToast("This is a warning toast!");
|
|
||||||
}}
|
|
||||||
className="absolute top-2 right-2 z-10 bg-white rounded-full p-1"
|
|
||||||
>
|
|
||||||
<MaterialIcons
|
|
||||||
name={isExpanded ? "close" : "close"}
|
|
||||||
size={20}
|
|
||||||
color="#666"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity> */}
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
|
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={{
|
style={[
|
||||||
transform: [{ translateY }],
|
styles.card,
|
||||||
}}
|
{
|
||||||
className="absolute bottom-0 gap-3 right-0 p-3 left-0 h-auto w-full rounded-t-xl bg-white shadow-md"
|
transform: [{ translateY }],
|
||||||
|
backgroundColor: colors.card,
|
||||||
|
borderRadius: 0,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
className="absolute bottom-0 gap-5 right-0 px-4 pt-12 pb-2 left-0 h-auto w-full rounded-t-xl shadow-md"
|
||||||
onLayout={(event) => setPanelHeight(event.nativeEvent.layout.height)}
|
onLayout={(event) => setPanelHeight(event.nativeEvent.layout.height)}
|
||||||
>
|
>
|
||||||
{/* Nút toggle ở top-right */}
|
{/* Nút toggle ở top-right */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={togglePanel}
|
onPress={togglePanel}
|
||||||
className="absolute top-2 right-2 z-10 bg-white rounded-full p-1"
|
className="absolute top-2 right-2 z-10 rounded-full p-1"
|
||||||
|
style={{ backgroundColor: colors.card }}
|
||||||
>
|
>
|
||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name={isExpanded ? "close" : "close"}
|
name={isExpanded ? "close" : "close"}
|
||||||
size={20}
|
size={20}
|
||||||
color="#666"
|
color={colors.icon}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
@@ -120,9 +113,10 @@ const GPSInfoPanel = ({ gpsData }: GPSInfoPanelProps) => {
|
|||||||
{!isExpanded && (
|
{!isExpanded && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={togglePanel}
|
onPress={togglePanel}
|
||||||
className="absolute bottom-5 right-2 z-20 bg-white rounded-full p-2 shadow-lg"
|
className="absolute bottom-5 right-2 z-20 rounded-full p-2 shadow-lg"
|
||||||
|
style={{ backgroundColor: colors.card }}
|
||||||
>
|
>
|
||||||
<MaterialIcons name="info-outline" size={24} />
|
<MaterialIcons name="info-outline" size={24} color={colors.icon} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { StyleSheet, Text, TextInput, View } from "react-native";
|
|||||||
import IconButton from "../IconButton";
|
import IconButton from "../IconButton";
|
||||||
import Select from "../Select";
|
import Select from "../Select";
|
||||||
import Modal from "../ui/modal";
|
import Modal from "../ui/modal";
|
||||||
|
import { useThemeColor } from "@/hooks/use-theme-color";
|
||||||
|
|
||||||
const SosButton = () => {
|
const SosButton = () => {
|
||||||
const [sosData, setSosData] = useState<Model.SosResponse | null>();
|
const [sosData, setSosData] = useState<Model.SosResponse | null>();
|
||||||
@@ -22,6 +23,16 @@ const SosButton = () => {
|
|||||||
const [customMessage, setCustomMessage] = useState("");
|
const [customMessage, setCustomMessage] = useState("");
|
||||||
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// Theme colors
|
||||||
|
const textColor = useThemeColor({}, 'text');
|
||||||
|
const borderColor = useThemeColor({}, 'border');
|
||||||
|
const errorColor = useThemeColor({}, 'error');
|
||||||
|
const backgroundColor = useThemeColor({}, 'background');
|
||||||
|
|
||||||
|
// Dynamic styles
|
||||||
|
const styles = SosButtonStyles(textColor, borderColor, errorColor, backgroundColor);
|
||||||
|
|
||||||
const sosOptions = [
|
const sosOptions = [
|
||||||
...sosMessage.map((msg) => ({
|
...sosMessage.map((msg) => ({
|
||||||
ma: msg.ma,
|
ma: msg.ma,
|
||||||
@@ -176,7 +187,7 @@ const SosButton = () => {
|
|||||||
errors.customMessage ? styles.errorInput : {},
|
errors.customMessage ? styles.errorInput : {},
|
||||||
]}
|
]}
|
||||||
placeholder={t("home.sos.enterStatus")}
|
placeholder={t("home.sos.enterStatus")}
|
||||||
placeholderTextColor="#999"
|
placeholderTextColor={textColor + '99'} // Add transparency
|
||||||
value={customMessage}
|
value={customMessage}
|
||||||
onChangeText={(text) => {
|
onChangeText={(text) => {
|
||||||
setCustomMessage(text);
|
setCustomMessage(text);
|
||||||
@@ -201,7 +212,7 @@ const SosButton = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const SosButtonStyles = (textColor: string, borderColor: string, errorColor: string, backgroundColor: string) => StyleSheet.create({
|
||||||
formGroup: {
|
formGroup: {
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
},
|
},
|
||||||
@@ -209,26 +220,27 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
color: "#333",
|
color: textColor,
|
||||||
},
|
},
|
||||||
errorBorder: {
|
errorBorder: {
|
||||||
borderColor: "#ff4444",
|
borderColor: errorColor,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#ddd",
|
borderColor: borderColor,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: "#333",
|
color: textColor,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
textAlignVertical: "top",
|
textAlignVertical: "top",
|
||||||
},
|
},
|
||||||
errorInput: {
|
errorInput: {
|
||||||
borderColor: "#ff4444",
|
borderColor: errorColor,
|
||||||
},
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: "#ff4444",
|
color: errorColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const InfoSection: React.FC<InfoSectionProps> = ({
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { colors } = useThemeContext();
|
const { colors } = useThemeContext();
|
||||||
const styles = React.useMemo(() => createStyles(colors), [colors]);
|
const styles = React.useMemo(() => createStyles(colors), [colors]);
|
||||||
|
|
||||||
if (!fishingLog) {
|
if (!fishingLog) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ const createStyles = (colors: any) =>
|
|||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
backgroundColor: colors.backgroundSecondary,
|
backgroundColor: colors.surfaceSecondary,
|
||||||
},
|
},
|
||||||
infoRow: {
|
infoRow: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const createStyles = (colors: typeof Colors.light) =>
|
|||||||
marginBottom: 15,
|
marginBottom: 15,
|
||||||
},
|
},
|
||||||
fishCard: {
|
fishCard: {
|
||||||
backgroundColor: colors.card,
|
backgroundColor: colors.surfaceSecondary,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
@@ -95,7 +95,7 @@ export const createStyles = (colors: typeof Colors.light) =>
|
|||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.border,
|
borderColor: colors.primary,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
paddingHorizontal: 12,
|
paddingHorizontal: 12,
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
@@ -106,7 +106,7 @@ export const createStyles = (colors: typeof Colors.light) =>
|
|||||||
inputDisabled: {
|
inputDisabled: {
|
||||||
backgroundColor: colors.backgroundSecondary,
|
backgroundColor: colors.backgroundSecondary,
|
||||||
color: colors.textSecondary,
|
color: colors.textSecondary,
|
||||||
borderColor: colors.separator,
|
borderColor: colors.border,
|
||||||
},
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: colors.error,
|
color: colors.error,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import type { ComponentProps } from "react";
|
import type { ComponentProps } from "react";
|
||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Animated,
|
Animated,
|
||||||
OpaqueColorValue,
|
OpaqueColorValue,
|
||||||
@@ -29,7 +29,7 @@ const PRESS_FEEDBACK_DURATION = 120;
|
|||||||
|
|
||||||
type IoniconName = ComponentProps<typeof Ionicons>["name"];
|
type IoniconName = ComponentProps<typeof Ionicons>["name"];
|
||||||
|
|
||||||
type RotateSwitchProps = {
|
type SliceSwitchProps = {
|
||||||
size?: SwitchSize;
|
size?: SwitchSize;
|
||||||
leftIcon?: IoniconName;
|
leftIcon?: IoniconName;
|
||||||
leftIconColor?: string | OpaqueColorValue | undefined;
|
leftIconColor?: string | OpaqueColorValue | undefined;
|
||||||
@@ -42,6 +42,7 @@ type RotateSwitchProps = {
|
|||||||
activeOverlayColor?: string;
|
activeOverlayColor?: string;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
onChange?: (value: boolean) => void;
|
onChange?: (value: boolean) => void;
|
||||||
|
value?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SliceSwitch = ({
|
const SliceSwitch = ({
|
||||||
@@ -57,19 +58,28 @@ const SliceSwitch = ({
|
|||||||
activeOverlayColor = "#000",
|
activeOverlayColor = "#000",
|
||||||
style,
|
style,
|
||||||
onChange,
|
onChange,
|
||||||
}: RotateSwitchProps) => {
|
value,
|
||||||
|
}: SliceSwitchProps) => {
|
||||||
const { width: containerWidth, height: containerHeight } =
|
const { width: containerWidth, height: containerHeight } =
|
||||||
SIZE_PRESETS[size] ?? SIZE_PRESETS.md;
|
SIZE_PRESETS[size] ?? SIZE_PRESETS.md;
|
||||||
const animationDuration = Math.max(duration ?? DEFAULT_TOGGLE_DURATION, 0);
|
const animationDuration = Math.max(duration ?? DEFAULT_TOGGLE_DURATION, 0);
|
||||||
const [isOn, setIsOn] = useState(false);
|
const [isOn, setIsOn] = useState(value ?? false);
|
||||||
const [bgOn, setBgOn] = useState(false);
|
const [bgOn, setBgOn] = useState(value ?? false);
|
||||||
const progress = useRef(new Animated.Value(0)).current;
|
const progress = useRef(new Animated.Value(value ? 1 : 0)).current;
|
||||||
const pressScale = useRef(new Animated.Value(1)).current;
|
const pressScale = useRef(new Animated.Value(1)).current;
|
||||||
const overlayTranslateX = useRef(new Animated.Value(0)).current;
|
const overlayTranslateX = useRef(
|
||||||
|
new Animated.Value(value ? containerWidth / 2 : 0)
|
||||||
|
).current;
|
||||||
const listenerIdRef = useRef<string | number | null>(null);
|
const listenerIdRef = useRef<string | number | null>(null);
|
||||||
|
|
||||||
const handleToggle = () => {
|
// Sync with external value prop if provided
|
||||||
const next = !isOn;
|
useEffect(() => {
|
||||||
|
if (value !== undefined && value !== isOn) {
|
||||||
|
animateToValue(value);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const animateToValue = (next: boolean) => {
|
||||||
const targetValue = next ? 1 : 0;
|
const targetValue = next ? 1 : 0;
|
||||||
const overlayTarget = next ? containerWidth / 2 : 0;
|
const overlayTarget = next ? containerWidth / 2 : 0;
|
||||||
|
|
||||||
@@ -81,7 +91,6 @@ const SliceSwitch = ({
|
|||||||
overlayTranslateX.setValue(overlayTarget);
|
overlayTranslateX.setValue(overlayTarget);
|
||||||
setIsOn(next);
|
setIsOn(next);
|
||||||
setBgOn(next);
|
setBgOn(next);
|
||||||
onChange?.(next);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +109,6 @@ const SliceSwitch = ({
|
|||||||
}),
|
}),
|
||||||
]).start(() => {
|
]).start(() => {
|
||||||
setBgOn(next);
|
setBgOn(next);
|
||||||
onChange?.(next);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove any previous listener
|
// Remove any previous listener
|
||||||
@@ -132,6 +140,14 @@ const SliceSwitch = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
const next = !isOn;
|
||||||
|
if (value === undefined) {
|
||||||
|
animateToValue(next);
|
||||||
|
}
|
||||||
|
onChange?.(next);
|
||||||
|
};
|
||||||
|
|
||||||
const handlePressIn = () => {
|
const handlePressIn = () => {
|
||||||
pressScale.stopAnimation();
|
pressScale.stopAnimation();
|
||||||
Animated.timing(pressScale, {
|
Animated.timing(pressScale, {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* Provides styled components and theme utilities
|
* Provides styled components and theme utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useThemeContext } from "@/hooks/use-theme-context";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { StyleSheet, TextStyle, ViewStyle } from "react-native";
|
import { StyleSheet, TextStyle, ViewStyle } from "react-native";
|
||||||
import { useThemeContext } from "@/hooks/use-theme-context";
|
|
||||||
|
|
||||||
export function useAppTheme() {
|
export function useAppTheme() {
|
||||||
const { colors, colorScheme, themeMode, setThemeMode, getColor } =
|
const { colors, colorScheme, themeMode, setThemeMode, getColor } =
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trip": {
|
"trip": {
|
||||||
|
"infoTrip": "Trip Information",
|
||||||
"createNewTrip": "Create New Trip",
|
"createNewTrip": "Create New Trip",
|
||||||
"endTrip": "End Trip",
|
"endTrip": "End Trip",
|
||||||
"cancelTrip": "Cancel Trip",
|
"cancelTrip": "Cancel Trip",
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trip": {
|
"trip": {
|
||||||
|
"infoTrip": "Thông Tin Chuyến Đi",
|
||||||
"createNewTrip": "Tạo chuyến mới",
|
"createNewTrip": "Tạo chuyến mới",
|
||||||
"endTrip": "Kết thúc chuyến",
|
"endTrip": "Kết thúc chuyến",
|
||||||
"cancelTrip": "Hủy chuyến",
|
"cancelTrip": "Hủy chuyến",
|
||||||
|
|||||||
Reference in New Issue
Block a user