From e20aa93e44432df1408fb825d7de9a1e57008d09 Mon Sep 17 00:00:00 2001 From: jinyaqiu Date: Thu, 24 Jul 2025 19:51:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A0=B7=E5=BC=8F=E8=B0=83=E6=95=B4+ma?= =?UTF-8?q?rkdown=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/_layout.tsx | 11 + app/(tabs)/privacy-policy.tsx | 18 +- app/(tabs)/rights.tsx | 124 ++++--- app/(tabs)/setting.tsx | 495 ++++++++++++++++++++++++++++ components/owner/album.tsx | 6 +- components/owner/rights/premium.tsx | 20 +- package-lock.json | 78 +++++ package.json | 5 +- 8 files changed, 674 insertions(+), 83 deletions(-) create mode 100644 app/(tabs)/setting.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index b5b2871..1482286 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -339,6 +339,17 @@ export default function TabLayout() { tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 }} /> + + {/* 设置页面 */} + null, // 隐藏底部标签栏 + headerShown: false, // 隐藏导航栏 + tabBarStyle: { display: 'none' } // 确保在标签栏中不显示 + }} + /> ); } diff --git a/app/(tabs)/privacy-policy.tsx b/app/(tabs)/privacy-policy.tsx index ac573ed..b55f37c 100644 --- a/app/(tabs)/privacy-policy.tsx +++ b/app/(tabs)/privacy-policy.tsx @@ -2,10 +2,12 @@ import { fetchApi } from "@/lib/server-api-util"; import { Policy } from "@/types/personal-info"; import { useEffect, useState } from "react"; import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import RenderHtml from 'react-native-render-html'; +import Markdown from 'react-native-markdown-display'; +import { useSafeAreaInsets } from "react-native-safe-area-context"; const PrivacyPolicy = () => { const [article, setArticle] = useState({} as Policy); + const insets = useSafeAreaInsets(); useEffect(() => { const loadArticle = async () => { fetchApi(`/system-config/policy/privacy_policy`).then((res: any) => { @@ -26,7 +28,7 @@ const PrivacyPolicy = () => { } return ( - + Settings @@ -36,14 +38,9 @@ const PrivacyPolicy = () => { - + + {article.content} + @@ -86,6 +83,7 @@ const styles = StyleSheet.create({ }, modalContent: { flex: 1, + paddingHorizontal: 8 }, modalText: { fontSize: 16, diff --git a/app/(tabs)/rights.tsx b/app/(tabs)/rights.tsx index 13442dc..8552a55 100644 --- a/app/(tabs)/rights.tsx +++ b/app/(tabs)/rights.tsx @@ -9,7 +9,7 @@ import { ThemedText } from '@/components/ThemedText'; import { fetchApi } from '@/lib/server-api-util'; import { useLocalSearchParams, useRouter } from "expo-router"; import { useEffect, useState } from 'react'; -import { Image, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; import { useSafeAreaInsets } from "react-native-safe-area-context"; export default function Rights() { @@ -43,59 +43,79 @@ export default function Rights() { }, []); return ( - - {/* 导航栏 */} - - { router.push('/owner') }} style={{ padding: 16 }}> - - - - Subscription - - 123 - - {/* 会员卡 */} - - {userType === 'normal' ? ( - - ) : ( - - )} - - - - Purchase - - - - {credit} + + + {/* 导航栏 */} + + { router.push('/owner') }} style={{ padding: 16 }}> + + + + Subscription + + 123 + + {/* 会员卡 */} + + {userType === 'normal' ? ( + + ) : ( + + )} + + + + Purchase + + + + {credit} + - - {/* 会员信息 */} - - {/* 切换按钮 */} - - { setUserType("normal") }} style={[styles.switchButtonItem, { backgroundColor: userType === 'normal' ? "#FFB645" : "#fff", borderColor: userType === 'normal' ? "#FFB645" : "#E2793F" }]}> - Free - - { setUserType("premium") }} style={[styles.switchButtonItem, { backgroundColor: userType === 'premium' ? "#E2793F" : "#fff", borderColor: userType === 'premium' ? "#E2793F" : "#E2793F" }]}> - Pro - + {/* 会员信息 */} + + {/* 切换按钮 */} + + { setUserType("normal") }} + style={[styles.switchButtonItem, { backgroundColor: userType === 'normal' ? "#FFB645" : "#fff", borderColor: userType === 'normal' ? "#FFB645" : "#E2793F" }]} + > + Free + + { setUserType("premium") }} + style={[styles.switchButtonItem, { backgroundColor: userType === 'premium' ? "#E2793F" : "#fff", borderColor: userType === 'premium' ? "#E2793F" : "#E2793F" }]} + > + Pro + + + {/* 普通权益 */} + + {/* 会员权益 */} + - {/* 普通权益 */} - - {/* 会员权益 */} - - - {/* 会员权益信息 */} - + {/* 会员权益信息 */} + + + + {/* 付费按钮 */} - + { @@ -153,13 +173,14 @@ const styles = StyleSheet.create({ marginVertical: 16, padding: 16, borderRadius: 12, - shadowColor: "#000", + backgroundColor: '#fff', + shadowColor: '#000', shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.25, - shadowRadius: 3.84, + shadowRadius: 5, elevation: 5, }, container: { @@ -216,6 +237,7 @@ const styles = StyleSheet.create({ cardPointsText: { fontSize: 32, fontWeight: '700', - color: '#4C320C' + color: '#4C320C', + lineHeight: 32 } }); diff --git a/app/(tabs)/setting.tsx b/app/(tabs)/setting.tsx new file mode 100644 index 0000000..9573118 --- /dev/null +++ b/app/(tabs)/setting.tsx @@ -0,0 +1,495 @@ +import DeleteSvg from '@/assets/icons/svg/delete.svg'; +import LogoutSvg from '@/assets/icons/svg/logout.svg'; +import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg'; +import RightArrowSvg from '@/assets/icons/svg/rightArrow.svg'; +import DeleteModal from '@/components/owner/delete'; +import LcensesModal from '@/components/owner/qualification/lcenses'; +import PrivacyModal from '@/components/owner/qualification/privacy'; +import CustomSwitch from '@/components/owner/switch'; +import UserInfo from '@/components/owner/userInfo'; +import { checkNotificationPermission, getLocationPermission, getPermissions, requestLocationPermission, requestMediaLibraryPermission, requestNotificationPermission, reverseGeocode } from '@/components/owner/utils'; +import { ThemedText } from '@/components/ThemedText'; +import { useAuth } from '@/contexts/auth-context'; +import { fetchApi } from '@/lib/server-api-util'; +import { Address, User } from '@/types/user'; +import * as Location from 'expo-location'; +import { useRouter } from 'expo-router'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Linking, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +const Setting = (props: { modalVisible: boolean, setModalVisible: (visible: boolean) => void, userInfo: User }) => { + const { modalVisible, setModalVisible, userInfo } = props; + const insets = useSafeAreaInsets(); + const { t } = useTranslation(); + const [modalType, setModalType] = useState<'ai' | 'terms' | 'privacy' | 'user'>('ai'); + // 协议弹窗 + const [privacyModalVisible, setPrivacyModalVisible] = useState(false); + // 许可证弹窗 + const [lcensesModalVisible, setLcensesModalVisible] = useState(false); + + // 删除弹窗 + const [deleteModalVisible, setDeleteModalVisible] = useState(false); + const { logout } = useAuth(); + const router = useRouter(); + // 打开设置 + const openAppSettings = () => { + Linking.openSettings(); + }; + // 通知消息权限开关 + const [notificationsEnabled, setNotificationsEnabled] = useState(false); + const toggleNotifications = async () => { + if (notificationsEnabled) { + // 引导去设置关闭权限 + openAppSettings() + } else { + requestNotificationPermission() + .then((granted: boolean | ((prevState: boolean) => boolean)) => { + setNotificationsEnabled(granted); + }); + setModalVisible(false); + } + }; + + // 相册权限 + const [albumEnabled, setAlbumEnabled] = useState(false); + const toggleAlbum = async () => { + if (albumEnabled) { + // 引导去设置关闭权限 + openAppSettings() + } else { + requestMediaLibraryPermission() + .then((granted: boolean | ((prevState: boolean) => boolean)) => { + setAlbumEnabled(granted); + }); + setModalVisible(false); + } + } + + // 位置权限 + const [locationEnabled, setLocationEnabled] = useState(false); + // 位置权限更改 + const toggleLocation = async () => { + if (locationEnabled) { + // 如果权限已开启,点击则引导用户去设置关闭 + openAppSettings(); + } else { + requestLocationPermission() + .then((granted: boolean | ((prevState: boolean) => boolean)) => { + setLocationEnabled(granted); + }); + setModalVisible(false); + } + }; + // 正在获取位置信息 + const [isLoading, setIsLoading] = useState(false); + // 动画开启 + const [isRefreshing, setIsRefreshing] = useState(false); + + // 当前位置状态 + const [currentLocation, setCurrentLocation] = useState
({} as Address); + + // 获取当前位置 + const getCurrentLocation = async () => { + setIsLoading(true); + setIsRefreshing(true); + + try { + // 1. 首先检查当前权限状态 -- 获取当前的位置权限 + let currentStatus = await getLocationPermission(); + console.log('当前权限状态:', currentStatus); + + // 2. 如果没有权限,则跳过获取位置 + if (!currentStatus) { + console.log('没有权限,跳过获取位置') + return; + // const newStatus = await requestLocationPermission(); + // setLocationEnabled(newStatus); + // currentStatus = newStatus; + + // if (!currentStatus) { + // // alert('需要位置权限才能继续'); + // return; + // } + } + + // 3. 确保位置服务已启用 + const isEnabled = await Location.hasServicesEnabledAsync(); + if (!isEnabled) { + alert('请先启用位置服务'); + return; + } + console.log('位置服务已启用'); + // 4. 获取当前位置 + const location = await Location.getCurrentPositionAsync({ + accuracy: Location.Accuracy.High, // 使用高精度 + timeInterval: 10000, // 可选:最大等待时间(毫秒) + }); + console.log('位置:', location); + + // 地理位置逆编码 + const address = await reverseGeocode(location.coords.latitude, location.coords.longitude); + // 5. 更新位置状态 + if (address) { + setCurrentLocation(address); + } + + return location; + } catch (error: any) { + if (error.code === 'PERMISSION_DENIED' || error.code === 'PERMISSION_DENIED_ERROR') { + alert('位置权限被拒绝,请在设置中启用位置服务'); + } else if (error.code === 'TIMEOUT') { + alert('获取位置超时,请检查网络和位置服务'); + } else { + alert(`无法获取您的位置: ${error.message || '未知错误'}`); + } + throw error; // 重新抛出错误以便上层处理 + } finally { + setIsLoading(false); + setIsRefreshing(false); + } + }; + + // 退出登录 + const handleLogout = () => { + fetchApi("/iam/logout", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }) + .then(async (res) => { + await logout(); + setModalVisible(false); + router.replace('/login'); + }) + .catch(() => { + console.error("jwt has expired."); + }); + }; + // 检查是否有权限 + useEffect(() => { + if (modalVisible) { + // 位置权限 + getLocationPermission().then((res: boolean | ((prevState: boolean) => boolean)) => { + console.log('位置权限:', res); + setLocationEnabled(res); + }) + // 媒体库权限 + getPermissions().then((res: boolean | ((prevState: boolean) => boolean)) => { + console.log('媒体库权限:', res); + setAlbumEnabled(res); + }) + // 通知权限 + checkNotificationPermission().then((res: boolean | ((prevState: boolean) => boolean)) => { + console.log('通知权限:', res); + setNotificationsEnabled(res); + }) + } + }, [modalVisible]) + + return ( + + + e.stopPropagation()}> + + { router.push('/owner') }}> + + + {t('generalSetting.allTitle', { ns: 'personal' })} + × + + + {/* 用户信息 */} + + {/* 升级版本 */} + {/* + {t('generalSetting.subscription', { ns: 'personal' })} + + + {t('generalSetting.subscriptionTitle', { ns: 'personal' })} + {t('generalSetting.subscriptionText', { ns: 'personal' })} + + { + + }} + > + + {t('generalSetting.upgrade', { ns: 'personal' })} + + + + */} + {/* 消息通知 */} + {/* + {t('permission.pushNotification', { ns: 'personal' })} + + + {t('permission.pushNotification', { ns: 'personal' })} + + + + */} + {/* 权限信息 */} + + {t('permission.permissionManagement', { ns: 'personal' })} + + {/* 相册权限 */} + + {t('permission.galleryAccess', { ns: 'personal' })} + + + {/* 分割线 */} + + {/* 位置权限 */} + + + {t('permission.locationPermission', { ns: 'personal' })} + + + + + + + {t('permission.pushNotification', { ns: 'personal' })} + + + + {/* 相册成片权限 */} + {/* + + Opus Permission + + + */} + + + {/* 账号 */} + {/* + Account + + + + Notifications + + + + + + Delete Account + + + + */} + {/* 协议 */} + + {t('lcenses.title', { ns: 'personal' })} + + { setModalType('privacy'); setPrivacyModalVisible(true) }} > + {t('lcenses.privacyPolicy', { ns: 'personal' })} + + + + { setModalType('terms'); setPrivacyModalVisible(true) }} > + {t('lcenses.applyPermission', { ns: 'personal' })} + + + + { setModalType('user'); setPrivacyModalVisible(true) }} > + {t('lcenses.userAgreement', { ns: 'personal' })} + + + + { setModalType('ai'); setPrivacyModalVisible(true) }} > + {t('lcenses.aiPolicy', { ns: 'personal' })} + + + + { setLcensesModalVisible(true) }} > + {t('lcenses.qualification', { ns: 'personal' })} + + + + Linking.openURL("https://beian.miit.gov.cn/")} > + {t('lcenses.ICP', { ns: 'personal' })}沪ICP备2023032876号-4 + + + + + {/* 其他信息 */} + + {t('generalSetting.otherInformation', { ns: 'personal' })} + + Linking.openURL("https://work.weixin.qq.com/kfid/kfca0ac87f4e05e8bfd")} > + {t('generalSetting.contactUs', { ns: 'personal' })} + {/* */} + + + + {t('generalSetting.version', { ns: 'personal' })} + {"0.5.0"} + + + + {/* 退出 */} + + {t('generalSetting.logout', { ns: 'personal' })} + + + {/* 注销账号 */} + setDeleteModalVisible(true)}> + {t('generalSetting.deleteAccount', { ns: 'personal' })} + + + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: 'flex-end', + backgroundColor: 'rgba(0,0,0,0.5)', + }, + modalView: { + width: '100%', + height: '100%', + backgroundColor: 'white', + paddingHorizontal: 16, + }, + modalHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 20, + }, + modalTitle: { + fontSize: 20, + fontWeight: 'bold', + color: '#4C320C', + }, + closeButton: { + fontSize: 28, + color: '#4C320C', + padding: 10, + }, + modalContent: { + flex: 1, + }, + modalText: { + fontSize: 16, + color: '#4C320C', + }, + premium: { + backgroundColor: "#FAF9F6", + padding: 16, + borderRadius: 24, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + content: { + flex: 1, + flexDirection: 'column', + gap: 4, + backgroundColor: '#FAF9F6', + borderRadius: 24, + paddingVertical: 8 + }, + item: { + paddingHorizontal: 16, + paddingVertical: 8, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + itemText: { + fontSize: 14, + fontWeight: '600', + color: '#4C320C', + }, + upgradeButton: { + backgroundColor: '#E2793F', + borderRadius: 20, + paddingHorizontal: 16, + paddingVertical: 8, + }, + upgradeButtonText: { + color: '#fff', + fontSize: 14, + fontWeight: "600" + }, + switchContainer: { + width: 50, + height: 30, + borderRadius: 15, + justifyContent: 'center', + paddingHorizontal: 2, + }, + switchOn: { + backgroundColor: '#E2793F', + alignItems: 'flex-end', + }, + switchOff: { + backgroundColor: '#E5E5E5', + alignItems: 'flex-start', + }, + switchCircle: { + width: 26, + height: 26, + borderRadius: 13, + }, + switchCircleOn: { + backgroundColor: 'white', + }, + switchCircleOff: { + backgroundColor: '#A5A5A5', + }, +}); + +const Divider = () => { + return ( + + ) +} +export default Setting; \ No newline at end of file diff --git a/components/owner/album.tsx b/components/owner/album.tsx index e89b393..ff94e61 100644 --- a/components/owner/album.tsx +++ b/components/owner/album.tsx @@ -11,10 +11,9 @@ interface CategoryProps { const AlbumComponent = ({ setModalVisible, style }: CategoryProps) => { const { t } = useTranslation(); const router = useRouter(); - return ( - { router.push("/download") }}> + {t('generalSetting.album', { ns: 'personal' })} @@ -22,7 +21,8 @@ const AlbumComponent = ({ setModalVisible, style }: CategoryProps) => { { - setModalVisible(true); + // setModalVisible(true); + router.push('/setting'); }} activeOpacity={0.7} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }} diff --git a/components/owner/rights/premium.tsx b/components/owner/rights/premium.tsx index 2485d10..ea9cdf2 100644 --- a/components/owner/rights/premium.tsx +++ b/components/owner/rights/premium.tsx @@ -1,7 +1,6 @@ import BlackStarSvg from '@/assets/icons/svg/blackStar.svg'; import { ThemedText } from "@/components/ThemedText"; -import { StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native"; -import { ScrollView } from 'react-native-gesture-handler'; +import { ScrollView, StyleProp, StyleSheet, TouchableOpacity, View, ViewStyle } from "react-native"; import { maxDiscountProduct } from './utils'; interface Props { @@ -65,7 +64,7 @@ const Premium = (props: Props) => { {item.product_code?.split('_')[item.product_code?.split('_')?.length - 1]} - + $ {item.unit_price.amount} @@ -104,22 +103,9 @@ const styles = StyleSheet.create({ borderColor: "#FFB645", borderWidth: 2, borderRadius: 24, - width: "48%", + width: 200, paddingBottom: 16 }, - quarterly: { - display: "flex", - flexDirection: "column", - alignItems: "center", - justifyContent: "center", - gap: 20, - borderColor: "#FAF9F6", - borderWidth: 2, - borderRadius: 24, - width: "48%", - paddingBottom: 16, - height: "100%", - }, title: { display: "flex", flexDirection: "row", diff --git a/package-lock.json b/package-lock.json index 1a876dc..21a7ef9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "react-native": "0.79.5", "react-native-gesture-handler": "~2.24.0", "react-native-linear-gradient": "^2.8.3", + "react-native-markdown-display": "^7.0.2", "react-native-modal": "^14.0.0-rc.1", "react-native-picker-select": "^9.3.1", "react-native-progress": "^5.0.1", @@ -12471,6 +12472,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -12704,6 +12714,37 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "license": "BSD-2-Clause" + }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", @@ -12725,6 +12766,12 @@ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "license": "CC0-1.0" }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", @@ -14964,6 +15011,15 @@ "react-native": "*" } }, + "node_modules/react-native-fit-image": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz", + "integrity": "sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg==", + "license": "Beerware", + "dependencies": { + "prop-types": "^15.5.10" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz", @@ -14999,6 +15055,22 @@ "react-native": "*" } }, + "node_modules/react-native-markdown-display": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-native-markdown-display/-/react-native-markdown-display-7.0.2.tgz", + "integrity": "sha512-Mn4wotMvMfLAwbX/huMLt202W5DsdpMO/kblk+6eUs55S57VVNni1gzZCh5qpznYLjIQELNh50VIozEfY6fvaQ==", + "license": "MIT", + "dependencies": { + "css-to-react-native": "^3.0.0", + "markdown-it": "^10.0.0", + "prop-types": "^15.7.2", + "react-native-fit-image": "^1.5.5" + }, + "peerDependencies": { + "react": ">=16.2.0", + "react-native": ">=0.50.4" + } + }, "node_modules/react-native-modal": { "version": "14.0.0-rc.1", "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-14.0.0-rc.1.tgz", @@ -17510,6 +17582,12 @@ "node": "*" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "license": "MIT" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", diff --git a/package.json b/package.json index 1d563ba..f0c441a 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "react-native": "0.79.5", "react-native-gesture-handler": "~2.24.0", "react-native-linear-gradient": "^2.8.3", + "react-native-markdown-display": "^7.0.2", "react-native-modal": "^14.0.0-rc.1", "react-native-picker-select": "^9.3.1", "react-native-progress": "^5.0.1", @@ -75,10 +76,10 @@ "react-native-svg": "^15.11.2", "react-native-toast-message": "^2.3.0", "react-native-uuid": "^2.0.3", + "react-native-view-shot": "4.0.3", "react-native-web": "~0.20.0", "react-native-webview": "13.13.5", - "react-redux": "^9.2.0", - "react-native-view-shot": "4.0.3" + "react-redux": "^9.2.0" }, "devDependencies": { "@babel/core": "^7.25.2",