diff --git a/app/settings.tsx b/app/settings.tsx
index 8a0eace..f860834 100644
--- a/app/settings.tsx
+++ b/app/settings.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from "react";
-import { View, StyleSheet, FlatList, Alert, KeyboardAvoidingView, Platform } from "react-native";
+import { View, StyleSheet, FlatList, Alert, KeyboardAvoidingView, Platform, ScrollView } from "react-native";
import { useTVEventHandler } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ThemedText } from "@/components/ThemedText";
@@ -20,6 +20,7 @@ import { getCommonResponsiveStyles } from "@/utils/ResponsiveStyles";
import ResponsiveNavigation from "@/components/navigation/ResponsiveNavigation";
import ResponsiveHeader from "@/components/navigation/ResponsiveHeader";
import { DeviceUtils } from "@/utils/DeviceUtils";
+import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
export default function SettingsScreen() {
const { loadSettings, saveSettings, setApiBaseUrl, setM3uUrl } = useSettingsStore();
@@ -50,6 +51,7 @@ export default function SettingsScreen() {
const realMessage = lastMessage.split("_")[0];
handleRemoteInput(realMessage);
clearMessage(); // Clear the message after processing
+ markAsChanged();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [lastMessage, targetPage]);
@@ -164,13 +166,22 @@ export default function SettingsScreen() {
[currentFocusIndex, sections.length, deviceType]
);
- useTVEventHandler(deviceType === "tv" ? handleTVEvent : () => {});
+ useTVEventHandler(deviceType === "tv" ? handleTVEvent : () => { });
// 动态样式
const dynamicStyles = createResponsiveStyles(deviceType, spacing, insets);
const renderSettingsContent = () => (
-
+ //
+
+
{deviceType === "tv" && (
@@ -203,7 +214,8 @@ export default function SettingsScreen() {
/>
-
+
+ //
);
// 根据设备类型决定是否包装在响应式导航中
diff --git a/components/VideoCard.tv.tsx b/components/VideoCard.tv.tsx
index 423b097..78411b9 100644
--- a/components/VideoCard.tv.tsx
+++ b/components/VideoCard.tv.tsx
@@ -150,126 +150,69 @@ const VideoCard = forwardRef(
return (
- {Platform.isTV || deviceType !== 'tv' ? (
-
-
-
- {isFocused && (
-
- {isContinueWatching && (
-
-
- 继续观看
-
- )}
-
- )}
+ [
+ styles.pressable,
+ {
+ zIndex: pressed ? 999 : 1, // 确保按下时有最高优先级
+ },
+ ]}
+ // activeOpacity={1}
+ delayLongPress={1000}
+ >
+
+
+ {isFocused && (
+
+ {isContinueWatching && (
+
+
+ 继续观看
+
+ )}
+
+ )}
- {/* 进度条 */}
- {isContinueWatching && (
-
-
-
- )}
-
- {rate && (
-
-
- {rate}
-
- )}
- {year && (
-
- {year}
-
- )}
- {sourceName && (
-
- {sourceName}
-
- )}
-
-
- {title}
- {isContinueWatching && (
-
-
- 第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
-
-
- )}
-
-
- ) : (
-
-
-
- {isFocused && (
-
- {isContinueWatching && (
-
-
- 继续观看
-
- )}
-
- )}
-
- {/* 进度条 */}
- {isContinueWatching && (
-
-
-
- )}
-
- {rate && (
-
-
- {rate}
-
- )}
- {year && (
-
- {year}
-
- )}
- {sourceName && (
-
- {sourceName}
-
- )}
-
-
- {title}
- {isContinueWatching && (
-
-
- 第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
-
-
- )}
-
-
-
- )}
+ {/* 进度条 */}
+ {isContinueWatching && (
+
+
+
+ )}
+ {rate && (
+
+
+ {rate}
+
+ )}
+ {year && (
+
+ {year}
+
+ )}
+ {sourceName && (
+
+ {sourceName}
+
+ )}
+
+
+ {title}
+ {isContinueWatching && (
+
+
+ 第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
+
+
+ )}
+
+
);
}
diff --git a/components/settings/APIConfigSection.tsx b/components/settings/APIConfigSection.tsx
index 7868eac..14fcaa4 100644
--- a/components/settings/APIConfigSection.tsx
+++ b/components/settings/APIConfigSection.tsx
@@ -1,5 +1,5 @@
import React, { useState, useRef, useImperativeHandle, forwardRef } from "react";
-import { View, TextInput, StyleSheet, Animated } from "react-native";
+import { View, TextInput, StyleSheet, Animated, Platform } from "react-native";
import { useTVEventHandler } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { SettingsSection } from "./SettingsSection";
@@ -7,11 +7,13 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { useRemoteControlStore } from "@/stores/remoteControlStore";
import { useButtonAnimation } from "@/hooks/useAnimation";
import { Colors } from "@/constants/Colors";
+import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
interface APIConfigSectionProps {
onChanged: () => void;
onFocus?: () => void;
onBlur?: () => void;
+ onPress?: () => void;
hideDescription?: boolean;
}
@@ -20,13 +22,14 @@ export interface APIConfigSectionRef {
}
export const APIConfigSection = forwardRef(
- ({ onChanged, onFocus, onBlur, hideDescription = false }, ref) => {
+ ({ onChanged, onFocus, onBlur, onPress, hideDescription = false }, ref) => {
const { apiBaseUrl, setApiBaseUrl, remoteInputEnabled } = useSettingsStore();
const { serverUrl } = useRemoteControlStore();
const [isInputFocused, setIsInputFocused] = useState(false);
const [isSectionFocused, setIsSectionFocused] = useState(false);
const inputRef = useRef(null);
const inputAnimationStyle = useButtonAnimation(isSectionFocused, 1.01);
+ const deviceType = useResponsiveLayout().deviceType;
const handleUrlChange = (url: string) => {
setApiBaseUrl(url);
@@ -60,10 +63,28 @@ export const APIConfigSection = forwardRef {
+ inputRef.current?.focus();
+ onPress?.();
+ }
+
useTVEventHandler(handleTVEvent);
+ const [selection, setSelection] = useState<{ start: number; end: number }>({
+ start: 0,
+ end: 0,
+ });
+ // 当用户手动移动光标或选中文本时,同步到 state(可选)
+ const onSelectionChange = ({
+ nativeEvent: { selection },
+ }: any) => {
+ setSelection(selection);
+ };
+
return (
-
+
API 地址
@@ -81,7 +102,21 @@ export const APIConfigSection = forwardRef setIsInputFocused(true)}
+ onFocus={() => {
+ setIsInputFocused(true);
+ // 将光标移动到文本末尾
+ const end = apiBaseUrl.length;
+ setSelection({ start: end, end: end });
+ // 有时需要延迟一下,让系统先完成 focus 再设置 selection
+ //(在 Android 上更可靠)
+ setTimeout(() => {
+ // 对于受控的 selection 已经生效,这里仅作保险
+ inputRef.current?.setNativeProps({ selection: { start: end, end: end } });
+ }, 0);
+ }}
+ selection={selection}
+ onSelectionChange={onSelectionChange} // 可选
+
onBlur={() => setIsInputFocused(false)}
/>
diff --git a/components/settings/LiveStreamSection.tsx b/components/settings/LiveStreamSection.tsx
index d09e680..7d1dd54 100644
--- a/components/settings/LiveStreamSection.tsx
+++ b/components/settings/LiveStreamSection.tsx
@@ -1,5 +1,5 @@
import React, { useState, useRef, useImperativeHandle, forwardRef } from "react";
-import { View, TextInput, StyleSheet, Animated } from "react-native";
+import { View, TextInput, StyleSheet, Animated, Platform } from "react-native";
import { useTVEventHandler } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { SettingsSection } from "./SettingsSection";
@@ -7,11 +7,13 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { useRemoteControlStore } from "@/stores/remoteControlStore";
import { useButtonAnimation } from "@/hooks/useAnimation";
import { Colors } from "@/constants/Colors";
+import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
interface LiveStreamSectionProps {
onChanged: () => void;
onFocus?: () => void;
onBlur?: () => void;
+ onPress?: () => void;
}
export interface LiveStreamSectionRef {
@@ -19,13 +21,14 @@ export interface LiveStreamSectionRef {
}
export const LiveStreamSection = forwardRef(
- ({ onChanged, onFocus, onBlur }, ref) => {
+ ({ onChanged, onFocus, onBlur, onPress }, ref) => {
const { m3uUrl, setM3uUrl, remoteInputEnabled } = useSettingsStore();
const { serverUrl } = useRemoteControlStore();
const [isInputFocused, setIsInputFocused] = useState(false);
const [isSectionFocused, setIsSectionFocused] = useState(false);
const inputRef = useRef(null);
const inputAnimationStyle = useButtonAnimation(isSectionFocused, 1.01);
+ const deviceType = useResponsiveLayout().deviceType;
const handleUrlChange = (url: string) => {
setM3uUrl(url);
@@ -49,6 +52,11 @@ export const LiveStreamSection = forwardRef {
+ inputRef.current?.focus();
+ onPress?.();
+ }
+
const handleTVEvent = React.useCallback(
(event: any) => {
if (isSectionFocused && event.eventType === "select") {
@@ -60,8 +68,22 @@ export const LiveStreamSection = forwardRef({
+ start: 0,
+ end: 0,
+ });
+ // 当用户手动移动光标或选中文本时,同步到 state(可选)
+ const onSelectionChange = ({
+ nativeEvent: { selection },
+ }: any) => {
+ setSelection(selection);
+ };
+
return (
-
+
直播源地址
@@ -79,8 +101,23 @@ export const LiveStreamSection = forwardRef setIsInputFocused(true)}
+ onFocus={() => {
+ setIsInputFocused(true);
+ // 将光标移动到文本末尾
+ const end = m3uUrl.length;
+ setSelection({ start: end, end: end });
+ // 有时需要延迟一下,让系统先完成 focus 再设置 selection
+ //(在 Android 上更可靠)
+ setTimeout(() => {
+ // 对于受控的 selection 已经生效,这里仅作保险
+ inputRef.current?.setNativeProps({ selection: { start: end, end: end } });
+ }, 0);
+ }}
+ selection={selection}
+ onSelectionChange={onSelectionChange} // 可选
+
onBlur={() => setIsInputFocused(false)}
+ // onPress={handlePress}
/>
diff --git a/components/settings/RemoteInputSection.tsx b/components/settings/RemoteInputSection.tsx
index 7a7323a..77218d0 100644
--- a/components/settings/RemoteInputSection.tsx
+++ b/components/settings/RemoteInputSection.tsx
@@ -1,5 +1,5 @@
import React, { useCallback } from "react";
-import { View, Switch, StyleSheet, Pressable, Animated } from "react-native";
+import { View, Switch, StyleSheet, Pressable, Animated, Platform } from "react-native";
import { useTVEventHandler } from "react-native";
import { ThemedText } from "@/components/ThemedText";
import { SettingsSection } from "./SettingsSection";
@@ -7,18 +7,21 @@ import { useSettingsStore } from "@/stores/settingsStore";
import { useRemoteControlStore } from "@/stores/remoteControlStore";
import { useButtonAnimation } from "@/hooks/useAnimation";
import { Colors } from "@/constants/Colors";
+import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
interface RemoteInputSectionProps {
onChanged: () => void;
onFocus?: () => void;
onBlur?: () => void;
+ onPress?: () => void;
}
-export const RemoteInputSection: React.FC = ({ onChanged, onFocus, onBlur }) => {
+export const RemoteInputSection: React.FC = ({ onChanged, onFocus, onBlur, onPress }) => {
const { remoteInputEnabled, setRemoteInputEnabled } = useSettingsStore();
const { isServerRunning, serverUrl, error } = useRemoteControlStore();
const [isFocused, setIsFocused] = React.useState(false);
const animationStyle = useButtonAnimation(isFocused, 1.2);
+ const deviceType = useResponsiveLayout().deviceType;
const handleToggle = useCallback(
(enabled: boolean) => {
@@ -38,6 +41,10 @@ export const RemoteInputSection: React.FC = ({ onChange
onBlur?.();
};
+ const handlePress = () => {
+ handleToggle(!remoteInputEnabled);
+ }
+
// TV遥控器事件处理
const handleTVEvent = React.useCallback(
(event: any) => {
@@ -51,7 +58,9 @@ export const RemoteInputSection: React.FC = ({ onChange
useTVEventHandler(handleTVEvent);
return (
-
+
启用远程输入
diff --git a/components/settings/SettingsSection.tsx b/components/settings/SettingsSection.tsx
index 2334581..d857f3f 100644
--- a/components/settings/SettingsSection.tsx
+++ b/components/settings/SettingsSection.tsx
@@ -1,17 +1,20 @@
import React, { useState } from "react";
-import { StyleSheet, Pressable } from "react-native";
+import { StyleSheet, Pressable, Platform } from "react-native";
import { ThemedView } from "@/components/ThemedView";
import { Colors } from "@/constants/Colors";
+import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
interface SettingsSectionProps {
children: React.ReactNode;
onFocus?: () => void;
onBlur?: () => void;
+ onPress?: () => void;
focusable?: boolean;
}
-export const SettingsSection: React.FC = ({ children, onFocus, onBlur, focusable = false }) => {
+export const SettingsSection: React.FC = ({ children, onFocus, onBlur, onPress, focusable = false }) => {
const [isFocused, setIsFocused] = useState(false);
+ const deviceType = useResponsiveLayout().deviceType;
const handleFocus = () => {
setIsFocused(true);
@@ -23,13 +26,24 @@ export const SettingsSection: React.FC = ({ children, onFo
onBlur?.();
};
+ const handlePress = () => {
+ onPress?.();
+ }
+
if (!focusable) {
return {children};
}
return (
-
+
{children}
diff --git a/package.json b/package.json
index d492b18..ad804e4 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"react-native-blob-util": "^0.22.2",
"react-native-file-viewer": "^2.1.5",
"react-native-gesture-handler": "~2.16.1",
+ "react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-media-console": "*",
"react-native-qrcode-svg": "^6.3.1",
"react-native-reanimated": "~3.10.1",
diff --git a/stores/homeStore.ts b/stores/homeStore.ts
index cb22752..fd275bc 100644
--- a/stores/homeStore.ts
+++ b/stores/homeStore.ts
@@ -55,6 +55,26 @@ const initialCategories: Category[] = [
{ title: "豆瓣 Top250", type: "movie", tag: "top250" },
];
+// 添加缓存项接口
+interface CacheItem {
+ data: RowItem[];
+ timestamp: number;
+ type: 'movie' | 'tv' | 'record';
+ hasMore: boolean;
+}
+
+const CACHE_EXPIRE_TIME = 5 * 60 * 1000; // 5分钟过期
+const MAX_CACHE_SIZE = 10; // 最大缓存容量
+const MAX_ITEMS_PER_CACHE = 40; // 每个缓存最大条目数
+
+const getCacheKey = (category: Category) => {
+ return `${category.type || 'unknown'}-${category.title}-${category.tag || ''}`;
+};
+
+const isValidCache = (cacheItem: CacheItem) => {
+ return Date.now() - cacheItem.timestamp < CACHE_EXPIRE_TIME;
+};
+
interface HomeState {
categories: Category[];
selectedCategory: Category;
@@ -72,7 +92,7 @@ interface HomeState {
}
// 内存缓存,应用生命周期内有效
-const dataCache = new Map();
+const dataCache = new Map();
const useHomeStore = create((set, get) => ({
categories: initialCategories,
@@ -87,29 +107,30 @@ const useHomeStore = create((set, get) => ({
fetchInitialData: async () => {
const { apiBaseUrl } = useSettingsStore.getState();
await useAuthStore.getState().checkLoginStatus(apiBaseUrl);
-
+
const { selectedCategory } = get();
- const cacheKey = `${selectedCategory.title}-${selectedCategory.tag || ''}`;
-
+ const cacheKey = getCacheKey(selectedCategory);
+
// 最近播放不缓存,始终实时获取
if (selectedCategory.type === 'record') {
set({ loading: true, contentData: [], pageStart: 0, hasMore: true, error: null });
await get().loadMoreData();
return;
}
-
+
// 检查缓存
- if (dataCache.has(cacheKey)) {
- set({
- loading: false,
- contentData: dataCache.get(cacheKey)!,
- pageStart: dataCache.get(cacheKey)!.length,
- hasMore: false,
- error: null
+ if (dataCache.has(cacheKey) && isValidCache(dataCache.get(cacheKey)!)) {
+ const cachedData = dataCache.get(cacheKey)!;
+ set({
+ loading: false,
+ contentData: cachedData.data,
+ pageStart: cachedData.data.length,
+ hasMore: cachedData.hasMore,
+ error: null
});
return;
}
-
+
set({ loading: true, contentData: [], pageStart: 0, hasMore: true, error: null });
await get().loadMoreData();
},
@@ -151,34 +172,74 @@ const useHomeStore = create((set, get) => ({
set({ contentData: rowItems, hasMore: false });
} else if (selectedCategory.type && selectedCategory.tag) {
- const result = await api.getDoubanData(selectedCategory.type, selectedCategory.tag, 20, pageStart);
- if (result.list.length === 0) {
- set({ hasMore: false });
- } else {
- const newItems = result.list.map((item) => ({
- ...item,
- id: item.title,
- source: "douban",
- })) as RowItem[];
-
- const cacheKey = `${selectedCategory.title}-${selectedCategory.tag || ''}`;
-
- if (pageStart === 0) {
- // 缓存新数据
- dataCache.set(cacheKey, newItems);
- set({
- contentData: newItems,
- pageStart: result.list.length,
- hasMore: true,
- });
- } else {
- // 增量加载时不缓存,直接追加
- set((state) => ({
- contentData: [...state.contentData, ...newItems],
- pageStart: state.pageStart + result.list.length,
- hasMore: true,
- }));
+ const result = await api.getDoubanData(
+ selectedCategory.type,
+ selectedCategory.tag,
+ 20,
+ pageStart
+ );
+
+ const newItems = result.list.map((item) => ({
+ ...item,
+ id: item.title,
+ source: "douban",
+ })) as RowItem[];
+
+ const cacheKey = getCacheKey(selectedCategory);
+
+ if (pageStart === 0) {
+ // 清理过期缓存
+ for (const [key, value] of dataCache.entries()) {
+ if (!isValidCache(value)) {
+ dataCache.delete(key);
+ }
}
+
+ // 如果缓存太大,删除最旧的项
+ if (dataCache.size >= MAX_CACHE_SIZE) {
+ const oldestKey = Array.from(dataCache.keys())[0];
+ dataCache.delete(oldestKey);
+ }
+
+ // 限制缓存的数据条目数,但不限制显示的数据
+ const cacheItems = newItems.slice(0, MAX_ITEMS_PER_CACHE);
+
+ // 存储新缓存
+ dataCache.set(cacheKey, {
+ data: cacheItems,
+ timestamp: Date.now(),
+ type: selectedCategory.type,
+ hasMore: true // 始终为 true,因为我们允许继续加载
+ });
+
+ set({
+ contentData: newItems, // 使用完整的新数据
+ pageStart: newItems.length,
+ hasMore: result.list.length !== 0,
+ });
+ } else {
+ // 增量加载时更新缓存
+ const existingCache = dataCache.get(cacheKey);
+ if (existingCache) {
+ // 只有当缓存数据少于最大限制时才更新缓存
+ if (existingCache.data.length < MAX_ITEMS_PER_CACHE) {
+ const updatedData = [...existingCache.data, ...newItems];
+ const limitedCacheData = updatedData.slice(0, MAX_ITEMS_PER_CACHE);
+
+ dataCache.set(cacheKey, {
+ ...existingCache,
+ data: limitedCacheData,
+ hasMore: true // 始终为 true,因为我们允许继续加载
+ });
+ }
+ }
+
+ // 更新状态时使用所有数据
+ set((state) => ({
+ contentData: [...state.contentData, ...newItems],
+ pageStart: state.pageStart + newItems.length,
+ hasMore: result.list.length !== 0,
+ }));
}
} else if (selectedCategory.tags) {
// It's a container category, do not load content, but clear current content
@@ -188,7 +249,7 @@ const useHomeStore = create((set, get) => ({
}
} catch (err: any) {
let errorMessage = "加载失败,请重试";
-
+
if (err.message === "API_URL_NOT_SET") {
errorMessage = "请点击右上角设置按钮,配置您的服务器地址";
} else if (err.message === "UNAUTHORIZED") {
@@ -204,7 +265,7 @@ const useHomeStore = create((set, get) => ({
} else if (err.message.includes("403")) {
errorMessage = "访问被拒绝,请检查权限设置";
}
-
+
set({ error: errorMessage });
} finally {
set({ loading: false, loadingMore: false });
@@ -213,27 +274,35 @@ const useHomeStore = create((set, get) => ({
selectCategory: (category: Category) => {
const currentCategory = get().selectedCategory;
- const cacheKey = `${category.title}-${category.tag || ''}`;
-
- // 只有当分类或标签真正变化时才处理
+ const cacheKey = getCacheKey(category);
+
if (currentCategory.title !== category.title || currentCategory.tag !== category.tag) {
- set({ selectedCategory: category, contentData: [], pageStart: 0, hasMore: true, error: null });
-
- // 最近播放始终实时获取
+ set({
+ selectedCategory: category,
+ contentData: [],
+ pageStart: 0,
+ hasMore: true,
+ error: null
+ });
+
if (category.type === 'record') {
get().fetchInitialData();
return;
}
-
- // 检查缓存,有则直接使用,无则请求
- if (dataCache.has(cacheKey)) {
- set({
- contentData: dataCache.get(cacheKey)!,
- pageStart: dataCache.get(cacheKey)!.length,
- hasMore: false,
- loading: false
+
+ const cachedData = dataCache.get(cacheKey);
+ if (cachedData && isValidCache(cachedData)) {
+ set({
+ contentData: cachedData.data,
+ pageStart: cachedData.data.length,
+ hasMore: cachedData.hasMore,
+ loading: false
});
} else {
+ // 删除过期缓存
+ if (cachedData) {
+ dataCache.delete(cacheKey);
+ }
get().fetchInitialData();
}
}
@@ -273,10 +342,10 @@ const useHomeStore = create((set, get) => ({
}
return {};
});
-
+
get().fetchInitialData();
},
-
+
clearError: () => {
set({ error: null });
},
diff --git a/yarn.lock b/yarn.lock
index 6573989..d4832c2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7775,7 +7775,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.7.2, prop-types@^15.8.0, prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.0, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7944,6 +7944,19 @@ react-native-helmet-async@2.0.4:
react-fast-compare "^3.2.2"
shallowequal "^1.1.0"
+react-native-iphone-x-helper@^1.0.3:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010"
+ integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==
+
+react-native-keyboard-aware-scroll-view@^0.9.5:
+ version "0.9.5"
+ resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.9.5.tgz#e2e9665d320c188e6b1f22f151b94eb358bf9b71"
+ integrity sha512-XwfRn+T/qBH9WjTWIBiJD2hPWg0yJvtaEw6RtPCa5/PYHabzBaWxYBOl0usXN/368BL1XktnZPh8C2lmTpOREA==
+ dependencies:
+ prop-types "^15.6.2"
+ react-native-iphone-x-helper "^1.0.3"
+
react-native-media-console@*:
version "2.2.4"
resolved "https://registry.yarnpkg.com/react-native-media-console/-/react-native-media-console-2.2.4.tgz#76a232cdcb645cfdb25bacddee514f360eb4947d"
@@ -8832,7 +8845,16 @@ string-length@^5.0.1:
char-regex "^2.0.0"
strip-ansi "^7.0.1"
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8923,7 +8945,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -8937,6 +8959,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -9768,7 +9797,7 @@ word-wrap@^1.2.5:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -9786,6 +9815,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"