From 9d35e5b3340c0a1468d5d226d520b5310dd81179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=B7=E5=B0=8F=E5=89=8D?= Date: Fri, 16 Jan 2026 19:45:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0ios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.gitignore | 0 .../.watchmanconfig | 0 {argon-pro-react-native => MeAgent}/App.js | 0 .../AuthKey_HSF578B626.p8 | 0 {argon-pro-react-native => MeAgent}/README.md | 0 {argon-pro-react-native => MeAgent}/app.json | 1 + .../assets/config/argon.json | 0 .../assets/font/OpenSans-Bold.ttf | Bin .../assets/font/OpenSans-Light.ttf | Bin .../assets/font/OpenSans-Regular.ttf | Bin .../assets/font/argon.ttf | Bin .../assets/icon.png | Bin .../assets/imgs/android.png | Bin .../assets/imgs/argon-logo-onboarding.png | Bin .../assets/imgs/argon-logo-onboarding@2x.png | Bin .../assets/imgs/argon-logo.png | Bin .../assets/imgs/argon-logo@2x.png | Bin .../assets/imgs/argonlogo.png | Bin .../assets/imgs/bg.png | Bin .../assets/imgs/getPro-bg.png | Bin .../assets/imgs/getPro-bg@2x.png | Bin .../assets/imgs/icon.png | Bin .../assets/imgs/ios.png | Bin .../assets/imgs/profile-img.jpg | Bin .../assets/imgs/profile-screen-bg.png | Bin .../assets/imgs/register-bg.png | Bin .../assets/imgs/splash.png | Bin .../assets/logo.jpg | Bin .../assets/nucleo icons/svg/bag-17.svg | 0 .../assets/nucleo icons/svg/basket.svg | 0 .../assets/nucleo icons/svg/bell.svg | 0 .../assets/nucleo icons/svg/calendar-date.svg | 0 .../assets/nucleo icons/svg/chart-pie-35.svg | 0 .../assets/nucleo icons/svg/diamond.svg | 0 .../assets/nucleo icons/svg/engine-start.svg | 0 .../assets/nucleo icons/svg/g-check.svg | 0 .../assets/nucleo icons/svg/hat-3.svg | 0 .../assets/nucleo icons/svg/ic_grain_48px.svg | 0 .../assets/nucleo icons/svg/ic_mail_24px.svg | 0 .../assets/nucleo icons/svg/map-big.svg | 0 .../assets/nucleo icons/svg/menu-8.svg | 0 .../assets/nucleo icons/svg/nav-down.svg | 0 .../assets/nucleo icons/svg/nav-left.svg | 0 .../assets/nucleo icons/svg/nav-right.svg | 0 .../nucleo icons/svg/padlock-unlocked.svg | 0 .../assets/nucleo icons/svg/palette.svg | 0 .../nucleo icons/svg/search-zoom-in.svg | 0 .../assets/nucleo icons/svg/shop.svg | 0 .../assets/nucleo icons/svg/spaceship.svg | 0 .../assets/nucleo icons/svg/support.svg | 0 .../assets/nucleo icons/svg/switches.svg | 0 .../assets/nucleo icons/svg/ungroup.svg | 0 .../assets/splash.png | Bin .../babel.config.js | 0 .../components/Button.js | 0 .../components/Card.js | 0 .../components/DrawerItem.js | 0 .../components/Header.js | 0 .../components/Icon.js | 0 .../components/Input.js | 0 .../components/Notification.js | 0 .../components/Select.js | 0 .../components/Switch.js | 0 .../components/Tabs.js | 0 .../components/index.js | 0 .../constants/Images.js | 0 .../constants/Theme.js | 0 .../constants/articles.js | 0 .../constants/cart.js | 0 .../constants/categories.js | 0 .../constants/deals.js | 0 .../constants/index.js | 0 .../constants/tabs.js | 0 .../constants/utils.js | 0 MeAgent/eas.json | 28 + {argon-pro-react-native => MeAgent}/index.js | 0 MeAgent/ios/.gitignore | 30 + MeAgent/ios/.xcode.env | 11 + MeAgent/ios/Podfile | 87 + MeAgent/ios/Podfile.lock | 1658 +++++++++++++++++ MeAgent/ios/Podfile.properties.json | 5 + MeAgent/ios/app.xcodeproj/project.pbxproj | 553 ++++++ .../xcshareddata/xcschemes/app.xcscheme | 88 + .../app.xcworkspace/contents.xcworkspacedata | 10 + MeAgent/ios/app/AppDelegate.h | 7 + MeAgent/ios/app/AppDelegate.mm | 62 + .../App-Icon-1024x1024@1x.png | Bin 0 -> 137238 bytes .../AppIcon.appiconset/Contents.json | 14 + MeAgent/ios/app/Images.xcassets/Contents.json | 6 + .../SplashScreen.imageset/Contents.json | 21 + .../SplashScreen.imageset/image.png | Bin 0 -> 195095 bytes .../Contents.json | 21 + .../SplashScreenBackground.imageset/image.png | Bin 0 -> 68 bytes MeAgent/ios/app/Info.plist | 76 + MeAgent/ios/app/PrivacyInfo.xcprivacy | 48 + MeAgent/ios/app/SplashScreen.storyboard | 51 + MeAgent/ios/app/Supporting/Expo.plist | 12 + MeAgent/ios/app/app-Bridging-Header.h | 3 + MeAgent/ios/app/app.entitlements | 8 + MeAgent/ios/app/main.m | 10 + MeAgent/ios/app/noop-file.swift | 4 + {argon-pro-react-native => MeAgent}/logo.jpg | Bin MeAgent/navigation/Menu.js | 413 ++++ .../navigation/Screens.js | 122 +- .../package.json | 10 +- .../screens/About.js | 0 .../screens/Agreement.js | 0 .../screens/Articles.js | 0 .../screens/Beauty.js | 0 .../screens/Cart.js | 0 .../screens/Category.js | 0 .../screens/Chat.js | 0 .../screens/Elements.js | 0 .../screens/Fashion.js | 0 .../screens/Gallery.js | 0 .../screens/Home.js | 0 .../screens/Notifications.js | 0 .../screens/Onboarding.js | 0 .../screens/PersonalNotifications.js | 0 .../screens/Privacy.js | 0 .../screens/Pro.js | 0 .../screens/Product.js | 0 .../screens/Profile.js | 0 .../screens/Register.js | 0 .../screens/Search.js | 0 .../screens/Settings.js | 0 .../screens/SystemNotifications.js | 0 MeAgent/src/components/AddWatchlistButton.js | 267 +++ .../src/components/PushNotificationHandler.js | 0 .../src/components/index.js | 0 .../src/constants/index.js | 0 .../src/contexts/AuthContext.js | 0 .../src/hooks/usePushNotifications.js | 0 MeAgent/src/hooks/useRealtimeQuote.js | 270 +++ MeAgent/src/hooks/useWatchlist.js | 234 +++ .../src/screens/Auth/LoginScreen.js | 0 .../src/screens/Auth/index.js | 0 MeAgent/src/screens/Concepts/ConceptDetail.js | 322 ++++ MeAgent/src/screens/Concepts/ConceptList.js | 1246 +++++++++++++ .../src/screens/Concepts/ConceptTimeline.js | 818 ++++++++ MeAgent/src/screens/Concepts/index.js | 1 + .../src/screens/Events/EventCard.js | 7 + .../src/screens/Events/EventComments.js | 0 .../src/screens/Events/EventDetail.js | 21 +- .../src/screens/Events/EventList.js | 59 +- .../src/screens/Events/FilterModal.js | 0 .../src/screens/Events/HistoricalEvents.js | 0 .../src/screens/Events/MainlineView.js | 0 .../src/screens/Events/RelatedConcepts.js | 0 .../src/screens/Events/RelatedStocks.js | 65 +- .../src/screens/Events/SankeyFlow.js | 0 .../src/screens/Events/StockDetailModal.js | 0 .../src/screens/Events/TransmissionChain.js | 0 .../src/screens/Events/index.js | 0 .../src/screens/Market/EventCalendar.js | 0 .../src/screens/Market/MarketHot.js | 33 +- .../src/screens/Market/SectorDetail.js | 0 .../src/screens/Market/StockDetail.js | 0 .../src/screens/Market/TodayStats.js | 0 .../src/screens/Market/index.js | 0 MeAgent/src/screens/Profile/ProfileScreen.js | 457 +++++ MeAgent/src/screens/Profile/index.js | 5 + .../screens/StockDetail/StockDetailScreen.js | 271 +++ .../components/AnnouncementsPanel.js | 186 ++ .../StockDetail/components/ChartTypeTabs.js | 67 + .../StockDetail/components/ConceptsPanel.js | 296 +++ .../StockDetail/components/EventsPanel.js | 204 ++ .../StockDetail/components/KlineChart.js | 791 ++++++++ .../StockDetail/components/MinuteChart.js | 545 ++++++ .../StockDetail/components/OrderBook.js | 238 +++ .../StockDetail/components/PriceHeader.js | 214 +++ .../StockDetail/components/RelatedInfoTabs.js | 59 + .../components/RiseAnalysisModal.js | 462 +++++ .../screens/StockDetail/components/index.js | 14 + MeAgent/src/screens/StockDetail/index.js | 6 + .../screens/Watchlist/AddWatchlistModal.js | 334 ++++ .../screens/Watchlist/WatchlistEventItem.js | 286 +++ .../src/screens/Watchlist/WatchlistScreen.js | 432 +++++ .../screens/Watchlist/WatchlistStockItem.js | 225 +++ MeAgent/src/screens/Watchlist/index.js | 8 + .../src/services/api.js | 2 +- .../src/services/authService.js | 0 MeAgent/src/services/companyService.js | 158 ++ .../src/services/eventService.js | 0 .../src/services/pushService.js | 0 MeAgent/src/services/stockService.js | 537 ++++++ MeAgent/src/services/watchlistService.js | 184 ++ MeAgent/src/services/websocketService.js | 556 ++++++ .../src/services/ztService.js | 0 .../src/store/index.js | 4 + .../src/store/slices/eventsSlice.js | 0 MeAgent/src/store/slices/stockSlice.js | 280 +++ MeAgent/src/store/slices/watchlistSlice.js | 325 ++++ .../src/theme/index.js | 0 .../src/types/event.js | 0 .../src/utils/tradingDayUtils.js | 0 argon-pro-react-native/navigation/Menu.js | 255 --- 197 files changed, 13758 insertions(+), 345 deletions(-) rename {argon-pro-react-native => MeAgent}/.gitignore (100%) rename {argon-pro-react-native => MeAgent}/.watchmanconfig (100%) rename {argon-pro-react-native => MeAgent}/App.js (100%) rename {argon-pro-react-native => MeAgent}/AuthKey_HSF578B626.p8 (100%) rename {argon-pro-react-native => MeAgent}/README.md (100%) rename {argon-pro-react-native => MeAgent}/app.json (96%) rename {argon-pro-react-native => MeAgent}/assets/config/argon.json (100%) rename {argon-pro-react-native => MeAgent}/assets/font/OpenSans-Bold.ttf (100%) rename {argon-pro-react-native => MeAgent}/assets/font/OpenSans-Light.ttf (100%) rename {argon-pro-react-native => MeAgent}/assets/font/OpenSans-Regular.ttf (100%) rename {argon-pro-react-native => MeAgent}/assets/font/argon.ttf (100%) rename {argon-pro-react-native => MeAgent}/assets/icon.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/android.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/argon-logo-onboarding.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/argon-logo-onboarding@2x.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/argon-logo.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/argon-logo@2x.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/argonlogo.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/bg.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/getPro-bg.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/getPro-bg@2x.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/icon.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/ios.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/profile-img.jpg (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/profile-screen-bg.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/register-bg.png (100%) rename {argon-pro-react-native => MeAgent}/assets/imgs/splash.png (100%) rename {argon-pro-react-native => MeAgent}/assets/logo.jpg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/bag-17.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/basket.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/bell.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/calendar-date.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/chart-pie-35.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/diamond.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/engine-start.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/g-check.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/hat-3.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/ic_grain_48px.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/ic_mail_24px.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/map-big.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/menu-8.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/nav-down.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/nav-left.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/nav-right.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/padlock-unlocked.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/palette.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/search-zoom-in.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/shop.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/spaceship.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/support.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/switches.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/nucleo icons/svg/ungroup.svg (100%) rename {argon-pro-react-native => MeAgent}/assets/splash.png (100%) rename {argon-pro-react-native => MeAgent}/babel.config.js (100%) rename {argon-pro-react-native => MeAgent}/components/Button.js (100%) rename {argon-pro-react-native => MeAgent}/components/Card.js (100%) rename {argon-pro-react-native => MeAgent}/components/DrawerItem.js (100%) rename {argon-pro-react-native => MeAgent}/components/Header.js (100%) rename {argon-pro-react-native => MeAgent}/components/Icon.js (100%) rename {argon-pro-react-native => MeAgent}/components/Input.js (100%) rename {argon-pro-react-native => MeAgent}/components/Notification.js (100%) rename {argon-pro-react-native => MeAgent}/components/Select.js (100%) rename {argon-pro-react-native => MeAgent}/components/Switch.js (100%) rename {argon-pro-react-native => MeAgent}/components/Tabs.js (100%) rename {argon-pro-react-native => MeAgent}/components/index.js (100%) rename {argon-pro-react-native => MeAgent}/constants/Images.js (100%) rename {argon-pro-react-native => MeAgent}/constants/Theme.js (100%) rename {argon-pro-react-native => MeAgent}/constants/articles.js (100%) rename {argon-pro-react-native => MeAgent}/constants/cart.js (100%) rename {argon-pro-react-native => MeAgent}/constants/categories.js (100%) rename {argon-pro-react-native => MeAgent}/constants/deals.js (100%) rename {argon-pro-react-native => MeAgent}/constants/index.js (100%) rename {argon-pro-react-native => MeAgent}/constants/tabs.js (100%) rename {argon-pro-react-native => MeAgent}/constants/utils.js (100%) create mode 100644 MeAgent/eas.json rename {argon-pro-react-native => MeAgent}/index.js (100%) create mode 100644 MeAgent/ios/.gitignore create mode 100644 MeAgent/ios/.xcode.env create mode 100644 MeAgent/ios/Podfile create mode 100644 MeAgent/ios/Podfile.lock create mode 100644 MeAgent/ios/Podfile.properties.json create mode 100644 MeAgent/ios/app.xcodeproj/project.pbxproj create mode 100644 MeAgent/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme create mode 100644 MeAgent/ios/app.xcworkspace/contents.xcworkspacedata create mode 100644 MeAgent/ios/app/AppDelegate.h create mode 100644 MeAgent/ios/app/AppDelegate.mm create mode 100644 MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png create mode 100644 MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 MeAgent/ios/app/Images.xcassets/Contents.json create mode 100644 MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/Contents.json create mode 100644 MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/image.png create mode 100644 MeAgent/ios/app/Images.xcassets/SplashScreenBackground.imageset/Contents.json create mode 100644 MeAgent/ios/app/Images.xcassets/SplashScreenBackground.imageset/image.png create mode 100644 MeAgent/ios/app/Info.plist create mode 100644 MeAgent/ios/app/PrivacyInfo.xcprivacy create mode 100644 MeAgent/ios/app/SplashScreen.storyboard create mode 100644 MeAgent/ios/app/Supporting/Expo.plist create mode 100644 MeAgent/ios/app/app-Bridging-Header.h create mode 100644 MeAgent/ios/app/app.entitlements create mode 100644 MeAgent/ios/app/main.m create mode 100644 MeAgent/ios/app/noop-file.swift rename {argon-pro-react-native => MeAgent}/logo.jpg (100%) create mode 100644 MeAgent/navigation/Menu.js rename {argon-pro-react-native => MeAgent}/navigation/Screens.js (86%) rename {argon-pro-react-native => MeAgent}/package.json (90%) rename {argon-pro-react-native => MeAgent}/screens/About.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Agreement.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Articles.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Beauty.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Cart.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Category.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Chat.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Elements.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Fashion.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Gallery.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Home.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Notifications.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Onboarding.js (100%) rename {argon-pro-react-native => MeAgent}/screens/PersonalNotifications.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Privacy.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Pro.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Product.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Profile.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Register.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Search.js (100%) rename {argon-pro-react-native => MeAgent}/screens/Settings.js (100%) rename {argon-pro-react-native => MeAgent}/screens/SystemNotifications.js (100%) create mode 100644 MeAgent/src/components/AddWatchlistButton.js rename {argon-pro-react-native => MeAgent}/src/components/PushNotificationHandler.js (100%) rename {argon-pro-react-native => MeAgent}/src/components/index.js (100%) rename {argon-pro-react-native => MeAgent}/src/constants/index.js (100%) rename {argon-pro-react-native => MeAgent}/src/contexts/AuthContext.js (100%) rename {argon-pro-react-native => MeAgent}/src/hooks/usePushNotifications.js (100%) create mode 100644 MeAgent/src/hooks/useRealtimeQuote.js create mode 100644 MeAgent/src/hooks/useWatchlist.js rename {argon-pro-react-native => MeAgent}/src/screens/Auth/LoginScreen.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Auth/index.js (100%) create mode 100644 MeAgent/src/screens/Concepts/ConceptDetail.js create mode 100644 MeAgent/src/screens/Concepts/ConceptList.js create mode 100644 MeAgent/src/screens/Concepts/ConceptTimeline.js create mode 100644 MeAgent/src/screens/Concepts/index.js rename {argon-pro-react-native => MeAgent}/src/screens/Events/EventCard.js (97%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/EventComments.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/EventDetail.js (97%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/EventList.js (93%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/FilterModal.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/HistoricalEvents.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/MainlineView.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/RelatedConcepts.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/RelatedStocks.js (85%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/SankeyFlow.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/StockDetailModal.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/TransmissionChain.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Events/index.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/EventCalendar.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/MarketHot.js (96%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/SectorDetail.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/StockDetail.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/TodayStats.js (100%) rename {argon-pro-react-native => MeAgent}/src/screens/Market/index.js (100%) create mode 100644 MeAgent/src/screens/Profile/ProfileScreen.js create mode 100644 MeAgent/src/screens/Profile/index.js create mode 100644 MeAgent/src/screens/StockDetail/StockDetailScreen.js create mode 100644 MeAgent/src/screens/StockDetail/components/AnnouncementsPanel.js create mode 100644 MeAgent/src/screens/StockDetail/components/ChartTypeTabs.js create mode 100644 MeAgent/src/screens/StockDetail/components/ConceptsPanel.js create mode 100644 MeAgent/src/screens/StockDetail/components/EventsPanel.js create mode 100644 MeAgent/src/screens/StockDetail/components/KlineChart.js create mode 100644 MeAgent/src/screens/StockDetail/components/MinuteChart.js create mode 100644 MeAgent/src/screens/StockDetail/components/OrderBook.js create mode 100644 MeAgent/src/screens/StockDetail/components/PriceHeader.js create mode 100644 MeAgent/src/screens/StockDetail/components/RelatedInfoTabs.js create mode 100644 MeAgent/src/screens/StockDetail/components/RiseAnalysisModal.js create mode 100644 MeAgent/src/screens/StockDetail/components/index.js create mode 100644 MeAgent/src/screens/StockDetail/index.js create mode 100644 MeAgent/src/screens/Watchlist/AddWatchlistModal.js create mode 100644 MeAgent/src/screens/Watchlist/WatchlistEventItem.js create mode 100644 MeAgent/src/screens/Watchlist/WatchlistScreen.js create mode 100644 MeAgent/src/screens/Watchlist/WatchlistStockItem.js create mode 100644 MeAgent/src/screens/Watchlist/index.js rename {argon-pro-react-native => MeAgent}/src/services/api.js (96%) rename {argon-pro-react-native => MeAgent}/src/services/authService.js (100%) create mode 100644 MeAgent/src/services/companyService.js rename {argon-pro-react-native => MeAgent}/src/services/eventService.js (100%) rename {argon-pro-react-native => MeAgent}/src/services/pushService.js (100%) create mode 100644 MeAgent/src/services/stockService.js create mode 100644 MeAgent/src/services/watchlistService.js create mode 100644 MeAgent/src/services/websocketService.js rename {argon-pro-react-native => MeAgent}/src/services/ztService.js (100%) rename {argon-pro-react-native => MeAgent}/src/store/index.js (67%) rename {argon-pro-react-native => MeAgent}/src/store/slices/eventsSlice.js (100%) create mode 100644 MeAgent/src/store/slices/stockSlice.js create mode 100644 MeAgent/src/store/slices/watchlistSlice.js rename {argon-pro-react-native => MeAgent}/src/theme/index.js (100%) rename {argon-pro-react-native => MeAgent}/src/types/event.js (100%) rename {argon-pro-react-native => MeAgent}/src/utils/tradingDayUtils.js (100%) delete mode 100644 argon-pro-react-native/navigation/Menu.js diff --git a/argon-pro-react-native/.gitignore b/MeAgent/.gitignore similarity index 100% rename from argon-pro-react-native/.gitignore rename to MeAgent/.gitignore diff --git a/argon-pro-react-native/.watchmanconfig b/MeAgent/.watchmanconfig similarity index 100% rename from argon-pro-react-native/.watchmanconfig rename to MeAgent/.watchmanconfig diff --git a/argon-pro-react-native/App.js b/MeAgent/App.js similarity index 100% rename from argon-pro-react-native/App.js rename to MeAgent/App.js diff --git a/argon-pro-react-native/AuthKey_HSF578B626.p8 b/MeAgent/AuthKey_HSF578B626.p8 similarity index 100% rename from argon-pro-react-native/AuthKey_HSF578B626.p8 rename to MeAgent/AuthKey_HSF578B626.p8 diff --git a/argon-pro-react-native/README.md b/MeAgent/README.md similarity index 100% rename from argon-pro-react-native/README.md rename to MeAgent/README.md diff --git a/argon-pro-react-native/app.json b/MeAgent/app.json similarity index 96% rename from argon-pro-react-native/app.json rename to MeAgent/app.json index 0b90eb7d..9da3017a 100644 --- a/argon-pro-react-native/app.json +++ b/MeAgent/app.json @@ -24,6 +24,7 @@ "ios": { "supportsTablet": true, "bundleIdentifier": "com.valuefrontier.meagent", + "deploymentTarget": "15.1", "infoPlist": { "UIBackgroundModes": ["remote-notification"] } diff --git a/argon-pro-react-native/assets/config/argon.json b/MeAgent/assets/config/argon.json similarity index 100% rename from argon-pro-react-native/assets/config/argon.json rename to MeAgent/assets/config/argon.json diff --git a/argon-pro-react-native/assets/font/OpenSans-Bold.ttf b/MeAgent/assets/font/OpenSans-Bold.ttf similarity index 100% rename from argon-pro-react-native/assets/font/OpenSans-Bold.ttf rename to MeAgent/assets/font/OpenSans-Bold.ttf diff --git a/argon-pro-react-native/assets/font/OpenSans-Light.ttf b/MeAgent/assets/font/OpenSans-Light.ttf similarity index 100% rename from argon-pro-react-native/assets/font/OpenSans-Light.ttf rename to MeAgent/assets/font/OpenSans-Light.ttf diff --git a/argon-pro-react-native/assets/font/OpenSans-Regular.ttf b/MeAgent/assets/font/OpenSans-Regular.ttf similarity index 100% rename from argon-pro-react-native/assets/font/OpenSans-Regular.ttf rename to MeAgent/assets/font/OpenSans-Regular.ttf diff --git a/argon-pro-react-native/assets/font/argon.ttf b/MeAgent/assets/font/argon.ttf similarity index 100% rename from argon-pro-react-native/assets/font/argon.ttf rename to MeAgent/assets/font/argon.ttf diff --git a/argon-pro-react-native/assets/icon.png b/MeAgent/assets/icon.png similarity index 100% rename from argon-pro-react-native/assets/icon.png rename to MeAgent/assets/icon.png diff --git a/argon-pro-react-native/assets/imgs/android.png b/MeAgent/assets/imgs/android.png similarity index 100% rename from argon-pro-react-native/assets/imgs/android.png rename to MeAgent/assets/imgs/android.png diff --git a/argon-pro-react-native/assets/imgs/argon-logo-onboarding.png b/MeAgent/assets/imgs/argon-logo-onboarding.png similarity index 100% rename from argon-pro-react-native/assets/imgs/argon-logo-onboarding.png rename to MeAgent/assets/imgs/argon-logo-onboarding.png diff --git a/argon-pro-react-native/assets/imgs/argon-logo-onboarding@2x.png b/MeAgent/assets/imgs/argon-logo-onboarding@2x.png similarity index 100% rename from argon-pro-react-native/assets/imgs/argon-logo-onboarding@2x.png rename to MeAgent/assets/imgs/argon-logo-onboarding@2x.png diff --git a/argon-pro-react-native/assets/imgs/argon-logo.png b/MeAgent/assets/imgs/argon-logo.png similarity index 100% rename from argon-pro-react-native/assets/imgs/argon-logo.png rename to MeAgent/assets/imgs/argon-logo.png diff --git a/argon-pro-react-native/assets/imgs/argon-logo@2x.png b/MeAgent/assets/imgs/argon-logo@2x.png similarity index 100% rename from argon-pro-react-native/assets/imgs/argon-logo@2x.png rename to MeAgent/assets/imgs/argon-logo@2x.png diff --git a/argon-pro-react-native/assets/imgs/argonlogo.png b/MeAgent/assets/imgs/argonlogo.png similarity index 100% rename from argon-pro-react-native/assets/imgs/argonlogo.png rename to MeAgent/assets/imgs/argonlogo.png diff --git a/argon-pro-react-native/assets/imgs/bg.png b/MeAgent/assets/imgs/bg.png similarity index 100% rename from argon-pro-react-native/assets/imgs/bg.png rename to MeAgent/assets/imgs/bg.png diff --git a/argon-pro-react-native/assets/imgs/getPro-bg.png b/MeAgent/assets/imgs/getPro-bg.png similarity index 100% rename from argon-pro-react-native/assets/imgs/getPro-bg.png rename to MeAgent/assets/imgs/getPro-bg.png diff --git a/argon-pro-react-native/assets/imgs/getPro-bg@2x.png b/MeAgent/assets/imgs/getPro-bg@2x.png similarity index 100% rename from argon-pro-react-native/assets/imgs/getPro-bg@2x.png rename to MeAgent/assets/imgs/getPro-bg@2x.png diff --git a/argon-pro-react-native/assets/imgs/icon.png b/MeAgent/assets/imgs/icon.png similarity index 100% rename from argon-pro-react-native/assets/imgs/icon.png rename to MeAgent/assets/imgs/icon.png diff --git a/argon-pro-react-native/assets/imgs/ios.png b/MeAgent/assets/imgs/ios.png similarity index 100% rename from argon-pro-react-native/assets/imgs/ios.png rename to MeAgent/assets/imgs/ios.png diff --git a/argon-pro-react-native/assets/imgs/profile-img.jpg b/MeAgent/assets/imgs/profile-img.jpg similarity index 100% rename from argon-pro-react-native/assets/imgs/profile-img.jpg rename to MeAgent/assets/imgs/profile-img.jpg diff --git a/argon-pro-react-native/assets/imgs/profile-screen-bg.png b/MeAgent/assets/imgs/profile-screen-bg.png similarity index 100% rename from argon-pro-react-native/assets/imgs/profile-screen-bg.png rename to MeAgent/assets/imgs/profile-screen-bg.png diff --git a/argon-pro-react-native/assets/imgs/register-bg.png b/MeAgent/assets/imgs/register-bg.png similarity index 100% rename from argon-pro-react-native/assets/imgs/register-bg.png rename to MeAgent/assets/imgs/register-bg.png diff --git a/argon-pro-react-native/assets/imgs/splash.png b/MeAgent/assets/imgs/splash.png similarity index 100% rename from argon-pro-react-native/assets/imgs/splash.png rename to MeAgent/assets/imgs/splash.png diff --git a/argon-pro-react-native/assets/logo.jpg b/MeAgent/assets/logo.jpg similarity index 100% rename from argon-pro-react-native/assets/logo.jpg rename to MeAgent/assets/logo.jpg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/bag-17.svg b/MeAgent/assets/nucleo icons/svg/bag-17.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/bag-17.svg rename to MeAgent/assets/nucleo icons/svg/bag-17.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/basket.svg b/MeAgent/assets/nucleo icons/svg/basket.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/basket.svg rename to MeAgent/assets/nucleo icons/svg/basket.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/bell.svg b/MeAgent/assets/nucleo icons/svg/bell.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/bell.svg rename to MeAgent/assets/nucleo icons/svg/bell.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/calendar-date.svg b/MeAgent/assets/nucleo icons/svg/calendar-date.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/calendar-date.svg rename to MeAgent/assets/nucleo icons/svg/calendar-date.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/chart-pie-35.svg b/MeAgent/assets/nucleo icons/svg/chart-pie-35.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/chart-pie-35.svg rename to MeAgent/assets/nucleo icons/svg/chart-pie-35.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/diamond.svg b/MeAgent/assets/nucleo icons/svg/diamond.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/diamond.svg rename to MeAgent/assets/nucleo icons/svg/diamond.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/engine-start.svg b/MeAgent/assets/nucleo icons/svg/engine-start.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/engine-start.svg rename to MeAgent/assets/nucleo icons/svg/engine-start.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/g-check.svg b/MeAgent/assets/nucleo icons/svg/g-check.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/g-check.svg rename to MeAgent/assets/nucleo icons/svg/g-check.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/hat-3.svg b/MeAgent/assets/nucleo icons/svg/hat-3.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/hat-3.svg rename to MeAgent/assets/nucleo icons/svg/hat-3.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/ic_grain_48px.svg b/MeAgent/assets/nucleo icons/svg/ic_grain_48px.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/ic_grain_48px.svg rename to MeAgent/assets/nucleo icons/svg/ic_grain_48px.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/ic_mail_24px.svg b/MeAgent/assets/nucleo icons/svg/ic_mail_24px.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/ic_mail_24px.svg rename to MeAgent/assets/nucleo icons/svg/ic_mail_24px.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/map-big.svg b/MeAgent/assets/nucleo icons/svg/map-big.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/map-big.svg rename to MeAgent/assets/nucleo icons/svg/map-big.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/menu-8.svg b/MeAgent/assets/nucleo icons/svg/menu-8.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/menu-8.svg rename to MeAgent/assets/nucleo icons/svg/menu-8.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/nav-down.svg b/MeAgent/assets/nucleo icons/svg/nav-down.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/nav-down.svg rename to MeAgent/assets/nucleo icons/svg/nav-down.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/nav-left.svg b/MeAgent/assets/nucleo icons/svg/nav-left.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/nav-left.svg rename to MeAgent/assets/nucleo icons/svg/nav-left.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/nav-right.svg b/MeAgent/assets/nucleo icons/svg/nav-right.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/nav-right.svg rename to MeAgent/assets/nucleo icons/svg/nav-right.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/padlock-unlocked.svg b/MeAgent/assets/nucleo icons/svg/padlock-unlocked.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/padlock-unlocked.svg rename to MeAgent/assets/nucleo icons/svg/padlock-unlocked.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/palette.svg b/MeAgent/assets/nucleo icons/svg/palette.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/palette.svg rename to MeAgent/assets/nucleo icons/svg/palette.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/search-zoom-in.svg b/MeAgent/assets/nucleo icons/svg/search-zoom-in.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/search-zoom-in.svg rename to MeAgent/assets/nucleo icons/svg/search-zoom-in.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/shop.svg b/MeAgent/assets/nucleo icons/svg/shop.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/shop.svg rename to MeAgent/assets/nucleo icons/svg/shop.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/spaceship.svg b/MeAgent/assets/nucleo icons/svg/spaceship.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/spaceship.svg rename to MeAgent/assets/nucleo icons/svg/spaceship.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/support.svg b/MeAgent/assets/nucleo icons/svg/support.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/support.svg rename to MeAgent/assets/nucleo icons/svg/support.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/switches.svg b/MeAgent/assets/nucleo icons/svg/switches.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/switches.svg rename to MeAgent/assets/nucleo icons/svg/switches.svg diff --git a/argon-pro-react-native/assets/nucleo icons/svg/ungroup.svg b/MeAgent/assets/nucleo icons/svg/ungroup.svg similarity index 100% rename from argon-pro-react-native/assets/nucleo icons/svg/ungroup.svg rename to MeAgent/assets/nucleo icons/svg/ungroup.svg diff --git a/argon-pro-react-native/assets/splash.png b/MeAgent/assets/splash.png similarity index 100% rename from argon-pro-react-native/assets/splash.png rename to MeAgent/assets/splash.png diff --git a/argon-pro-react-native/babel.config.js b/MeAgent/babel.config.js similarity index 100% rename from argon-pro-react-native/babel.config.js rename to MeAgent/babel.config.js diff --git a/argon-pro-react-native/components/Button.js b/MeAgent/components/Button.js similarity index 100% rename from argon-pro-react-native/components/Button.js rename to MeAgent/components/Button.js diff --git a/argon-pro-react-native/components/Card.js b/MeAgent/components/Card.js similarity index 100% rename from argon-pro-react-native/components/Card.js rename to MeAgent/components/Card.js diff --git a/argon-pro-react-native/components/DrawerItem.js b/MeAgent/components/DrawerItem.js similarity index 100% rename from argon-pro-react-native/components/DrawerItem.js rename to MeAgent/components/DrawerItem.js diff --git a/argon-pro-react-native/components/Header.js b/MeAgent/components/Header.js similarity index 100% rename from argon-pro-react-native/components/Header.js rename to MeAgent/components/Header.js diff --git a/argon-pro-react-native/components/Icon.js b/MeAgent/components/Icon.js similarity index 100% rename from argon-pro-react-native/components/Icon.js rename to MeAgent/components/Icon.js diff --git a/argon-pro-react-native/components/Input.js b/MeAgent/components/Input.js similarity index 100% rename from argon-pro-react-native/components/Input.js rename to MeAgent/components/Input.js diff --git a/argon-pro-react-native/components/Notification.js b/MeAgent/components/Notification.js similarity index 100% rename from argon-pro-react-native/components/Notification.js rename to MeAgent/components/Notification.js diff --git a/argon-pro-react-native/components/Select.js b/MeAgent/components/Select.js similarity index 100% rename from argon-pro-react-native/components/Select.js rename to MeAgent/components/Select.js diff --git a/argon-pro-react-native/components/Switch.js b/MeAgent/components/Switch.js similarity index 100% rename from argon-pro-react-native/components/Switch.js rename to MeAgent/components/Switch.js diff --git a/argon-pro-react-native/components/Tabs.js b/MeAgent/components/Tabs.js similarity index 100% rename from argon-pro-react-native/components/Tabs.js rename to MeAgent/components/Tabs.js diff --git a/argon-pro-react-native/components/index.js b/MeAgent/components/index.js similarity index 100% rename from argon-pro-react-native/components/index.js rename to MeAgent/components/index.js diff --git a/argon-pro-react-native/constants/Images.js b/MeAgent/constants/Images.js similarity index 100% rename from argon-pro-react-native/constants/Images.js rename to MeAgent/constants/Images.js diff --git a/argon-pro-react-native/constants/Theme.js b/MeAgent/constants/Theme.js similarity index 100% rename from argon-pro-react-native/constants/Theme.js rename to MeAgent/constants/Theme.js diff --git a/argon-pro-react-native/constants/articles.js b/MeAgent/constants/articles.js similarity index 100% rename from argon-pro-react-native/constants/articles.js rename to MeAgent/constants/articles.js diff --git a/argon-pro-react-native/constants/cart.js b/MeAgent/constants/cart.js similarity index 100% rename from argon-pro-react-native/constants/cart.js rename to MeAgent/constants/cart.js diff --git a/argon-pro-react-native/constants/categories.js b/MeAgent/constants/categories.js similarity index 100% rename from argon-pro-react-native/constants/categories.js rename to MeAgent/constants/categories.js diff --git a/argon-pro-react-native/constants/deals.js b/MeAgent/constants/deals.js similarity index 100% rename from argon-pro-react-native/constants/deals.js rename to MeAgent/constants/deals.js diff --git a/argon-pro-react-native/constants/index.js b/MeAgent/constants/index.js similarity index 100% rename from argon-pro-react-native/constants/index.js rename to MeAgent/constants/index.js diff --git a/argon-pro-react-native/constants/tabs.js b/MeAgent/constants/tabs.js similarity index 100% rename from argon-pro-react-native/constants/tabs.js rename to MeAgent/constants/tabs.js diff --git a/argon-pro-react-native/constants/utils.js b/MeAgent/constants/utils.js similarity index 100% rename from argon-pro-react-native/constants/utils.js rename to MeAgent/constants/utils.js diff --git a/MeAgent/eas.json b/MeAgent/eas.json new file mode 100644 index 00000000..51284cc2 --- /dev/null +++ b/MeAgent/eas.json @@ -0,0 +1,28 @@ +{ + "cli": { + "version": ">= 12.0.0" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal", + "ios": { + "simulator": false + } + }, + "preview": { + "distribution": "internal", + "ios": { + "buildConfiguration": "Release" + } + }, + "production": { + "ios": { + "buildConfiguration": "Release" + } + } + }, + "submit": { + "production": {} + } +} diff --git a/argon-pro-react-native/index.js b/MeAgent/index.js similarity index 100% rename from argon-pro-react-native/index.js rename to MeAgent/index.js diff --git a/MeAgent/ios/.gitignore b/MeAgent/ios/.gitignore new file mode 100644 index 00000000..8beb3443 --- /dev/null +++ b/MeAgent/ios/.gitignore @@ -0,0 +1,30 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace +.xcode.env.local + +# Bundle artifacts +*.jsbundle + +# CocoaPods +/Pods/ diff --git a/MeAgent/ios/.xcode.env b/MeAgent/ios/.xcode.env new file mode 100644 index 00000000..3d5782c7 --- /dev/null +++ b/MeAgent/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/MeAgent/ios/Podfile b/MeAgent/ios/Podfile new file mode 100644 index 00000000..87b0637d --- /dev/null +++ b/MeAgent/ios/Podfile @@ -0,0 +1,87 @@ +require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") +require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +use_autolinking_method_symbol = ('use' + '_native' + '_modules!').to_sym +origin_autolinking_method = self.method(use_autolinking_method_symbol) +self.define_singleton_method(use_autolinking_method_symbol) do |*args| + if ENV['EXPO_UNSTABLE_CORE_AUTOLINKING'] == '1' + Pod::UI.puts('Using expo-modules-autolinking as core autolinking source'.green) + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + origin_autolinking_method.call(config_command) + else + origin_autolinking_method.call() + end +end + +platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'app' do + use_expo_modules! + config = use_native_modules! + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + + # 抑制第三方库的警告并统一部署目标 + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = 'YES' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.1' + end + end + end + + post_integrate do |installer| + begin + expo_patch_react_imports!(installer) + rescue => e + Pod::UI.warn e + end + end +end diff --git a/MeAgent/ios/Podfile.lock b/MeAgent/ios/Podfile.lock new file mode 100644 index 00000000..cc030802 --- /dev/null +++ b/MeAgent/ios/Podfile.lock @@ -0,0 +1,1658 @@ +PODS: + - boost (1.83.0) + - DoubleConversion (1.1.6) + - EXApplication (5.9.1): + - ExpoModulesCore + - EXConstants (16.0.2): + - ExpoModulesCore + - EXNotifications (0.28.19): + - ExpoModulesCore + - Expo (51.0.39): + - ExpoModulesCore + - ExpoAsset (10.0.10): + - ExpoModulesCore + - ExpoBlur (13.0.3): + - ExpoModulesCore + - ExpoClipboard (6.0.3): + - ExpoModulesCore + - ExpoDevice (6.0.2): + - ExpoModulesCore + - ExpoFileSystem (17.0.1): + - ExpoModulesCore + - ExpoFont (12.0.10): + - ExpoModulesCore + - ExpoKeepAwake (13.0.2): + - ExpoModulesCore + - ExpoLinearGradient (13.0.2): + - ExpoModulesCore + - ExpoModulesCore (1.12.26): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsinspector + - React-NativeModulesApple + - React-RCTAppDelegate + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - ExpoWebBrowser (13.0.3): + - ExpoModulesCore + - EXSplashScreen (0.27.7): + - DoubleConversion + - ExpoModulesCore + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - FBLazyVector (0.74.5) + - fmt (9.1.0) + - glog (0.3.5) + - hermes-engine (0.74.5): + - hermes-engine/Pre-built (= 0.74.5) + - hermes-engine/Pre-built (0.74.5) + - RCT-Folly (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Default (= 2024.01.01.00) + - RCT-Folly/Default (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Fabric (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCTDeprecation (0.74.5) + - RCTRequired (0.74.5) + - RCTTypeSafety (0.74.5): + - FBLazyVector (= 0.74.5) + - RCTRequired (= 0.74.5) + - React-Core (= 0.74.5) + - React (0.74.5): + - React-Core (= 0.74.5) + - React-Core/DevSupport (= 0.74.5) + - React-Core/RCTWebSocket (= 0.74.5) + - React-RCTActionSheet (= 0.74.5) + - React-RCTAnimation (= 0.74.5) + - React-RCTBlob (= 0.74.5) + - React-RCTImage (= 0.74.5) + - React-RCTLinking (= 0.74.5) + - React-RCTNetwork (= 0.74.5) + - React-RCTSettings (= 0.74.5) + - React-RCTText (= 0.74.5) + - React-RCTVibration (= 0.74.5) + - React-callinvoker (0.74.5) + - React-Codegen (0.74.5): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-FabricImage + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-Core (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.74.5) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/CoreModulesHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/Default (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/DevSupport (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.74.5) + - React-Core/RCTWebSocket (= 0.74.5) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTActionSheetHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTAnimationHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTBlobHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTImageHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTLinkingHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTNetworkHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTSettingsHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTTextHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTVibrationHeaders (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-Core/RCTWebSocket (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.74.5) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.0) + - Yoga + - React-CoreModules (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety (= 0.74.5) + - React-Codegen + - React-Core/CoreModulesHeaders (= 0.74.5) + - React-jsi (= 0.74.5) + - React-jsinspector + - React-NativeModulesApple + - React-RCTBlob + - React-RCTImage (= 0.74.5) + - ReactCommon + - SocketRocket (= 0.7.0) + - React-cxxreact (0.74.5): + - boost (= 1.83.0) + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.74.5) + - React-debug (= 0.74.5) + - React-jsi (= 0.74.5) + - React-jsinspector + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - React-runtimeexecutor (= 0.74.5) + - React-debug (0.74.5) + - React-Fabric (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/animations (= 0.74.5) + - React-Fabric/attributedstring (= 0.74.5) + - React-Fabric/componentregistry (= 0.74.5) + - React-Fabric/componentregistrynative (= 0.74.5) + - React-Fabric/components (= 0.74.5) + - React-Fabric/core (= 0.74.5) + - React-Fabric/imagemanager (= 0.74.5) + - React-Fabric/leakchecker (= 0.74.5) + - React-Fabric/mounting (= 0.74.5) + - React-Fabric/scheduler (= 0.74.5) + - React-Fabric/telemetry (= 0.74.5) + - React-Fabric/templateprocessor (= 0.74.5) + - React-Fabric/textlayoutmanager (= 0.74.5) + - React-Fabric/uimanager (= 0.74.5) + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/animations (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/attributedstring (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/componentregistry (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/componentregistrynative (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/components/inputaccessory (= 0.74.5) + - React-Fabric/components/legacyviewmanagerinterop (= 0.74.5) + - React-Fabric/components/modal (= 0.74.5) + - React-Fabric/components/rncore (= 0.74.5) + - React-Fabric/components/root (= 0.74.5) + - React-Fabric/components/safeareaview (= 0.74.5) + - React-Fabric/components/scrollview (= 0.74.5) + - React-Fabric/components/text (= 0.74.5) + - React-Fabric/components/textinput (= 0.74.5) + - React-Fabric/components/unimplementedview (= 0.74.5) + - React-Fabric/components/view (= 0.74.5) + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/inputaccessory (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/legacyviewmanagerinterop (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/modal (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/rncore (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/root (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/safeareaview (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/scrollview (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/text (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/textinput (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/unimplementedview (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/view (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - Yoga + - React-Fabric/core (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/imagemanager (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/leakchecker (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/mounting (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/scheduler (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/telemetry (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/templateprocessor (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/textlayoutmanager (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/uimanager + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/uimanager (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-FabricImage (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired (= 0.74.5) + - RCTTypeSafety (= 0.74.5) + - React-Fabric + - React-graphics + - React-ImageManager + - React-jsi + - React-jsiexecutor (= 0.74.5) + - React-logger + - React-rendererdebug + - React-utils + - ReactCommon + - Yoga + - React-featureflags (0.74.5) + - React-graphics (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-Core/Default (= 0.74.5) + - React-utils + - React-hermes (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-cxxreact (= 0.74.5) + - React-jsi + - React-jsiexecutor (= 0.74.5) + - React-jsinspector + - React-perflogger (= 0.74.5) + - React-runtimeexecutor + - React-ImageManager (0.74.5): + - glog + - RCT-Folly/Fabric + - React-Core/Default + - React-debug + - React-Fabric + - React-graphics + - React-rendererdebug + - React-utils + - React-jserrorhandler (0.74.5): + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-debug + - React-jsi + - React-Mapbuffer + - React-jsi (0.74.5): + - boost (= 1.83.0) + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-jsiexecutor (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) + - React-jsinspector + - React-perflogger (= 0.74.5) + - React-jsinspector (0.74.5): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-featureflags + - React-jsi + - React-runtimeexecutor (= 0.74.5) + - React-jsitracing (0.74.5): + - React-jsi + - React-logger (0.74.5): + - glog + - React-Mapbuffer (0.74.5): + - glog + - React-debug + - react-native-safe-area-context (4.10.5): + - React-Core + - react-native-webview (13.8.6): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - React-nativeconfig (0.74.5) + - React-NativeModulesApple (0.74.5): + - glog + - hermes-engine + - React-callinvoker + - React-Core + - React-cxxreact + - React-jsi + - React-jsinspector + - React-runtimeexecutor + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-perflogger (0.74.5) + - React-RCTActionSheet (0.74.5): + - React-Core/RCTActionSheetHeaders (= 0.74.5) + - React-RCTAnimation (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Codegen + - React-Core/RCTAnimationHeaders + - React-jsi + - React-NativeModulesApple + - ReactCommon + - React-RCTAppDelegate (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-CoreModules + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-nativeconfig + - React-NativeModulesApple + - React-RCTFabric + - React-RCTImage + - React-RCTNetwork + - React-rendererdebug + - React-RuntimeApple + - React-RuntimeCore + - React-RuntimeHermes + - React-runtimescheduler + - React-utils + - ReactCommon + - React-RCTBlob (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-Codegen + - React-Core/RCTBlobHeaders + - React-Core/RCTWebSocket + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTNetwork + - ReactCommon + - React-RCTFabric (0.74.5): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-Core + - React-debug + - React-Fabric + - React-FabricImage + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-jsinspector + - React-nativeconfig + - React-RCTImage + - React-RCTText + - React-rendererdebug + - React-runtimescheduler + - React-utils + - Yoga + - React-RCTImage (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Codegen + - React-Core/RCTImageHeaders + - React-jsi + - React-NativeModulesApple + - React-RCTNetwork + - ReactCommon + - React-RCTLinking (0.74.5): + - React-Codegen + - React-Core/RCTLinkingHeaders (= 0.74.5) + - React-jsi (= 0.74.5) + - React-NativeModulesApple + - ReactCommon + - ReactCommon/turbomodule/core (= 0.74.5) + - React-RCTNetwork (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Codegen + - React-Core/RCTNetworkHeaders + - React-jsi + - React-NativeModulesApple + - ReactCommon + - React-RCTSettings (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Codegen + - React-Core/RCTSettingsHeaders + - React-jsi + - React-NativeModulesApple + - ReactCommon + - React-RCTText (0.74.5): + - React-Core/RCTTextHeaders (= 0.74.5) + - Yoga + - React-RCTVibration (0.74.5): + - RCT-Folly (= 2024.01.01.00) + - React-Codegen + - React-Core/RCTVibrationHeaders + - React-jsi + - React-NativeModulesApple + - ReactCommon + - React-rendererdebug (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - RCT-Folly (= 2024.01.01.00) + - React-debug + - React-rncore (0.74.5) + - React-RuntimeApple (0.74.5): + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-callinvoker + - React-Core/Default + - React-CoreModules + - React-cxxreact + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-Mapbuffer + - React-NativeModulesApple + - React-RCTFabric + - React-RuntimeCore + - React-runtimeexecutor + - React-RuntimeHermes + - React-utils + - React-RuntimeCore (0.74.5): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-cxxreact + - React-featureflags + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - React-runtimeexecutor (0.74.5): + - React-jsi (= 0.74.5) + - React-RuntimeHermes (0.74.5): + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-featureflags + - React-hermes + - React-jsi + - React-jsinspector + - React-jsitracing + - React-nativeconfig + - React-RuntimeCore + - React-utils + - React-runtimescheduler (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker + - React-cxxreact + - React-debug + - React-featureflags + - React-jsi + - React-rendererdebug + - React-runtimeexecutor + - React-utils + - React-utils (0.74.5): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-debug + - React-jsi (= 0.74.5) + - ReactCommon (0.74.5): + - ReactCommon/turbomodule (= 0.74.5) + - ReactCommon/turbomodule (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - ReactCommon/turbomodule/bridging (= 0.74.5) + - ReactCommon/turbomodule/core (= 0.74.5) + - ReactCommon/turbomodule/bridging (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - ReactCommon/turbomodule/core (0.74.5): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.74.5) + - React-cxxreact (= 0.74.5) + - React-debug (= 0.74.5) + - React-jsi (= 0.74.5) + - React-logger (= 0.74.5) + - React-perflogger (= 0.74.5) + - React-utils (= 0.74.5) + - RNCAsyncStorage (1.23.1): + - React-Core + - RNCMaskedView (0.3.1): + - React-Core + - RNGestureHandler (2.16.2): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNReanimated (3.10.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNScreens (3.31.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-RCTImage + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - RNSVG (15.2.0): + - React-Core + - SocketRocket (0.7.0) + - Yoga (0.0.0) + +DEPENDENCIES: + - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - EXApplication (from `../node_modules/expo-application/ios`) + - EXConstants (from `../node_modules/expo-constants/ios`) + - EXNotifications (from `../node_modules/expo-notifications/ios`) + - Expo (from `../node_modules/expo`) + - ExpoAsset (from `../node_modules/expo-asset/ios`) + - ExpoBlur (from `../node_modules/expo-blur/ios`) + - ExpoClipboard (from `../node_modules/expo-clipboard/ios`) + - ExpoDevice (from `../node_modules/expo-device/ios`) + - ExpoFileSystem (from `../node_modules/expo-file-system/ios`) + - ExpoFont (from `../node_modules/expo-font/ios`) + - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) + - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) + - ExpoModulesCore (from `../node_modules/expo-modules-core`) + - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) + - EXSplashScreen (from `../node_modules/expo-splash-screen/ios`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) + - RCTRequired (from `../node_modules/react-native/Libraries/Required`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Codegen (from `build/generated/ios`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) + - React-Fabric (from `../node_modules/react-native/ReactCommon`) + - React-FabricImage (from `../node_modules/react-native/ReactCommon`) + - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) + - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) + - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) + - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) + - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - react-native-webview (from `../node_modules/react-native-webview`) + - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) + - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../node_modules/react-native/React`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) + - React-rncore (from `../node_modules/react-native/ReactCommon`) + - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" + - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)" + - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) + - RNReanimated (from `../node_modules/react-native-reanimated`) + - RNScreens (from `../node_modules/react-native-screens`) + - RNSVG (from `../node_modules/react-native-svg`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - SocketRocket + +EXTERNAL SOURCES: + boost: + :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + EXApplication: + :path: "../node_modules/expo-application/ios" + EXConstants: + :path: "../node_modules/expo-constants/ios" + EXNotifications: + :path: "../node_modules/expo-notifications/ios" + Expo: + :path: "../node_modules/expo" + ExpoAsset: + :path: "../node_modules/expo-asset/ios" + ExpoBlur: + :path: "../node_modules/expo-blur/ios" + ExpoClipboard: + :path: "../node_modules/expo-clipboard/ios" + ExpoDevice: + :path: "../node_modules/expo-device/ios" + ExpoFileSystem: + :path: "../node_modules/expo-file-system/ios" + ExpoFont: + :path: "../node_modules/expo-font/ios" + ExpoKeepAwake: + :path: "../node_modules/expo-keep-awake/ios" + ExpoLinearGradient: + :path: "../node_modules/expo-linear-gradient/ios" + ExpoModulesCore: + :path: "../node_modules/expo-modules-core" + ExpoWebBrowser: + :path: "../node_modules/expo-web-browser/ios" + EXSplashScreen: + :path: "../node_modules/expo-splash-screen/ios" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + fmt: + :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + :tag: hermes-2024-06-28-RNv0.74.3-7bda0c267e76d11b68a585f84cfdd65000babf85 + RCT-Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + RCTDeprecation: + :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + RCTRequired: + :path: "../node_modules/react-native/Libraries/Required" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Codegen: + :path: build/generated/ios + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-debug: + :path: "../node_modules/react-native/ReactCommon/react/debug" + React-Fabric: + :path: "../node_modules/react-native/ReactCommon" + React-FabricImage: + :path: "../node_modules/react-native/ReactCommon" + React-featureflags: + :path: "../node_modules/react-native/ReactCommon/react/featureflags" + React-graphics: + :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" + React-hermes: + :path: "../node_modules/react-native/ReactCommon/hermes" + React-ImageManager: + :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + React-jserrorhandler: + :path: "../node_modules/react-native/ReactCommon/jserrorhandler" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" + React-jsitracing: + :path: "../node_modules/react-native/ReactCommon/hermes/executor/" + React-logger: + :path: "../node_modules/react-native/ReactCommon/logger" + React-Mapbuffer: + :path: "../node_modules/react-native/ReactCommon" + react-native-safe-area-context: + :path: "../node_modules/react-native-safe-area-context" + react-native-webview: + :path: "../node_modules/react-native-webview" + React-nativeconfig: + :path: "../node_modules/react-native/ReactCommon" + React-NativeModulesApple: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + React-perflogger: + :path: "../node_modules/react-native/ReactCommon/reactperflogger" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../node_modules/react-native/Libraries/AppDelegate" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTFabric: + :path: "../node_modules/react-native/React" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + React-rendererdebug: + :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" + React-rncore: + :path: "../node_modules/react-native/ReactCommon" + React-RuntimeApple: + :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" + React-RuntimeCore: + :path: "../node_modules/react-native/ReactCommon/react/runtime" + React-runtimeexecutor: + :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + React-RuntimeHermes: + :path: "../node_modules/react-native/ReactCommon/react/runtime" + React-runtimescheduler: + :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + React-utils: + :path: "../node_modules/react-native/ReactCommon/react/utils" + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + RNCAsyncStorage: + :path: "../node_modules/@react-native-async-storage/async-storage" + RNCMaskedView: + :path: "../node_modules/@react-native-masked-view/masked-view" + RNGestureHandler: + :path: "../node_modules/react-native-gesture-handler" + RNReanimated: + :path: "../node_modules/react-native-reanimated" + RNScreens: + :path: "../node_modules/react-native-screens" + RNSVG: + :path: "../node_modules/react-native-svg" + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost: d3f49c53809116a5d38da093a8aa78bf551aed09 + DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 + EXApplication: ec862905fdab3a15bf6bd8ca1a99df7fc02d7762 + EXConstants: 89d35611505a8ce02550e64e43cd05565da35f9a + EXNotifications: 6ce128c0d3d3d161cd68bfd07d593db40e140396 + Expo: ed0a748eb6be0efd2c3df7f6de3f3158a14464c9 + ExpoAsset: 286fee7ba711ce66bf20b315e68106b13b8629fc + ExpoBlur: 99901a4531f5d3ac4a19b362907b8f75da4ed9c8 + ExpoClipboard: 243e22ff4161bbffcd3d2db469ae860ddc1156be + ExpoDevice: 84b3ed79df1234c17edfbf335f6ecf3c636f74de + ExpoFileSystem: 2988caaf68b7cb706e36d382829d99811d9d76a5 + ExpoFont: 38dddf823e32740c2a9f37c926a33aeca736b5c4 + ExpoKeepAwake: dd02e65d49f1cfd9194640028ae2857e536eb1c9 + ExpoLinearGradient: 4c44b3803b441724874b232e6520b51ca6a50db1 + ExpoModulesCore: 9ac73e2f60e0ea1d30137ca96cfc8c2aa34ef2b2 + ExpoWebBrowser: cf10afe886891ab495877dada977fe6c269614a4 + EXSplashScreen: a4ce3dd5d28d48e8b9132bcd9b58ee8e340db78c + FBLazyVector: ac12dc084d1c8ec4cc4d7b3cf1b0ebda6dab85af + fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 + glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f + hermes-engine: 8c1577f3fdb849cbe7729c2e7b5abc4b845e88f8 + RCT-Folly: 5dc73daec3476616d19e8a53f0156176f7b55461 + RCTDeprecation: 3afceddffa65aee666dafd6f0116f1d975db1584 + RCTRequired: ec1239bc9d8bf63e10fb92bd8b26171a9258e0c1 + RCTTypeSafety: f5ecbc86c5c5fa163c05acb7a1c5012e15b5f994 + React: fc9fa7258eff606f44d58c5b233a82dc9cf09018 + React-callinvoker: e3fab14d69607fb7e8e3a57e5a415aed863d3599 + React-Codegen: 3963186cb6a4ef21b5e67dcf7badf359867ff6df + React-Core: c3f589f104983dec3c3eeec5e70d61aa811bc236 + React-CoreModules: 864932ddae3ead5af5bfb05f9bbc2cedcb958b39 + React-cxxreact: bd9146108c44e6dbb99bba4568ce7af0304a2419 + React-debug: d30893c49ae1bce4037ea5cd8bb2511d2a38d057 + React-Fabric: a171830e52baf8ec2b175c6a3791e01bbb92f1fb + React-FabricImage: ad154af0067f4b5dc5a41f607e48ee343641e903 + React-featureflags: 4ae83e72d9a92452793601ac9ac7d2280e486089 + React-graphics: ed7d57965140168de86835946e8f1210c72c65dc + React-hermes: 177b1efdf3b8f10f4ca12b624b83fb4d4ccb2884 + React-ImageManager: 3a50d0ee0bf81b1a6f23a0c5b30388293bcd6004 + React-jserrorhandler: dcd62f5ca1c724c19637595ef7f45b78018e758f + React-jsi: 0abe1b0881b67caf8d8df6a57778dd0d3bb9d9a5 + React-jsiexecutor: f6ca8c04f19f6a3acaa9610f7fb728f39d6e3248 + React-jsinspector: db98771eae84e6f86f0ca5d9dcc572baadbfefc0 + React-jsitracing: f8367edacc50bb3f9f056a5aeafb8cee5849fafb + React-logger: 780b9ee9cec7d44eabc4093de90107c379078cb6 + React-Mapbuffer: f544f00b98dbdd8cbae96dd2bdb8b47f719976e0 + react-native-safe-area-context: df9763c5de6fa38883028e243a0b60123acb8858 + react-native-webview: a4483a25c71098e407df1c1d9056ab907647d7c7 + React-nativeconfig: ba9a2e54e2f0882cf7882698825052793ed4c851 + React-NativeModulesApple: 84aaad2b0e546d7b839837ca537f6e72804a4cad + React-perflogger: ed4e0c65781521e0424f2e5e40b40cc7879d737e + React-RCTActionSheet: 49d53ff03bb5688ca4606c55859053a0cd129ea5 + React-RCTAnimation: 3075449f26cb98a52bcbf51cccd0c7954e2a71db + React-RCTAppDelegate: 9a419c4dda9dd039ad851411546dd297b930c454 + React-RCTBlob: e81ab773a8fc1e9dceed953e889f936a7b7b3aa6 + React-RCTFabric: 47a87a3e3fa751674f7e64d0bcd58976b8c57db9 + React-RCTImage: d570531201c6dce7b5b63878fa8ecec0cc311c4c + React-RCTLinking: af888972b925d2811633d47853c479e88c35eb4d + React-RCTNetwork: 5728a06ff595003eca628f43f112a804f4a9a970 + React-RCTSettings: ba3665b0569714a8aaceee5c7d23b943e333fa55 + React-RCTText: b733fa984f0336b072e47512898ba91214f66ddb + React-RCTVibration: 0cbcbbd8781b6f6123671bae9ee5dd20d621af6c + React-rendererdebug: 9fc8f7d0bd19f2a3fe3791982af550b5e1535ff7 + React-rncore: 4013508a2f3fcf46c961919bbbd4bfdda198977e + React-RuntimeApple: a852a6e06ab20711658873f39cb10b0033bea19d + React-RuntimeCore: 12e5e176c0cb09926f3e6f37403a84d2e0f203a7 + React-runtimeexecutor: 0e688aefc14c6bc8601f4968d8d01c3fb6446844 + React-RuntimeHermes: 80c03a5215520c9733764ba11cbe535053c9746d + React-runtimescheduler: 2cbd0f3625b30bba08e8768776107f6f0203159b + React-utils: 9fa4e5d0b5e6c6c85c958f19d6ef854337886417 + ReactCommon: 9f285823dbe955099978d9bff65a7653ca029256 + RNCAsyncStorage: aa75595c1aefa18f868452091fa0c411a516ce11 + RNCMaskedView: de80352547bd4f0d607bf6bab363d826822bd126 + RNGestureHandler: 326e35460fb6c8c64a435d5d739bea90d7ed4e49 + RNReanimated: def444e044c354f38bb0a5926a8583ba19d944c1 + RNScreens: a2d8a2555b4653d7a19706eb172f855657ac30d7 + RNSVG: 0e7deccab0678200815104223aadd5ca734dd41d + SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d + Yoga: 950bbfd7e6f04790fdb51149ed51df41f329fcc8 + +PODFILE CHECKSUM: b710501c35a8aa0e7f8bc9bef05d8871882e1580 + +COCOAPODS: 1.16.2 diff --git a/MeAgent/ios/Podfile.properties.json b/MeAgent/ios/Podfile.properties.json new file mode 100644 index 00000000..3540391c --- /dev/null +++ b/MeAgent/ios/Podfile.properties.json @@ -0,0 +1,5 @@ +{ + "expo.jsEngine": "hermes", + "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", + "ios.deploymentTarget": "15.1" +} diff --git a/MeAgent/ios/app.xcodeproj/project.pbxproj b/MeAgent/ios/app.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3322cf0e --- /dev/null +++ b/MeAgent/ios/app.xcodeproj/project.pbxproj @@ -0,0 +1,553 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; + 5EF73AEC3ABB8432EA5E20D1 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 50B8B0092BBC618FDE9C0B04 /* PrivacyInfo.xcprivacy */; }; + 96905EF65AED1B983A6B3ABC /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-app.a */; }; + B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; + BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; + E7343D6D90954730BB45D8E1 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2985DBCB70452992045F05 /* noop-file.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 13B07F961A680F5B00A75B9A /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = app/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = app/AppDelegate.mm; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = app/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = app/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = app/main.m; sourceTree = ""; }; + 50B8B0092BBC618FDE9C0B04 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = app/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C2E3173556A471DD304B334 /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = ""; }; + 7A4D352CD337FB3A3BF06240 /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = ""; }; + AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = app/SplashScreen.storyboard; sourceTree = ""; }; + BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; + E24A9282120D4E608D7E34DA /* app-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "app-Bridging-Header.h"; path = "app/app-Bridging-Header.h"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; + FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-app/ExpoModulesProvider.swift"; sourceTree = ""; }; + FE2985DBCB70452992045F05 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "app/noop-file.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 96905EF65AED1B983A6B3ABC /* libPods-app.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 13B07FAE1A68108700A75B9A /* app */ = { + isa = PBXGroup; + children = ( + BB2F792B24A3F905000567C9 /* Supporting */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB71A68108700A75B9A /* main.m */, + AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, + FE2985DBCB70452992045F05 /* noop-file.swift */, + E24A9282120D4E608D7E34DA /* app-Bridging-Header.h */, + 50B8B0092BBC618FDE9C0B04 /* PrivacyInfo.xcprivacy */, + ); + name = app; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-app.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* app */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + D65327D7A22EEC0BE12398D9 /* Pods */, + D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* app.app */, + ); + name = Products; + sourceTree = ""; + }; + 92DBD88DE9BF7D494EA9DA96 /* app */ = { + isa = PBXGroup; + children = ( + FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */, + ); + name = app; + sourceTree = ""; + }; + BB2F792B24A3F905000567C9 /* Supporting */ = { + isa = PBXGroup; + children = ( + BB2F792C24A3F905000567C9 /* Expo.plist */, + ); + name = Supporting; + path = app/Supporting; + sourceTree = ""; + }; + D65327D7A22EEC0BE12398D9 /* Pods */ = { + isa = PBXGroup; + children = ( + 6C2E3173556A471DD304B334 /* Pods-app.debug.xcconfig */, + 7A4D352CD337FB3A3BF06240 /* Pods-app.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = { + isa = PBXGroup; + children = ( + 92DBD88DE9BF7D494EA9DA96 /* app */, + ); + name = ExpoModulesProviders; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 13B07F861A680F5B00A75B9A /* app */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */; + buildPhases = ( + 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, + F50DEC99269F1C170908EB3D /* [Expo] Configure project */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, + 4E9C7677DD0DF9724695D156 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = app; + productName = app; + productReference = 13B07F961A680F5B00A75B9A /* app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1130; + TargetAttributes = { + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1250; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* app */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, + 5EF73AEC3ABB8432EA5E20D1 /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; + }; + 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-app-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 4E9C7677DD0DF9724695D156 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F50DEC99269F1C170908EB3D /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-app/expo-configure-project.sh\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */, + E7343D6D90954730BB45D8E1 /* noop-file.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-app.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = app/app.entitlements; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XML2LHR2J; + ENABLE_BITCODE = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "FB_SONARKIT_ENABLED=1", + ); + INFOPLIST_FILE = app/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; + PRODUCT_BUNDLE_IDENTIFIER = com.valuefrontier.meagent; + PRODUCT_NAME = app; + SWIFT_OBJC_BRIDGING_HEADER = "app/app-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-app.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = app/app.entitlements; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 6XML2LHR2J; + INFOPLIST_FILE = app/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; + PRODUCT_BUNDLE_IDENTIFIER = com.valuefrontier.meagent; + PRODUCT_NAME = app; + SWIFT_OBJC_BRIDGING_HEADER = "app/app-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CXX = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD = ""; + LDPLUSPLUS = ""; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "$(inherited) "; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + USE_HERMES = true; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + CXX = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD = ""; + LDPLUSPLUS = ""; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "$(inherited) "; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + USE_HERMES = true; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "app" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "app" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/MeAgent/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme b/MeAgent/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme new file mode 100644 index 00000000..07f5cb69 --- /dev/null +++ b/MeAgent/ios/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MeAgent/ios/app.xcworkspace/contents.xcworkspacedata b/MeAgent/ios/app.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b83e63c3 --- /dev/null +++ b/MeAgent/ios/app.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/MeAgent/ios/app/AppDelegate.h b/MeAgent/ios/app/AppDelegate.h new file mode 100644 index 00000000..1658a437 --- /dev/null +++ b/MeAgent/ios/app/AppDelegate.h @@ -0,0 +1,7 @@ +#import +#import +#import + +@interface AppDelegate : EXAppDelegateWrapper + +@end diff --git a/MeAgent/ios/app/AppDelegate.mm b/MeAgent/ios/app/AppDelegate.mm new file mode 100644 index 00000000..b27f8328 --- /dev/null +++ b/MeAgent/ios/app/AppDelegate.mm @@ -0,0 +1,62 @@ +#import "AppDelegate.h" + +#import +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.moduleName = @"main"; + + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ + return [self bundleURL]; +} + +- (NSURL *)bundleURL +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +// Linking API +- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; +} + +// Universal Links +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; + return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; +} + +// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; +} + +// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler +{ + return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; +} + +@end diff --git a/MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png b/MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..bb5119fb6cb7cd15c171361105c6ef12b8fed757 GIT binary patch literal 137238 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
*SNB@ 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 literal 0 HcmV?d00001 diff --git a/MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/Contents.json b/MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..90d8d4c2 --- /dev/null +++ b/MeAgent/ios/app/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images": [ + { + "filename": "App-Icon-1024x1024@1x.png", + "idiom": "universal", + "platform": "ios", + "size": "1024x1024" + } + ], + "info": { + "version": 1, + "author": "expo" + } +} \ No newline at end of file diff --git a/MeAgent/ios/app/Images.xcassets/Contents.json b/MeAgent/ios/app/Images.xcassets/Contents.json new file mode 100644 index 00000000..ed285c2e --- /dev/null +++ b/MeAgent/ios/app/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "expo" + } +} diff --git a/MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/Contents.json b/MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/Contents.json new file mode 100644 index 00000000..3cf84897 --- /dev/null +++ b/MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images": [ + { + "idiom": "universal", + "filename": "image.png", + "scale": "1x" + }, + { + "idiom": "universal", + "scale": "2x" + }, + { + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "version": 1, + "author": "expo" + } +} \ No newline at end of file diff --git a/MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/image.png b/MeAgent/ios/app/Images.xcassets/SplashScreen.imageset/image.png new file mode 100644 index 0000000000000000000000000000000000000000..e803295a0f83c22ec2c71e8c370e42beb0729f53 GIT binary patch literal 195095 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
!pQdVY)#wmV!0RsXsU*3p=#D2!edxw@6C zMiiE2E-4QG;c`uS-)Or(uea*rV50+g=J5^OKarGh<*~81dmM|I0GcL~A5Aw>IQ@VF z3uXC#7ykeLH2IhTONn;>A+wA`yZuG-Td!ndHwC8uc#tBuXZDWmb#}I=S@sv{YY&jw zFrFo6%#Wr;S;PtdOyNfBVdl*>`N_x&$U(qauk-Qp*5sLOzP_>tM^#+1Tn~qK?x*}hZsQ{2u8T?3XC)RKs%vW#L{b} z5WL5_x0G15Gn8o6dNTT`;|EHc7jtZJ(2U9Qr=4GSFo4b*6GhgrxTZxzaH1N$EfgG0 zZhxrBf4jfs>9cX6BSvloKiFlsYSr6I4cHgabV+o=F={N&=*aJK{`)b^Z|Tjaip$IP zGT?h@f(p8{suQ%n-2Ey;Jux7q=)3SC(9J(dZcsSL#xV+1E*hfQ2@DCE*~!fp+L)Bt z&rVRMon`V3IpI`HWX*TVl2FK@NeDcBG+zBw>AR>pdS_zN<_B!^Qvb92hj$bA|6b+2 z+%hN`qh-#0H`8j-j$c+>4aX)$OT$mr(1J$utA!h&C}~W|m>^kQL4z&rTP1O&o9We% zh|Z+}`G{R%n-i`wV7l07CT2hqj;835DYe#6vyLCplU%J(|B9h3xO*w-^Jflb90PSn z_*eOpa& zK+y?69JCZ}jW{^i7dPg5l|we7z)3kX_0Y)>d9Q*|!XNw7qf0+U**8Xgp7y}_g9J1l z{ZpYIuA4#GrVrBs2FYf0_7e?k*va z0F12$Bkv5{9H2fE(!H1+jVn1qbFhzGkgLMmTef7(^J8A!%AkS}EkUTF&uk)avCua- z??baTi(5ZP06-rqvTP&~Z$>9VryZ}Meg^KhcH0(5FoK6IX=VU}Wd;%;JpcqEM@x%8 zSp@bg0*22Yp4q0NW`1ZGQ(e7%4+*4vGQEd17f;NiP-sl}Fxf#Eff`y63iU49!v6k| z=+u_k2Q%`|ZB<)GVr?`$uYD_^p+ZxbQdSeA=?lAPuQ+%IH>b~*Pgvhs4*gZ0xclrD zAMrPrRFnjUH;$UqQOyFwE}=QRsz52OX5q-*k8)9eCdz*6^-~I?kS`}^P4@0jY=5vy zKXZe&{3Sj;ugYE`zS(O?;UEQ;6|NKsku_z#@NmyWe*`-dxWG?QB$CV0nL-8!;~Dsx zkJF-XUI1g5m`#o5TN0w-~kwg z27^K$v=?MhyG2DYz{@Y-r-|XHdgcju;nB$`W|EKp&exbw|0D~Z^5>Uw`!_k~{p`|N z<-1+x)I;hg&XrQeXqe({GtHjMpSG!8-n})W^6bd58o$y6^{7YYg;e4Uw+{+}zeB*{ z|H*4_l(kdudf#~RVCbOLe>r~?2_1j%D-_5-=s*!cO!)Sk7n;*?l>*ggf=h6cPhV&v zKSBXQ0IO|M@Y7zHfpF{hI4&p{@}U4%!RAMTdmRpK7_G9jM2z}C@dej=3@Ts!qXww} z(CZ(?7X%Q4sl$g4Ll*&C0x;%x>ytbLAwg?JP>_OHVRp7#3tRGh(uTzu&spE}d6rZZ zHu)s%P_P(ix3c^zR}}LmizV7|2b@0 z0|~&Fq1;pcA}o-cq{d>1++7gcjhKM`Yml9%pT{i~m%*vv4&C#E7FA26eRfF$V{I0x z49~w3eiIQwUieDB0M($G5ec<+C-PdhGU4p)OaRQ8&~ApReAl-IF_X za&7a;Nzz73WaJGc|GSBfE>%Vpmq~T9j`t)zkd8ojSZr$B6u1++mh285pm$zKA7Gp2vX8Ctd2pe<+> z6Qo{~CD+r{e9D?Vlmy3rMp~r7&%P)6b6-cPCqG`4KWjBB^!4?8)T33F?y)0FQkJXI zbF0~n&)>PC4J{=>8X=uKGE5xYbXnn6ke`<{M@R^v{$urv;&kfqZ!|k%r5^vn)J=V; zOznjONs{@)%gm4I!>oZ^reb%g6W_moFEFtJy!6`S&8?-66Wbe;X=gK6)L3%KXxSVy zLxkx%)szL(bo=jAM^cFYn(A2iAtNTVEi z4|+_fde*#pKKgWy!t^ZkkpSdXIoWxFj<1%mWQNx8k5W^kI-w7`aZ62XjDlaB8Lr=v+HPL94f$ zMn;Z8b2yU22g4a*H|d%hUgs~pjm$eMe7i}()B90)u@Q2Lv;l776^9_c;*WFGs%`x< zk;IJFE*<=+8Z|?d1wVT|M~#S+a2mNL6~p^5XpT)|sq)=Nl+0psXM;P#($;rLR-DWP z=950AK%Bf-4PfBhY+7kc2WM|bJg_vNGGPbweEC~T3JCCG3wf{m|;tLFR9=iHSQ zwc770`2Ut%e;OH2{n3=uEP&>9v@EpWC^VgEQsf931vl|$F$EJ$4amal!Z{-M#_WrL zOI>t~#7`gO4yimHdZJ`*P^YFT#ln#cFjI*0Yav0#!F!ckbT$~aG|K*%n;ef=ixu#* zKV~B0eImEm-zJoL_p~V;tlp9Xn1}J~TWc0)(6VmQv_%EaTphIad7=$PhH;6?C^cVr zSBKLCk!E?Xe`aPsft;fPP`aFc1@iGB1%H^cKWhJE=;c?B>&;#QWSbdU zY7)vnZo(p$8dYp)@Vy0m{D)={!H}1&N}vF05Jref$U&32HHs&kzw$-o>CZx5qQqzfTVN z%-w&mvAVgi7?3u38MBo3HL*d#SJ%K!chJwNw5lq2>}CUdqlr?JWW6xTv!rT4%{aGhU>YaA78<*W9^H{~@v zO2a!!E+Cvzor~O4opx;`JjB&%DIFSOq2MzOrf9RF#?h!JY3P=@bjMKA|C~-5{~(#i z$3Lo2h&n;^BikP>dD<1ZKeHEh_PBeWT%y&Hddi3CS1O#b^bEa#Um_peb2I})h8qih zcC=PK*NGxe@4}7s@51~)t;%KQ78?u3+!J)kZT7vxnd$}R?Bf9wHazTd?Fn*AM!Uh{ zKWO&`_P^Mxw2k|=7Z7v<(DAwkY)K4gD1lh?{<*S$6To_bxPylga~DQXn7yYxL%toZ z@@VQW>s`scQl#Ckc}eiqxJ_b8IgM0Cy`6yK|A6=Z{V9}-@UH&nh5gs3g-liOEs?Qg zMseMhC?ejkpS&H^eet3KUOes5X!W-Z(hg5HeX5o#?L2;%LY&rs@eQgYDtMNM!`LdW ziHX6p`S8=fO8Y0aGmnR-e+fwZ(JS`Y-)Fdi)!|duoX_{4?R;C2`_KM_2l&R)8XtD9 zRG8yogRTpOPKu-=cV4H(lo&?AZ&5VXpU^fsPC5n@Yu9_8h28kVL{ixK_+TUR^qOaS zRo`%yCihG7?@cbQii_5PnF{7Vvj^lPxUyqXx^Q!^t!rX+MuKGPQXXCDI`@scj-zH1 z6IQR;NT@o0*z`kB_SU^>9nL%0w6g*X9VkObcIzlyJFMP`mA^OtKfQ$cO4!UFUI2-1 zuP6#*u}~hJ;s#>jN!3TFE6!UYZ8v}N`^){{pAOTgCxHZqTOTQ=VEYM)Z8_2-F*`F2jjgU41OmP|)*MPe>2F;PCrRAMmkB@m` zQxKwBE!kG*(qN6ajfH*}7Bdz;%h)U?{iwGz<^l$rk89_kNA9U#7rX3mG~7mg&!c;U#XI31$^tmmgzn2mZBCi`YeGO)#fPLF(>AzbHP zW$s0TbN4=b6x`sVqPEV}v};p=ncljs79Uc9ktjS$gpIMz){=-XB$%{y>tR^a(&xIw zd6fM*&+k9+`Cc@$bF2$3%1ek&Hc^;e4HoUaphgxgl}YJjoUeJVOqyLp$MJr^vmKND z9iMjvrzPMdwRK+?p|| zpxH}b084FY(cLm zM6&B3eWPX-jX9ep7sqhX;ET1CTbd39;OT4+I=%-Q7m`s^(Vo>kQO;EuBcVA|N=7Tp9|aat zfT&O*IO-cD*BUWGfwmEXyYvAWf{Ef^|J)h{&<<)0VBj|qoiM~s%N9WS9blg*3~bK8 zluZiF&m3rt$zU|uGGhH#L`H8S^A;lTB?beI1I*ch0mn>d_XSx2;Hm!H#D%IlF${KOH03rQ=bpiYpwj>@Kq5g{yWL10M#Da7s{_zIAt zK)A|FFt=JJ6)p5%V+`W(N5h&L=_)$bZsayJDnrOKh`z?S{khx_nP0B-1nn{GJgxf0 zHNAO#F_`YYSDj);Cm~XN^{36jjkNb8hg%_EoH9t1#|_qO^1mL9JUp6j4oGhXtFH%3 z`XCt|zNr|w_oQ>7H{$^zm7UAh{blXT9W3;@YV3DW2#3CGMbH2FTdh-A6nwO)*+(@| zcqZC;<=prB;N+aA#mgg>k-jzeCU1ZHhI{DpKQT?`HuFKIAc!RE!t)G$Y15u8Q&w5t zM4w*Qy&k!I;(X{1@ySu@O1N#I-7f{$oEG&|zs}kHSqN83K=?AD`S{H5%sZj}H)bAD5a!l`S(1jB7 zYu&nr$IZh7TymI?PA$Ehl4uddi;bUQeUr3GQHr|1^N^L*k#5P6WJcEL_4q2Q%I=21 z;lu1am+p+q9H-&pX!^pN))ML`KfC*|*>;u|@WN_84W4^3?iqY@D-SvbgP;D8U7%xe z#GR!!ZFntXcMsGCKns!|utH%Y(8ik7K6+%i=H&{L8caSXaQ&%L)o7+!&q3k+FDhVeP5DdPF67|)m;@Cq7tM5zZ9Tp8?a3dS<88jP zWtYl+@{J<$^`Qdv_$Sx7!Ktt4U6@KEJjXV<9qeuT{_0FU!Ep*lhF6PwSu7 zg1Um*Dg4^W)MUcyx5ZW&nUQph8f%Jv9Vb6@P0qx#$4CtF;%ybTPmA_7}%Uzu588rS0D~ zXU|T2wk~x>3G~0B~@6(@Qrl#Fy;mkXW+7taI^(R z9_rP&j`})U$I z<&H)C4NZ8e`>^Llm3;Jl$0#Sx1PK>;S>IgYP3<@a^B#;fKSikN2o2%;OI+P_e*H>f|bNGD4;_sbR^`eGKZ*rlY0Ys*V45K@c6 z($S@IYVyC*)>iWR(uVC-uYcUs-P03ieEXf;>zJTqAy-O#*W=#SUUH|r&uw{imt+nV zrzL^<+KNQ{!I_*8`&YXEkMjqP1a@)tiQ{o-nm(`j8(N5nXss-l1*=XfFaxoJJ;(*+ zK{;T!@>ZVn0&%t{K5qh|S3n*M3jx_hH$JYd9mRIPxo@>I$Ls)QhPNzt&+xF!hakk{ z53qmn3R43CP+G4IQeA?!UQqsU`q9mJ#aZd(dF#!POU!z+{EJ< z7y*QYCzXfNt=tkh8^iFl-vo2p{ju!q8gVhBwWM-byC zkEU6Q`E;HA;h4OKlk!n1UZw8-0x@faO*Fl4P9c@>gU^cN%{;SrE<9FZX~9_&b;(fl zc&B0IK|$2Ni{wphco_)dkLWfv`rWhp-1S|Pou0&5@tppMH#GlVUwQvVK1caI-sM=6 zpW7vS6F0VVR6BKC>L2^16-{vv??`0lBc7uG(&p#%{q2I2Y!2>Cw}ke_LhUcJn>iaL zdjm5|&)iMa3-GfX&r>O!+Y(uYX{n8HIYKP6gRx-}|9?%=zh4wfHh*EA1luT{I$OMH z@SD5RJl$5%12*pN8F9jU~ zDZSUeHSY8Y#W8X&F_}<293_k8G;gD7WIxy|*?I>x5y>8ULf;>6Pw<8XTzgWK5FELQ z&7pc2Jb6*^;#g9Q(_mkr@2zMFih0MkSzDP`ENJeBEPQL(S^S;9$-t#Pj#AgI*AvsM zpk(NXWx>QJ+Q{K4&X6-n39#IzqEdUV&T2Hkx{>n9)!iwyD3|_KKc`1@Apxx5)?kJp zT6nT3Y?L)$L8oU##vh*K00WeV?p1Y{l%p;1ha{qHoL0Va+{*9*)~TQLGtBPk%X!Kk z#WpHsf4zfiXP*htBq5X;p0M^|ajNT;L8g8T77K(?=kY$O3x%fPi`d6tpN+L z@Ztgr&h%xduntJRkw7TemJB46KYwi#6|}au|MXfyPffzK>gJ4*sX>{SExe*eT$6Mz z3j5E~QvSgUA2?LDXDJY|(`7lKWuu8eEi~|rw?;iaa_B!kkUajG0%s{;v;CL8RFy2< z8;wKf&|0wPqF5Yt4$c|A0;uo1{L)%uwptYY&t}MT^1UE?ZphxAlXt_Ry{F*^ePQVL zU++TthVJzkiK7U5_xdqs&W*%Q|2^X8CUA5gxQyj`U{Cm zb_K1apVXJmoT<Xg1$8&iJkyzc)?jpj1>^gY(e0f- z)C|<-=_RXoOTy#waBeOJ62ONZ)P!*Wyo4E(9=LYTI8%7=>VW5=bMUk`;tvV_|n^EByPAC4;!677_~9 z4mdi@`QYALp@qY>J@%&;UaqCBm>3;y-u@Q3!> zjq9DHid4Ra_Qs4eS+6iHT)D|nX268(W>Z>|p|sXLrt(*@6uRX%DFOpR|ewh(U9_qoHi zcIIhJ<-OQ+JWiCt)pSYS%1xsdTcJIUZSAGHt-fm9Z?_wg5v=;STT64&idHAUH7YxH zeC5=O?BK`LcV9drh}u(F3W7KD!u|R0Zvkd*Zkoz0v>FVJAJ(-{nODCd&;NnvYxSor z!hK~wx1uu5CZ2?Pyi{7JAzJY6l-{JM8tBC3t!2B&#jwILM!J>d>80tjWmR+*=8xVj&W8Xr%^Bcq|!l71v;-qe9N6msXWrUudB*)BS3 z@*{WOyOc@3mW%z#QDq|az1By0Y-_Agm>hSPAv>CaDL?Kx5#E4_6`pHG4<`P6PwAqj zV2Q8v9CVXnpc-)_z{qvURyp5P*OZODy})vsupo*-c)WeT0Q>~Z3;@Z1i{D-KPbS~q zhUIi88h2OM5ClDi6{p&p7opcM-8|i8)qvg+$zV8dsh646lOI4bA0p6Xu6wjAI>K;Lj4WA+VX|uk^W+#bbXtyGJsP)h zhw8k8JGMv%y7u@S>cv8E1ww@1FxDXie~GAi>0=)759WOViWR(d8~?mA@pmn3jrgnh zDuaSn14EwDQ#hB8OFDRH(dh6Suzc032RU_SqXg2FxbYilBgs)T6bfZp6S&hd)kU^7 z4-RLX`Y9f0|0+m&jW+mP3lX^m6cWsuus)V zXx;37n-)ABc0+Y%nzHWvE!kJH{ZYpJEJfD@(6=YThzbk|vp9n}rjvz6Rw!yguCr9m zSN|TbI(sCu+>LKBz#<%*EpOmipK-6eKw9=L%lRUwmEnPKVqjvlWfR)pPezOamoMut ziXm^}mc@6WGSxJfxCb!F66u3%+#aEi6*Tz;`U=e`&UPu)YiQw@C*2YbVWR?5SHC5T z$$8JOfvHnF2)yhk=GG9*fOxDR6`t~59gU1Tl^eG#1;Lw@mr+5zTTRdjJhwFa!z@(XcqWO+IREO$Is7)25p0Qm^E?XOr05($Zc{ zX$eBpe=*3EBvcT<<(tuAqmjK89BFG~Q|8HKAR#3!4Y2w47n8_glgKMsu@rj3FqWCu z3Y3byp`kJn8I@FU_alXWyX4>kLV?!06)5MRdYFA)S9i(G0E&tHR2ul|twGFQ>vJdJO$V20kbWAFD0;z|egje!KBm!1gJ6Rha_OPcb8 zqT#>v4+y3RQ$ZG%q9PH}1em#ln@Wg$rUEdiOgeP|4CR?9rq59v9%V++9G}$~Z zyG^Cpc8&RPhS|?Qi~L<;?sn2z@o98FA9gD4+40Jkxl%$$J~yGbUexQ1%N9s5%l_}V zg>B_VN%5mynd&KVbBlwIStQ;F-|yo2rFBc&*Td|E$eFej8tf03NkzTg_np)8`@hTi zeD^dxbY7h6ea(zjT+fl$6ubV0!emF01amr1>Q5eTt-) zn~M)Um=!tP2z!6_*E$z>bbZbY9DVyO=Y)WQFm)*9y~-acZ;yOfY#RDvR%RHV?UsAI zcgOjE^a$Ah$dZyl;rnlVJ$ZbdH;kHZd?~OeA>8j$j&a83(lg4vXZt@-+P!B~q8y3#MA>~k2F{0C&1m@-PHL9wB-5zM`1ewo zWQU_z%FuZV&4sNeB#iob%CC#a8GB_OGQoeL`$*mG>}SMJj#}i`q@j&bOPXhNir=7h zVnptRkk{N5<4p@8SJT{HmJN~9KNrdlYVCORj6{kf&WWa>$p`Vc_gdGl$+;z^JAhzX z-w10SBh#I)QD*89op=5POH)#eIDa&n934zUhv{OF7v~A9m{W`YR(tiV$zP7c8OqOU z-LyQJta(v5AsCQhV4=66!mb{k1>CtmYKp|TQmo0Gc;k1;mkU-H7 zjbPvF#HO=Zg8VU8nsvRseeGCD3`)f+S5s`k6Y(okjr{i4h2yWtRJCiA=_1%AyP_Fr ziHT*UD9KV3x1O3l?WDk)S=_(05|OqbI0f1g8NaIoP7A3UUQ}Ote5LlFK&rTL-Z?b1 z0I++IY2i6vC|$|U)cUWIp9BRZ6!=NAwJp;J^lU$4bZpEq>fjH}u63SW(PDh@h$~t> zkfVi!Y+~@&He|*QDTx05%{#vyk(U6ZK+PN|O6@0P4xy!`%lNa1+yh?}J-Ae?UL7BK zBz@V($N$GyOSr`jCL3U@!UPe(5LKRB92^{w#w@I$iPqy!b`Sy4Y;Y5Rtw^5hRPcWre8+yr-qv|dgK4p$f9ovw?6Sc+eRgc22_?o6s(`lw=RSCNl$YHV64zp}=&!NS zsPPTF)bFtW#$;!ua}Q_r0L8=xvqLH(pr_Y0flpjftQa-2_{G~=PM(8T^}qgC=hQi! zb2T%cc`x_M<~pOKG5%oVE)AT0Suwv`Q;H|XebJpy-|oZ zr2^Py9>(_qUm#cl;I;@XKmihPiDP|FQrMD8LG#U}E$B#cL&jyfGRh^b>i z-bWT%vrpa{<|jlBn6G>_f3=aeSW64ujb)J3NfC$JLE|cTV238kJ7AaNheKwbdQGu7BQDC7~bKtJk7Pt#L z30q)azd2kUg%@0U@494OSJiV7#bnn}$+Ki6}Q(Wga2fdqMBXOj#* zoBh^(I0|F5UjrnVQFlFI@}iw7y}cwfMo2Y(yltpqB!jM*aG#7n+eRb>y zek9(8(4n2dqEuYTJdnsjK((^D-ljeMa^SFw){PdXc2@buFIV3JbG^_)BmIQ>Wi~|L zQC3r9I#{9g?Su&w??XXa({M0NHF0aZ^<+CN)*UQ1;&_bZ^_|Zv+*|sbtN!oc%=+Jv z?dx^R2|i07UF9ATVj^|WP3>-nllH`PslO;^6c1g;LNyP6SX2qp#=@q6*(g$dSeYMc zS@HK!N2zhK_Em?yP{!cAM!y7VPa`KY1gjuehFHL5d#O0^y+(#$2;2B8Tg>Z{|I{Ws zAN~gK9qmurj_t2T){lC^{IT}Qp`y3P21iPCr>u!Add5$0uM_u;cS5@k}B7YN)=QfdD#aA z|Mo@`s2%RCpLf-jRW&be2RnME7n!@-)Jf z9B^_LdxUzcP{_jzlv=priP>eKNrd-VLdy=jIq@UW!6c4GH{?982%&&Epm2Y8S(N*A z1t9ytaxgJK-j{9tZyQJ_#31Y-O(N_1K7i%(-?t=}2yn^%p)l{|=ugj9__pZ1{KGaZ z#;ErRiS!Ka?}o;A_$!w#VTIaG#!$E~rG%su2>iEf{C^l4|GS)2DNe((S|^hq>9a9l z$u?5IxMe>ssUP>dG~{oV9UAYY#u^bF+J3Q1Ir)`bu0GdV&lBU@_QOl`JC7q71~?~%JLk8c5?*$!&Geq1A0 z`TrY&UcU$J=~{; z{A~4B!o;_vKnwPftuJjG=3AduwU*mi4d-OpZJYyxZ}RVDWG)b_cxLbF^-AV6?Xk`4 z45x$6W|)|+b(^XV1J>yi%nY*157|AAkKdR%Sow0Kw5F_Md?0EGgo?Oh5kC~5cbo%WTD|RUS9a^ z?L?PbQyK_*)k{m*W7^i*fKt?9#3ard1Hm5`8CiLp0no&q z>6;^h9wd_YcA}ARP3(HOm_ibUp1!4Fq+!(Hvi-Wle zlmKaO$Z!`Q-k9Xy`qsNeId+Fw_pJ-m^QC1Rz#Ml6=LIzpu9W7cOT=stn!qqU2}%9{ zHD-oT?eO0QFo=)_crHVT1ax$cwAG=0eQ-PmHVU9Nnb66&sEZ)JTL%y3nOUF;1sXQA z5G*p8qF-HI9kRLH_HA(y=-a$BD3ui;v<>S4b@Zq%KzK=qDjLqA%Y}>^Gx%!@Y)!^W z(g_;+12ouc;J-2nfCFerfPSnXW1;{SN#HAFKj2rt7j_?@?KU=kK#bPd-=Q%BaZej4-w4$zcm-Q?HwA*RPCUht28(- z`!M`Za)ESQ@KKmiqB+J;%ov*8DMR)03XjW|qrQJJRbVuH+`IH_Yr1H&ce`_KeM#!$ zyed`6_uT$*wr2es zG3qm>G>TA=wk}ESl|@8+@QS%ptv}S8`tVy{3tt6oKvDN$UjY3IPyh2TCz<>en%2kN)Kdv1JQy{lEG$b6KtU+VIWOCL&Ah4o%`(4LC! z^I0U}qN5e6_E``)l7(VK5X$A%NVIhOKXiBc6*CUtGGn`~^gnzMqn&W^4Ad*3Tr1TX zIF1WUJwu7?#6?OsDO8F`&7NqoRFxsSp2S@$=PC#nq~4a8cyaMd<(q{0dxWO&dsqc- zOUj%mmhm2)p$)h1vHjjg1i|1xoR>>9u8iZ&o<38Zr(Sw(bl}we4q6aV>O{Ats)!W> zU$V~5bQq-}^CaNy0gm$LsvS?b_`Pll-Z@-%^!%^P?#Kuh2btHZg-$vWR816yr%T8Q zzDb8gkj6?%wrg0K(7GzUfai%+k?&!~QE0GR)SjN@mmG{_7MFoH3EV{><$&xp4h_>u z55vXb)MgkuLYD^S*2AHHyz93AE|SI=_H%OID2Zwl*KPq?JV^caa{`bCzo~ijetOU! zm_*G=`Oa7%bl^}6ld;8AvUCr1$KeO8#lhVdGzm6>8lK4f+^8oLF_HN7?KA}I#O;xA z_6PwV^T3@+48zB&mz|HF)pfvpwNL;q{gU&-TwGkhP-U_2$wXj9t>3c+PjXa_EJhr0>n?PYzaBb2Vo=^8P!^-QP z%N`Gw_ZLoEbmtv=Z+|39gI!^H_BS!Ej)prQ7p_YYN9hdj*nj5o42;$Kq&(s+GMcr7I`CiS3yhAqRCi{_68GlgnSd1 zj9GQ;`YUTt0vt&JgyFz;XttL$PSY z^ryse#M6wA>a|?VQV2ibWn@K=6MU}R4W)c zz?-4Bxy8F}I5#W$G`1mIU-3rCyl-H!wPj&^)v-GzORal+e8Dg4-!T;)!%;bM3uux! z+kpeX2`Dax-9B)@%gaj($~3Cxn!x=0DAz)=l_4MUw5rwq9o;u6uwF3oW@lrL63(=I zeVdw}O?&4uohfHVTA3_(_(^w6iJMT!e^MqfReC`Nm>kYbraE-v`|v30$-QL0XSL5; z#6c&pQlM7C8$OP@ktJ&mohHt&y@9FKn;BEzNH5iuA|$VFGA+W_ik&zZ!OC&caA~lf zl(;>r={VIKH}P>@^`YzYEGGe?xF99}8p_TE>C%k|shVGY&0<5cY@&-s|4SxOq|fE(eTr6WfRl)(np_eqn?h!x;65w@b z6&&h2C;9*)efi(Gx!8rmdG$#%`+@WmJQ5K*;#V*-EClZy;78$vd3k>Vzrb?K^iMC`t*TX5k5vkyXXTz>3<>={=Zw0t%WFCSdge{{*}%kQm**EfN?? z;JZ!8@UzW-+lMz^8v6&4j+9nccf!>I(A}&0`ratOzq6tX3uO=`2k6JcLBF)Pl|qs& z1cm_2-a#%FR|Vs>iG*A~u=1e)Eq5LtvPCYxj6N!zvA=4_zBC4DSWz-0!R2zI+WeJ1Bq+Y+JQ5@{0k(M z+9O%rGdPa$Q}yC*F`ZV;0_SCo&D%KC$t>Q>6RdSEkLtO)e&i0eM*{KGz&u9^epFY<|{FC|5 zPkw19&uYUV$?d<}n+L@gO+O6{y1GaV6THvn<#WY-9A<895XeZys~VytkNEkZe03h~ zrb~LmV<+Cou#U~*n&md)kdf3_{YuKz{_|>NJgLsoN^P+$i>%9M2S2)OD_@&)2)Z7) zQslZ>YNRcH{&lA?3l-Do`$zT9qQXGe!)p=5_ZhV--81B!SLkvR33fCOzkRsUvDX7vq^lyk+Vt zO0YEk%#A#;I|*k9!u*^V0+EjAQ|bPi~}wMGL5KEU-a&%nWZ?F60%i z|5XqaGVe%y!o|aZ6KKkqj(sND?{v{@EbCIgQptFtcAVzc{wu3eA@=wujKl2j#Pa-1 zj(19gpOa4J(c+Ri#u-%h!{U*z^g^6`tfU*6A~j|lD{a7cfj5Psp?~DO%`HneQXm5xctQ#r?cHF`W6wxtbwL#yDDHRAQC2KtBH7I%Gv5kq8=|24u~g zYhM*0yjs99&zr^pHz6Xr0{&EO%!7z@`2~A)M9<`CI8Y<}d(>xU|Hlrz_E0SNold4V z2oNUbjcw+Fxaa5f0bPf5lz%<*M3vh@YKQMmS=UCST)tx8= zsWgZx$i$C_@-=7}E97fLC{A+V^(fZ0V*PUhyg*eDto!@@jyiDRuqY}i1c(r#NG3xp zKp)`BI`dw^*UZAkh4N5w+j=-y;N$Qmgwv|2si{5#miZORj&e3O&*7r@EF8owDUd`* zC@5e%d$twP4}oq>Lmf#9MqDrSDZGn^fC)tfp;`fSZ{PwC3}L_DKkx?+Mo)Hy9i<~! zL)4A@LIga$@jxLb9)1bm<&^`&A@lZJvdZ~i3yw!C39QLh;oo!i$y~X6i0c8DyYZls zpO@A8C&$hB)tO(PsyJ`cPDE#f<3DB?WoP#Jg-`HnmN?d+6t%?4BycV~I$pty5OaHM&@*?cdgK}mZ*M=k-!ey5K|Ga^^jWwsL^Q8Z z?l?NQSw{^}rpOtR;rcw}L+-1znGCjVl zy-^NY65-@%7Bc~Z4SQK9hH-{iBLi?7Y?|Lo)vD`gB&XbXm;}RlFw4eS?5?@{}^tOAc^Po+dwx9j0;{C5t&yG$vh-6G~ z(Res4bgsTF9xC$Gw>x2XBGH>;4b@2wNjTK9kt0|F z2?7*l0dftLLPz1g^XqZ;fICLd$YA|7J2sglONIz6gk{F>ItfD~peK`J*2Lhno{dEx z@zc%}x=kQpM_Cvjdiiou3%tjcTKQD!)&+{pvcbt0EBR1SL8gwfjNs=*sYmd91ke*v#|fWX!u=BXvuGTafB3A6<(Z@Hl{yn>50xQ3~0`nTZ;**H@_=#3w2-YUTB2Z zE47U&&EO$kJ(w@{WNI3a?^WhXib=m^y?k6I?x?2j3mMd@IhuIryu#K`ajJ{o^9P@* zOfU%;69eOrOxO^Jzc%veLG}kzJc7BI@A*4>(~OPmJX%GZFo}uF8>v?>2!(qSx|jM2 z8FSW`c6r_OQc`~UHtQYh=XZYlR9&?Ms0yNZ57P`hdq52-pjm+6`e|44mgd(kGn-U+N@^Z#%SH@#b z*Qa;kwZnr_U}cZz_E-uWS!VkBnbZa_1j0)PtcdK8l+M>u>(`TVq7eeqahaxwc`#x~ zuMxOF#7Cm7Z%If7G;8=KK4Z6O!9m4QP+lfUC0>sI=O3HmV&LKQWF`WuLbA1eAe#gh zs?e8Xq~Oei+Uy4V?n-|q&^$qAFBv0+HzdN7wG>!OTg%bxqJX;5lgQ4l?-05nFtYXQ z)zz(y8Q|;RN8BIDJTM#y5n4PX3LKHyhl-MVkbetIVzBTTT~}&aFE$`|MA4l+i@13C zPIW-UkJeXfLEij=;vd%zA^Iu^;XP$v7}TCAlzY;~!UmV!*m83a(XInLT=wc!%uepK z=!^)rrcL1lq;??23@#9qGLsXWc*o&ds+5rqgHt$~6iG@%9^D#0{Di-qX?3{UH^A*>iRX{LlX^Vyg*|xln@?D`*RwCsD4sW1eMjr?ZQHE56DOdf8gZO86Q9dvGG z)-q1)qR5LLFfz*MJPNFJ+86`J&(ACTTmW2vpySKMiFl5AH520IZsY^jr_i6@t zzYe!n$%rZsww#-H*YAiH{YLKXQ54qEdG(}+4|gi-Lxuxcd;fcQv#ZZY7}ms4$A145 zy%HEm3wEKv#R>r+Z=q8FzcoL=lJ?numfS;#=R1(9SALNrzy{?SA}Lo00>U!bG!j7PJIRtDC8`KKNKuZkhmV3O{s~;>?i@5lv(-G zS9cIYH^6(fz9I&PqKFYTB+7xx3)a1b)}|gB#KPbU?-9M0)AMtW@7z0O#EUCPt2NlX zexxnf92xoS-T+sdfP>5^dk^#Be z@_*$kw8Iy=^In(F!05e%42X_6U|4m{+^Xm!WcWlrbdz>e*u z`WjuErcl+;JMdizwAAu}^9hpEho+wq0RnG{*5At=!dDCzHeeeif>_{)^*! z_3_@9KmO9+mYb~Pc8heC^@7x_|Gs8%Jlxfj)HpmJJeRr3*KMtzlWV`gWKA`i4R?<7 z_MKgshB_h=5?m@may+OSo%@a9bH~vThe4R4;ji^z&^|CTky2US(NMa;jt0V5>Dr}eHCt0hjHA7ZgiZrfl z3YFMxa-O}bOp{@5vKbH9lfpbB0*C(cc_gPEPSUoNH8C3*vQCc>?^t3{&NVh8OM#Z% z_yQ>gjiw-uS3V=tPGPxlA$`ddf~_)61&8WVfF_-A4H`y+75CFCfoSJbv&`&M|MwsZ z%jh!9qz8aEU48Q8ZScu=1@cXseUA_vY;Z00#E6H+GP>NCU)YO$fdKgwOG-jhQ9t6T zDzkm;!#+=KWS$L%)7WcvfE5B*8(01RViE z5Fw?)XL-B02F4bU0EzrG3va%u2l-sULj(}L=#%;jUj-M zkJrheQamOS8M_l$Ta{+r56>)IG=voD=IyG2=I{Li`x9*aVINi1kZ>I`8kO*0#PuW}}5PfSmDi(bflw|slf%`c$r$DHwW&U$6bxNrNXncT_oKR*6` zq)0m8cb+|~IjZPE)^Iht_4bQoBQcxbIbFm)8 z#PQr4tfW@T(&Zel`Fp!Y&k;$`QnpfQyPGfgozc-mopwaG3Tx zKQ4$67fE7rmX#^ArS%mULBc55pNIwaXuv5xSb;Enwl?W6+e7(u7AmeavBg8#>3n!Y zXpQ|)pPZcOLs!xudQbdsY!7%{VYp(3e1_rTgNtKnrBkK62IWnr5*%*?<|N(=0Cc3k zp=Tekrh^dyFGdRKj3KEezIde+v3mmAIbtLTzo=jTy&p;7>c<2v`f(}A#2ZDSAL{it zwA_4k6++(;n_i@mf#lWx{h(~kdY}tk9HbC)S0kAvAU1{$(bnPWjXMr!ifn0H^oTOz ze=Z0C01N_mmbFI!d1NYMb<)j=Uhhbn?9i|`#+=DkIbXTO9!yz(UW3%4!P^hc!={1q z^4Y*d$4~vupCjLw)xyGV4E~W2vvzv-EnmL=SOy6#+7fi1)q?e%FxwACk<*SY-dAG_ zS#^b{&rB;4WxIruUMM9d&Xl7Htzw{gE@=x=oobvq`%O&8=W2>lwNu=^$`L(u0H6|7 z*h@B%6YNW0)pACIRkA17%Rr@n**`d;*ny^=mzMqZD95byT%S;K)$W^@$DvE)_^>Nz zE|Xtt?VC}}uiQ&zYQsgvqTahax?sCe@o8TUGh;?k_x)qh3hw^V?6#xpg@amuCM|*g zF#Gd^rH%iPoG13ZmPKSAo}9=^n$F-Sx|kTX2SlotS0o4hZf2Mnj`)t3=5wI4^yOOW zI@L`%5P&LNpTW66x4}th+-)%}La2-JIAVXlnEZut;-}h(Yt`;YMx9UoiQr)<$p$Fn z(Q)HvlCe_k1``n%ilYWC=KuTxzw_S894zm#%n`jSIpe&};GCmCn@W8Wi~ z3EMdKxdKwNebw)mco7l$H%wPPX2NHO{rmkpkOP7s5HV=#)*X!iG-Q0|dO&~Qqk){# zD$_`9rGl0iWt7mNdt~7h^0us8+j%iBf8Afh6K;+!Sj|C;$VWo3h3(&~uPT?EC8gvO z&kLlo#ztU_8Q;-yzV(fTflpsJPn zzAug&bU(iwe8;hFgX^;RFF95cBRoeE01ilLYCvvjIRZ1&jfGD(b-}-`<{Wz7WAWf) zz}>q29FHjjj5RNVysD(Cbo?Wv52gO7w5|H&?w-#eY%Qm?`s2Jq$QG)$F4=$bCZLe` z84(G?G}YrB(u`U^egkSn9tiun?@q~(q+HUpPJCpV7{E6v=~-zDCbW>uP@_Q*@g}Iog;bWl}<; zL9vuAH+p@4^UDauwb|46J{Y&ZI`xD*Xt^skY;^()HzcTL4w2 ziScf6A+{$5Z>6}@N?Kk)!5 z(YWY*+o(wLb9MLqbbd>;HnojsNm{JSvVjG~L*|M@x7gMUY^v`5E!8V>Nis|FCenn7 ze=o59qku1&xL`EzI10Ve=EjKKcVy#aswvp4N&z8xZpiY2}m+a=S5yLq1a!g%-6QKs7ghUMf@wqizN<4}0>&^}R{sazA3K?ZAn z+g8uv`_WFVW%X~k9X>kEx8k=Jw`UGVyDUl`zcSgyN2Xp{RYw*9^S zkF56JIrEJ$AA@TllBh1qbLFT&r--U(p-+VqmP8FJfR#@9!+jni1PW@Kb|IuUmS4Dp z!$3BG4diXY#16c-S~gZ^KAl&nDJm!q01`A{&p;HEXf?JUfYJdm&!X_s`wvDUDCqa^ z-|6&3bp#bzip-gjo*D^B{J7IU!=ZMNOVSsNU(@kT`^4G;)zg8`X!z}&<7*2QGLW=tGjZ}j;|uVZb; zJbL=x{Fr_5*)>b?O!VaJ)tLgX&2Nh%qX~g3eHNpfCnOKFewKWqv=8^~pIB>j{L*Q~ zkZyUBo{?V{6A|xzJNWvgk*y3Q2q;}(QKA(+tp7kh)F*DlIkDr0E4)^aY^mA@(P{Z{%Rm_eLN>K|`w!S3j+NC@{@JY*)Km?<>zN<>s}${8oN`26+j z_*KFA`NuVzn?jfD>sx!C-nI;DP=6V+uOJAGsh44H4D--y<2ZaRjJ^IpsGsc zm>I%kB#4S!p>=FF(2T;Oprnj10C>ZS9j!%V)-}IAd%@sH^P5FPA z7~*reJYh5k`FvvDfQS6^*cWN`f)@|Lo92b0$SAuQ`pKZLkBX3LLHz|$C!9)%+6qTb z*;P1YXDAU zEe|2-<@-qYuFVe2gbuh}T2uEnbAw5%qp?4Nu6LJ3JhmqyD-%v)?G_Z?>1|X9J18 zH$lW!Q8>P|)H|qg&Mx7etqh{aMr&O(NL$~%cl9$-HCsFS&3Qs{iMg)(R!=7;s(MNW zm@;Hj3MP(UO^y~B%~F_eq7Peo$NH<)3S_O^mH#Bp`87nKrWN4_1Bo9Vv6vCkkUcX- z88pDa&tk|P>HIU7w*D+aBI{=4+Hm!o{ShQEc5id&8G=#W`QLwmEJ-Zxm;j+PBF=y5 zH886_Q>kyG5dY`Vao_Rv*xb#@!I14gBR~!W3**Z`)-l&wMsa76xR9YD3G_xgcJ#>9 zz_X{c#;$-39~CQsCNyaFHBtplw+K-wew1+8IdlMsl6{i|#zs zGA%Ov?;+L9VUWSAOScGbZY`(H-kTF1V1GxrS*N7v9+8;@wc*k2&AGERS3>W8NA>8s z04`%U`P_roU{+K8=cJ|K-(AP}^~`Z{ZiK^4a4^*E66q*%XAR&xMBMdz^LNuRyTO_S zdRaYwDRtWC=zEoN2c7n*zA`Ur8=7lcDp<^(b_`oPf6~_Z_4VvzZp;r#hhoF3j)qZN zAKvb8Ln^$AP0nIXKvvY#q$=OJUEa*npojg8iUQX(O6U@UJ6w9K@+bSM5nlVsVfpfnGd{YZS|=yOl9wC`2{rxI%>2m zBpeHQ@y7JngS4dpN`u|~AYaOe^X6^WR>jYcEO>Eznye1mf!Q6R5Hi427yRJfM0)_r zeP18mS(b0s%~@8%yv-Bvt^jW7)b59mv+kHF(*kp`gU>d8K3lWA86ft|+4d=)F) z2T4*0lm+JgBnbwHNhJvc%1I)8^!kmvj=n#IfBb25;z7Elwj5F=&qKviXU_s6@b7oe zRta)sn8myo>!M(!2!K8Q{gJ&$9lJZSDGPqSI~Ef#OzvA;L&A8uA(IW#I(c}J6i|Q{ zePPb{;ll^G=BT8g#r0zATbixi8D0wf@?V6axzy4_Vs4xS{Qf^*Ho;H@5iTvrsNK|C}HA zl$HhD=&cF!k;(40SD!lRzsr<~0}m(*Wyc-RF>es9kLR}-&@{aKe$pR~`+b1$3U z(5uekXXY|+o{7)&Y^rt_ci6M8-Dis|c7d5_AK5HDJQjy<`=HyjWsTL50Ox|>7?gRI zA z1FdXhCbsRd-mo4JPuSP3jw|^0hf?zEoI;LyI93`e8_EzI{ z1?5w^*kH)a)WpS0bxl277mx5rsGGktxbL#>gqFJOrYG|$msW}f3p#=IIsV#vbw9g0 zp^bHwFq;3N@iy%(d{LtKIFvj4tJT!6{g96mMigqk&IWC)1lcZ3VhpXZp})dPBqg+- zyG-BT)!}e?4`NoEPpG+QxZNdM}Jc^2e{D_nB zg{<}$GXSj?2n8c})yUTO5l|@AJCo9cQo5)T!>%2!$}<#aju;@UBf@Cv#Jm~8>VfO@ zEiW$-h!jwb@kqt$Nalx6Q(`Ry*7Uf!bRbI<);mQ+M3XqFsp7K)v@0;|?V@NJpg0W3 zG1-}*fCB`~>H1$`O*O1x`(b{nzU4xTkvQ$S>u&YBigrK}#E=#juHvSiomm!=cpbC| z9L!OOUVy-j?21}WUJl5Ve{ZSvx{HIS3qKyrg;mo1Dpm2beUs0+IjC=zG^84c@B!Ry?_LALKUxjb}0Fc1@pX^Kc_}VW@x4N^n zRnf1eo4O)&PmAN80>qA)TDQDBi{j-{8F-??@kTpDOx(wJ-ZnfkO~GpS`SK#Docs`g zrE(81u{fTt)&Y=wIda3qz8Oe$XvQqVSPik~b^QC)c5}*cOXii2XII7}uW9SK3@EYE z`Ldzx$4UZ_*@K1ibN#iI4ODdnQj!74#Od=(uWyS<8}^a{Er5T5CK~#WR=tR=kkGcMTvmhJ6 zXTGr6O?Av{ph7ttBL%!jCQuzVgK!W&9E{xwS{v(%6TFB7n*L@SLIci~LDT^rdU+EI z7%+5%uIYYyLsikyagY6{CO!uk^4~di_(jXUc|@UBe|7A#!&P<@2r?^Xz8SPnf?iqh zt$!}bl3p%RG+))y$l{6xHiPc@XnkQ4#Jm)KZoVHs(^t+d{cIpK`71_7Le zfgC&4VNrX;U%-3c6J;97Tx?RMZiA;S=_IA;@`+E3BeX6YLv~}V*>fyr^v{r452u-2 z>YM=+(Y>NuBH@#rM#D18LG^3rqRdt7?lEaaQB?4UJ#QpFdF#i(o}$4X&^6lc<@+n! z{vem>RbTfsxShPZzTgGbI5c1FD^YM4Jub)KuR5R@>;de$XRn6+|{=UCChl zT589%i<&s5gjzuT;j*E;KZ;?#YH~$xzjC_q1!k>3A9>TIVUnv=$6Rz}i&PD8z zkhb;&SbD?)rrVE%8X!?qctdHZcfAL}v=`LU5{`d9OFdDF;EB*FGdTGm&=VxqoJWlt zLE}dBD6M7z7gQ|#tvFC(59%yRiYBn&Qkpqap*R5#f8gdrkEiwPqbrs86UTCpFPln0 zwD#J^kjHjBLkOK*N@rsO3A}j2b32RGLo81?MQCZOEqA40ckgn+ZpW!j_lI|!ROwOj zZ}A4DI?c;kQ=z-V1)S&K(nqoqu!8bjE%=+8W^6<3T?XjryUrd0LZ9-x!T#3v*&^P% zf&NCgXVJty>1(xT%j!_(hp&=-wEy7Rl6+K4?OifY404E2ccEO2+k~7jmpYZc`Dwmc z=S%C4^CwN^EZcpkH3#PUw7Pcu=PpyD&^f@0-k2P*^ zSC%?H)RR1^pY>^9?O+!J$$ytY+d6gGiw`{E@M_*M76a*J-S=-eQmeW;A_hvI`8+Qx zi|j8QRZim`_WJQhB=Y zkLvXQ`NueIgVy6!n*Y}U@g7b`I-Y4rQjC0ejQNriP2T?rEIVtN{FC<&T0ZtrJ$&>4 zvH6qZ!jjMS7su5oaeJqC;hcQsvdP!!OwdN?kjP;D{c;)dLn9{@aZ%fIVe4G;cU`k; z10@8<vbateo6E- zfIFoP2}ML~jr{`uuhltOI>h>8r)K)6^q((nR&uPax|RCJ-*M3D<_`6XFs~@ue>g9W z(eR&GSC`rU@g(E6Ok8goQF2a zYfn`vuh+)Hct6(kg5UXVzU5N&&aSJt&X<0_{;t}Md};e?+HZG+7Yz*9$OwASDk1!P zYaNuL1pSR+z&FUq0?+B7Mu@R4ipODB6J^0{KKol zFcBE5LxQQv7`t4*EXZ177xVOK7Rcqc_na_64jdl6QRbt@_oP^lZXP5|n1M$}GFUBwEf}QC zs;HE|e%@VD1q#A=g#SovfF8B;Sm%!gPGFjwhkjFGR?$u_guB(GU(6tdnO>0HLwc)VnAP6(UH5H-u^}hx<9Wz2U z&b0J-ogS{Jgdy-yr<0IUlzS<|$p+O^t%vv-$O0+4UJ)XagqzuZRMJwj%HvY>VM&esAST%Qw16Wd&6r`f6?fC)?HSB^faKXXYu;9FK=<&ucRl4^Esuc(Y@q*d43R>%< z;bF@vTN=oP2s-ZjNUVA@22Vf^+5qIIAtxZEVNnav(E+a+l#ZFau9FaZtF^UWK<1HG zbaR*P^P?s@Z(=n(iS)?^3c#6#ejCD^c1GgTNHvg&&o;wDDDET`so?Y3J8{RZE&p6WqS}68plLdMI4Mi47xY$n;)^^Sw@jaV!}fOQ$}H5t8b! zu1!zS;XK%UvHc0B>7FLN7GLYM2wR8kQ> zp^M9U7~jknCqtoc4R<6oz!_IteG-w_UDD%Ids4y`G#bI;8@Rw?W5cUQmt+iaJ5C;U zRV%FEl`w^1U4w(k;kukwx~(vBp|RKA-ILVTjDZjeWcqf(1WTs75u8=c?|L8V#(QZb z4`5zBaK;)Y`v!6}(G?B+nb2BUN?r_ItKNJwkc2cri$Ku5@gnM9!)RD{ZAb?LEKgUD zp*jB^UFbey3p8#j!{MlGuG`$JUkE3b!Cox51Z$PAhXG0^69!?~9;PaL_ z*H`Gd_T79l$ri`jS>XQQcs^3v19h@%*0}NChu+jxNy0!24l__Mu*45HDeDQ9dQ}|3{z#=06=Ihe~+`dyMXJ-_c^q~f!$rp}tr5b#9=@jxikRP;isjB4hvII{745epc7zBNX;z>(t~cHx3jMnv;&NM@d{sG#_S z{TbBy`=j3&QH_JN7CwC?kVC-n`ro1)9G(-UiD0nT277pc8A)S)a9+NU#}%#-VxfT8 z)k$8&yOIklOs2!)Y}vvra5zSKE@MfuPH6)t=taa3KCXr*bXKtYLFfmrAxOCd5_-5i zliI*}7AUrW%fe-&|6?IE(aJg&dxAUqeH2zfmRNq0BSsM4*z$kaIuCHF`~Uyjj(zNr zy;s7qj%>0sQ;O`7>^)N1E1P5-BNC|y4MN$ggQQLNEZK#K|LfEDKfeF#`gL{P_nq#m zx=x?>=lvSb=i_P00ag%B#Fql)2o4jfY>;b_30*$qiTo%;>?6oVwzroIp~Z@ray!Z) zua7k<`n^x^0UlUZ6Lg6>K-#-B_1$xtr=8iT0`^WF&x;=aR6nj`TQa2(55qyf-6Uo( zVN83N;>h4Y(k^YB;7F}YzP~f(>1C=XS;sy9ZY_!D=$p~ulzK#QhMLEh?G#4DY zCz(6{R0tk-=#TbH`I3wNyYI1zwxFbN1|vyl6b!LXylx#tBuC?{oD}o)coc1dxg~4&I%FgIhTwGh<4kTg6ef$Wf_N zM%JWTQUm2c=H?Ks-+`}2(~Iu-R26FIL`CClb;Qyc+Zk9=4SqybCo-S?5gD|?*l>YU z=}@|BH14k#6TknHm=mjKZ43eEL)*bc>_SiNLeI`*)WmK+d{So9wHE}GRB>o&C~C2+ z6I{~DCr+^6zjn{Uf4*H@q9WG#`3UCQ$wxE{-XZ)aP5AgFSz@A3@M<6@kV~vL39#Vh z2=J#1-Xj#q7uV-Z^kSGCb5^SXKeneQ%7QL$H&;UNQed)qN7?YZF&FSMg8q{>eRpGzGC4Q+{P`UDq z5GCgmQ6@7)b+&0nW6!WK*yj~AcH&%>wY?kFy!LwM`aU16Uca%GFlOxdRM{moG)2o{ zm>S)`!d5;f*-ykJHb$@O9@8ji?G&o8z*=YTBMrqe#t5 zr_aZ*NWSv`fZzkt8nc4FOY#IAh$4V+Ro5o z@Q(gCGtvAIfZjF;k-Nhos(@Y#+FQh@6q*BnsLgIh(~W+CA`*v>QS99>g_n%i*Mvir z1F9^mH1yh$h?9)tO;SW<1)7O}okQvM-|TQffc9%G2-ytxIl~gOZ4hO(6nbii zh)Ro8FrFJ@Kjpg#4gGef>8Z_5(~-@ics#XPL{u`-Jt{mla8NR-xm|1K1v^!F=rNFn zl-&|Yvf;yk2$i!Z`P?q3JKA=9%E;JjT%z5ccu?V$Zd2%%A^Rq5Y`RjYSHC*JIu+j_ zBT?Wu%}sBO!izQVE~b*c!Cy2IS>(!QulOL(trn8hl{n(R!tk_Gv-dc$9&If;Yovm= zzEZHAU{LhHdT&SkaA#pV^}BQA+zC^Mkb+#0MwfX0S#DrE0DJrjqQ(VLb~lELt>f8u$ET zM-*A+Tq^4+EcUh-9UuT4!<*K=0?>0R*U`SK7?M)Un!B z_-{hU5&P!>em9Edfo0XlK%an~Bis8(=E{L1$rLgod0v>erDyGQT{(&EouqE`vWm&F$_5VmVptfcPPQFf5F<5EdX4M_ zVaz3K_GS!5Ma8~qoq?umgNtE6wW5}wxf)Wf#aAHeme1_A7Q`WxdzClPmqL<+1$T;( zs$iJcZKC+w-ye@2r|;#smn`PoVhuYUx}2b$&!XlYnYD>MqfF^Ed}%SYxwDS#wNqR~ z+)@H4GY^L2MmL;kkN0USnUwiRfHU3+B!le0#fL5di;{2YqtkPiN!Ymp7t3`-rh^iG zDTqI5;0XVIqEAVEKXZ2~vCSZwqkmY-f$yyh5tYgMZyDzfYiUTg!umqxh+5-aq%3!? zVE#O{w37gPQ(ki;RSt=BwzK9M>>d@YpL$&dmV(>AykAJzkYBRzy!qw2z@Ze z5QA|5+)z0dvo{dG13*|oM{I&q>iF|Vx%g#mla)9g*=12zAi%K6usIrdWIj(#xhoVw zlmYxkh@vO-CGctB$Ym?}&yt9kmb6o&smXTMLe_XS4Ko?;k%(!g20eXk%9a6m%+NIk z^*^Ijt$%-y=UQ0XK?4YTiT@PUNW}pEg#_*d&iB)EM=bMkM{a0-{8+}6xn~O;ieGEz zktT4e<|{=po}-w;nWc90BFF07fB z3xzJZwUYQq`C+j2Pt={^HRC2Sst`|XMEBM+U|)KgMU63_P5pTBtY zyKK%+ZwJHFEFpf<=S;sewUQ~AsW!m9y}O<50)_T@q_K)Tma6a#KOwc%a_$obyv9b z^5k9GC)e8IGxMp~sRO)LnAAS8EaHj^2MdPXCY~&vwml3=yt%zjyJ32~f;rqTRnx5& z9~{Qwc{kY`+b%nOcq^8-jQ27Y8P6P6S`UzL8IBo~kuB~|#X6g0HRJD)nnwBiwGNJ^ z#mbG*gs6j>u+4m$L+NC(@%1*S1wDmEK zJe|Lfe+AwjTi>{IbN`Ml_GaPm)$6xk){gEt|Ni}(ErYVCsL1x%OJBsMY^+<2@>1O^-QCZ2I!8YjVo>FLveHIa=ee*_4 z;vLT;?HjS7>iN>nMU{kJHt_|cgJn!8=5CbF+vg6wxRK@e0m=sWpRl`%c%g?hU^nP6 ziyuECaiw5Hp9wSeuvrVSvH~PZV**&V`e4_JsA6wy#PT3CrkKbVHGS^2`%o#dcZJr zmgltv&0&YoEpG8ieki01dgrn0OOVUjy3x41ztQz04$Vd<9&$gjQp)})6;`Nz4wn(| zU#gw}d}RGt1OWEPyJ@T$IaX`};Il?{7IbR+QAVZD*?@&Ot<;ng*js)V#PC;EJ=d+N?6=60VHfVullMeL^>#>vY0ku-e}pOYzgg#l{Q>BAF8#5ZA{O zTDU03V=J{f!fQhnZBM})eL^2CUM=1a2~M9{7!f4}UHvp@1v;V#`qCc-?rgC1*f`0a zKYt$dd1n4aq%5h}H$gDH>BgmwV7U}WJq**nc0WyKqNz6v48E3F+7?lGysh5aE=dFZ z7!BWzp>~4N7G|vW*wcAiHfo4u$Y-~UYtiV%`CrKH9)eeTlO{v)IrdmS`{|uERT^e4 zk+<}{T6Y_y~_k*B` zOUmA3vTW4VS~?8Pss5o(9F~=QEzbv!Zp?gmF|z$h{ST=x&RvQ|Z~l2yDz=_?k%(&e z@&42J(%1X6u`;P2UxdvsXL3^I7Jk)LI>0A43syOQ3=r{q<`DnT5c8CWe0U$ zL4bgvEl_4a{%R%SZv%4L0%R8;u>^Ph|E-hPhU?;^*z-qQ&UjOet)xPlt4MDW5S3xS zEs_ibHwt+6k);?%+rc<+SJ%?1bOplJn)UW3cVG2=di7g=q8JLbRC<{IXTi`OCUNb5 zYjd?Ar@#%G$T)No%s(Np{0RUzfT+A*^m_lqr25FjAVWTTV@U5YPl#JVqx(p}A|AZi z&7BU?P_@Goi#+v6sRU=VCS|TK<6jFn9V?)GW;(zr2~8BZ26Gzm_!F?OVg{`rC|Qdk z6etmC0Gdui3kd=d2vr7Mrz>ckPZtaR$I%4vqk88)WJ@Aq!s#v-R}+K&XYeqy!rvyU zL_WgM8mN|9e3MV$;`7pFDi|qMo>25{puomhbUjI_!0~j7s`dQyo3njm8p?r%s-XGEAM^U@v9VUIjXy4 z48^?cy%8%T&!qAQAV=R6@gxtwdnZepZxUZkK^gRjN!bm5hQ)27P>ir|f5AQ7?)t6G zOY{3nJT9)Hwf3)!IRurn*o*HOi$I4OJ#K)n{!Td~HA(#4D&!im^oM`OXMn037u zTE36A<&3G#{V_IW0xX6oNfKqO&SLjTkc{-=pj`>lb%u0le4!C_;=@O&0wU>jVSj#{ zpnhl?KC@)``$T_bW=%|I)JH070zyi>*r-2CYG=&9cfa<(cYg+Yx`Ujq5k~2P00-|V zN`PXg1&STz#``m|bZXN`lv&T6-=9g-Rizi9l2c?$*kh)k5R7DD+{s?B;cu6 zA4Lf8z8i`hY0{Vg+8{%*{}`o3kW%jTK}(};3z=2avu7y=&bCwId>9Ak;8@S`mdM+j zebSd$VQ@*|PK%pC29pty0Ma4(-IP;+Ttu+j1m%|-;+{tO+OJ<8!U0Gg;6^5%ywg!F zV=XN`Yy-!qu5Enq`V!8)_xHHf@iCXl>I5o{mLN-$s7(KocGkPpbC_C-+Y93MscNz6 zu@A!*=w>~q_BzjG48BJTY7mj-b9i-|XpjdfU!R@47QcI)+xcgOux3GmtygDN*Pp+5 zUJ383glEtyWt8!W8EP*RJnLW@Y=85J9tKDrKgL1y@}#rP8=*p3ry$E%aI2WA?F)hk zDl+0PF`V{}-_(UHlH#DmU6+ynw_Ij|ml@s31=>o1r$Iz53EU+Sv(kybPdRvK?Zjw5 zC@h=_BYC=dvac48lM8z9sw`{>)}(?eXAr|10|Hdk(hgV5IEL*vTl(kO0AJAiayUpG$lit)JsBmJ8%9hrJ1EKov_yn}n2Hjy5T1l9)`rJcwiC%Fz+A zq$ihFAa%PENFy`1QNHxHGyix8Qq$kqA7$H@HtT+ixoRYP#Y3#ek`G@19Tits{X;}u z^;-+6^^fekQAl*S3OBSTsAvEGprygY!c| zl^8JSF8{qqQ4Nk49A*(ZWZ>IqTX5?RW3^B_+&*BbRzjX1a@tjQfN@?*E66HBShTkn z2t$)lTnJQSOb*I0Za2_7EO8ds)HL0=-V5p{D78Fo#|uZrs78VmdZbfh9A$JwKNtGU z?5AMKYnll7JAaP%j{BC*ylnz@E91luTp(x^ADA(b4m01&MbkT_B62t5yMxGO8CRV*d-V( zYBMHwR}`&Yc9r2Vtti{PmN<(-XsSsW+BkQ=rg|?ETJ~eF^G*zFRw*lT0wyB_=zXa9Bh<-qUk1 zZTtn>W6s&`v8ncFC-9f_WILFh&pF6?ntQ|jQpQf69~PHh~w!NX)BwXKqDJOacj^`KN=f+=kmTrCh^m zC7(Ut(QV>~7V2K^h9(7{*@psH+-b3aEpzbNlKKo1>9YF8=pIQ0;W=i!n|!S7yMyoY zX$|%DnqzVR%qS!?fCctRI)x=pfFQ}Z<9pb^dfqJ><{LGQ)0WXd)05l{^_6(T0Ke}) zPkCsy_)})CKwZ74SaQQxN587}78PG7C%vPmUz5gASCB({+0Ay5y%v^&KJJip5TLs> z-O+{IkfY6js%hH=o$uhig}{V@`lZ&yTn%O|4HfHzpFhPA(gC=u(mAo69e)o1!DZ_U zr`2`msW(;FS}@Q>=6YBKJU#3DZ_wQTz|@&E89OqAhTjz2<1~m|-b-Oly;T|n0cBTV zEstdakc7^dPd!L8{gkHd6pDsmh^=z4+6eZI;~&@6_FW9;ZeM>Wr1;UZb@xh0P_}Tu zFlhdjWMNM9LJ`L3>vwOvRw4C4Ilx23X;qmBEnX!0+VIRsDT z&-Bk|C*b_m%1ks!{MfYCCl}kQylr3oA$> z(c?mF7e*i&6(|+U?!>O?7nS6Yx6Gz1LKXO>ULr75E5-*g6+@~a> zse?(%#NS+eo@wV#cRwL`pfQ;nP7ii)0yGmu7F*@wor&?Th@q z{ZMY;ig#i;wVZt0LB3hzn?{MZGI1uceed~d@7?W?uJzXb+fju(@X8?#FG1jBvE#vG zU;Ml1Z_M!SDB;!%Ui~!IjqHJP2Qty%5(XiLa)B4fc`HF72kiWC0p#DB1c8-R^ce!7z3$xF%(n8$N1STEmXnehNv{Leds(Up_r6A_fv^Zv9LPqPWQm zec_413ymXcu(kCo6NSKSCasHk#@`dj3j))jkpmxEjp4<-)$tb@U@&T(it3fdpvh}o z-mpO&!bdq8nCcH>ol@h_W>%0akZ3q2s51?JJNs{ZRr?T`zJLUxNda>os*}qt0mk2S zr`J{8bmfacb+tKn28zCd2kyYe1`n#D?aQEoM%KGmHKH_U9@vt#Tu**u3Cg(mAwO)7V^*ExiXIL?40W+s0r7^tQ(v6NC#ZxOb?a0eS47XKA1_oBa=r1(kI7C)DxR-fe(<-Nod>bD-(+g1n%Wnhjv52olyMF!EolVj#!OuAOQTasQd3fX!SFN1H5Rhe#ov~f2en`>~9vz`Zj($hjv zb`1p6=&nn+xPkZZNwZz!^XCcNq8pO{n5yiK9mbnkbyLB#6+&5a&GLj2>3G~!UXh%L z63-ts&z=0+fz?A1V!%{;^h~65qVM(bH7)+_Hw+0p#bM+TYjVf5aU^mP-!F0uNRg9E#T4kQvyUR8;*ERX) zxrvw0dccrjed*F{`!uhSkr5Mr99my~_bHrunDWDP9=2f+z*mr=ew07*T9A(XHvn5a8>)ngO_quUmNOam7DM%!DH)K&Vv-DS(M^haoGiLUyN}Y~h(RXR;WY2W_ zrH`;*yp6+L8!C7=U{b(X=}e*=J0lx>*_j%&zbo&ALA!vtu{DPA^3fJHt7H)Gnn258 zJgMb0IW@+f7=2k-R$A-dISOhXqE2-F`fGgRWwXik$oLsQeN;4r0VG9}7eEOZ1l%y! zujdDvd#=rF%*0AcDWgeeLOBV$qVgu&hOTAomOS@vu8=eW!cF+h0NB75m`v5fGe`Ba zhG-;(%1wck6MR2sD4JcIzN?>Q1;xniCVvvLob4T|YWMd#U2pt)XutVm*C+7Wi3Xd_ zZZlv-+GAK{IWU5YB+~7~fJi^I3t0{5#8IsdPRJ8VY7=#1+@oK{H;BR#+vpSZ@xxvA zL0Uu;>-fMy*^?dnJMVT7CkJy?6ia?uQhacSK;ul^aT$ z@k>eq;1vUFSSCb^x3#&kDD8Mq3%ew zYHzzZ7&3$-nAG;eCu=#aQp`_U=#B z{Z4_@2HsOnDKO`lX5m68*LM^+k?51a$9H$9-{CYkmHMPyrP!4EhKFeZbo4+0d+kWM zPX3cvO3cF2ZyD~4yAPV~{G57S3Rb@7z84TfGe_E$ahAFgFd2c?%oPVfVS5MX3VwlV zj_;&PoP~&A>;>z8I>asm+F6zi>2Ixr@4kyu`Dl%|-#j|#irik9TRj!p-xw@71S~i9 z`w3_C_~*_@6Egl!-~WG7Dh>mPy>bA4{QDOhu`y-&33`_}Q&PFCD@LhZlox7El%D)S zrSi<}6lCn%AMNsNkJ;O~YV)n0YWBZYluBecNK+^Q{>iT<(JWV@Nd!|vE zD=jBp)sx1GT~JV;sXLl;KG($(6x+mxYD^k2lhaO7D67@NSKpGqWPkU8jtrCb|M1;d zby}Lym=vSQEEg-D6xQx2oKNW%BYuU<3bA1Pcf0xDKgt9qXUwDY`@v`dh6LC%qzW9I z-=F_$?{{$3?QN$dMLbp07TcI$J4;&&Js}+%FVMt@NkmCvGzfLd2>8tiNHYlusWWn- zn!9*!IXrAK2!kF-_lGroMy@oYw=WI;{} zQ(1ko1V=WvlbOm=_$o+X>OYBp(aYwG=@J!v+QUvAr2DknLTpb?po+uy zWH%8HCrwGs*;URXn_*az?tx)jeKIm5^pu^W4d1nVgR_GaIdQy6V%d~}r1!)!g`Jw? zso4T4UCKx~c!>)}*c~(!`pLw!;nhRR1m*+Feu_t=mk_1{3z0$G!*QWIio`?9o~!D= zuIF7iRe#>wQeNKQvHdMw^^XMU*mx)7E4*$Q7(FxcawU!87`7NcMcjBtGZ$Vy-&21$ z2Um5Q@G-U7cAS#SGK0ChGt+(kTF;j8G+I+lO|istnj%o6_dc{{C2%NwU$eSf`4W>lLY(gQ+OtC1G z4i1aM#&Bq+IT%WxF(VN=Ep)o_m+p_WG&$jYs}v=j$7R*R-RBx{_azs+x$W(y{jT(< zH3VMI8OJ;UYd}GTUjctaN&pORZ%470-r}Y2jwRt|C(qR&(bb@Mk5r93O8{`909?MV zPSf1p<{SxptxJLgE@!d|dZ@!z?cM%yS0#-c2HQ-mJmg`9EK2E=`t&=%HGyabv&yBB zZHPKTXfG9%eC;34pY`>v%w4`ml4I}&)+}8!XjU$8cZ4@3qmW9%W5rVKAQii=T4U(D z9!^&i6%AOVkZ|RLJG+gLK)aaEKvHsz1+p*MOVpkLooMn-k+CrB7ikmo*%_gp-&1k| zmf9ysa)%@tAn#vPJ6Kh=>aiVu`+Pd?pahZw5GEfYybW4maJ{&-d<%>RIAsuU8Tbv; zJ5Sp|yv2WNn_(=NjmYV^=ev`|>`yljLd?XMt05?oPHFgG3R9sy4mTsEH;b={x&L4f z5;jFsV%A?pa$+iifulIMnUBNdJ%^dT^lZufnpJrDxtCmYP$;Uu5t1`B#1Tk~>-t*w za^MQa)r4qPRhIzs7SDyu#s2R9=OY$9XZf4w;`H~R6fsUL>Xhm+^_(V2ZaP~_+%-eq z9Qjy|t0^o3I&n3@U^n$Uc1CJB#VKsp&Hd3+*aNX#p*T&4m&MCJA5VPbb)d8<+F0o+ z0&y0RqBtxwnL4G+k22>p9RNYPkU@_rfvKN>yRdEZ%Y+J{rv9hjI_`?LwLn3 z>kJ)x;8lmnQ3bB0Dy5~3N9O&h_3aNVwU54^GC=~g8gJrYzm|B}L8~tZ>YE&jpi7*= zo=>q1&fSAh!h4xqf)qvw>yr9ZXTS9QgZEb!oJML{0eJ#og#imT(c~YCxb-1={MB=1 z7Y~p4o_8=w9Tij45ftVq(y9j3NEO59h5h5$0{_H(9O6CdOfwhKuJ)kM~QFHZ0ks3yh5zh{h#Z;DXrc(MDr>|8hI3s9K$ja1OfBn+=msRQ(rt$u2&?RYt{t|9 zfGWuaGTK=r2ns}8VnT)Y?*hORq+8bj35{`9;@pCe&8^kOk*)chKI=;yIhe3@cIr4b zRet=%;Y>_^Gzw~=Boq+}Q55a|n-ncP<3GT=YzrUUA$op$?~U(kE%X_yO;IjDx?+_J z+Ho$wvp+ER`01x-J@@u#sM-;#wdT`*vyxG}4Qm?i^~2vR%+q~lx7@3UoZ{ZU8#kg@ z7ZCKLm5O}V@an;PJ;V1WGjSDj9$zYhbOh+gDxAwBv&w>uZO&g{2|wJ7iD}sI=89@&Kpzg zqeT@vnv{~4c+unZA)j7V7GLqlysXW9CGIh$zM=8xTdXg_e(ao8+beUdAX;}ci($W# zaG#7mi9TS~-aG`mCW2t?VP5jG)c{jMUmdd(kVY$ZW zCBEF=6JNj$VE<8o5kxn{HAuYqwExG5`2H`=$?zqzA{r$JHkJpLX|fI%-qeb5^+{ER z+HaH2lQoQ1ldoq+JLWy`$GyJ)K7f!@<@hkOkLRMR-?V6F>PaiI@ZxmE#a5Z6p;2>x zd~!tve-QPE)Ix23m9wb5plGqbzV)-{i%vHS_HK#nZuW2ZCnzs`8=`pOR={>wvi)>* zAs!c=M9+lgv!QSk^p?R;yWV*RE$ht`@TFe6vVfxibxwzXgfq@(3n0m3&ly{HCw_oM zPts_Edx`T5@-W^Lgf;t(!#SSq-{U+1*SKO5l|&1SuU^4)>-%ODjM7KXplj{oq1=@Y z_ln!)PE9OC7&!HP+;b~cbPd1OSc+`qi|-{*ax(%Q42wZ8vamX;Bmk4{wIisXZr>&> zMiYS?_R7f`!}T}N{K1q*j7-Az^^M}G)@vn5o4n%AECxx%7Enq5(an5jGY+N{nkfok_BV2WKa}^C4 zF+iStuBMbv|0lbkVT|PK?@z5qYr-{|CA-tA`Lgb0aD=#IIv~s4ZryYtng^K< z7)G-M^t3yidmnkQPkOX{yfMYN%`vIzh~eZFC8`e7h>GG3GhYMowJSdL6rMx6JZgH-HO-jr$dc0Lw<;SE=vxVr3=C{ZmkY8}YkK$UI_-KXGTH5ke@ zmAc;RA~9!6WCV8)-=QZF%8Y>f9s$ubAsv4du{-Z{10Ned4gV9lxVen3ho?yF_Wx8I z{(6y7uTslC?`=EjaeRX^s{5cBvzl61b(`7 zBY2Tt{ot2KWRQRHFAhnTr`WLp_OC4X&#n%X{IuxydZW)mL8;xaFa@j;;E|yH6MWLf z^`+3u(I;!OVP=q?L&1P{1JiIp`8!iRA*xJ>mv`{O0Kn9M!3^y1B>>g&*48Weh@}t( z<*b+p%NIPS2*WT4#Z(9=#c>eET{c{QwMeW)7bdMiiZNBF95ADH$aAph>UAmd$SJ6d z5hRwAY|NmP$xzh9&luBd=x9I?(kB5eu{Bc#ZX`LEEh zbRP9|7-guwZ{Ub!xNiwbUG7MNrlb)raN-Bz7k^pnQAlP5ao+(5S`nm`Qg&WIam~am zKP6gnkS3syxP}K5q$Lxn7^2#R?sf-UYKBPEJY;>#fEkJ3j46F`(lXbJ-D-pT{DqnK z5Bm0hzj!@_P^_{oIlpxzR5=RWBH{TYkfuhWO--)BH&7TpCM$ z19`!To*|~&qZZ-bh79GgV|~@}@-KV20_3~Szl80m>ANU+g-kdXiI9XP#5D9jws@&G zZ~eOyf$W7`jr^TK?`G%FgV*yy%t9DL`Mf?USXF5^8}{>}>z zvQo1Nd!(g%@Y2MXGZRrOE{XR_J}k&Aa+caQ_5Xgpx$J0N3cxw0GttoF?Hs*kBqT{a z4Kq~|⁢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 + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + 价值前沿 + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + com.valuefrontier.meagent + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Light + UIViewControllerBasedStatusBarAppearance + + + \ No newline at end of file diff --git a/MeAgent/ios/app/PrivacyInfo.xcprivacy b/MeAgent/ios/app/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..c6b452ea --- /dev/null +++ b/MeAgent/ios/app/PrivacyInfo.xcprivacy @@ -0,0 +1,48 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + 0A2A.1 + 3B52.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + 85F4.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/MeAgent/ios/app/SplashScreen.storyboard b/MeAgent/ios/app/SplashScreen.storyboard new file mode 100644 index 00000000..ed03a529 --- /dev/null +++ b/MeAgent/ios/app/SplashScreen.storyboard @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MeAgent/ios/app/Supporting/Expo.plist b/MeAgent/ios/app/Supporting/Expo.plist new file mode 100644 index 00000000..750be020 --- /dev/null +++ b/MeAgent/ios/app/Supporting/Expo.plist @@ -0,0 +1,12 @@ + + + + + EXUpdatesCheckOnLaunch + ALWAYS + EXUpdatesEnabled + + EXUpdatesLaunchWaitMs + 0 + + \ No newline at end of file diff --git a/MeAgent/ios/app/app-Bridging-Header.h b/MeAgent/ios/app/app-Bridging-Header.h new file mode 100644 index 00000000..e11d920b --- /dev/null +++ b/MeAgent/ios/app/app-Bridging-Header.h @@ -0,0 +1,3 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// diff --git a/MeAgent/ios/app/app.entitlements b/MeAgent/ios/app/app.entitlements new file mode 100644 index 00000000..018a6e20 --- /dev/null +++ b/MeAgent/ios/app/app.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + \ No newline at end of file diff --git a/MeAgent/ios/app/main.m b/MeAgent/ios/app/main.m new file mode 100644 index 00000000..25181b6c --- /dev/null +++ b/MeAgent/ios/app/main.m @@ -0,0 +1,10 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} + diff --git a/MeAgent/ios/app/noop-file.swift b/MeAgent/ios/app/noop-file.swift new file mode 100644 index 00000000..b2ffafbf --- /dev/null +++ b/MeAgent/ios/app/noop-file.swift @@ -0,0 +1,4 @@ +// +// @generated +// A blank Swift file must be created for native modules with Swift files to work correctly. +// diff --git a/argon-pro-react-native/logo.jpg b/MeAgent/logo.jpg similarity index 100% rename from argon-pro-react-native/logo.jpg rename to MeAgent/logo.jpg diff --git a/MeAgent/navigation/Menu.js b/MeAgent/navigation/Menu.js new file mode 100644 index 00000000..83297f54 --- /dev/null +++ b/MeAgent/navigation/Menu.js @@ -0,0 +1,413 @@ +import React from "react"; +import { + ScrollView, + StyleSheet, + Dimensions, + Image, + TouchableOpacity, + Linking, +} from "react-native"; +import { Block, Text, theme } from "galio-framework"; +import { useSafeArea } from "react-native-safe-area-context"; +import { Box, HStack, VStack, Icon, Pressable, Spinner } from "native-base"; +import { Ionicons } from "@expo/vector-icons"; +import { LinearGradient } from "expo-linear-gradient"; +import Images from "../constants/Images"; +import { DrawerItem as DrawerCustomItem } from "../components/index"; +import { useAuth } from "../src/contexts/AuthContext"; + +const { width } = Dimensions.get("screen"); + +// 金色主题色 +const GOLD_PRIMARY = '#D4AF37'; + +// 用户卡片组件 +const UserCard = ({ navigation }) => { + const { user, isLoggedIn, isLoading, subscription, logout } = useAuth(); + + const handleLoginPress = () => { + navigation.closeDrawer(); + // 使用 getParent 获取根导航器来导航到 Login + navigation.getParent()?.navigate("Login"); + }; + + const handleLogoutPress = async () => { + await logout(); + }; + + // 获取订阅显示文本 + const getSubscriptionText = () => { + if (!subscription || !subscription.is_active) { + return "免费用户"; + } + const typeMap = { pro: "Pro 会员", max: "Max 会员" }; + return typeMap[subscription.type] || "免费用户"; + }; + + if (isLoading) { + return ( + + + + + 加载中... + + + + ); + } + + if (!isLoggedIn) { + return ( + + + + + + + + + 登录/注册 + + + 登录解锁更多功能 + + + + + + + ); + } + + // 已登录状态 + return ( + + + + + {(user?.username || user?.nickname || "U").charAt(0).toUpperCase()} + + + + + {user?.nickname || user?.username || "用户"} + + + + + {getSubscriptionText()} + + + + + + + + + + ); +}; + +function CustomDrawerContent({ + drawerPosition, + navigation, + profile, + focused, + state, + ...rest +}) { + const insets = useSafeArea(); + // 菜单项配置 + const screens = [ + { title: "事件中心", navigateTo: "EventsDrawer", icon: "flash", gradient: ["#7C3AED", "#A78BFA"] }, + { title: "市场热点", navigateTo: "MarketDrawer", icon: "flame", gradient: ["#F59E0B", "#FBBF24"] }, + { title: "概念中心", navigateTo: "ConceptsDrawer", icon: "bulb", gradient: ["#06B6D4", "#22D3EE"] }, + { title: "我的自选", navigateTo: "WatchlistDrawer", icon: "star", gradient: ["#EC4899", "#F472B6"] }, + { title: "个人中心", navigateTo: "ProfileDrawerNew", icon: "person", gradient: ["#8B5CF6", "#A78BFA"] }, + ]; + return ( + + {/* 品牌头部 - 黑金主题 */} + + + + + + 价值前沿 + + + VALUE FRONTIER + + + + + + + {/* 用户卡片 - 深色版本 */} + + + + + {/* 导航菜单 */} + + {screens.map((item, index) => { + const isFocused = state.index === index; + return ( + navigation.navigate(item.navigateTo)} + mb={2} + > + {isFocused ? ( + + + + + + + {item.title} + + + + ) : ( + + + + + + + {item.title} + + + + )} + + ); + })} + + + {/* 底部版本信息 */} + + + + 价值前沿 v1.0.0 + + + + + + ); +} + +// 深色主题用户卡片 +const UserCardDark = ({ navigation }) => { + const { user, isLoggedIn, isLoading, subscription, logout } = useAuth(); + + const handleLoginPress = () => { + navigation.closeDrawer(); + navigation.getParent()?.navigate("Login"); + }; + + const handleLogoutPress = async () => { + await logout(); + }; + + const getSubscriptionText = () => { + if (!subscription || !subscription.is_active) return "免费用户"; + const typeMap = { pro: "Pro 会员", max: "Max 会员" }; + return typeMap[subscription.type] || "免费用户"; + }; + + if (isLoading) { + return ( + + + + 加载中... + + + ); + } + + if (!isLoggedIn) { + return ( + + + + + + + + + 登录/注册 + + + 登录解锁更多功能 + + + + + + + ); + } + + return ( + + + + + {(user?.username || user?.nickname || "U").charAt(0).toUpperCase()} + + + + + {user?.nickname || user?.username || "用户"} + + + + {getSubscriptionText()} + + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + header: { + paddingHorizontal: 28, + paddingBottom: theme.SIZES.BASE, + paddingTop: theme.SIZES.BASE * 3, + justifyContent: "center", + }, +}); + +export default CustomDrawerContent; diff --git a/argon-pro-react-native/navigation/Screens.js b/MeAgent/navigation/Screens.js similarity index 86% rename from argon-pro-react-native/navigation/Screens.js rename to MeAgent/navigation/Screens.js index 9a266804..811f8f89 100644 --- a/argon-pro-react-native/navigation/Screens.js +++ b/MeAgent/navigation/Screens.js @@ -39,7 +39,19 @@ import { createStackNavigator } from "@react-navigation/stack"; import { EventList, EventDetail } from "../src/screens/Events"; // 市场热点页面 -import { MarketHot, SectorDetail, EventCalendar, StockDetail, TodayStats } from "../src/screens/Market"; +import { MarketHot, SectorDetail, EventCalendar, TodayStats } from "../src/screens/Market"; + +// 概念中心页面 +import { ConceptList } from "../src/screens/Concepts"; + +// 自选股页面 +import WatchlistScreen from "../src/screens/Watchlist/WatchlistScreen"; + +// 新股票详情页面 +import { StockDetailScreen } from "../src/screens/StockDetail"; + +// 新个人中心页面 +import { ProfileScreen as NewProfileScreen } from "../src/screens/Profile"; // 认证页面 import { LoginScreen } from "../src/screens/Auth"; @@ -264,6 +276,13 @@ function EventsStack(props) { cardStyle: { backgroundColor: "#0F172A" }, }} /> + ); } @@ -309,9 +328,9 @@ function MarketStack(props) { /> + + + ); +} + +// 自选股导航栈 +function WatchlistStack(props) { + return ( + + + + + + ); +} + +// 新个人中心导航栈 +function NewProfileStack(props) { + return ( + + + + ); +} + function ProfileStack(props) { return ( } drawerStyle={{ - backgroundColor: "white", + backgroundColor: "#0F172A", width: width * 0.8, }} screenOptions={{ @@ -598,6 +691,27 @@ function AppStack(props) { headerShown: false, }} /> + + + { + const [isLoading, setIsLoading] = useState(false); + const { isInWatchlist, toggleStock } = useWatchlist({ autoLoad: false }); + + const inWatchlist = isInWatchlist(stockCode); + + const handlePress = useCallback(async () => { + if (isLoading) return; + + setIsLoading(true); + try { + const result = await toggleStock(stockCode, stockName); + if (result.success) { + onSuccess?.(inWatchlist ? 'removed' : 'added'); + } else { + onError?.(result.error); + } + } catch (error) { + onError?.(error.message); + } finally { + setIsLoading(false); + } + }, [stockCode, stockName, inWatchlist, toggleStock, isLoading, onSuccess, onError]); + + // 尺寸配置 + const sizeConfig = { + sm: { px: 2, py: 1, fontSize: 10, iconSize: 'xs' }, + md: { px: 3, py: 1.5, fontSize: 11, iconSize: 'sm' }, + lg: { px: 4, py: 2, fontSize: 12, iconSize: 'sm' }, + }; + + const config = sizeConfig[size] || sizeConfig.sm; + + // 只显示图标 + if (variant === 'icon') { + return ( + + + {isLoading ? ( + + ) : ( + + )} + + + ); + } + + // 带文字的按钮 + const bgColor = inWatchlist + ? 'rgba(239, 68, 68, 0.15)' + : 'rgba(59, 130, 246, 0.15)'; + const borderColor = inWatchlist + ? 'rgba(239, 68, 68, 0.3)' + : 'rgba(59, 130, 246, 0.3)'; + const textColor = inWatchlist ? '#EF4444' : '#3B82F6'; + + return ( + + {({ pressed }) => ( + + + {isLoading ? ( + + ) : ( + <> + + + {inWatchlist ? '已自选' : '加自选'} + + + )} + + + )} + + ); +}); + +StockWatchlistButton.displayName = 'StockWatchlistButton'; + +/** + * 事件关注按钮 + * @param {object} props + * @param {number|string} props.eventId - 事件ID + * @param {string} props.size - 按钮大小 'sm' | 'md' | 'lg' + * @param {string} props.variant - 样式变体 'solid' | 'outline' | 'icon' + * @param {function} props.onSuccess - 成功回调 + * @param {function} props.onError - 失败回调 + */ +export const EventFollowButton = memo(({ + eventId, + size = 'sm', + variant = 'outline', + onSuccess, + onError, +}) => { + const [isLoading, setIsLoading] = useState(false); + const { isEventFollowed, toggleEventFollow } = useWatchlist({ autoLoad: false }); + + const isFollowed = isEventFollowed(eventId); + + const handlePress = useCallback(async () => { + if (isLoading) return; + + setIsLoading(true); + try { + const result = await toggleEventFollow(eventId); + if (result.success) { + onSuccess?.(isFollowed ? 'unfollowed' : 'followed'); + } else { + onError?.(result.error); + } + } catch (error) { + onError?.(error.message); + } finally { + setIsLoading(false); + } + }, [eventId, isFollowed, toggleEventFollow, isLoading, onSuccess, onError]); + + // 尺寸配置 + const sizeConfig = { + sm: { px: 2, py: 1, fontSize: 10, iconSize: 'xs' }, + md: { px: 3, py: 1.5, fontSize: 11, iconSize: 'sm' }, + lg: { px: 4, py: 2, fontSize: 12, iconSize: 'sm' }, + }; + + const config = sizeConfig[size] || sizeConfig.sm; + + // 只显示图标 + if (variant === 'icon') { + return ( + + + {isLoading ? ( + + ) : ( + + )} + + + ); + } + + // 带文字的按钮 + const bgColor = isFollowed + ? 'rgba(236, 72, 153, 0.15)' + : 'rgba(124, 58, 237, 0.15)'; + const borderColor = isFollowed + ? 'rgba(236, 72, 153, 0.3)' + : 'rgba(124, 58, 237, 0.3)'; + const textColor = isFollowed ? '#EC4899' : '#7C3AED'; + + return ( + + {({ pressed }) => ( + + + {isLoading ? ( + + ) : ( + <> + + + {isFollowed ? '已关注' : '关注'} + + + )} + + + )} + + ); +}); + +EventFollowButton.displayName = 'EventFollowButton'; + +export default { + StockWatchlistButton, + EventFollowButton, +}; diff --git a/argon-pro-react-native/src/components/PushNotificationHandler.js b/MeAgent/src/components/PushNotificationHandler.js similarity index 100% rename from argon-pro-react-native/src/components/PushNotificationHandler.js rename to MeAgent/src/components/PushNotificationHandler.js diff --git a/argon-pro-react-native/src/components/index.js b/MeAgent/src/components/index.js similarity index 100% rename from argon-pro-react-native/src/components/index.js rename to MeAgent/src/components/index.js diff --git a/argon-pro-react-native/src/constants/index.js b/MeAgent/src/constants/index.js similarity index 100% rename from argon-pro-react-native/src/constants/index.js rename to MeAgent/src/constants/index.js diff --git a/argon-pro-react-native/src/contexts/AuthContext.js b/MeAgent/src/contexts/AuthContext.js similarity index 100% rename from argon-pro-react-native/src/contexts/AuthContext.js rename to MeAgent/src/contexts/AuthContext.js diff --git a/argon-pro-react-native/src/hooks/usePushNotifications.js b/MeAgent/src/hooks/usePushNotifications.js similarity index 100% rename from argon-pro-react-native/src/hooks/usePushNotifications.js rename to MeAgent/src/hooks/usePushNotifications.js diff --git a/MeAgent/src/hooks/useRealtimeQuote.js b/MeAgent/src/hooks/useRealtimeQuote.js new file mode 100644 index 00000000..8a37fe14 --- /dev/null +++ b/MeAgent/src/hooks/useRealtimeQuote.js @@ -0,0 +1,270 @@ +/** + * 实时行情 Hook + * 通过 WebSocket 订阅股票实时行情 + */ + +import { useState, useEffect, useCallback, useRef } from 'react'; +import { useDispatch } from 'react-redux'; +import { realtimeQuoteService } from '../services/websocketService'; +import { updateRealtimeQuotes } from '../store/slices/watchlistSlice'; + +/** + * 实时行情 Hook + * @param {object} options - 配置选项 + * @param {string[]} options.codes - 要订阅的股票代码列表 + * @param {boolean} options.autoConnect - 是否自动连接(默认 true) + * @param {boolean} options.updateRedux - 是否更新 Redux 状态(默认 true) + */ +export const useRealtimeQuote = (options = {}) => { + const { + codes = [], + autoConnect = true, + updateRedux = true, + } = options; + + const dispatch = useDispatch(); + const [quotes, setQuotes] = useState({}); + const [connectionState, setConnectionState] = useState('disconnected'); + const [isConnected, setIsConnected] = useState(false); + const subscribedCodesRef = useRef(new Set()); + + // 处理行情更新 + const handleQuoteUpdate = useCallback((newQuotes) => { + setQuotes(prev => ({ + ...prev, + ...newQuotes, + })); + + // 同步到 Redux + if (updateRedux) { + dispatch(updateRealtimeQuotes(newQuotes)); + } + }, [dispatch, updateRedux]); + + // 处理连接状态变化 + const handleStateChange = useCallback((state) => { + setConnectionState(state); + setIsConnected(state === 'connected'); + }, []); + + // 连接 WebSocket + const connect = useCallback(() => { + realtimeQuoteService.connect(); + }, []); + + // 断开连接 + const disconnect = useCallback(() => { + realtimeQuoteService.disconnect(); + }, []); + + // 订阅股票 + const subscribe = useCallback((stockCodes) => { + if (!Array.isArray(stockCodes)) { + stockCodes = [stockCodes]; + } + + const newCodes = stockCodes.filter(code => !subscribedCodesRef.current.has(code)); + if (newCodes.length > 0) { + newCodes.forEach(code => subscribedCodesRef.current.add(code)); + realtimeQuoteService.subscribe(newCodes); + } + }, []); + + // 取消订阅 + const unsubscribe = useCallback((stockCodes) => { + if (!Array.isArray(stockCodes)) { + stockCodes = [stockCodes]; + } + + const existingCodes = stockCodes.filter(code => subscribedCodesRef.current.has(code)); + if (existingCodes.length > 0) { + existingCodes.forEach(code => subscribedCodesRef.current.delete(code)); + realtimeQuoteService.unsubscribe(existingCodes); + } + }, []); + + // 获取单个股票行情 + const getQuote = useCallback((code) => { + return quotes[code] || null; + }, [quotes]); + + // 初始化连接和事件监听 + useEffect(() => { + // 添加事件处理器 + const removeQuoteHandler = realtimeQuoteService.addQuoteHandler(handleQuoteUpdate); + const removeStateHandler = realtimeQuoteService.addStateHandler(handleStateChange); + + // 自动连接 + if (autoConnect) { + connect(); + } + + // 获取初始状态 + setConnectionState(realtimeQuoteService.getConnectionState()); + setIsConnected(realtimeQuoteService.isConnected()); + + return () => { + removeQuoteHandler(); + removeStateHandler(); + }; + }, [autoConnect, connect, handleQuoteUpdate, handleStateChange]); + + // 处理股票代码变化 + useEffect(() => { + if (codes.length > 0) { + // 计算需要新订阅的 + const codesToSubscribe = codes.filter(code => !subscribedCodesRef.current.has(code)); + + // 计算需要取消订阅的 + const currentCodes = new Set(codes); + const codesToUnsubscribe = Array.from(subscribedCodesRef.current).filter( + code => !currentCodes.has(code) + ); + + if (codesToUnsubscribe.length > 0) { + unsubscribe(codesToUnsubscribe); + } + + if (codesToSubscribe.length > 0) { + subscribe(codesToSubscribe); + } + } + }, [codes, subscribe, unsubscribe]); + + // 组件卸载时取消所有订阅 + useEffect(() => { + return () => { + const allCodes = Array.from(subscribedCodesRef.current); + if (allCodes.length > 0) { + realtimeQuoteService.unsubscribe(allCodes); + subscribedCodesRef.current.clear(); + } + }; + }, []); + + return { + // 状态 + quotes, + connectionState, + isConnected, + + // 操作 + connect, + disconnect, + subscribe, + unsubscribe, + getQuote, + }; +}; + +/** + * 单个股票实时行情 Hook + * @param {string} code - 股票代码 + */ +export const useSingleQuote = (code) => { + const { quotes, isConnected, subscribe, unsubscribe } = useRealtimeQuote({ + codes: code ? [code] : [], + updateRedux: false, + }); + + return { + quote: code ? quotes[code] : null, + isConnected, + }; +}; + +/** + * 自选股列表实时行情 Hook + * 自动从 Redux 获取自选股列表并订阅 + */ +export const useWatchlistRealtimeQuotes = () => { + const dispatch = useDispatch(); + const [connectionState, setConnectionState] = useState('disconnected'); + const subscribedCodesRef = useRef(new Set()); + + // 处理行情更新 + const handleQuoteUpdate = useCallback((newQuotes) => { + dispatch(updateRealtimeQuotes(newQuotes)); + }, [dispatch]); + + // 处理连接状态变化 + const handleStateChange = useCallback((state) => { + setConnectionState(state); + }, []); + + // 订阅股票列表 + const subscribeStocks = useCallback((stocks) => { + if (!Array.isArray(stocks) || stocks.length === 0) return; + + const codes = stocks.map(s => s.stock_code).filter(Boolean); + const newCodes = codes.filter(code => !subscribedCodesRef.current.has(code)); + + if (newCodes.length > 0) { + newCodes.forEach(code => subscribedCodesRef.current.add(code)); + realtimeQuoteService.subscribe(newCodes); + } + }, []); + + // 更新订阅列表 + const updateSubscription = useCallback((stocks) => { + if (!Array.isArray(stocks)) return; + + const newCodes = new Set(stocks.map(s => s.stock_code).filter(Boolean)); + + // 取消不再需要的订阅 + const codesToUnsubscribe = []; + subscribedCodesRef.current.forEach(code => { + if (!newCodes.has(code)) { + codesToUnsubscribe.push(code); + } + }); + + if (codesToUnsubscribe.length > 0) { + codesToUnsubscribe.forEach(code => subscribedCodesRef.current.delete(code)); + realtimeQuoteService.unsubscribe(codesToUnsubscribe); + } + + // 添加新订阅 + const codesToSubscribe = []; + newCodes.forEach(code => { + if (!subscribedCodesRef.current.has(code)) { + codesToSubscribe.push(code); + subscribedCodesRef.current.add(code); + } + }); + + if (codesToSubscribe.length > 0) { + realtimeQuoteService.subscribe(codesToSubscribe); + } + }, []); + + // 初始化 + useEffect(() => { + const removeQuoteHandler = realtimeQuoteService.addQuoteHandler(handleQuoteUpdate); + const removeStateHandler = realtimeQuoteService.addStateHandler(handleStateChange); + + realtimeQuoteService.connect(); + setConnectionState(realtimeQuoteService.getConnectionState()); + + return () => { + removeQuoteHandler(); + removeStateHandler(); + + // 取消所有订阅 + const allCodes = Array.from(subscribedCodesRef.current); + if (allCodes.length > 0) { + realtimeQuoteService.unsubscribe(allCodes); + subscribedCodesRef.current.clear(); + } + }; + }, [handleQuoteUpdate, handleStateChange]); + + return { + connectionState, + isConnected: connectionState === 'connected', + subscribeStocks, + updateSubscription, + }; +}; + +export default useRealtimeQuote; diff --git a/MeAgent/src/hooks/useWatchlist.js b/MeAgent/src/hooks/useWatchlist.js new file mode 100644 index 00000000..4a3b3603 --- /dev/null +++ b/MeAgent/src/hooks/useWatchlist.js @@ -0,0 +1,234 @@ +/** + * 自选股管理 Hook + * 提供自选股和自选事件的操作方法 + */ + +import { useCallback, useEffect, useRef } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + fetchWatchlist, + fetchWatchlistRealtime, + fetchFollowingEvents, + addToWatchlist, + removeFromWatchlist, + toggleEventFollow, + selectWatchlistStocks, + selectWatchlistEvents, + selectRealtimeQuotes, + selectWatchlistLoading, + selectWatchlistError, + selectIsInWatchlist, + selectIsEventFollowed, + optimisticAddStock, + optimisticRemoveStock, +} from '../store/slices/watchlistSlice'; + +/** + * 自选股管理 Hook + * @param {object} options - 配置选项 + * @param {boolean} options.autoLoad - 是否自动加载数据 + * @param {number} options.refreshInterval - 实时行情刷新间隔(毫秒),0 表示不自动刷新 + */ +export const useWatchlist = (options = {}) => { + const { + autoLoad = true, + refreshInterval = 30000, // 默认 30 秒刷新一次 + } = options; + + const dispatch = useDispatch(); + const refreshTimerRef = useRef(null); + + // Redux 状态 + const stocks = useSelector(selectWatchlistStocks); + const events = useSelector(selectWatchlistEvents); + const realtimeQuotes = useSelector(selectRealtimeQuotes); + const loading = useSelector(selectWatchlistLoading); + const error = useSelector(selectWatchlistError); + + // 加载自选股列表 + const loadWatchlist = useCallback(() => { + return dispatch(fetchWatchlist()); + }, [dispatch]); + + // 加载自选股实时行情 + const loadRealtimeQuotes = useCallback(() => { + return dispatch(fetchWatchlistRealtime()); + }, [dispatch]); + + // 加载关注的事件 + const loadFollowingEvents = useCallback(() => { + return dispatch(fetchFollowingEvents()); + }, [dispatch]); + + // 添加股票到自选 + const handleAddStock = useCallback(async (stockCode, stockName = '') => { + // 乐观更新 + dispatch(optimisticAddStock({ stockCode, stockName })); + + try { + const result = await dispatch(addToWatchlist({ stockCode, stockName })).unwrap(); + return { success: true, data: result }; + } catch (error) { + // 回滚乐观更新 + dispatch(optimisticRemoveStock(stockCode)); + return { success: false, error }; + } + }, [dispatch]); + + // 从自选移除股票 + const handleRemoveStock = useCallback(async (stockCode) => { + // 先保存原始数据用于回滚 + const originalStocks = stocks; + + // 乐观更新 + dispatch(optimisticRemoveStock(stockCode)); + + try { + await dispatch(removeFromWatchlist(stockCode)).unwrap(); + return { success: true }; + } catch (error) { + // 回滚 - 重新加载列表 + dispatch(fetchWatchlist()); + return { success: false, error }; + } + }, [dispatch, stocks]); + + // 切换股票自选状态 + const toggleStock = useCallback(async (stockCode, stockName = '') => { + const isInList = isInWatchlist(stockCode); + + if (isInList) { + return handleRemoveStock(stockCode); + } else { + return handleAddStock(stockCode, stockName); + } + }, [handleAddStock, handleRemoveStock]); + + // 切换事件关注状态 + const handleToggleEventFollow = useCallback(async (eventId) => { + try { + const result = await dispatch(toggleEventFollow(eventId)).unwrap(); + return { success: true, data: result }; + } catch (error) { + return { success: false, error }; + } + }, [dispatch]); + + // 检查股票是否在自选中 + const isInWatchlist = useCallback((stockCode) => { + const normalizeCode = (code) => String(code).match(/\d{6}/)?.[0] || code; + const normalizedCode = normalizeCode(stockCode); + return stocks.some(item => normalizeCode(item.stock_code) === normalizedCode); + }, [stocks]); + + // 检查事件是否被关注 + const isEventFollowed = useCallback((eventId) => { + return events.some(event => event.id === eventId); + }, [events]); + + // 获取股票的实时行情 + const getStockQuote = useCallback((stockCode) => { + const normalizeCode = (code) => String(code).match(/\d{6}/)?.[0] || code; + const normalizedCode = normalizeCode(stockCode); + + // 尝试多种格式匹配 + return realtimeQuotes[stockCode] || + realtimeQuotes[normalizedCode] || + realtimeQuotes[`${normalizedCode}.SH`] || + realtimeQuotes[`${normalizedCode}.SZ`] || + null; + }, [realtimeQuotes]); + + // 合并股票列表和实时行情 + const stocksWithQuotes = stocks.map(stock => { + const quote = getStockQuote(stock.stock_code); + return { + ...stock, + quote, + }; + }); + + // 刷新全部数据 + const refreshAll = useCallback(async () => { + await Promise.all([ + loadWatchlist(), + loadRealtimeQuotes(), + loadFollowingEvents(), + ]); + }, [loadWatchlist, loadRealtimeQuotes, loadFollowingEvents]); + + // 启动定时刷新 + const startRefreshTimer = useCallback(() => { + if (refreshInterval > 0 && !refreshTimerRef.current) { + refreshTimerRef.current = setInterval(() => { + loadRealtimeQuotes(); + }, refreshInterval); + } + }, [refreshInterval, loadRealtimeQuotes]); + + // 停止定时刷新 + const stopRefreshTimer = useCallback(() => { + if (refreshTimerRef.current) { + clearInterval(refreshTimerRef.current); + refreshTimerRef.current = null; + } + }, []); + + // 自动加载数据 + useEffect(() => { + if (autoLoad) { + loadWatchlist(); + loadRealtimeQuotes(); + loadFollowingEvents(); + } + }, [autoLoad, loadWatchlist, loadRealtimeQuotes, loadFollowingEvents]); + + // 启动/停止定时刷新 + useEffect(() => { + if (autoLoad && refreshInterval > 0) { + startRefreshTimer(); + } + + return () => { + stopRefreshTimer(); + }; + }, [autoLoad, refreshInterval, startRefreshTimer, stopRefreshTimer]); + + return { + // 数据 + stocks, + events, + realtimeQuotes, + stocksWithQuotes, + + // 加载状态 + loading, + error, + isLoadingStocks: loading.stocks, + isLoadingEvents: loading.events, + isLoadingRealtime: loading.realtime, + + // 操作方法 + loadWatchlist, + loadRealtimeQuotes, + loadFollowingEvents, + refreshAll, + + // 自选股操作 + addStock: handleAddStock, + removeStock: handleRemoveStock, + toggleStock, + isInWatchlist, + getStockQuote, + + // 事件操作 + toggleEventFollow: handleToggleEventFollow, + isEventFollowed, + + // 定时刷新控制 + startRefreshTimer, + stopRefreshTimer, + }; +}; + +export default useWatchlist; diff --git a/argon-pro-react-native/src/screens/Auth/LoginScreen.js b/MeAgent/src/screens/Auth/LoginScreen.js similarity index 100% rename from argon-pro-react-native/src/screens/Auth/LoginScreen.js rename to MeAgent/src/screens/Auth/LoginScreen.js diff --git a/argon-pro-react-native/src/screens/Auth/index.js b/MeAgent/src/screens/Auth/index.js similarity index 100% rename from argon-pro-react-native/src/screens/Auth/index.js rename to MeAgent/src/screens/Auth/index.js diff --git a/MeAgent/src/screens/Concepts/ConceptDetail.js b/MeAgent/src/screens/Concepts/ConceptDetail.js new file mode 100644 index 00000000..6c36f908 --- /dev/null +++ b/MeAgent/src/screens/Concepts/ConceptDetail.js @@ -0,0 +1,322 @@ +/** + * 概念详情页面 - 内嵌 WebView + * 展示概念板块的详细信息 + */ + +import React, { useState, useCallback, memo } from 'react'; +import { StyleSheet, ActivityIndicator, StatusBar } from 'react-native'; +import { + Box, + HStack, + Text, + Icon, + Pressable, + Center, +} from 'native-base'; +import { WebView } from 'react-native-webview'; +import { Ionicons } from '@expo/vector-icons'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useNavigation, useRoute } from '@react-navigation/native'; + +// 简单的 MD5 实现 +const md5 = (string) => { + function md5cycle(x, k) { + var a = x[0], b = x[1], c = x[2], d = x[3]; + a = ff(a, b, c, d, k[0], 7, -680876936); + d = ff(d, a, b, c, k[1], 12, -389564586); + c = ff(c, d, a, b, k[2], 17, 606105819); + b = ff(b, c, d, a, k[3], 22, -1044525330); + a = ff(a, b, c, d, k[4], 7, -176418897); + d = ff(d, a, b, c, k[5], 12, 1200080426); + c = ff(c, d, a, b, k[6], 17, -1473231341); + b = ff(b, c, d, a, k[7], 22, -45705983); + a = ff(a, b, c, d, k[8], 7, 1770035416); + d = ff(d, a, b, c, k[9], 12, -1958414417); + c = ff(c, d, a, b, k[10], 17, -42063); + b = ff(b, c, d, a, k[11], 22, -1990404162); + a = ff(a, b, c, d, k[12], 7, 1804603682); + d = ff(d, a, b, c, k[13], 12, -40341101); + c = ff(c, d, a, b, k[14], 17, -1502002290); + b = ff(b, c, d, a, k[15], 22, 1236535329); + a = gg(a, b, c, d, k[1], 5, -165796510); + d = gg(d, a, b, c, k[6], 9, -1069501632); + c = gg(c, d, a, b, k[11], 14, 643717713); + b = gg(b, c, d, a, k[0], 20, -373897302); + a = gg(a, b, c, d, k[5], 5, -701558691); + d = gg(d, a, b, c, k[10], 9, 38016083); + c = gg(c, d, a, b, k[15], 14, -660478335); + b = gg(b, c, d, a, k[4], 20, -405537848); + a = gg(a, b, c, d, k[9], 5, 568446438); + d = gg(d, a, b, c, k[14], 9, -1019803690); + c = gg(c, d, a, b, k[3], 14, -187363961); + b = gg(b, c, d, a, k[8], 20, 1163531501); + a = gg(a, b, c, d, k[13], 5, -1444681467); + d = gg(d, a, b, c, k[2], 9, -51403784); + c = gg(c, d, a, b, k[7], 14, 1735328473); + b = gg(b, c, d, a, k[12], 20, -1926607734); + a = hh(a, b, c, d, k[5], 4, -378558); + d = hh(d, a, b, c, k[8], 11, -2022574463); + c = hh(c, d, a, b, k[11], 16, 1839030562); + b = hh(b, c, d, a, k[14], 23, -35309556); + a = hh(a, b, c, d, k[1], 4, -1530992060); + d = hh(d, a, b, c, k[4], 11, 1272893353); + c = hh(c, d, a, b, k[7], 16, -155497632); + b = hh(b, c, d, a, k[10], 23, -1094730640); + a = hh(a, b, c, d, k[13], 4, 681279174); + d = hh(d, a, b, c, k[0], 11, -358537222); + c = hh(c, d, a, b, k[3], 16, -722521979); + b = hh(b, c, d, a, k[6], 23, 76029189); + a = hh(a, b, c, d, k[9], 4, -640364487); + d = hh(d, a, b, c, k[12], 11, -421815835); + c = hh(c, d, a, b, k[15], 16, 530742520); + b = hh(b, c, d, a, k[2], 23, -995338651); + a = ii(a, b, c, d, k[0], 6, -198630844); + d = ii(d, a, b, c, k[7], 10, 1126891415); + c = ii(c, d, a, b, k[14], 15, -1416354905); + b = ii(b, c, d, a, k[5], 21, -57434055); + a = ii(a, b, c, d, k[12], 6, 1700485571); + d = ii(d, a, b, c, k[3], 10, -1894986606); + c = ii(c, d, a, b, k[10], 15, -1051523); + b = ii(b, c, d, a, k[1], 21, -2054922799); + a = ii(a, b, c, d, k[8], 6, 1873313359); + d = ii(d, a, b, c, k[15], 10, -30611744); + c = ii(c, d, a, b, k[6], 15, -1560198380); + b = ii(b, c, d, a, k[13], 21, 1309151649); + a = ii(a, b, c, d, k[4], 6, -145523070); + d = ii(d, a, b, c, k[11], 10, -1120210379); + c = ii(c, d, a, b, k[2], 15, 718787259); + b = ii(b, c, d, a, k[9], 21, -343485551); + x[0] = add32(a, x[0]); + x[1] = add32(b, x[1]); + x[2] = add32(c, x[2]); + x[3] = add32(d, x[3]); + } + + function cmn(q, a, b, x, s, t) { + a = add32(add32(a, q), add32(x, t)); + return add32((a << s) | (a >>> (32 - s)), b); + } + + function ff(a, b, c, d, x, s, t) { + return cmn((b & c) | ((~b) & d), a, b, x, s, t); + } + + function gg(a, b, c, d, x, s, t) { + return cmn((b & d) | (c & (~d)), a, b, x, s, t); + } + + function hh(a, b, c, d, x, s, t) { + return cmn(b ^ c ^ d, a, b, x, s, t); + } + + function ii(a, b, c, d, x, s, t) { + return cmn(c ^ (b | (~d)), a, b, x, s, t); + } + + function md51(s) { + var n = s.length, + state = [1732584193, -271733879, -1732584194, 271733878], + i; + for (i = 64; i <= s.length; i += 64) { + md5cycle(state, md5blk(s.substring(i - 64, i))); + } + s = s.substring(i - 64); + var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for (i = 0; i < s.length; i++) + tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); + tail[i >> 2] |= 0x80 << ((i % 4) << 3); + if (i > 55) { + md5cycle(state, tail); + for (i = 0; i < 16; i++) tail[i] = 0; + } + tail[14] = n * 8; + md5cycle(state, tail); + return state; + } + + function md5blk(s) { + var md5blks = [], + i; + for (i = 0; i < 64; i += 4) { + md5blks[i >> 2] = + s.charCodeAt(i) + + (s.charCodeAt(i + 1) << 8) + + (s.charCodeAt(i + 2) << 16) + + (s.charCodeAt(i + 3) << 24); + } + return md5blks; + } + + var hex_chr = '0123456789abcdef'.split(''); + + function rhex(n) { + var s = '', + j = 0; + for (; j < 4; j++) + s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]; + return s; + } + + function hex(x) { + for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]); + return x.join(''); + } + + function add32(a, b) { + return (a + b) & 0xffffffff; + } + + // 处理 UTF-8 编码 + function utf8Encode(str) { + let utf8 = ''; + for (let i = 0; i < str.length; i++) { + let charCode = str.charCodeAt(i); + if (charCode < 128) { + utf8 += String.fromCharCode(charCode); + } else if (charCode < 2048) { + utf8 += String.fromCharCode((charCode >> 6) | 192); + utf8 += String.fromCharCode((charCode & 63) | 128); + } else { + utf8 += String.fromCharCode((charCode >> 12) | 224); + utf8 += String.fromCharCode(((charCode >> 6) & 63) | 128); + utf8 += String.fromCharCode((charCode & 63) | 128); + } + } + return utf8; + } + + return hex(md51(utf8Encode(string))); +}; + +// 生成概念详情页 URL +const getConceptUrl = (conceptName) => { + const hash = md5(conceptName); + return `https://valuefrontier.cn/htmls/concept/${hash}/`; +}; + +const ConceptDetail = () => { + const navigation = useNavigation(); + const route = useRoute(); + const insets = useSafeAreaInsets(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + const { conceptName } = route.params || {}; + const url = conceptName ? getConceptUrl(conceptName) : ''; + + const handleGoBack = useCallback(() => { + navigation.goBack(); + }, [navigation]); + + const handleRefresh = useCallback(() => { + setLoading(true); + setError(false); + }, []); + + if (!conceptName) { + return ( + +
+ + 概念名称缺失 +
+
+ ); + } + + return ( + + + + {/* 顶部导航栏 */} + + + + + + + + {conceptName} + + + + + + + + + {/* WebView */} + + setLoading(true)} + onLoadEnd={() => setLoading(false)} + onError={() => { + setLoading(false); + setError(true); + }} + startInLoadingState={true} + renderLoading={() => ( +
+ + 加载中... +
+ )} + javaScriptEnabled={true} + domStorageEnabled={true} + scalesPageToFit={true} + allowsBackForwardNavigationGestures={true} + /> + + {/* 加载遮罩 */} + {loading && ( +
+ + 加载中... +
+ )} + + {/* 错误状态 */} + {error && !loading && ( +
+ + 加载失败 + + 重新加载 + +
+ )} +
+
+ ); +}; + +const styles = StyleSheet.create({ + webview: { + flex: 1, + backgroundColor: '#0F172A', + }, +}); + +export default memo(ConceptDetail); diff --git a/MeAgent/src/screens/Concepts/ConceptList.js b/MeAgent/src/screens/Concepts/ConceptList.js new file mode 100644 index 00000000..1efa692e --- /dev/null +++ b/MeAgent/src/screens/Concepts/ConceptList.js @@ -0,0 +1,1246 @@ +/** + * 概念中心页面 - iOS 17 毛玻璃风格 Treemap + * 嵌套显示当前层级和子层级 + */ + +import React, { useState, useEffect, useCallback, useMemo, memo, useRef } from 'react'; +import { + StyleSheet, + RefreshControl, + Dimensions, + StatusBar, + View, + TextInput, + Keyboard, + ActivityIndicator, +} from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, + Pressable, + Spinner, + Center, + ScrollView, + Input, + FlatList, +} from 'native-base'; +import { BlurView } from 'expo-blur'; +import { LinearGradient } from 'expo-linear-gradient'; +import { Ionicons } from '@expo/vector-icons'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useNavigation } from '@react-navigation/native'; +import * as WebBrowser from 'expo-web-browser'; +import { API_BASE_URL } from '../../services/api'; +import ConceptTimeline from './ConceptTimeline'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const PADDING = 12; +const GAP = 8; + +// MD5 实现 +const md5 = (string) => { + function md5cycle(x, k) { + var a = x[0], b = x[1], c = x[2], d = x[3]; + a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); + c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); + a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); + c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); + a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); + c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); + a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); + c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); + a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); + c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); + a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); + c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); + a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); + c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); + a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); + c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); + a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); + c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); + a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); + c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); + a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); + c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); + a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); + c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); + a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); + c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); + a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); + c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); + a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); + c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); + a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); + c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); + x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); + } + function cmn(q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); } + function ff(a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); } + function gg(a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); } + function hh(a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); } + function ii(a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); } + function md51(s) { + var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i; + for (i = 64; i <= s.length; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } + s = s.substring(i - 64); + var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for (i = 0; i < s.length; i++) tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); + tail[i >> 2] |= 0x80 << ((i % 4) << 3); + if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i++) tail[i] = 0; } + tail[14] = n * 8; md5cycle(state, tail); return state; + } + function md5blk(s) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } + return md5blks; + } + var hex_chr = '0123456789abcdef'.split(''); + function rhex(n) { var s = '', j = 0; for (; j < 4; j++) s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]; return s; } + function hex(x) { for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]); return x.join(''); } + function add32(a, b) { return (a + b) & 0xffffffff; } + function utf8Encode(str) { + let utf8 = ''; + for (let i = 0; i < str.length; i++) { + let charCode = str.charCodeAt(i); + if (charCode < 128) { utf8 += String.fromCharCode(charCode); } + else if (charCode < 2048) { utf8 += String.fromCharCode((charCode >> 6) | 192); utf8 += String.fromCharCode((charCode & 63) | 128); } + else { utf8 += String.fromCharCode((charCode >> 12) | 224); utf8 += String.fromCharCode(((charCode >> 6) & 63) | 128); utf8 += String.fromCharCode((charCode & 63) | 128); } + } + return utf8; + } + return hex(md51(utf8Encode(string))); +}; + +const getConceptUrl = (conceptName) => `https://valuefrontier.cn/htmls/concept/${md5(conceptName)}/`; +const CONCEPT_API_BASE = `${API_BASE_URL}/concept-api`; + +// ============ 颜色工具 ============ +const getChangeColor = (value, opacity = 1) => { + if (value === null || value === undefined) return `rgba(100, 116, 139, ${opacity})`; + if (value > 7) return `rgba(185, 28, 28, ${opacity})`; + if (value > 5) return `rgba(220, 38, 38, ${opacity})`; + if (value > 3) return `rgba(239, 68, 68, ${opacity})`; + if (value > 1) return `rgba(248, 113, 113, ${opacity})`; + if (value > 0) return `rgba(252, 165, 165, ${opacity})`; + if (value < -7) return `rgba(20, 83, 45, ${opacity})`; + if (value < -5) return `rgba(22, 101, 52, ${opacity})`; + if (value < -3) return `rgba(21, 128, 61, ${opacity})`; + if (value < -1) return `rgba(22, 163, 74, ${opacity})`; + if (value < 0) return `rgba(34, 197, 94, ${opacity})`; + return `rgba(100, 116, 139, ${opacity})`; +}; + +const formatChange = (value) => { + if (value === null || value === undefined) return ''; + const sign = value > 0 ? '+' : ''; + return `${sign}${value.toFixed(2)}%`; +}; + +const extractPureName = (apiName) => { + if (!apiName) return ''; + return apiName.replace(/^\[(一级|二级|三级)\]\s*/, ''); +}; + +// ============ 列表项组件(卡片模式用,简单版) ============ +const ListItem = memo(({ item, onPress, showArrow = true }) => { + const changeColor = item.avg_change_pct > 0 ? '#F87171' : item.avg_change_pct < 0 ? '#4ADE80' : '#94A3B8'; + const bgColor = getChangeColor(item.avg_change_pct, 0.1); + + return ( + onPress?.(item)}> + {({ pressed }) => ( + + {/* 涨跌指示条 */} + + + {/* 名称和子分类数 */} + + + {item.name} + + + {item.concept_count > 0 && ( + + {item.concept_count} 个概念 + + )} + {item.stock_count > 0 && ( + + {item.stock_count} 只股票 + + )} + + + + {/* 涨跌幅 */} + + + + {formatChange(item.avg_change_pct)} + + + {showArrow && ( + + )} + + + )} + + ); +}); + +ListItem.displayName = 'ListItem'; + +// ============ 叶子概念列表项组件(列表模式用,详细版) ============ +const LeafListItem = memo(({ item, onPress, onTimelinePress, sortBy }) => { + const changeColor = item.avg_change_pct > 0 ? '#F87171' : item.avg_change_pct < 0 ? '#4ADE80' : '#94A3B8'; + const bgColor = getChangeColor(item.avg_change_pct, 0.08); + + // 获取最近的启动日期 + const latestOutbreak = item.outbreak_dates && item.outbreak_dates.length > 0 + ? item.outbreak_dates[0] + : null; + + // 格式化启动日期显示 + const formatOutbreakDate = (dateStr) => { + if (!dateStr) return ''; + // 假设格式是 YYYY-MM-DD + const parts = dateStr.split('-'); + if (parts.length === 3) { + return `${parts[1]}/${parts[2]}`; + } + return dateStr; + }; + + // 处理时间轴按钮点击(阻止事件冒泡) + const handleTimelinePress = (e) => { + e?.stopPropagation?.(); + onTimelinePress?.(item); + }; + + return ( + onPress?.(item)}> + {({ pressed }) => ( + + + {/* 涨跌指示条 */} + + + {/* 主要内容 */} + + {/* 概念名称 */} + + {item.name} + + + {/* 层级路径 */} + {item.hierarchy && ( + + {item.hierarchy.lv1 && ( + + {item.hierarchy.lv1} + + )} + {item.hierarchy.lv2 && ( + <> + + + {item.hierarchy.lv2} + + + )} + {item.hierarchy.lv3 && ( + <> + + + {item.hierarchy.lv3} + + + )} + + )} + + {/* 标签和统计 */} + + {item.stock_count > 0 && ( + + + + {item.stock_count} 只 + + + )} + {latestOutbreak && ( + + + + {formatOutbreakDate(latestOutbreak)} + + + )} + {item.tags && item.tags.length > 0 && ( + + {item.tags.slice(0, 2).join(' · ')} + + )} + + + + {/* 涨跌幅 */} + + + {formatChange(item.avg_change_pct)} + + {sortBy === 'outbreak' && latestOutbreak && ( + + 启动 {latestOutbreak} + + )} + + + {/* 时间轴按钮 */} + + + + + {/* 箭头 */} + + + + )} + + ); +}); + +LeafListItem.displayName = 'LeafListItem'; + +// ============ 子分类小块组件 ============ +const ChildBlock = memo(({ item, onPress, width }) => { + const bgColor = getChangeColor(item.avg_change_pct, 0.85); + + return ( + onPress?.(item)} style={{ width, marginBottom: 6 }}> + {({ pressed }) => ( + + + {item.name} + + {item.avg_change_pct !== undefined && ( + + {formatChange(item.avg_change_pct)} + + )} + + )} + + ); +}); + +ChildBlock.displayName = 'ChildBlock'; + +// ============ 父分类卡片组件(毛玻璃风格)============ +const ParentCard = memo(({ item, children, priceMap, onPress, onChildPress }) => { + const borderColor = getChangeColor(item.avg_change_pct, 0.4); + const accentColor = getChangeColor(item.avg_change_pct, 0.8); + + // 计算子分类数据 + const childData = useMemo(() => { + if (!children || children.length === 0) return []; + return children.slice(0, 8).map(child => { + const price = priceMap[child.name] || {}; + return { + ...child, + avg_change_pct: price.avg_change_pct, + stock_count: price.stock_count, + }; + }); + }, [children, priceMap]); + + const hasMore = children && children.length > 8; + const childWidth = (SCREEN_WIDTH - PADDING * 2 - 24 - GAP) / 2; + + return ( + onPress?.(item)} style={styles.parentCard}> + {({ pressed }) => ( + + {/* 背景渐变 */} + + + {/* 毛玻璃效果 */} + + + {/* 边框和内容 */} + + {/* 标题栏 */} + + + + + + {item.name} + + + {item.concept_count || children?.length || 0} 个子分类 + + + + + + {item.avg_change_pct !== undefined && item.avg_change_pct !== 0 && ( + 0 ? 'trending-up' : 'trending-down'} + size="xs" + color={item.avg_change_pct > 0 ? '#F87171' : '#4ADE80'} + /> + )} + 0 ? '#F87171' : item.avg_change_pct < 0 ? '#4ADE80' : 'gray.400'} + fontSize={16} + fontWeight="bold" + > + {formatChange(item.avg_change_pct)} + + + + + {/* 子分类网格 */} + {childData.length > 0 ? ( + + {childData.map((child, index) => ( + + ))} + + ) : ( +
+ 暂无子分类 +
+ )} + + {/* 更多提示 */} + {hasMore && ( + + + 还有 {children.length - 8} 个子分类 + + + + )} +
+
+ )} +
+ ); +}); + +ParentCard.displayName = 'ParentCard'; + +// ============ 叶子概念卡片 ============ +const LeafCard = memo(({ item, onPress }) => { + const bgColor = getChangeColor(item.avg_change_pct, 0.7); + + return ( + onPress?.(item)} style={styles.leafCard}> + {({ pressed }) => ( + + + + + + {item.name} + + + {item.stock_count && ( + + {item.stock_count} 只 + + )} + + {formatChange(item.avg_change_pct)} + + + + + )} + + ); +}); + +LeafCard.displayName = 'LeafCard'; + +// ============ 面包屑组件 ============ +const Breadcrumbs = memo(({ path, onNavigate }) => { + const items = [{ label: '全部分类', level: -1 }, ...path]; + + return ( + + + {items.map((item, index) => ( + + onNavigate(index - 1)} _pressed={{ opacity: 0.6 }}> + + {item.label} + + + {index < items.length - 1 && ( + + )} + + ))} + + + ); +}); + +Breadcrumbs.displayName = 'Breadcrumbs'; + +// ============ 搜索框组件 ============ +const SearchBar = memo(({ value, onChangeText, onClear, onSubmit, placeholder }) => { + const inputRef = useRef(null); + + return ( + + + + + {value ? ( + + + + ) : null} + + + ); +}); + +SearchBar.displayName = 'SearchBar'; + +// ============ 主组件 ============ +const ConceptList = () => { + const navigation = useNavigation(); + const insets = useSafeAreaInsets(); + const flatListRef = useRef(null); + const searchTimeoutRef = useRef(null); + + const [hierarchy, setHierarchy] = useState([]); + const [priceData, setPriceData] = useState({ lv1Map: {}, lv2Map: {}, lv3Map: {}, leafMap: {} }); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [error, setError] = useState(null); + const [drillPath, setDrillPath] = useState([]); + const [viewMode, setViewMode] = useState('card'); // 'card' | 'list' + const [sortBy, setSortBy] = useState('change'); // 'change' | 'outbreak' + const [leafConcepts, setLeafConcepts] = useState([]); // 列表模式下的叶子概念 + const [leafLoading, setLeafLoading] = useState(false); + + // 搜索和分页状态 + const [searchQuery, setSearchQuery] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalCount, setTotalCount] = useState(0); + const [hasMore, setHasMore] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); + const PAGE_SIZE = 30; + + // 时间轴模态框状态 + const [timelineVisible, setTimelineVisible] = useState(false); + const [timelineConcept, setTimelineConcept] = useState(null); + + // 获取数据 + const fetchHierarchy = useCallback(async () => { + const response = await fetch(`${CONCEPT_API_BASE}/hierarchy`); + if (!response.ok) throw new Error('获取数据失败'); + const data = await response.json(); + setHierarchy(data.hierarchy || []); + }, []); + + const fetchPriceData = useCallback(async () => { + try { + const response = await fetch(`${CONCEPT_API_BASE}/hierarchy/price`); + if (!response.ok) return; + const data = await response.json(); + + const lv1Map = {}, lv2Map = {}, lv3Map = {}, leafMap = {}; + (data.lv1_concepts || []).forEach(item => { lv1Map[extractPureName(item.concept_name)] = item; }); + (data.lv2_concepts || []).forEach(item => { lv2Map[extractPureName(item.concept_name)] = item; }); + (data.lv3_concepts || []).forEach(item => { lv3Map[extractPureName(item.concept_name)] = item; }); + (data.leaf_concepts || []).forEach(item => { leafMap[item.concept_name] = item; }); + + setPriceData({ lv1Map, lv2Map, lv3Map, leafMap }); + + // 保存叶子概念列表供列表模式使用 + return data.leaf_concepts || []; + } catch (err) { + console.error('[ConceptList] fetchPriceData error:', err); + return []; + } + }, []); + + // 搜索概念(支持分页) + const searchConcepts = useCallback(async (query, page = 1, append = false) => { + if (page === 1) { + setLeafLoading(true); + if (!append) setLeafConcepts([]); + } else { + setLoadingMore(true); + } + + try { + // 有搜索词时按相关度排序,没有搜索词时按用户选择的排序方式 + const actualSortBy = query?.trim() + ? '_score' + : (sortBy === 'outbreak' ? 'outbreak_date' : 'change_pct'); + + const response = await fetch(`${CONCEPT_API_BASE}/search`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: query || '', + size: PAGE_SIZE, + page: page, + sort_by: actualSortBy, + search_size: 1000, + }), + }); + + if (response.ok) { + const data = await response.json(); + const results = data.results || []; + + // 转换为组件需要的格式 + const formatted = results.map(item => ({ + concept_id: item.concept_id, + name: item.concept, + avg_change_pct: item.price_info?.avg_change_pct, + stock_count: item.stock_count, + outbreak_dates: item.outbreak_dates || [], + hierarchy: item.hierarchy, + tags: item.tags || [], + })); + + if (append && page > 1) { + setLeafConcepts(prev => [...prev, ...formatted]); + } else { + setLeafConcepts(formatted); + } + + setCurrentPage(data.page || page); + setTotalPages(data.total_pages || 1); + setTotalCount(data.total || 0); + setHasMore(page < (data.total_pages || 1)); + } + } catch (err) { + console.error('[ConceptList] searchConcepts error:', err); + } finally { + setLeafLoading(false); + setLoadingMore(false); + } + }, [sortBy, PAGE_SIZE]); + + // 加载更多 + const loadMore = useCallback(() => { + if (loadingMore || !hasMore || leafLoading) return; + searchConcepts(searchQuery, currentPage + 1, true); + }, [loadingMore, hasMore, leafLoading, searchQuery, currentPage, searchConcepts]); + + // 防抖搜索 + const handleSearchChange = useCallback((text) => { + setSearchQuery(text); + + // 清除之前的定时器 + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + + // 设置新的防抖定时器 + searchTimeoutRef.current = setTimeout(() => { + setCurrentPage(1); + setHasMore(true); + searchConcepts(text, 1, false); + }, 300); + }, [searchConcepts]); + + // 清除搜索 + const handleClearSearch = useCallback(() => { + setSearchQuery(''); + setCurrentPage(1); + setHasMore(true); + searchConcepts('', 1, false); + Keyboard.dismiss(); + }, [searchConcepts]); + + // 提交搜索 + const handleSubmitSearch = useCallback(() => { + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + setCurrentPage(1); + setHasMore(true); + searchConcepts(searchQuery, 1, false); + Keyboard.dismiss(); + }, [searchQuery, searchConcepts]); + + const loadData = useCallback(async (isRefresh = false) => { + if (isRefresh) setRefreshing(true); + else setLoading(true); + setError(null); + + try { + await fetchHierarchy(); + await fetchPriceData(); + // 如果当前是列表模式,加载概念列表 + if (viewMode === 'list') { + await searchConcepts(searchQuery, 1, false); + } + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + setRefreshing(false); + } + }, [fetchHierarchy, fetchPriceData, searchConcepts, viewMode, searchQuery]); + + useEffect(() => { loadData(); }, []); + + // 切换到列表模式时,加载概念列表 + useEffect(() => { + if (viewMode === 'list' && leafConcepts.length === 0) { + searchConcepts(searchQuery, 1, false); + } + }, [viewMode]); + + // 排序方式变化时,重新获取数据 + useEffect(() => { + if (viewMode === 'list') { + setCurrentPage(1); + setHasMore(true); + searchConcepts(searchQuery, 1, false); + } + }, [sortBy]); + + // 当前显示数据 + const { currentItems, childPriceMap, isLeafLevel } = useMemo(() => { + const { lv1Map, lv2Map, lv3Map, leafMap } = priceData; + + if (drillPath.length === 0) { + // 一级分类,子分类是二级 + const items = hierarchy.map(lv1 => ({ + ...lv1, + avg_change_pct: lv1Map[lv1.name]?.avg_change_pct, + stock_count: lv1Map[lv1.name]?.stock_count, + })); + return { currentItems: items, childPriceMap: lv2Map, isLeafLevel: false }; + } + + if (drillPath.length === 1) { + // 二级分类,子分类是三级 + const lv1 = drillPath[0].data; + const items = (lv1.children || []).map(lv2 => ({ + ...lv2, + avg_change_pct: lv2Map[lv2.name]?.avg_change_pct, + stock_count: lv2Map[lv2.name]?.stock_count, + })); + return { currentItems: items, childPriceMap: lv3Map, isLeafLevel: false }; + } + + if (drillPath.length === 2) { + // 三级分类,子分类是叶子概念 + const lv2 = drillPath[1].data; + const items = (lv2.children || []).map(lv3 => ({ + ...lv3, + avg_change_pct: lv3Map[lv3.name]?.avg_change_pct, + stock_count: lv3Map[lv3.name]?.stock_count, + // 把 concepts 转换为 children 格式 + children: (lv3.concepts || []).map(c => ({ name: c })), + })); + return { currentItems: items, childPriceMap: leafMap, isLeafLevel: false }; + } + + if (drillPath.length === 3) { + // 叶子概念列表 + const lv3 = drillPath[2].data; + const items = (lv3.concepts || []).map(concept => ({ + name: concept, + avg_change_pct: leafMap[concept]?.avg_change_pct, + stock_count: leafMap[concept]?.stock_count, + })); + return { currentItems: items, childPriceMap: {}, isLeafLevel: true }; + } + + return { currentItems: [], childPriceMap: {}, isLeafLevel: false }; + }, [hierarchy, priceData, drillPath]); + + // 点击父分类 + const handleParentPress = useCallback((item) => { + if (item.children?.length > 0 || item.concepts?.length > 0) { + setDrillPath(prev => [...prev, { label: item.name, data: item }]); + } + }, []); + + // 点击子分类 + const handleChildPress = useCallback((item) => { + // 找到完整的子分类数据并钻取 + const currentLevel = drillPath.length; + if (currentLevel === 0) { + const lv1 = hierarchy.find(h => h.name === drillPath[0]?.data?.name); + const lv2 = lv1?.children?.find(c => c.name === item.name) || item; + setDrillPath(prev => [...prev, { label: item.name, data: lv2 }]); + } else { + setDrillPath(prev => [...prev, { label: item.name, data: item }]); + } + }, [drillPath, hierarchy]); + + // 点击叶子概念 - 打开详情页 + const handleLeafPress = useCallback(async (item) => { + const url = getConceptUrl(item.name); + try { + await WebBrowser.openBrowserAsync(url, { + toolbarColor: '#0F172A', + controlsColor: '#06B6D4', + }); + } catch (error) { + console.error('打开浏览器失败:', error); + } + }, []); + + // 点击时间轴按钮 - 打开日历时间轴模态框 + const handleTimelinePress = useCallback((item) => { + setTimelineConcept(item); + setTimelineVisible(true); + }, []); + + // 关闭时间轴模态框 + const handleCloseTimeline = useCallback(() => { + setTimelineVisible(false); + setTimelineConcept(null); + }, []); + + const handleBreadcrumbNavigate = useCallback((index) => { + if (index < 0) setDrillPath([]); + else setDrillPath(prev => prev.slice(0, index + 1)); + }, []); + + const openDrawer = useCallback(() => navigation.openDrawer(), [navigation]); + const goBack = useCallback(() => setDrillPath(prev => prev.slice(0, -1)), []); + const toggleViewMode = useCallback(() => { + const newMode = viewMode === 'card' ? 'list' : 'card'; + setViewMode(newMode); + // 切换到列表模式时重置下钻路径 + if (newMode === 'list') { + setDrillPath([]); + } + }, [viewMode]); + const levelTitles = ['一级分类', '二级分类', '三级分类', '概念板块']; + const currentLevelTitle = viewMode === 'list' ? '全部概念' : levelTitles[Math.min(drillPath.length, 3)]; + + return ( + + + + {/* 顶部导航 */} + + + + + {drillPath.length > 0 ? ( + + + + ) : ( + + + + )} + + + + 概念中心 + + + {currentLevelTitle} · {viewMode === 'list' ? totalCount : currentItems.length} 项 + + + + + {/* 视图模式切换按钮 - 显示将要切换到的模式图标 */} + + + + loadData(true)} hitSlop={10}> + + + + + + + + {/* 列表模式:搜索框 + 排序选项 */} + {viewMode === 'list' && ( + + {/* 搜索框 */} + + + {/* 排序栏 */} + + + {searchQuery ? `搜索结果:${totalCount} 个` : `共 ${totalCount} 个概念`} + {leafConcepts.length < totalCount && ` · 已加载 ${leafConcepts.length}`} + + {searchQuery ? ( + // 搜索时显示相关度排序提示 + + + 按相关度排序 + + ) : ( + // 无搜索时显示排序选项 + + 排序: + setSortBy('change')} + bg={sortBy === 'change' ? 'rgba(248, 113, 113, 0.2)' : 'transparent'} + px={3} + py={1} + borderRadius={12} + borderWidth={1} + borderColor={sortBy === 'change' ? 'rgba(248, 113, 113, 0.4)' : 'rgba(255,255,255,0.1)'} + > + + + + 涨幅 + + + + setSortBy('outbreak')} + bg={sortBy === 'outbreak' ? 'rgba(251, 191, 36, 0.2)' : 'transparent'} + px={3} + py={1} + borderRadius={12} + borderWidth={1} + borderColor={sortBy === 'outbreak' ? 'rgba(251, 191, 36, 0.4)' : 'rgba(255,255,255,0.1)'} + > + + + + 启动日期 + + + + + )} + + + )} + + {/* 卡片模式:面包屑 */} + {viewMode === 'card' && drillPath.length > 0 && ( + + + + )} + + {/* 内容 */} + {loading ? ( +
+ + 加载概念数据... +
+ ) : error ? ( +
+ + {error} + loadData()} bg="rgba(6, 182, 212, 0.2)" px={6} py={2} rounded="full"> + 重新加载 + +
+ ) : viewMode === 'list' ? ( + // 列表模式 - 使用 FlatList 支持无限滚动 + item.concept_id || item.name || String(index)} + renderItem={({ item }) => ( + + )} + contentContainerStyle={{ padding: PADDING, paddingBottom: 30 }} + showsVerticalScrollIndicator={false} + refreshControl={ + { + setCurrentPage(1); + setHasMore(true); + loadData(true); + }} + tintColor="#06B6D4" + /> + } + onEndReached={loadMore} + onEndReachedThreshold={0.3} + ListEmptyComponent={ + leafLoading ? ( +
+ + 加载概念列表... +
+ ) : ( +
+ + {searchQuery ? '未找到匹配的概念' : '暂无概念数据'} +
+ ) + } + ListFooterComponent={ + loadingMore ? ( +
+ + + 加载更多... + +
+ ) : !hasMore && leafConcepts.length > 0 ? ( +
+ 已加载全部 {totalCount} 个概念 +
+ ) : null + } + /> + ) : ( + // 卡片模式 - 使用 ScrollView + loadData(true)} tintColor="#06B6D4" /> + } + contentContainerStyle={{ padding: PADDING, paddingBottom: 30 }} + > + {isLeafLevel ? ( + // 叶子概念网格 + + {currentItems.map((item, index) => ( + + ))} + + ) : ( + // 父分类卡片列表 + + {currentItems.map((item, index) => ( + + ))} + + )} + + {currentItems.length === 0 && ( +
+ + 暂无数据 +
+ )} +
+ )} + + {/* 时间轴模态框 */} + +
+ ); +}; + +const styles = StyleSheet.create({ + searchInput: { + flex: 1, + color: 'white', + fontSize: 14, + paddingVertical: 4, + }, + parentCard: { + borderRadius: 16, + overflow: 'hidden', + }, + cardContainer: { + borderRadius: 16, + overflow: 'hidden', + }, + cardContent: { + padding: 12, + borderWidth: 1, + borderRadius: 16, + }, + childGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + }, + childBlockShadow: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 3, + elevation: 3, + }, + leafCard: { + width: (SCREEN_WIDTH - PADDING * 2 - GAP * 2) / 3, + height: 80, + marginBottom: GAP, + borderRadius: 10, + overflow: 'hidden', + }, + leafContainer: { + flex: 1, + borderRadius: 10, + overflow: 'hidden', + }, + leafContent: { + flex: 1, + padding: 8, + justifyContent: 'space-between', + }, + leafGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + }, +}); + +export default memo(ConceptList); diff --git a/MeAgent/src/screens/Concepts/ConceptTimeline.js b/MeAgent/src/screens/Concepts/ConceptTimeline.js new file mode 100644 index 00000000..06cb8c4c --- /dev/null +++ b/MeAgent/src/screens/Concepts/ConceptTimeline.js @@ -0,0 +1,818 @@ +/** + * 概念时间轴模态框 - 日历形式展示历史事件和涨跌幅 + */ + +import React, { useState, useEffect, useCallback, useMemo, memo } from 'react'; +import { + Modal, + StyleSheet, + Dimensions, + TouchableOpacity, + ActivityIndicator, +} from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, + Pressable, + ScrollView, + Center, + Spinner, + Badge, +} from 'native-base'; +import { BlurView } from 'expo-blur'; +import { LinearGradient } from 'expo-linear-gradient'; +import { Ionicons } from '@expo/vector-icons'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useNavigation } from '@react-navigation/native'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const CELL_SIZE = (SCREEN_WIDTH - 32) / 7; + +// API 基础地址 - 生产环境使用代理 +const API_BASE = 'https://api.valuefrontier.cn'; +const CONCEPT_API_BASE = `${API_BASE}/concept-api`; +const REPORT_API_BASE = `${API_BASE}/report-api`; +const MAIN_API_URL = API_BASE; + +// 日期工具函数 +const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +}; + +const getMonthDays = (year, month) => { + return new Date(year, month + 1, 0).getDate(); +}; + +const getFirstDayOfMonth = (year, month) => { + return new Date(year, month, 1).getDay(); +}; + +// 涨跌幅颜色 +const getChangeColor = (change, alpha = 1) => { + if (change > 3) return `rgba(239, 68, 68, ${alpha})`; // 大涨 - 深红 + if (change > 0) return `rgba(248, 113, 113, ${alpha})`; // 小涨 - 红 + if (change < -3) return `rgba(34, 197, 94, ${alpha})`; // 大跌 - 深绿 + if (change < 0) return `rgba(74, 222, 128, ${alpha})`; // 小跌 - 绿 + return `rgba(148, 163, 184, ${alpha})`; // 平盘 - 灰 +}; + +// ============ 日历单元格组件 ============ +const CalendarCell = memo(({ day, isCurrentMonth, data, isToday, onPress }) => { + if (!day) { + return ; + } + + const hasEvents = data?.events?.length > 0; + const hasNews = data?.events?.some(e => e.type === 'news'); + const hasReport = data?.events?.some(e => e.type === 'report'); + const change = data?.price?.avg_change_pct; + + return ( + onPress?.(day, data)} disabled={!isCurrentMonth}> + {({ pressed }) => ( + + {/* 日期数字 */} + + {day} + + + {/* 涨跌幅指示 */} + {change !== undefined && isCurrentMonth && ( + + {Math.abs(change) >= 3 && ( + 0 ? 'caret-up' : 'caret-down'} + size="2xs" + color="white" + /> + )} + + )} + + {/* 事件指示点 */} + {hasEvents && isCurrentMonth && ( + + {hasNews && ( + + )} + {hasReport && ( + + )} + + )} + + )} + + ); +}); + +CalendarCell.displayName = 'CalendarCell'; + +// 重要性颜色 +const getImportanceColor = (importance) => { + switch (importance) { + case 'S': return '#EF4444'; // 特级 - 红色 + case 'A': return '#F97316'; // 重要 - 橙色 + case 'B': return '#FBBF24'; // 一般 - 黄色 + default: return '#94A3B8'; // 普通 - 灰色 + } +}; + +const getImportanceLabel = (importance) => { + switch (importance) { + case 'S': return '特级'; + case 'A': return '重要'; + case 'B': return '一般'; + default: return ''; + } +}; + +// 事件类型图标和颜色 +const getEventTypeInfo = (eventType) => { + switch (eventType) { + case '政策': return { icon: 'document-text', color: '#F97316' }; + case '公司': return { icon: 'business', color: '#06B6D4' }; + case '行业': return { icon: 'trending-up', color: '#8B5CF6' }; + case '市场': return { icon: 'stats-chart', color: '#10B981' }; + default: return { icon: 'newspaper', color: '#60A5FA' }; + } +}; + +// ============ 日期详情卡片 ============ +const DayDetail = memo(({ date, data, onClose, onEventPress }) => { + if (!data) return null; + + const change = data.price?.avg_change_pct; + const stockCount = data.price?.stock_count; + const events = data.events || []; + const newsEvents = events.filter(e => e.type === 'news'); + const reportEvents = events.filter(e => e.type === 'report'); + + return ( + + {/* 头部渐变 */} + + + + + + + + {date} + + {newsEvents.length} 条事件 · {reportEvents.length} 篇研报 + + + + + + + + + + + {/* 涨跌幅卡片 */} + {change !== undefined && ( + + + + 概念平均涨跌幅 + {stockCount && ( + 基于 {stockCount} 只成分股 + )} + + + + {change > 0 ? '+' : ''}{change?.toFixed(2)} + + % + + + + )} + + {/* 事件列表 */} + {newsEvents.length > 0 && ( + 0 ? 3 : 0}> + + + 相关事件 + + {newsEvents.map((event, index) => { + const typeInfo = getEventTypeInfo(event.eventType); + const hasId = !!event.id; + + return ( + hasId && onEventPress?.(event)} + disabled={!hasId} + > + {({ pressed }) => ( + + {/* 事件头部 */} + + + + {event.eventType && ( + + + {event.eventType} + + + )} + {event.importance && ( + + + {getImportanceLabel(event.importance)} + + + )} + + {hasId && ( + + )} + + + {/* 事件标题 */} + + {event.title} + + + {/* 事件底部信息 */} + + + {event.hotScore && ( + + + {event.hotScore.toFixed(1)} + + )} + {event.viewCount > 0 && ( + + + {event.viewCount} + + )} + + {hasId && ( + 查看详情 + )} + + + )} + + ); + })} + + )} + + {/* 研报列表 */} + {reportEvents.length > 0 && ( + + + + 相关研报 + + {reportEvents.map((report, index) => ( + + + {report.title} + + + {report.source && ( + + + {report.source} + + )} + {report.rating && ( + + {report.rating} + + )} + {report.security_name && ( + · {report.security_name} + )} + + + ))} + + )} + + {/* 无事件提示 */} + {events.length === 0 && ( +
+ + 当日无相关事件记录 +
+ )} +
+
+ ); +}); + +DayDetail.displayName = 'DayDetail'; + +// ============ 主组件 ============ +const ConceptTimeline = ({ visible, onClose, concept }) => { + const insets = useSafeAreaInsets(); + const navigation = useNavigation(); + const [currentDate, setCurrentDate] = useState(new Date()); + const [loading, setLoading] = useState(false); + const [priceData, setPriceData] = useState({}); // 日期 -> 价格数据 + const [eventsData, setEventsData] = useState({}); // 日期 -> 事件数据 + const [selectedDay, setSelectedDay] = useState(null); + const [selectedData, setSelectedData] = useState(null); + + const year = currentDate.getFullYear(); + const month = currentDate.getMonth(); + + // 获取数据 + const fetchData = useCallback(async () => { + if (!concept?.name && !concept?.concept_id) return; + + setLoading(true); + + // 计算日期范围(最近100天) + const endDate = new Date(); + const startDate = new Date(); + startDate.setDate(startDate.getDate() - 100); + const startStr = formatDate(startDate); + const endStr = formatDate(endDate); + + const conceptId = concept.concept_id; + const conceptName = concept.name; + + console.log('[ConceptTimeline] Fetching data for:', { conceptId, conceptName, startStr, endStr }); + + try { + // 并行获取价格、事件和研报数据 + const [priceRes, eventsRes, reportsRes] = await Promise.all([ + // 1. 价格时序数据 + fetch(`${CONCEPT_API_BASE}/concept/${conceptId}/price-timeseries?start_date=${startStr}&end_date=${endStr}`) + .then(r => { + console.log('[ConceptTimeline] Price API response status:', r.status); + return r.ok ? r.json() : { timeseries: [] }; + }) + .catch(err => { + console.error('[ConceptTimeline] Price API error:', err); + return { timeseries: [] }; + }), + + // 2. 事件数据 + fetch(`${MAIN_API_URL}/api/events/by-concept`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + concept_name: conceptName, + start_date: startStr, + end_date: endStr, + limit: 200, + }), + }) + .then(r => { + console.log('[ConceptTimeline] Events API response status:', r.status); + return r.ok ? r.json() : { success: false, data: [] }; + }) + .catch(err => { + console.error('[ConceptTimeline] Events API error:', err); + return { success: false, data: [] }; + }), + + // 3. 研报数据 + fetch(`${REPORT_API_BASE}/search?query=${encodeURIComponent(conceptName)}&mode=text&exact_match=1&size=30&start_date=${startStr}`) + .then(r => { + console.log('[ConceptTimeline] Report API response status:', r.status); + return r.ok ? r.json() : { data: { results: [] } }; + }) + .catch(err => { + console.error('[ConceptTimeline] Report API error:', err); + return { data: { results: [] } }; + }), + ]); + + console.log('[ConceptTimeline] Price data count:', priceRes.timeseries?.length || 0); + console.log('[ConceptTimeline] Events data count:', eventsRes.data?.length || 0); + console.log('[ConceptTimeline] Reports data count:', reportsRes.data?.results?.length || 0); + + // 辅助函数:从各种日期格式中提取 YYYY-MM-DD + const extractDateStr = (dateValue) => { + if (!dateValue) return null; + // 处理 ISO 格式 (2026-01-14T14:45:36) 或空格格式 (2026-01-14 14:45:36) + const str = String(dateValue); + const match = str.match(/^(\d{4}-\d{2}-\d{2})/); + return match ? match[1] : null; + }; + + // 处理价格数据 + const priceMap = {}; + (priceRes.timeseries || []).forEach(item => { + const dateStr = extractDateStr(item.trade_date); + if (dateStr) { + priceMap[dateStr] = { + avg_change_pct: item.avg_change_pct, + stock_count: item.stock_count, + }; + } + }); + setPriceData(priceMap); + + // 处理事件和研报数据,合并到同一个 map + const eventsMap = {}; + + // 处理事件 + (eventsRes.data || []).forEach(event => { + const dateStr = extractDateStr(event.event_date) || extractDateStr(event.created_at); + if (!dateStr) return; + + if (!eventsMap[dateStr]) { + eventsMap[dateStr] = []; + } + eventsMap[dateStr].push({ + type: 'news', + id: event.id, // 事件ID,用于跳转详情 + title: event.title || event.summary, + source: event.source || 'event', + content: event.description || event.summary, + eventType: event.event_type, // 事件类型:政策、公司、行业、市场 + importance: event.importance, // 重要性:S、A、B + hotScore: event.hot_score, // 热度分数 + viewCount: event.view_count, // 浏览次数 + }); + }); + + // 处理研报 + const reports = reportsRes.data?.results || reportsRes.results || []; + reports.forEach(report => { + const dateStr = extractDateStr(report.declare_date) || extractDateStr(report.publish_time); + if (!dateStr) return; + + if (!eventsMap[dateStr]) { + eventsMap[dateStr] = []; + } + eventsMap[dateStr].push({ + type: 'report', + title: report.report_title || report.title, + source: report.publisher || report.author, + content: report.content, + rating: report.rating, + security_name: report.security_name, + }); + }); + + setEventsData(eventsMap); + + console.log('[ConceptTimeline] Processed price dates:', Object.keys(priceMap).length); + console.log('[ConceptTimeline] Processed event dates:', Object.keys(eventsMap).length); + + } catch (error) { + console.error('[ConceptTimeline] fetchData error:', error); + } finally { + setLoading(false); + } + }, [concept]); + + useEffect(() => { + if (visible) { + fetchData(); + setSelectedDay(null); + setSelectedData(null); + } + }, [visible, fetchData]); + + // 切换月份 + const goToPrevMonth = useCallback(() => { + setCurrentDate(new Date(year, month - 1, 1)); + setSelectedDay(null); + setSelectedData(null); + }, [year, month]); + + const goToNextMonth = useCallback(() => { + setCurrentDate(new Date(year, month + 1, 1)); + setSelectedDay(null); + setSelectedData(null); + }, [year, month]); + + const goToToday = useCallback(() => { + setCurrentDate(new Date()); + setSelectedDay(null); + setSelectedData(null); + }, []); + + // 点击事件跳转到事件详情 + const handleEventPress = useCallback((event) => { + if (!event.id) return; + + // 先关闭时间轴模态框 + onClose?.(); + + // 延迟一下再跳转,确保模态框已关闭 + setTimeout(() => { + navigation.navigate('EventsDrawer', { + screen: 'EventDetail', + params: { + eventId: event.id, + }, + }); + }, 300); + }, [navigation, onClose]); + + // 生成日历数据 + const calendarData = useMemo(() => { + const daysInMonth = getMonthDays(year, month); + const firstDay = getFirstDayOfMonth(year, month); + const daysInPrevMonth = getMonthDays(year, month - 1); + + const days = []; + + // 上个月的天数 + for (let i = firstDay - 1; i >= 0; i--) { + days.push({ + day: daysInPrevMonth - i, + isCurrentMonth: false, + }); + } + + // 当月天数 + const today = new Date(); + const todayStr = formatDate(today); + + for (let i = 1; i <= daysInMonth; i++) { + const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(i).padStart(2, '0')}`; + const isToday = dateStr === todayStr; + + days.push({ + day: i, + isCurrentMonth: true, + isToday, + dateStr, + data: { + price: priceData[dateStr], + events: eventsData[dateStr] || [], + }, + }); + } + + // 下个月的天数(补齐6行) + const remainingDays = 42 - days.length; + for (let i = 1; i <= remainingDays; i++) { + days.push({ + day: i, + isCurrentMonth: false, + }); + } + + return days; + }, [year, month, priceData, eventsData]); + + // 点击日期 + const handleDayPress = useCallback((day, data) => { + const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + setSelectedDay(dateStr); + setSelectedData(data); + }, [year, month]); + + const weekDays = ['日', '一', '二', '三', '四', '五', '六']; + const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; + + return ( + + + + {/* 头部 */} + + + + + + + + + + 历史时间轴 + + + {concept?.name} + + + + + 今天 + + + + + {/* 月份切换 */} + + + + + + {year}年 {monthNames[month]} + + + + + + + {/* 图例 */} + + + + + + + + + + + + 新闻 + + + + 研报 + + + + {/* 星期标题 */} + + {weekDays.map((day, index) => ( + + + {day} + + + ))} + + + {/* 日历主体 */} + + {loading ? ( +
+ + 加载历史数据... +
+ ) : ( + + {/* 日历网格 */} + + {calendarData.map((item, index) => ( + + ))} + + + {/* 选中日期详情 */} + {selectedDay && ( + { + setSelectedDay(null); + setSelectedData(null); + }} + onEventPress={handleEventPress} + /> + )} + + + + )} +
+
+
+
+ ); +}; + +const styles = StyleSheet.create({ + cell: { + width: CELL_SIZE, + height: CELL_SIZE * 1.1, + alignItems: 'center', + justifyContent: 'center', + padding: 2, + }, + cellDisabled: { + opacity: 0.3, + }, + cellToday: { + backgroundColor: 'rgba(139, 92, 246, 0.2)', + borderRadius: 8, + borderWidth: 1, + borderColor: 'rgba(139, 92, 246, 0.5)', + }, + cellPressed: { + backgroundColor: 'rgba(255, 255, 255, 0.1)', + borderRadius: 8, + }, +}); + +export default memo(ConceptTimeline); diff --git a/MeAgent/src/screens/Concepts/index.js b/MeAgent/src/screens/Concepts/index.js new file mode 100644 index 00000000..b1beac99 --- /dev/null +++ b/MeAgent/src/screens/Concepts/index.js @@ -0,0 +1 @@ +export { default as ConceptList } from './ConceptList'; diff --git a/argon-pro-react-native/src/screens/Events/EventCard.js b/MeAgent/src/screens/Events/EventCard.js similarity index 97% rename from argon-pro-react-native/src/screens/Events/EventCard.js rename to MeAgent/src/screens/Events/EventCard.js index a47996de..3dbc0eaa 100644 --- a/argon-pro-react-native/src/screens/Events/EventCard.js +++ b/MeAgent/src/screens/Events/EventCard.js @@ -16,6 +16,7 @@ import { LinearGradient } from 'expo-linear-gradient'; import { Ionicons } from '@expo/vector-icons'; import { StyleSheet, Dimensions } from 'react-native'; import { gradients, importanceGradients } from '../../theme'; +import { EventFollowButton } from '../../components/AddWatchlistButton'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); @@ -182,6 +183,12 @@ const EventCard = memo(({ event, onPress }) => { {event.view_count || 0} + {/* 关注按钮 */} + diff --git a/argon-pro-react-native/src/screens/Events/EventComments.js b/MeAgent/src/screens/Events/EventComments.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/EventComments.js rename to MeAgent/src/screens/Events/EventComments.js diff --git a/argon-pro-react-native/src/screens/Events/EventDetail.js b/MeAgent/src/screens/Events/EventDetail.js similarity index 97% rename from argon-pro-react-native/src/screens/Events/EventDetail.js rename to MeAgent/src/screens/Events/EventDetail.js index 3db55759..979c272f 100644 --- a/argon-pro-react-native/src/screens/Events/EventDetail.js +++ b/MeAgent/src/screens/Events/EventDetail.js @@ -160,16 +160,23 @@ const EventDetail = ({ route, navigation }) => { setSelectedStock(null); }, []); - // 查看更多股票信息 + // 查看更多股票信息 - 导航到股票详情页 const handleViewMoreStock = useCallback((stock) => { setSelectedStock(null); - // TODO: 跳转到股票详情页面 - toast.show({ - description: `即将查看 ${stock.stock_name || stock.name} 详情`, - placement: 'top', - bg: 'primary.500', + + // 获取股票代码和名称 + const stockCode = stock.stock_code || stock.code || ''; + const stockName = stock.stock_name || stock.name || ''; + + console.log('[EventDetail] 导航到股票详情:', { stockCode, stockName }); + + // 导航到股票详情页 + navigation.navigate('StockDetail', { + stockCode, + stockName, + eventTime: currentEvent?.event_time, // 传递事件时间用于历史K线定位 }); - }, [toast]); + }, [navigation, currentEvent]); // 点击概念 const handleConceptPress = useCallback((concept) => { diff --git a/argon-pro-react-native/src/screens/Events/EventList.js b/MeAgent/src/screens/Events/EventList.js similarity index 93% rename from argon-pro-react-native/src/screens/Events/EventList.js rename to MeAgent/src/screens/Events/EventList.js index a0dc4ded..57cf9a3c 100644 --- a/argon-pro-react-native/src/screens/Events/EventList.js +++ b/MeAgent/src/screens/Events/EventList.js @@ -225,45 +225,34 @@ const EventList = ({ navigation }) => { {/* 标题区域 */} - - - {/* 标题图标 */} - - - - - 事件中心 + + {/* 菜单按钮 - 移到左边 */} + navigation.openDrawer?.()} hitSlop={10}> + + + + + {/* 标题图标 */} + + + + + 事件中心 + + + + 发现市场热点,把握投资机会 - - - 发现市场热点,把握投资机会 - - + + {/* 模式切换 */} {renderModeToggle()} - {/* 菜单按钮 */} - navigation.openDrawer?.()} - > - {({ isPressed }) => ( - - - - )} - diff --git a/argon-pro-react-native/src/screens/Events/FilterModal.js b/MeAgent/src/screens/Events/FilterModal.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/FilterModal.js rename to MeAgent/src/screens/Events/FilterModal.js diff --git a/argon-pro-react-native/src/screens/Events/HistoricalEvents.js b/MeAgent/src/screens/Events/HistoricalEvents.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/HistoricalEvents.js rename to MeAgent/src/screens/Events/HistoricalEvents.js diff --git a/argon-pro-react-native/src/screens/Events/MainlineView.js b/MeAgent/src/screens/Events/MainlineView.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/MainlineView.js rename to MeAgent/src/screens/Events/MainlineView.js diff --git a/argon-pro-react-native/src/screens/Events/RelatedConcepts.js b/MeAgent/src/screens/Events/RelatedConcepts.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/RelatedConcepts.js rename to MeAgent/src/screens/Events/RelatedConcepts.js diff --git a/argon-pro-react-native/src/screens/Events/RelatedStocks.js b/MeAgent/src/screens/Events/RelatedStocks.js similarity index 85% rename from argon-pro-react-native/src/screens/Events/RelatedStocks.js rename to MeAgent/src/screens/Events/RelatedStocks.js index 49fab0f7..0b4120b7 100644 --- a/argon-pro-react-native/src/screens/Events/RelatedStocks.js +++ b/MeAgent/src/screens/Events/RelatedStocks.js @@ -4,7 +4,8 @@ */ import React, { memo, useCallback } from 'react'; -import { StyleSheet, Clipboard } from 'react-native'; +import { StyleSheet } from 'react-native'; +import * as ExpoClipboard from 'expo-clipboard'; import { Box, VStack, @@ -19,6 +20,7 @@ import { import { LinearGradient } from 'expo-linear-gradient'; import { Ionicons } from '@expo/vector-icons'; import { gradients } from '../../theme'; +import { StockWatchlistButton } from '../../components/AddWatchlistButton'; // 格式化涨跌幅 const formatChange = (value) => { @@ -114,28 +116,37 @@ const StockItem = memo(({ stock, quote, index, total, onPress, onCopyCode }) => - {/* 右侧:涨跌幅和价格 */} - - - + + - {formatChange(change)} - - - {price != null && ( - - ¥{price.toFixed(2)} - - )} - + + {formatChange(change)} + +
+ {price != null && ( + + ¥{price.toFixed(2)} + + )} + + {/* 加自选按钮 */} + + ); @@ -157,10 +168,10 @@ const RelatedStocks = ({ const toast = useToast(); // 复制股票代码 - const handleCopyCode = useCallback((code) => { + const handleCopyCode = useCallback(async (code) => { if (!code) return; try { - Clipboard.setString(code); + await ExpoClipboard.setStringAsync(code); toast.show({ description: `已复制 ${code}`, placement: 'top', @@ -169,6 +180,12 @@ const RelatedStocks = ({ }); } catch (error) { console.error('复制失败:', error); + toast.show({ + description: `复制失败`, + placement: 'top', + bg: 'danger.500', + duration: 1500, + }); } }, [toast]); diff --git a/argon-pro-react-native/src/screens/Events/SankeyFlow.js b/MeAgent/src/screens/Events/SankeyFlow.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/SankeyFlow.js rename to MeAgent/src/screens/Events/SankeyFlow.js diff --git a/argon-pro-react-native/src/screens/Events/StockDetailModal.js b/MeAgent/src/screens/Events/StockDetailModal.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/StockDetailModal.js rename to MeAgent/src/screens/Events/StockDetailModal.js diff --git a/argon-pro-react-native/src/screens/Events/TransmissionChain.js b/MeAgent/src/screens/Events/TransmissionChain.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/TransmissionChain.js rename to MeAgent/src/screens/Events/TransmissionChain.js diff --git a/argon-pro-react-native/src/screens/Events/index.js b/MeAgent/src/screens/Events/index.js similarity index 100% rename from argon-pro-react-native/src/screens/Events/index.js rename to MeAgent/src/screens/Events/index.js diff --git a/argon-pro-react-native/src/screens/Market/EventCalendar.js b/MeAgent/src/screens/Market/EventCalendar.js similarity index 100% rename from argon-pro-react-native/src/screens/Market/EventCalendar.js rename to MeAgent/src/screens/Market/EventCalendar.js diff --git a/argon-pro-react-native/src/screens/Market/MarketHot.js b/MeAgent/src/screens/Market/MarketHot.js similarity index 96% rename from argon-pro-react-native/src/screens/Market/MarketHot.js rename to MeAgent/src/screens/Market/MarketHot.js index c6fe2f30..ec925046 100644 --- a/argon-pro-react-native/src/screens/Market/MarketHot.js +++ b/MeAgent/src/screens/Market/MarketHot.js @@ -162,14 +162,23 @@ const MarketHot = ({ navigation }) => { {/* 标题栏 */} - - - 市场热点 - - - 涨停板块与个股分析 - - + + {/* 菜单按钮 - 移到左边 */} + navigation.openDrawer?.()} hitSlop={10}> + + + + + + + 市场热点 + + + + 涨停板块与个股分析 + + + navigation.navigate('TodayStats')} @@ -187,14 +196,6 @@ const MarketHot = ({ navigation }) => { > - navigation.openDrawer?.()} - p={2} - rounded="full" - bg="rgba(255,255,255,0.1)" - > - - diff --git a/argon-pro-react-native/src/screens/Market/SectorDetail.js b/MeAgent/src/screens/Market/SectorDetail.js similarity index 100% rename from argon-pro-react-native/src/screens/Market/SectorDetail.js rename to MeAgent/src/screens/Market/SectorDetail.js diff --git a/argon-pro-react-native/src/screens/Market/StockDetail.js b/MeAgent/src/screens/Market/StockDetail.js similarity index 100% rename from argon-pro-react-native/src/screens/Market/StockDetail.js rename to MeAgent/src/screens/Market/StockDetail.js diff --git a/argon-pro-react-native/src/screens/Market/TodayStats.js b/MeAgent/src/screens/Market/TodayStats.js similarity index 100% rename from argon-pro-react-native/src/screens/Market/TodayStats.js rename to MeAgent/src/screens/Market/TodayStats.js diff --git a/argon-pro-react-native/src/screens/Market/index.js b/MeAgent/src/screens/Market/index.js similarity index 100% rename from argon-pro-react-native/src/screens/Market/index.js rename to MeAgent/src/screens/Market/index.js diff --git a/MeAgent/src/screens/Profile/ProfileScreen.js b/MeAgent/src/screens/Profile/ProfileScreen.js new file mode 100644 index 00000000..b89e6f83 --- /dev/null +++ b/MeAgent/src/screens/Profile/ProfileScreen.js @@ -0,0 +1,457 @@ +/** + * 个人中心页面 + * 显示用户信息、自选概览、功能菜单 + */ + +import React, { useCallback } from 'react'; +import { StyleSheet, ScrollView, RefreshControl } from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, + Pressable, + Spinner, +} from 'native-base'; +import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useNavigation } from '@react-navigation/native'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +import { useAuth } from '../../contexts/AuthContext'; +import { useWatchlist } from '../../hooks/useWatchlist'; + +// 菜单配置 +const MENU_SECTIONS = [ + { + title: '我的服务', + items: [ + { + key: 'watchlist', + icon: 'star', + label: '我的自选', + desc: '股票和事件', + color: '#EC4899', + route: 'WatchlistDrawer', + }, + { + key: 'subscription', + icon: 'card', + label: '订阅管理', + desc: '会员服务', + color: '#F59E0B', + route: 'Subscription', + }, + { + key: 'messages', + icon: 'notifications', + label: '消息中心', + desc: '通知提醒', + color: '#3B82F6', + route: 'Messages', + }, + ], + }, + { + title: '更多功能', + items: [ + { + key: 'feedback', + icon: 'chatbubble-ellipses', + label: '意见反馈', + desc: '帮助改进', + color: '#8B5CF6', + route: 'Feedback', + }, + { + key: 'settings', + icon: 'settings', + label: '设置', + desc: '偏好设置', + color: '#6B7280', + route: 'Settings', + }, + { + key: 'about', + icon: 'information-circle', + label: '关于我们', + desc: '版本信息', + color: '#06B6D4', + route: 'About', + }, + ], + }, +]; + +/** + * 用户信息卡片 + */ +const UserCard = ({ user, subscription, isLoggedIn, onLogin, onLogout }) => { + if (!isLoggedIn) { + return ( + + + + + + + + + 登录/注册 + + + 登录后解锁更多功能 + + + + + + + ); + } + + const getSubscriptionInfo = () => { + if (!subscription || !subscription.is_active) { + return { label: '免费用户', color: '#6B7280', bgColor: 'rgba(107, 114, 128, 0.2)' }; + } + if (subscription.type === 'max') { + return { label: 'Max 会员', color: '#D4AF37', bgColor: 'rgba(212, 175, 55, 0.2)' }; + } + if (subscription.type === 'pro') { + return { label: 'Pro 会员', color: '#7C3AED', bgColor: 'rgba(124, 58, 237, 0.2)' }; + } + return { label: '会员', color: '#3B82F6', bgColor: 'rgba(59, 130, 246, 0.2)' }; + }; + + const subInfo = getSubscriptionInfo(); + + return ( + + + {/* 头像 */} + + + {(user?.nickname || user?.username || 'U').charAt(0).toUpperCase()} + + + + {/* 用户信息 */} + + + + {user?.nickname || user?.username || '用户'} + + + + {subInfo.label} + + + + {user?.email && ( + + {user.email} + + )} + + + {/* 退出按钮 */} + + + + + + + + ); +}; + +/** + * 自选概览卡片 + */ +const WatchlistOverview = ({ stocks = [], events = [], onPress }) => { + const stockCount = Array.isArray(stocks) ? stocks.length : 0; + const eventCount = Array.isArray(events) ? events.length : 0; + + return ( + + + + + + + + + + 我的自选 + + + + + + {stockCount} 只股票 + + + + + + {eventCount} 个事件 + + + + + + + + + + ); +}; + +/** + * 菜单项 + */ +const MenuItem = ({ item, onPress }) => { + return ( + onPress(item)}> + {({ pressed }) => ( + + + + + + + {item.label} + + + {item.desc} + + + + + )} + + ); +}; + +/** + * 菜单分组 + */ +const MenuSection = ({ section, onItemPress }) => { + return ( + + + {section.title} + + }> + {section.items.map((item) => ( + + ))} + + + ); +}; + +/** + * 个人中心主页面 + */ +const ProfileScreen = () => { + const navigation = useNavigation(); + const { user, isLoggedIn, isLoading, subscription, logout } = useAuth(); + const watchlistData = useWatchlist({ autoLoad: true }); + + // 确保 stocks 和 events 是数组,防止 undefined 导致渲染错误 + const stocks = watchlistData?.stocks || []; + const events = watchlistData?.events || []; + const refreshAll = watchlistData?.refreshAll; + + const [refreshing, setRefreshing] = React.useState(false); + + // 刷新数据 + const handleRefresh = useCallback(async () => { + setRefreshing(true); + if (refreshAll) { + await refreshAll(); + } + setRefreshing(false); + }, [refreshAll]); + + // 登录 + const handleLogin = useCallback(() => { + navigation.getParent()?.navigate('Login'); + }, [navigation]); + + // 退出登录 + const handleLogout = useCallback(async () => { + await logout(); + }, [logout]); + + // 跳转自选 + const handleWatchlistPress = useCallback(() => { + navigation.navigate('WatchlistDrawer'); + }, [navigation]); + + // 菜单项点击 + const handleMenuItemPress = useCallback((item) => { + if (item.route === 'WatchlistDrawer') { + navigation.navigate('WatchlistDrawer'); + } else if (item.route === 'Settings') { + navigation.navigate('SettingsDrawer'); + } else if (item.route === 'About') { + navigation.navigate('SettingsDrawer', { screen: 'About' }); + } else { + // 其他页面暂时显示提示 + console.log('Navigate to:', item.route); + } + }, [navigation]); + + if (isLoading) { + return ( + + + + ); + } + + return ( + + + {/* 背景渐变 */} + + + + } + > + {/* 标题 */} + + + 个人中心 + + + + {/* 用户卡片 */} + + + {/* 自选概览 */} + {isLoggedIn && ( + + )} + + {/* 菜单 */} + {MENU_SECTIONS.map((section) => ( + + ))} + + {/* 底部间距 */} + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + headerGradient: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: 200, + }, + avatar: { + width: 64, + height: 64, + borderRadius: 32, + alignItems: 'center', + justifyContent: 'center', + }, +}); + +export default ProfileScreen; diff --git a/MeAgent/src/screens/Profile/index.js b/MeAgent/src/screens/Profile/index.js new file mode 100644 index 00000000..cb916e9e --- /dev/null +++ b/MeAgent/src/screens/Profile/index.js @@ -0,0 +1,5 @@ +/** + * 个人中心模块导出 + */ + +export { default as ProfileScreen } from './ProfileScreen'; diff --git a/MeAgent/src/screens/StockDetail/StockDetailScreen.js b/MeAgent/src/screens/StockDetail/StockDetailScreen.js new file mode 100644 index 00000000..877ea748 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/StockDetailScreen.js @@ -0,0 +1,271 @@ +/** + * 股票详情页面 - Wind 风格 + * 显示股票实时行情、分时/K线图、相关事件/概念/公告 + */ + +import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import { StyleSheet, Dimensions } from 'react-native'; +import { Box, useToast, ScrollView } from 'native-base'; +import { useNavigation, useRoute } from '@react-navigation/native'; +import { useDispatch, useSelector } from 'react-redux'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +import { + PriceHeader, + ChartTypeTabs, + MinuteChart, + KlineChart, + OrderBook, + RelatedInfoTabs, + EventsPanel, + ConceptsPanel, + AnnouncementsPanel, + RiseAnalysisModal, +} from './components'; + +import { stockDetailService } from '../../services/stockService'; + +import { useWatchlist } from '../../hooks/useWatchlist'; +import { useSingleQuote } from '../../hooks/useRealtimeQuote'; +import { + fetchStockDetail, + fetchMinuteData, + fetchKlineData, + setChartType, + selectCurrentStock, + selectMinuteData, + selectMinutePrevClose, + selectKlineData, + selectChartType, + selectOrderBook, + selectStockLoading, +} from '../../store/slices/stockSlice'; + +const { height: SCREEN_HEIGHT } = Dimensions.get('window'); + +/** + * 股票详情页面 + */ +const StockDetailScreen = () => { + const navigation = useNavigation(); + const route = useRoute(); + const dispatch = useDispatch(); + const toast = useToast(); + + // 从路由获取股票信息 + const { stockCode, stockName, eventTime } = route.params || {}; + + // 本地状态 + const [infoTab, setInfoTab] = useState('events'); // events | concepts | announcements + const [riseAnalysisData, setRiseAnalysisData] = useState([]); + const [selectedAnalysis, setSelectedAnalysis] = useState(null); + const [analysisModalOpen, setAnalysisModalOpen] = useState(false); + + // Redux 状态 + const currentStock = useSelector(selectCurrentStock); + const minuteData = useSelector(selectMinuteData); + const minutePrevClose = useSelector(selectMinutePrevClose); + const klineData = useSelector(selectKlineData); + const chartType = useSelector(selectChartType); + const orderBook = useSelector(selectOrderBook); + const loading = useSelector(selectStockLoading); + + // WebSocket 实时行情 + const { quote: realtimeQuote } = useSingleQuote(stockCode); + + // 自选股操作 + const { isInWatchlist, toggleStock } = useWatchlist({ autoLoad: false }); + const inWatchlist = isInWatchlist(stockCode); + + // 合并行情数据 + const quote = useMemo(() => ({ + ...currentStock, + ...realtimeQuote, + }), [currentStock, realtimeQuote]); + + // 加载涨幅分析数据 + const loadRiseAnalysis = useCallback(async () => { + if (!stockCode) return; + + try { + const result = await stockDetailService.getRiseAnalysis(stockCode); + if (result.success && result.data) { + setRiseAnalysisData(result.data); + console.log('[StockDetailScreen] 涨幅分析数据:', result.data.length, '条'); + } + } catch (error) { + console.error('[StockDetailScreen] 加载涨幅分析失败:', error); + } + }, [stockCode]); + + // 加载股票数据 + const loadStockData = useCallback(async () => { + if (!stockCode) { + console.log('[StockDetailScreen] 无股票代码'); + return; + } + + console.log('[StockDetailScreen] 开始加载数据:', { stockCode, stockName, chartType }); + + // 加载股票详情 + dispatch(fetchStockDetail(stockCode)); + + // 根据当前图表类型加载数据 + if (chartType === 'minute') { + dispatch(fetchMinuteData(stockCode)); + } else { + dispatch(fetchKlineData({ stockCode, type: chartType, eventTime })); + } + + // 异步加载涨幅分析数据 + loadRiseAnalysis(); + }, [dispatch, stockCode, stockName, chartType, eventTime, loadRiseAnalysis]); + + // 初始加载 + useEffect(() => { + loadStockData(); + }, [loadStockData]); + + // 切换图表类型 + const handleChartTypeChange = useCallback((type) => { + dispatch(setChartType(type)); + + if (type === 'minute') { + dispatch(fetchMinuteData(stockCode)); + } else { + if (!klineData[type] || klineData[type].length === 0) { + dispatch(fetchKlineData({ stockCode, type, eventTime })); + } + } + }, [dispatch, stockCode, klineData, eventTime]); + + // 返回 + const handleBack = useCallback(() => { + navigation.goBack(); + }, [navigation]); + + // 切换自选 + const handleToggleWatchlist = useCallback(async () => { + const result = await toggleStock(stockCode, stockName); + if (result.success) { + toast.show({ + description: inWatchlist ? '已从自选中移除' : '已添加到自选', + placement: 'top', + duration: 2000, + }); + } + }, [toggleStock, stockCode, stockName, inWatchlist, toast]); + + // 打开涨幅分析弹窗 + const handleAnalysisPress = useCallback((analysis) => { + console.log('[StockDetailScreen] 点击涨幅分析:', analysis); + if (analysis) { + setSelectedAnalysis(analysis); + setAnalysisModalOpen(true); + } + }, []); + + // 关闭涨幅分析弹窗 + const handleCloseAnalysisModal = useCallback(() => { + setAnalysisModalOpen(false); + setSelectedAnalysis(null); + }, []); + + // 获取当前图表数据 + const currentChartData = useMemo(() => { + if (chartType === 'minute') { + return minuteData; + } + return klineData[chartType] || []; + }, [chartType, minuteData, klineData]); + + const isChartLoading = chartType === 'minute' ? loading.minute : loading.kline; + + // 渲染相关信息内容 + const renderInfoContent = () => { + switch (infoTab) { + case 'events': + return ; + case 'concepts': + return ; + case 'announcements': + return ; + default: + return null; + } + }; + + return ( + + + {/* 价格头部 - Wind 风格 */} + + + {/* 图表类型切换 */} + + + {/* 图表区域 */} + + {chartType === 'minute' ? ( + + ) : ( + + )} + + + {/* 5档盘口(可选,放在分时图右侧效果更好,这里简化) */} + {chartType === 'minute' && orderBook.bidPrices?.length > 0 && ( + + )} + + {/* 相关信息 Tab */} + + + {/* 相关信息内容 */} + + {renderInfoContent()} + + + {/* 涨幅分析弹窗 */} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); + +export default StockDetailScreen; diff --git a/MeAgent/src/screens/StockDetail/components/AnnouncementsPanel.js b/MeAgent/src/screens/StockDetail/components/AnnouncementsPanel.js new file mode 100644 index 00000000..f4e952d9 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/AnnouncementsPanel.js @@ -0,0 +1,186 @@ +/** + * 公司公告面板 + * 显示股票的公司公告列表 + */ + +import React, { memo, useEffect, useState, useCallback } from 'react'; +import { FlatList, Linking } from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Pressable, + Spinner, + Icon, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { companyService } from '../../../services/companyService'; + +// 格式化日期 +const formatDate = (dateStr) => { + if (!dateStr) return ''; + const date = new Date(dateStr); + return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; +}; + +// 公告类型颜色 +const getAnnouncementTypeColor = (type) => { + const typeColors = { + '定期报告': '#3B82F6', + '业绩预告': '#F59E0B', + '重大事项': '#EF4444', + '股权激励': '#8B5CF6', + '增减持': '#EC4899', + }; + return typeColors[type] || '#6B7280'; +}; + +/** + * 公告卡片 + */ +const AnnouncementCard = memo(({ announcement, onPress }) => { + return ( + onPress?.(announcement)}> + {({ pressed }) => ( + + + {/* 公告类型标签 */} + {announcement.type && ( + + + + {announcement.type} + + + + )} + + {/* 标题 */} + + {announcement.title} + + + {/* 底部信息 */} + + + + + {announcement.source || '公司公告'} + + + + + {formatDate(announcement.publish_date || announcement.date)} + + + + + )} + + ); +}); + +/** + * 公司公告面板 + */ +const AnnouncementsPanel = memo(({ stockCode }) => { + const [announcements, setAnnouncements] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 加载公告数据 + const loadAnnouncements = useCallback(async () => { + if (!stockCode) return; + + setLoading(true); + setError(null); + + const result = await companyService.getAnnouncements(stockCode, 30); + + if (result.success) { + setAnnouncements(result.data); + } else { + setError(result.error); + } + + setLoading(false); + }, [stockCode]); + + useEffect(() => { + loadAnnouncements(); + }, [loadAnnouncements]); + + // 点击公告 + const handleAnnouncementPress = useCallback((announcement) => { + if (announcement.url) { + Linking.openURL(announcement.url); + } + }, []); + + // 加载中 + if (loading) { + return ( + + + 加载公告... + + ); + } + + // 错误 + if (error) { + return ( + + + {error} + + 重试 + + + ); + } + + // 无数据 + if (announcements.length === 0) { + return ( + + + 暂无公司公告 + + ); + } + + return ( + String(item.id || index)} + renderItem={({ item }) => ( + + )} + contentContainerStyle={{ paddingVertical: 8 }} + showsVerticalScrollIndicator={false} + /> + ); +}); + +AnnouncementsPanel.displayName = 'AnnouncementsPanel'; + +export default AnnouncementsPanel; diff --git a/MeAgent/src/screens/StockDetail/components/ChartTypeTabs.js b/MeAgent/src/screens/StockDetail/components/ChartTypeTabs.js new file mode 100644 index 00000000..1d7c4ca4 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/ChartTypeTabs.js @@ -0,0 +1,67 @@ +/** + * 图表类型切换组件 + * 支持分时、日K、周K、月K切换 + */ + +import React, { memo } from 'react'; +import { Box, HStack, VStack, Text, Pressable } from 'native-base'; + +// 图表类型配置 +const CHART_TYPES = [ + { key: 'minute', label: '分时' }, + { key: 'daily', label: '日K' }, + { key: 'weekly', label: '周K' }, + { key: 'monthly', label: '月K' }, +]; + +/** + * 图表类型切换 + * @param {object} props + * @param {string} props.activeType - 当前选中的类型 + * @param {function} props.onChange - 切换回调 + */ +const ChartTypeTabs = memo(({ activeType, onChange }) => { + return ( + + + {CHART_TYPES.map((type) => { + const isActive = activeType === type.key; + return ( + onChange?.(type.key)} + flex={1} + > + + + {type.label} + + + + ); + })} + + + ); +}); + +ChartTypeTabs.displayName = 'ChartTypeTabs'; + +export default ChartTypeTabs; diff --git a/MeAgent/src/screens/StockDetail/components/ConceptsPanel.js b/MeAgent/src/screens/StockDetail/components/ConceptsPanel.js new file mode 100644 index 00000000..0ae224ac --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/ConceptsPanel.js @@ -0,0 +1,296 @@ +/** + * 相关概念面板 + * 显示股票相关的概念板块 + */ + +import React, { memo, useEffect, useState, useCallback } from 'react'; +import { FlatList } from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Pressable, + Spinner, + Icon, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import * as WebBrowser from 'expo-web-browser'; +import { companyService } from '../../../services/companyService'; + +// MD5 实现(与概念中心保持一致) +const md5 = (string) => { + function md5cycle(x, k) { + var a = x[0], b = x[1], c = x[2], d = x[3]; + a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586); + c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330); + a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426); + c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983); + a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417); + c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162); + a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101); + c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329); + a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632); + c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302); + a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083); + c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848); + a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690); + c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501); + a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784); + c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734); + a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463); + c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556); + a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353); + c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640); + a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222); + c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189); + a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835); + c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651); + a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415); + c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055); + a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606); + c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799); + a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744); + c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649); + a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379); + c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551); + x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]); + } + function cmn(q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); } + function ff(a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); } + function gg(a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); } + function hh(a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); } + function ii(a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); } + function md51(s) { + var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i; + for (i = 64; i <= s.length; i += 64) { md5cycle(state, md5blk(s.substring(i - 64, i))); } + s = s.substring(i - 64); + var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for (i = 0; i < s.length; i++) tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3); + tail[i >> 2] |= 0x80 << ((i % 4) << 3); + if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i++) tail[i] = 0; } + tail[14] = n * 8; md5cycle(state, tail); return state; + } + function md5blk(s) { + var md5blks = [], i; + for (i = 0; i < 64; i += 4) { md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); } + return md5blks; + } + var hex_chr = '0123456789abcdef'.split(''); + function rhex(n) { var s = '', j = 0; for (; j < 4; j++) s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]; return s; } + function hex(x) { for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]); return x.join(''); } + function add32(a, b) { return (a + b) & 0xffffffff; } + function utf8Encode(str) { + let utf8 = ''; + for (let i = 0; i < str.length; i++) { + let charCode = str.charCodeAt(i); + if (charCode < 128) { utf8 += String.fromCharCode(charCode); } + else if (charCode < 2048) { utf8 += String.fromCharCode((charCode >> 6) | 192); utf8 += String.fromCharCode((charCode & 63) | 128); } + else { utf8 += String.fromCharCode((charCode >> 12) | 224); utf8 += String.fromCharCode(((charCode >> 6) & 63) | 128); utf8 += String.fromCharCode((charCode & 63) | 128); } + } + return utf8; + } + return hex(md51(utf8Encode(string))); +}; + +// 概念详情页 URL +const getConceptUrl = (conceptName) => `https://valuefrontier.cn/htmls/concept/${md5(conceptName)}/`; + +// 涨跌颜色 +const getChangeColor = (change) => { + if (change > 0) return '#EF4444'; + if (change < 0) return '#22C55E'; + return '#94A3B8'; +}; + +// 格式化涨跌幅 +const formatChange = (change) => { + if (change === undefined || change === null) return '--'; + const sign = change > 0 ? '+' : ''; + return `${sign}${Number(change).toFixed(2)}%`; +}; + +/** + * 概念卡片 + */ +const ConceptCard = memo(({ concept, onPress }) => { + const changePercent = concept.price_info?.avg_change_pct || 0; + const changeColor = getChangeColor(changePercent); + + return ( + onPress?.(concept)}> + {({ pressed }) => ( + + + {/* 左侧: 概念信息 */} + + + {concept.concept} + + + {/* 层级信息 */} + {concept.hierarchy && ( + + {concept.hierarchy.lv1 && ( + + {concept.hierarchy.lv1} + + )} + {concept.hierarchy.lv2 && ( + <> + / + + {concept.hierarchy.lv2} + + + )} + + )} + + {/* 标签 */} + {concept.tags && concept.tags.length > 0 && ( + + {concept.tags.slice(0, 3).map((tag, i) => ( + + + {tag} + + + ))} + + )} + + {/* 成分股数量 */} + + + + {concept.stock_count || 0}只成分股 + + + + + {/* 右侧: 涨跌幅 */} + + + {formatChange(changePercent)} + + + {concept.price_info?.trade_date || ''} + + + + + )} + + ); +}); + +/** + * 相关概念面板 + */ +const ConceptsPanel = memo(({ stockCode }) => { + const [concepts, setConcepts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 加载概念数据 + const loadConcepts = useCallback(async () => { + if (!stockCode) return; + + setLoading(true); + setError(null); + + const result = await companyService.getRelatedConcepts(stockCode); + + if (result.success) { + setConcepts(result.data); + } else { + setError(result.error); + } + + setLoading(false); + }, [stockCode]); + + useEffect(() => { + loadConcepts(); + }, [loadConcepts]); + + // 点击概念 - 打开概念详情页 + const handleConceptPress = useCallback(async (concept) => { + const conceptName = concept.concept; + if (!conceptName) return; + + const url = getConceptUrl(conceptName); + try { + await WebBrowser.openBrowserAsync(url, { + toolbarColor: '#0A0A0F', + controlsColor: '#3B82F6', + }); + } catch (error) { + console.error('打开概念详情失败:', error); + } + }, []); + + // 加载中 + if (loading) { + return ( + + + 加载概念... + + ); + } + + // 错误 + if (error) { + return ( + + + {error} + + 重试 + + + ); + } + + // 无数据 + if (concepts.length === 0) { + return ( + + + 暂无相关概念 + + ); + } + + return ( + String(item.concept_id || item.concept)} + renderItem={({ item }) => ( + + )} + contentContainerStyle={{ paddingVertical: 8 }} + showsVerticalScrollIndicator={false} + /> + ); +}); + +ConceptsPanel.displayName = 'ConceptsPanel'; + +export default ConceptsPanel; diff --git a/MeAgent/src/screens/StockDetail/components/EventsPanel.js b/MeAgent/src/screens/StockDetail/components/EventsPanel.js new file mode 100644 index 00000000..73c0e5d5 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/EventsPanel.js @@ -0,0 +1,204 @@ +/** + * 相关事件面板 + * 显示股票相关的事件列表 + */ + +import React, { memo, useEffect, useState, useCallback } from 'react'; +import { FlatList } from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Pressable, + Spinner, + Icon, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { useNavigation } from '@react-navigation/native'; +import { companyService } from '../../../services/companyService'; + +// 格式化日期 +const formatDate = (dateStr) => { + if (!dateStr) return ''; + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now - date; + const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); + const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); + + if (diffHours < 1) return '刚刚'; + if (diffHours < 24) return `${diffHours}小时前`; + if (diffDays < 7) return `${diffDays}天前`; + + return `${date.getMonth() + 1}/${date.getDate()}`; +}; + +// 重要性颜色 +const getImportanceColor = (importance) => { + if (importance >= 4) return '#EF4444'; + if (importance >= 3) return '#F59E0B'; + return '#6B7280'; +}; + +/** + * 事件卡片 + */ +const EventCard = memo(({ event, onPress }) => { + return ( + onPress?.(event)}> + {({ pressed }) => ( + + + {/* 标题 */} + + {event.title || event.event_title} + + + {/* 摘要 */} + {event.summary && ( + + {event.summary} + + )} + + {/* 底部信息 */} + + + {/* 重要性标签 */} + {event.importance && ( + + + {event.importance >= 4 ? '重要' : event.importance >= 3 ? '关注' : '一般'} + + + )} + + {/* 相关股票数 */} + {event.related_stocks_count > 0 && ( + + + + {event.related_stocks_count}只股票 + + + )} + + + {/* 时间 */} + + {formatDate(event.event_time || event.created_at)} + + + + + )} + + ); +}); + +/** + * 相关事件面板 + */ +const EventsPanel = memo(({ stockName, stockCode }) => { + const navigation = useNavigation(); + const [events, setEvents] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // 加载事件数据 + const loadEvents = useCallback(async () => { + if (!stockName) return; + + setLoading(true); + setError(null); + + const result = await companyService.getRelatedEvents(stockName, 1, 20); + + if (result.success) { + setEvents(result.data); + } else { + setError(result.error); + } + + setLoading(false); + }, [stockName]); + + useEffect(() => { + loadEvents(); + }, [loadEvents]); + + // 点击事件 + const handleEventPress = useCallback((event) => { + navigation.navigate('EventDetail', { + eventId: event.id || event.event_id, + eventTitle: event.title || event.event_title, + }); + }, [navigation]); + + // 加载中 + if (loading) { + return ( + + + 加载事件... + + ); + } + + // 错误 + if (error) { + return ( + + + {error} + + 重试 + + + ); + } + + // 无数据 + if (events.length === 0) { + return ( + + + 暂无相关事件 + + ); + } + + return ( + String(item.id || item.event_id)} + renderItem={({ item }) => ( + + )} + contentContainerStyle={{ paddingVertical: 8 }} + showsVerticalScrollIndicator={false} + /> + ); +}); + +EventsPanel.displayName = 'EventsPanel'; + +export default EventsPanel; diff --git a/MeAgent/src/screens/StockDetail/components/KlineChart.js b/MeAgent/src/screens/StockDetail/components/KlineChart.js new file mode 100644 index 00000000..cab3899c --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/KlineChart.js @@ -0,0 +1,791 @@ +/** + * K线图组件 - Wind 风格 + * 显示日K/周K/月K蜡烛图,带成交量柱和触摸交互 + * 支持涨幅分析标记显示 + */ + +import React, { memo, useMemo, useState, useCallback } from 'react'; +import { Dimensions, PanResponder } from 'react-native'; +import { Box, Text, HStack, VStack, Spinner, Pressable, Icon } from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import Svg, { + Rect, + Line, + Text as SvgText, + G, + Path, + Defs, + LinearGradient as SvgLinearGradient, + Stop, + Circle, +} from 'react-native-svg'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const CHART_WIDTH = SCREEN_WIDTH - 32; +const PRICE_CHART_HEIGHT = 180; +const VOLUME_CHART_HEIGHT = 50; +const CHART_HEIGHT = PRICE_CHART_HEIGHT + VOLUME_CHART_HEIGHT + 20; +const PADDING = { top: 25, right: 55, bottom: 25, left: 10 }; +const CANDLE_GAP = 1.5; +const MAX_CANDLES = 60; + +// K线类型标签 +const TYPE_LABELS = { + daily: '日K', + weekly: '周K', + monthly: '月K', +}; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化成交量 +const formatVolume = (volume) => { + if (!volume) return '--'; + if (volume >= 100000000) { + return `${(volume / 100000000).toFixed(2)}亿`; + } + if (volume >= 10000) { + return `${(volume / 10000).toFixed(0)}万`; + } + return String(volume); +}; + +// 格式化成交额 +const formatAmount = (amount) => { + if (!amount) return '--'; + if (amount >= 100000000) { + return `${(amount / 100000000).toFixed(2)}亿`; + } + if (amount >= 10000) { + return `${(amount / 10000).toFixed(0)}万`; + } + return String(Math.round(amount)); +}; + +// 格式化日期 +const formatDate = (dateStr) => { + if (!dateStr) return ''; + const cleaned = dateStr.replace(/-/g, ''); + if (cleaned.length === 8) { + return `${cleaned.substring(4, 6)}/${cleaned.substring(6, 8)}`; + } + return dateStr.substring(5, 10).replace('-', '/'); +}; + +// 格式化完整日期 +const formatFullDate = (dateStr) => { + if (!dateStr) return ''; + const cleaned = dateStr.replace(/-/g, ''); + if (cleaned.length === 8) { + return `${cleaned.substring(0, 4)}-${cleaned.substring(4, 6)}-${cleaned.substring(6, 8)}`; + } + return dateStr; +}; + +// 计算涨跌幅 +const calcChangePercent = (close, preClose) => { + if (!preClose || preClose === 0) return 0; + return ((close - preClose) / preClose) * 100; +}; + +// 标准化日期格式用于比较 +const normalizeDate = (dateStr) => { + if (!dateStr) return ''; + return dateStr.replace(/-/g, '').substring(0, 8); +}; + +/** + * K线图 + * @param {Array} data - K线数据 + * @param {string} type - K线类型: daily | weekly | monthly + * @param {boolean} loading - 加载状态 + * @param {Array} riseAnalysisData - 涨幅分析数据 + * @param {function} onAnalysisPress - 点击分析标记回调 + */ +const KlineChart = memo(({ data = [], type = 'daily', loading, riseAnalysisData = [], onAnalysisPress }) => { + const [selectedIndex, setSelectedIndex] = useState(null); + + // 构建日期到分析数据的映射 + const analysisMap = useMemo(() => { + const map = {}; + if (riseAnalysisData && riseAnalysisData.length > 0) { + riseAnalysisData.forEach(item => { + const dateKey = normalizeDate(item.trade_date); + if (dateKey) { + map[dateKey] = item; + } + }); + } + return map; + }, [riseAnalysisData]); + + // 计算图表数据 + const chartData = useMemo(() => { + if (!data || data.length === 0) { + return null; + } + + // 只取最后 MAX_CANDLES 根K线 + const displayData = data.slice(-MAX_CANDLES); + + // 计算价格范围 + let minPrice = Infinity; + let maxPrice = -Infinity; + let maxVolume = 0; + + displayData.forEach(d => { + const high = d.high || d.high_price || 0; + const low = d.low || d.low_price || 0; + const vol = d.volume || d.vol || 0; + + if (high > maxPrice) maxPrice = high; + if (low < minPrice) minPrice = low; + if (vol > maxVolume) maxVolume = vol; + }); + + // 增加上下边距 + const pricePadding = (maxPrice - minPrice) * 0.08; + minPrice = minPrice - pricePadding; + maxPrice = maxPrice + pricePadding; + + const priceRange = maxPrice - minPrice || 1; + const volumeRange = maxVolume || 1; + + // 图表绘制区域 + const drawWidth = CHART_WIDTH - PADDING.left - PADDING.right; + const candleWidth = Math.max(3, (drawWidth - (displayData.length - 1) * CANDLE_GAP) / displayData.length); + const priceDrawHeight = PRICE_CHART_HEIGHT - PADDING.top; + const volumeDrawHeight = VOLUME_CHART_HEIGHT - 5; + + // 计算每根K线的位置和尺寸 + const candles = displayData.map((d, i) => { + const open = d.open || d.open_price || 0; + const close = d.close || d.close_price || 0; + const high = d.high || d.high_price || 0; + const low = d.low || d.low_price || 0; + const volume = d.volume || d.vol || 0; + const amount = d.amount || d.turnover || 0; + const preClose = d.pre_close || (i > 0 ? displayData[i - 1].close || displayData[i - 1].close_price : open); + + const isUp = close >= open; + const x = PADDING.left + i * (candleWidth + CANDLE_GAP); + + // 价格图 + const bodyTop = PADDING.top + (1 - (Math.max(open, close) - minPrice) / priceRange) * priceDrawHeight; + const bodyBottom = PADDING.top + (1 - (Math.min(open, close) - minPrice) / priceRange) * priceDrawHeight; + const bodyHeight = Math.max(bodyBottom - bodyTop, 1); + + const highY = PADDING.top + (1 - (high - minPrice) / priceRange) * priceDrawHeight; + const lowY = PADDING.top + (1 - (low - minPrice) / priceRange) * priceDrawHeight; + + // 成交量图 + const volumeTop = PRICE_CHART_HEIGHT + 10; + const volumeHeight = Math.max((volume / volumeRange) * volumeDrawHeight, 1); + const volumeY = volumeTop + volumeDrawHeight - volumeHeight; + + // 检查是否有涨幅分析数据 + const dateKey = normalizeDate(d.date || d.trade_date); + const hasAnalysis = !!analysisMap[dateKey]; + const analysisData = analysisMap[dateKey] || null; + + return { + index: i, + x, + isUp, + bodyX: x, + bodyY: bodyTop, + bodyWidth: candleWidth, + bodyHeight, + wickX: x + candleWidth / 2, + highY, + lowY, + volumeX: x, + volumeY, + volumeWidth: candleWidth, + volumeHeight, + // 原始数据 + date: d.date || d.trade_date, + open, + close, + high, + low, + volume, + amount, + preClose, + changePercent: calcChangePercent(close, preClose), + // 涨幅分析 + hasAnalysis, + analysisData, + }; + }); + + // 计算5日/10日/20日均线 + const ma5 = [], ma10 = [], ma20 = []; + for (let i = 0; i < candles.length; i++) { + // MA5 + if (i >= 4) { + const sum5 = candles.slice(i - 4, i + 1).reduce((s, c) => s + c.close, 0); + const ma5Value = sum5 / 5; + const ma5Y = PADDING.top + (1 - (ma5Value - minPrice) / priceRange) * priceDrawHeight; + ma5.push({ x: candles[i].wickX, y: ma5Y, value: ma5Value }); + } + // MA10 + if (i >= 9) { + const sum10 = candles.slice(i - 9, i + 1).reduce((s, c) => s + c.close, 0); + const ma10Value = sum10 / 10; + const ma10Y = PADDING.top + (1 - (ma10Value - minPrice) / priceRange) * priceDrawHeight; + ma10.push({ x: candles[i].wickX, y: ma10Y, value: ma10Value }); + } + // MA20 + if (i >= 19) { + const sum20 = candles.slice(i - 19, i + 1).reduce((s, c) => s + c.close, 0); + const ma20Value = sum20 / 20; + const ma20Y = PADDING.top + (1 - (ma20Value - minPrice) / priceRange) * priceDrawHeight; + ma20.push({ x: candles[i].wickX, y: ma20Y, value: ma20Value }); + } + } + + // 生成均线路径 + const generateLinePath = (points) => { + if (points.length === 0) return ''; + return points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' '); + }; + + // 日期轴标签(显示5个) + const dateInterval = Math.floor(displayData.length / 4); + const dateLabels = [0, dateInterval, dateInterval * 2, dateInterval * 3, displayData.length - 1] + .filter(i => i < displayData.length) + .map(i => ({ + x: PADDING.left + i * (candleWidth + CANDLE_GAP) + candleWidth / 2, + label: formatDate(displayData[i]?.date || displayData[i]?.trade_date), + })); + + // 价格网格线 + const priceStep = priceRange / 4; + const priceGrids = [ + { price: maxPrice, y: PADDING.top }, + { price: minPrice + priceStep * 3, y: PADDING.top + priceDrawHeight * 0.25 }, + { price: minPrice + priceStep * 2, y: PADDING.top + priceDrawHeight * 0.5 }, + { price: minPrice + priceStep, y: PADDING.top + priceDrawHeight * 0.75 }, + { price: minPrice, y: PADDING.top + priceDrawHeight }, + ]; + + return { + candles, + dateLabels, + priceGrids, + maxVolume, + priceDrawHeight, + candleWidth, + ma5Path: generateLinePath(ma5), + ma10Path: generateLinePath(ma10), + ma20Path: generateLinePath(ma20), + ma5, + ma10, + ma20, + }; + }, [data, analysisMap]); + + // 创建触摸响应器 + const panResponder = useMemo(() => { + if (!chartData) return null; + + let startX = 0; + let startY = 0; + let startTime = 0; + + return PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onMoveShouldSetPanResponder: () => true, + onPanResponderGrant: (evt) => { + const { locationX, locationY } = evt.nativeEvent; + startX = locationX; + startY = locationY; + startTime = Date.now(); + updateSelectedCandle(locationX, locationY, false); + }, + onPanResponderMove: (evt) => { + const { locationX, locationY } = evt.nativeEvent; + updateSelectedCandle(locationX, locationY, false); + }, + onPanResponderRelease: (evt) => { + const { locationX, locationY } = evt.nativeEvent; + const endTime = Date.now(); + const duration = endTime - startTime; + const distance = Math.sqrt(Math.pow(locationX - startX, 2) + Math.pow(locationY - startY, 2)); + + // 判断是否为点击(短时间、小移动距离) + const isTap = duration < 300 && distance < 15; + + if (isTap) { + // 检查是否点击了分析标记 + updateSelectedCandle(locationX, locationY, true); + } + + // 保持选中状态一段时间后清除 + setTimeout(() => setSelectedIndex(null), 2000); + }, + }); + }, [chartData, updateSelectedCandle]); + + // 更新选中的K线 + const updateSelectedCandle = useCallback((locationX, locationY, isTap = false) => { + if (!chartData) return; + + const { candles, candleWidth } = chartData; + const adjustedX = locationX; + + // 找到最近的K线 + let nearestIndex = -1; + let minDistance = Infinity; + + candles.forEach((candle, i) => { + const candleCenter = candle.x + candleWidth / 2; + const distance = Math.abs(adjustedX - candleCenter); + if (distance < minDistance) { + minDistance = distance; + nearestIndex = i; + } + }); + + if (nearestIndex >= 0 && minDistance < (candleWidth + CANDLE_GAP) * 2) { + const candle = candles[nearestIndex]; + + // 检查是否点击了涨幅分析标记(在顶部区域) + if (isTap && candle.hasAnalysis && locationY < PADDING.top + 20) { + // 触发分析弹窗 + onAnalysisPress?.(candle.analysisData); + return; + } + + setSelectedIndex(nearestIndex); + } + }, [chartData, onAnalysisPress]); + + // 获取选中的K线数据 + const selectedCandle = useMemo(() => { + if (selectedIndex === null || !chartData) return null; + return chartData.candles[selectedIndex]; + }, [selectedIndex, chartData]); + + // 加载状态 + if (loading) { + return ( + + + 加载K线数据... + + ); + } + + // 无数据状态 + if (!chartData) { + return ( + + 暂无K线数据 + + ); + } + + const { candles, dateLabels, priceGrids, maxVolume, ma5Path, ma10Path, ma20Path } = chartData; + + // 获取显示的均线值(选中的K线或最后一根) + const displayIndex = selectedIndex !== null ? selectedIndex : candles.length - 1; + const displayCandle = candles[displayIndex]; + const ma5Value = chartData.ma5[Math.max(0, displayIndex - 4)]?.value; + const ma10Value = chartData.ma10[Math.max(0, displayIndex - 9)]?.value; + const ma20Value = chartData.ma20[Math.max(0, displayIndex - 19)]?.value; + + return ( + + {/* 顶部信息栏 - Wind 风格 */} + + {/* 第一行:选中日期和OHLC */} + {selectedCandle ? ( + + + + {formatFullDate(selectedCandle.date)} + + + + + {formatPrice(selectedCandle.open)} + + + + {formatPrice(selectedCandle.high)} + + + + {formatPrice(selectedCandle.low)} + + + + + {formatPrice(selectedCandle.close)} + + + + + + + = 0 ? '#EF4444' : '#22C55E'} fontSize={11} fontWeight="bold"> + {selectedCandle.changePercent >= 0 ? '+' : ''}{selectedCandle.changePercent.toFixed(2)}% + + {/* 涨幅分析按钮 */} + {selectedCandle.hasAnalysis && ( + onAnalysisPress?.(selectedCandle.analysisData)} + hitSlop={5} + > + + + + 涨幅分析 + + + + )} + + + + + {formatVolume(selectedCandle.volume)} + + + + {formatAmount(selectedCandle.amount)} + + + + + ) : ( + + + {TYPE_LABELS[type] || 'K线'}走势 + + 触摸查看详情 + + )} + + + {/* 均线标签栏 */} + + + + MA5: + + {ma5Value ? formatPrice(ma5Value) : '--'} + + + + + MA10: + + {ma10Value ? formatPrice(ma10Value) : '--'} + + + + + MA20: + + {ma20Value ? formatPrice(ma20Value) : '--'} + + + + + {/* SVG 图表区域 */} + + + + + + + + + + + + + + {/* 价格区域网格线 */} + {priceGrids.map((item, i) => ( + + + + {formatPrice(item.price)} + + + ))} + + {/* 成交量分隔线 */} + + + {/* 均线 */} + {ma20Path && ( + + )} + {ma10Path && ( + + )} + {ma5Path && ( + + )} + + {/* K线蜡烛 */} + {candles.map((candle, i) => ( + + {/* 上下影线 */} + + {/* 蜡烛体 */} + + {/* 成交量柱 */} + + + ))} + + {/* 涨幅分析标记 - 金色五角星 */} + {candles.filter(c => c.hasAnalysis).map((candle, i) => { + const starCenterX = candle.wickX; + const starCenterY = Math.max(PADDING.top - 2, candle.highY - 14); + const starSize = 7; + // 五角星路径(相对于中心点) + const starPath = ` + M ${starCenterX} ${starCenterY - starSize} + L ${starCenterX + starSize * 0.22} ${starCenterY - starSize * 0.31} + L ${starCenterX + starSize * 0.95} ${starCenterY - starSize * 0.31} + L ${starCenterX + starSize * 0.36} ${starCenterY + starSize * 0.12} + L ${starCenterX + starSize * 0.59} ${starCenterY + starSize * 0.81} + L ${starCenterX} ${starCenterY + starSize * 0.38} + L ${starCenterX - starSize * 0.59} ${starCenterY + starSize * 0.81} + L ${starCenterX - starSize * 0.36} ${starCenterY + starSize * 0.12} + L ${starCenterX - starSize * 0.95} ${starCenterY - starSize * 0.31} + L ${starCenterX - starSize * 0.22} ${starCenterY - starSize * 0.31} + Z + `; + return ( + + {/* 连接线 */} + + {/* 星星光晕 */} + + {/* 金色五角星 */} + + + ); + })} + + {/* 选中的十字线 */} + {selectedCandle && ( + + {/* 垂直线 */} + + {/* 水平线 */} + + {/* 价格标签 */} + + + {formatPrice(selectedCandle.close)} + + {/* 成交量区域垂直线 */} + + + )} + + {/* 成交量标签 */} + + {formatVolume(maxVolume)} + + + {/* 日期轴标签 */} + {dateLabels.map((item, i) => ( + + {item.label} + + ))} + + + + {/* 底部图例 */} + + + + 阳线(涨) + + + + 阴线(跌) + + + + ); +}); + +KlineChart.displayName = 'KlineChart'; + +export default KlineChart; diff --git a/MeAgent/src/screens/StockDetail/components/MinuteChart.js b/MeAgent/src/screens/StockDetail/components/MinuteChart.js new file mode 100644 index 00000000..11e6adea --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/MinuteChart.js @@ -0,0 +1,545 @@ +/** + * 分时图组件 - Wind 风格 (使用 react-native-svg) + * 功能: + * - 分时线 + 均价线 + * - 触控十字线交互,显示选中点的详细数据 + * - 成交量柱状图 + * - 昨收参考线 + */ + +import React, { memo, useMemo, useState, useCallback, useRef } from 'react'; +import { Dimensions, StyleSheet } from 'react-native'; +import { Box, Text, HStack, VStack, Spinner, Pressable } from 'native-base'; +import Svg, { + Path, + Line, + Rect, + Circle, + Text as SvgText, + G, + Defs, + LinearGradient, + Stop, +} from 'react-native-svg'; +import { + GestureHandlerRootView, + PanGestureHandler, +} from 'react-native-gesture-handler'; + +const { width: SCREEN_WIDTH } = Dimensions.get('window'); +const CHART_WIDTH = SCREEN_WIDTH - 32; +const CHART_HEIGHT = 180; +const VOLUME_HEIGHT = 50; +const PADDING = { top: 15, right: 55, bottom: 5, left: 10 }; + +// 格式化时间 +const formatTime = (time) => { + if (!time) return ''; + if (typeof time === 'string') { + return time.substring(0, 5); + } + return ''; +}; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null || isNaN(price)) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化涨跌幅 +const formatPercent = (current, preClose) => { + if (!current || !preClose) return '0.00%'; + const change = ((current - preClose) / preClose) * 100; + const sign = change >= 0 ? '+' : ''; + return `${sign}${change.toFixed(2)}%`; +}; + +// 格式化成交量 +const formatVolume = (vol) => { + if (!vol) return '0'; + if (vol >= 10000) { + return `${(vol / 10000).toFixed(0)}万`; + } + return String(Math.round(vol)); +}; + +/** + * 生成折线路径 + */ +const generateLinePath = (points) => { + if (!points || points.length === 0) return ''; + + let path = `M ${points[0].x} ${points[0].y}`; + for (let i = 1; i < points.length; i++) { + path += ` L ${points[i].x} ${points[i].y}`; + } + return path; +}; + +/** + * 生成填充区域路径(用于渐变填充) + */ +const generateAreaPath = (points, bottomY) => { + if (!points || points.length === 0) return ''; + + let path = `M ${points[0].x} ${bottomY}`; + path += ` L ${points[0].x} ${points[0].y}`; + + for (let i = 1; i < points.length; i++) { + path += ` L ${points[i].x} ${points[i].y}`; + } + + path += ` L ${points[points.length - 1].x} ${bottomY}`; + path += ' Z'; + return path; +}; + +/** + * 分时图主组件 - Wind 风格 + */ +const MinuteChart = memo(({ data = [], preClose, loading }) => { + const [activeIndex, setActiveIndex] = useState(null); + const svgRef = useRef(null); + + // 处理图表数据 + const chartData = useMemo(() => { + if (!data || data.length === 0) { + return null; + } + + const prices = data.map(d => d.price || d.current_price || d.close || 0).filter(p => p > 0); + const avgPrices = data.map(d => d.avg_price || d.average_price || 0); + + if (prices.length === 0) { + return null; + } + + // 使用传入的昨收价,或从数据中获取,或使用第一个价格作为基准 + const effectivePreClose = preClose || + data[0]?.prev_close || + data[0]?.pre_close || + prices[0]; + + // 计算价格范围(以昨收为中心对称) + const validPrices = prices.filter(p => p > 0); + const validAvgPrices = avgPrices.filter(p => p > 0); + const allPrices = [...validPrices, ...validAvgPrices, effectivePreClose].filter(p => p > 0); + + const maxDiff = Math.max( + Math.abs(Math.max(...allPrices) - effectivePreClose), + Math.abs(effectivePreClose - Math.min(...allPrices)), + effectivePreClose * 0.02 // 至少 2% 的波动范围 + ) * 1.1; + + const minPrice = effectivePreClose - maxDiff; + const maxPrice = effectivePreClose + maxDiff; + const priceRange = maxPrice - minPrice; + const lastPrice = prices[prices.length - 1]; + const isUp = lastPrice >= effectivePreClose; + + // 图表绘制区域 + const drawWidth = CHART_WIDTH - PADDING.left - PADDING.right; + const drawHeight = CHART_HEIGHT - PADDING.top - PADDING.bottom; + + // 坐标转换函数 + const xScale = (index) => PADDING.left + (index / (data.length - 1 || 1)) * drawWidth; + const yScale = (price) => PADDING.top + ((maxPrice - price) / priceRange) * drawHeight; + + // 分时线点位 + const pricePoints = data.map((d, i) => { + const price = d.price || d.current_price || d.close || effectivePreClose; + return { + x: xScale(i), + y: yScale(price), + price, + time: d.time, + volume: d.volume, + avgPrice: d.avg_price || d.average_price, + }; + }); + + // 均价线点位 + const avgPoints = data + .map((d, i) => { + const avgPrice = d.avg_price || d.average_price; + if (!avgPrice || avgPrice <= 0) return null; + return { + x: xScale(i), + y: yScale(avgPrice), + price: avgPrice, + }; + }) + .filter(p => p !== null); + + // 成交量数据 + const volumes = data.map(d => d.volume || 0); + const maxVolume = Math.max(...volumes, 1); + + return { + pricePoints, + avgPoints, + volumes, + maxVolume, + minPrice, + maxPrice, + preClose: effectivePreClose, + lastPrice, + isUp, + drawWidth, + drawHeight, + yScale, + xScale, + priceRange, + }; + }, [data, preClose]); + + // 处理触控 + const handleTouch = useCallback((event) => { + if (!chartData || !data || data.length === 0) return; + + const { locationX } = event.nativeEvent; + const drawWidth = CHART_WIDTH - PADDING.left - PADDING.right; + + // 计算最近的数据点索引 + const relativeX = locationX - PADDING.left; + const index = Math.round((relativeX / drawWidth) * (data.length - 1)); + const clampedIndex = Math.max(0, Math.min(data.length - 1, index)); + + setActiveIndex(clampedIndex); + }, [chartData, data]); + + // 处理触控结束 + const handleTouchEnd = useCallback(() => { + setActiveIndex(null); + }, []); + + // 加载状态 + if (loading) { + return ( + + + 加载分时数据... + + ); + } + + // 无数据状态 + if (!chartData || chartData.pricePoints.length === 0) { + return ( + + 暂无分时数据 + + ); + } + + const lineColor = chartData.isUp ? '#EF4444' : '#22C55E'; + const areaGradientId = chartData.isUp ? 'areaGradientUp' : 'areaGradientDown'; + const activePoint = activeIndex !== null ? chartData.pricePoints[activeIndex] : null; + + // 计算昨收参考线 Y 坐标 + const preCloseY = chartData.yScale(chartData.preClose); + + return ( + + {/* 图表标题 */} + + 分时走势 + + + + 分时 + + + + 均价 + + + + + {/* 触控时显示的数据 */} + {activePoint && ( + + + + 时间 + {formatTime(activePoint.time) || '--'} + + + 价格 + {formatPrice(activePoint.price)} + + + 涨跌 + + {formatPercent(activePoint.price, chartData.preClose)} + + + {activePoint.avgPrice && ( + + 均价 + {formatPrice(activePoint.avgPrice)} + + )} + + 成交量 + {formatVolume(activePoint.volume)} + + + + )} + + {/* 主图表区域 */} + + + {/* 渐变定义 */} + + + + + + + + + + + + {/* 背景网格线 */} + + {/* 横向网格线(价格)- 5条 */} + {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => { + const y = PADDING.top + ratio * chartData.drawHeight; + return ( + + ); + })} + + + {/* 昨收参考虚线 */} + + + {/* 分时线填充区域 */} + + + {/* 分时线 */} + + + {/* 均价线 */} + {chartData.avgPoints.length > 1 && ( + + )} + + {/* 右侧价格和涨跌幅标签 - 分两行显示 */} + + {/* 最高价区域 */} + + {formatPrice(chartData.maxPrice)} + + + {formatPercent(chartData.maxPrice, chartData.preClose)} + + + {/* 昨收价区域 */} + + {formatPrice(chartData.preClose)} + + + 0.00% + + + {/* 最低价区域 */} + + {formatPrice(chartData.minPrice)} + + + {formatPercent(chartData.minPrice, chartData.preClose)} + + + + {/* 十字线和选中点 */} + {activeIndex !== null && activePoint && ( + + {/* 垂直线 */} + + {/* 水平线 */} + + {/* 选中点圆圈 */} + + + )} + + + + {/* 时间轴 */} + + {['09:30', '10:30', '11:30/13:00', '14:00', '15:00'].map((t, i) => ( + {t} + ))} + + + {/* 成交量图 */} + + + {data.map((item, i) => { + const volume = item.volume || 0; + const barHeight = (volume / chartData.maxVolume) * (VOLUME_HEIGHT - 10); + const price = item.price || item.close || 0; + const prevPrice = i > 0 ? (data[i - 1].price || data[i - 1].close || 0) : chartData.preClose; + const isUp = price >= prevPrice; + const isActive = activeIndex === i; + + const x = PADDING.left + (i / (data.length - 1 || 1)) * chartData.drawWidth; + const barWidth = Math.max(1, chartData.drawWidth / data.length - 1); + + return ( + + ); + })} + {/* 最大成交量标注 */} + + {formatVolume(chartData.maxVolume)} + + + + + ); +}); + +MinuteChart.displayName = 'MinuteChart'; + +export default MinuteChart; diff --git a/MeAgent/src/screens/StockDetail/components/OrderBook.js b/MeAgent/src/screens/StockDetail/components/OrderBook.js new file mode 100644 index 00000000..d7ac3eb1 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/OrderBook.js @@ -0,0 +1,238 @@ +/** + * 5档盘口组件 - 优化版 + * 显示买卖5档挂单信息 + */ + +import React, { memo, useMemo } from 'react'; +import { Box, HStack, VStack, Text, Icon } from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null || price === 0) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化成交量(手) +const formatVolume = (volume) => { + if (!volume) return '--'; + // 假设volume单位是股,转换为手(100股=1手) + const lots = Math.round(volume / 100); + if (lots >= 10000) { + return `${(lots / 10000).toFixed(1)}万`; + } + return String(lots); +}; + +/** + * 单行盘口 + */ +const OrderRow = memo(({ label, price, volume, maxVolume, type, preClose }) => { + // 计算涨跌颜色 + const getPriceColor = () => { + if (!price || !preClose) return 'white'; + if (price > preClose) return '#EF4444'; + if (price < preClose) return '#22C55E'; + return 'white'; + }; + + // 计算成交量条宽度 + const barWidth = maxVolume > 0 ? (volume / maxVolume) * 100 : 0; + const barColor = type === 'ask' ? 'rgba(34, 197, 94, 0.2)' : 'rgba(239, 68, 68, 0.2)'; + + return ( + + {/* 成交量背景条 */} + + + {/* 档位标签 */} + + {label} + + + {/* 价格 */} + + {formatPrice(price)} + + + {/* 成交量 */} + + {formatVolume(volume)} + + + ); +}); + +/** + * 5档盘口 + * @param {object} props + * @param {array} props.askPrices - 卖盘价格 [卖1, 卖2, 卖3, 卖4, 卖5] + * @param {array} props.askVolumes - 卖盘数量 + * @param {array} props.bidPrices - 买盘价格 [买1, 买2, 买3, 买4, 买5] + * @param {array} props.bidVolumes - 买盘数量 + * @param {number} props.preClose - 昨收价 + */ +const OrderBook = memo(({ + askPrices = [], + askVolumes = [], + bidPrices = [], + bidVolumes = [], + preClose, +}) => { + // 计算最大成交量(用于背景条宽度) + const maxVolume = useMemo(() => { + const allVolumes = [...askVolumes, ...bidVolumes].filter(v => v > 0); + return allVolumes.length > 0 ? Math.max(...allVolumes) : 0; + }, [askVolumes, bidVolumes]); + + // 准备卖盘数据(卖5到卖1,从上到下) + const askRows = useMemo(() => { + const rows = []; + for (let i = 4; i >= 0; i--) { + rows.push({ + label: `卖${i + 1}`, + price: askPrices[i], + volume: askVolumes[i], + }); + } + return rows; + }, [askPrices, askVolumes]); + + // 准备买盘数据(买1到买5,从上到下) + const bidRows = useMemo(() => { + const rows = []; + for (let i = 0; i < 5; i++) { + rows.push({ + label: `买${i + 1}`, + price: bidPrices[i], + volume: bidVolumes[i], + }); + } + return rows; + }, [bidPrices, bidVolumes]); + + // 检查是否有数据 + const hasData = askPrices.some(p => p > 0) || bidPrices.some(p => p > 0); + + if (!hasData) { + return ( + + + 暂无盘口数据 + + + ); + } + + return ( + + {/* 标题栏 */} + + + + + 五档盘口 + + + + 单位: 手 + + + + + + {/* 表头 */} + + 档位 + 价格 + 数量 + + + {/* 卖盘 */} + + {askRows.map((row, i) => ( + + ))} + + + {/* 分隔线 */} + + + {/* 买盘 */} + + {bidRows.map((row, i) => ( + + ))} + + + + ); +}); + +OrderBook.displayName = 'OrderBook'; + +export default OrderBook; diff --git a/MeAgent/src/screens/StockDetail/components/PriceHeader.js b/MeAgent/src/screens/StockDetail/components/PriceHeader.js new file mode 100644 index 00000000..4567b5ba --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/PriceHeader.js @@ -0,0 +1,214 @@ +/** + * 股票价格头部组件 - Wind 风格 + * 显示实时价格、涨跌幅、成交量等关键信息 + */ + +import React, { memo } from 'react'; +import { + Box, + HStack, + VStack, + Text, + Icon, + Pressable, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; + +// 涨跌颜色 +const getChangeColor = (change) => { + if (change > 0) return '#EF4444'; // 红色 + if (change < 0) return '#22C55E'; // 绿色 + return '#94A3B8'; // 灰色 +}; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化涨跌幅 +const formatChange = (change) => { + if (change === undefined || change === null) return '--'; + const sign = change > 0 ? '+' : ''; + return `${sign}${Number(change).toFixed(2)}%`; +}; + +// 格式化涨跌额 +const formatChangeAmount = (amount) => { + if (amount === undefined || amount === null) return '--'; + const sign = amount > 0 ? '+' : ''; + return `${sign}${Number(amount).toFixed(2)}`; +}; + +// 格式化成交量 +const formatVolume = (volume) => { + if (!volume) return '--'; + if (volume >= 100000000) { + return `${(volume / 100000000).toFixed(2)}亿`; + } + if (volume >= 10000) { + return `${(volume / 10000).toFixed(0)}万`; + } + return String(volume); +}; + +// 格式化成交额 +const formatAmount = (amount) => { + if (!amount) return '--'; + if (amount >= 100000000) { + return `${(amount / 100000000).toFixed(2)}亿`; + } + if (amount >= 10000) { + return `${(amount / 10000).toFixed(0)}万`; + } + return String(amount); +}; + +// 格式化市值 +const formatMarketCap = (value) => { + if (!value) return '--'; + if (value >= 100000000) { + return `${(value / 100000000).toFixed(0)}亿`; + } + return formatAmount(value); +}; + +/** + * 指标项组件 + */ +const MetricItem = memo(({ label, value, color = 'white' }) => ( + + + {value} + + + {label} + + +)); + +/** + * 价格头部组件 - Wind 风格 + */ +const PriceHeader = memo(({ + stock, + quote, + isInWatchlist, + onToggleWatchlist, + onBack, +}) => { + const { + stock_code, + stock_name, + } = stock || {}; + + const { + current_price, + price, + change_percent, + change_amount, + volume, + amount, + open, + high, + low, + pre_close, + market_cap, + pe_ratio, + turnover_rate, + } = quote || {}; + + // 使用 current_price 或 price + const displayPrice = current_price || price; + const changeColor = getChangeColor(change_percent); + + return ( + + {/* 顶部导航栏 */} + + + + + + + + {stock_name || '--'} + + + {stock_code || '--'} + + + + + + + + + + + + + + {/* 价格信息区域 - Wind 风格 */} + + {/* 左侧: 主价格 */} + + + {formatPrice(displayPrice)} + + + + {formatChangeAmount(change_amount)} + + + {formatChange(change_percent)} + + + + + {/* 右侧: 指标网格 */} + + + + + + + + + + + pre_close ? '#EF4444' : high < pre_close ? '#22C55E' : 'white'} + /> + pre_close ? '#EF4444' : low < pre_close ? '#22C55E' : 'white'} + /> + + + + + ); +}); + +PriceHeader.displayName = 'PriceHeader'; + +export default PriceHeader; diff --git a/MeAgent/src/screens/StockDetail/components/RelatedInfoTabs.js b/MeAgent/src/screens/StockDetail/components/RelatedInfoTabs.js new file mode 100644 index 00000000..d662d14e --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/RelatedInfoTabs.js @@ -0,0 +1,59 @@ +/** + * 相关信息 Tab 组件 + * 显示相关事件、概念、公告的切换 Tab + */ + +import React, { memo } from 'react'; +import { Box, HStack, Text, Pressable } from 'native-base'; + +// Tab 配置 +const INFO_TABS = [ + { key: 'events', label: '相关事件' }, + { key: 'concepts', label: '相关概念' }, + { key: 'announcements', label: '公司公告' }, +]; + +/** + * 相关信息 Tab 切换 + */ +const RelatedInfoTabs = memo(({ activeTab, onChange }) => { + return ( + + {INFO_TABS.map((tab) => { + const isActive = activeTab === tab.key; + return ( + onChange?.(tab.key)} + flex={1} + > + + + {tab.label} + + + + ); + })} + + ); +}); + +RelatedInfoTabs.displayName = 'RelatedInfoTabs'; + +export default RelatedInfoTabs; diff --git a/MeAgent/src/screens/StockDetail/components/RiseAnalysisModal.js b/MeAgent/src/screens/StockDetail/components/RiseAnalysisModal.js new file mode 100644 index 00000000..9ada505a --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/RiseAnalysisModal.js @@ -0,0 +1,462 @@ +/** + * 涨幅分析弹窗组件 + * 显示股票涨幅超过5%时的异动原因分析 + */ + +import React, { memo, useCallback } from 'react'; +import { + StyleSheet, + ScrollView, + Modal, + TouchableOpacity, + View, + Dimensions, +} from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, +} from 'native-base'; +import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; +import { BlurView } from 'expo-blur'; +import { LinearGradient } from 'expo-linear-gradient'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +const { height: SCREEN_HEIGHT } = Dimensions.get('window'); + +// 格式化日期 +const formatDate = (dateStr) => { + if (!dateStr) return ''; + return dateStr.replace(/-/g, '/'); +}; + +// 格式化涨幅 +const formatRiseRate = (rate) => { + if (rate === undefined || rate === null) return '--'; + const num = Number(rate); + const sign = num > 0 ? '+' : ''; + return `${sign}${num.toFixed(2)}%`; +}; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化成交额 +const formatAmount = (amount) => { + if (!amount) return '--'; + if (amount >= 100000000) { + return `${(amount / 100000000).toFixed(2)}亿`; + } + if (amount >= 10000) { + return `${(amount / 10000).toFixed(0)}万`; + } + return String(Math.round(amount)); +}; + +/** + * 简单的Markdown渲染器 + * 支持标题、加粗、列表等基本格式 + */ +const SimpleMarkdown = memo(({ content }) => { + if (!content) return null; + + // 按行分割并处理 + const lines = content.split('\n'); + const elements = []; + + lines.forEach((line, index) => { + const trimmed = line.trim(); + if (!trimmed) { + elements.push(); + return; + } + + // 标题处理 + if (trimmed.startsWith('### ')) { + elements.push( + + {trimmed.substring(4)} + + ); + } else if (trimmed.startsWith('## ')) { + elements.push( + + {trimmed.substring(3)} + + ); + } else if (trimmed.startsWith('# ')) { + elements.push( + + {trimmed.substring(2)} + + ); + } + // 列表项处理 + else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { + elements.push( + + + + {renderInlineFormatting(trimmed.substring(2))} + + + ); + } + // 数字列表处理 + else if (/^\d+\.\s/.test(trimmed)) { + const match = trimmed.match(/^(\d+)\.\s(.*)$/); + if (match) { + elements.push( + + {match[1]}. + + {renderInlineFormatting(match[2])} + + + ); + } + } + // 普通段落 + else { + elements.push( + + {renderInlineFormatting(trimmed)} + + ); + } + }); + + return {elements}; +}); + +/** + * 渲染行内格式(加粗等) + */ +const renderInlineFormatting = (text) => { + if (!text) return text; + + // 处理加粗 **text** 或 __text__ + const parts = text.split(/(\*\*[^*]+\*\*|__[^_]+__)/g); + + return parts.map((part, i) => { + if (part.startsWith('**') && part.endsWith('**')) { + return ( + + {part.slice(2, -2)} + + ); + } + if (part.startsWith('__') && part.endsWith('__')) { + return ( + + {part.slice(2, -2)} + + ); + } + return part; + }); +}; + +/** + * 内容区块组件 + */ +const Section = memo(({ title, icon, children, color = '#D4AF37' }) => ( + + + + + + + {title} + + + + {children} + + +)); + +/** + * 涨幅分析弹窗 + */ +const RiseAnalysisModal = memo(({ isOpen, onClose, analysis }) => { + const insets = useSafeAreaInsets(); + + const handleClose = useCallback(() => { + onClose?.(); + }, [onClose]); + + // 解构分析数据(即使 analysis 为 null 也安全) + const { + stock_code = '', + stock_name = '', + trade_date = '', + rise_rate = 0, + close_price = 0, + volume = 0, + amount = 0, + main_business = '', + rise_reason_brief = '', + rise_reason_detail = '', + announcements = '', + update_time = '', + } = analysis || {}; + + const riseRateNum = Number(rise_rate) || 0; + + return ( + + + + + + + + {/* 头部 */} + + + + + + + + 涨幅分析详情 + + + 异动原因解读 + + + + + + + + + {/* 股票信息头部 */} + + + + + + + {stock_name || '--'} + + + + {stock_code || '--'} + + + + + + + 日期: {formatDate(trade_date)} + + + = 0 ? 'rgba(239, 68, 68, 0.2)' : 'rgba(34, 197, 94, 0.2)'} + px={2} + py={1} + borderRadius={6} + > + + = 0 ? 'trending-up' : 'trending-down'} + size="xs" + color={riseRateNum >= 0 ? '#EF4444' : '#22C55E'} + /> + = 0 ? '#EF4444' : '#22C55E'} fontSize={11}> + 涨幅: {formatRiseRate(rise_rate)} + + + + + + 收盘价: ¥{formatPrice(close_price)} + + + + + + + + {/* 内容区域 */} + + {/* 涨幅原因简述 */} + {!!rise_reason_brief && ( +
+ + {rise_reason_brief} + +
+ )} + + {/* 详细分析 */} + {!!rise_reason_detail ? ( +
+ +
+ ) : !rise_reason_brief && ( +
+ + 暂无详细分析数据 + +
+ )} + + {/* 主营业务 */} + {!!main_business && ( +
+ + {main_business} + +
+ )} + + {/* 相关公告 */} + {!!announcements && announcements !== '[]' && ( +
+ +
+ )} + + {/* 交易数据 */} +
+ + + + 成交额 + + + {formatAmount(amount)} + + + + + + 收盘价 + + + ¥{formatPrice(close_price)} + + + + + + 涨幅 + + = 0 ? '#EF4444' : '#22C55E'} + fontSize={15} + fontWeight="medium" + > + {formatRiseRate(rise_rate)} + + + +
+ + {/* 更新时间 */} + {!!update_time && ( + + + 数据更新时间: {update_time} + + + )} + + {/* 底部安全区域 */} + +
+
+
+
+ ); +}); + +const styles = StyleSheet.create({ + overlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'flex-end', + }, + backdrop: { + flex: 1, + }, + modalContainer: { + minHeight: SCREEN_HEIGHT * 0.5, + maxHeight: SCREEN_HEIGHT * 0.9, + borderTopLeftRadius: 24, + borderTopRightRadius: 24, + overflow: 'hidden', + backgroundColor: '#0F0F1A', + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: 'rgba(212, 175, 55, 0.2)', + }, + closeButton: { + backgroundColor: 'rgba(255,255,255,0.1)', + padding: 8, + borderRadius: 20, + }, + stockHeader: { + overflow: 'hidden', + }, + content: { + flex: 1, + }, + contentContainer: { + padding: 16, + }, +}); + +RiseAnalysisModal.displayName = 'RiseAnalysisModal'; + +export default RiseAnalysisModal; diff --git a/MeAgent/src/screens/StockDetail/components/index.js b/MeAgent/src/screens/StockDetail/components/index.js new file mode 100644 index 00000000..cd8f3cd2 --- /dev/null +++ b/MeAgent/src/screens/StockDetail/components/index.js @@ -0,0 +1,14 @@ +/** + * 股票详情组件导出 + */ + +export { default as PriceHeader } from './PriceHeader'; +export { default as ChartTypeTabs } from './ChartTypeTabs'; +export { default as MinuteChart } from './MinuteChart'; +export { default as KlineChart } from './KlineChart'; +export { default as OrderBook } from './OrderBook'; +export { default as RelatedInfoTabs } from './RelatedInfoTabs'; +export { default as EventsPanel } from './EventsPanel'; +export { default as ConceptsPanel } from './ConceptsPanel'; +export { default as AnnouncementsPanel } from './AnnouncementsPanel'; +export { default as RiseAnalysisModal } from './RiseAnalysisModal'; diff --git a/MeAgent/src/screens/StockDetail/index.js b/MeAgent/src/screens/StockDetail/index.js new file mode 100644 index 00000000..164c5cae --- /dev/null +++ b/MeAgent/src/screens/StockDetail/index.js @@ -0,0 +1,6 @@ +/** + * 股票详情模块导出 + */ + +export { default as StockDetailScreen } from './StockDetailScreen'; +export * from './components'; diff --git a/MeAgent/src/screens/Watchlist/AddWatchlistModal.js b/MeAgent/src/screens/Watchlist/AddWatchlistModal.js new file mode 100644 index 00000000..df20d483 --- /dev/null +++ b/MeAgent/src/screens/Watchlist/AddWatchlistModal.js @@ -0,0 +1,334 @@ +/** + * 添加自选股弹窗组件 + * 支持搜索股票并添加到自选 + */ + +import React, { useState, useCallback, useRef } from 'react'; +import { StyleSheet, Keyboard, ActivityIndicator } from 'react-native'; +import { + Modal, + Box, + VStack, + HStack, + Text, + Input, + Icon, + Pressable, + FlatList, + useToast, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { BlurView } from 'expo-blur'; +import { stockDetailService } from '../../services/stockService'; +import { useWatchlist } from '../../hooks/useWatchlist'; + +// 搜索结果项组件 +const SearchResultItem = ({ item, onAdd, isAdding, isInWatchlist }) => { + const { stock_code, stock_name, industry } = item; + const alreadyAdded = isInWatchlist(stock_code); + + return ( + !alreadyAdded && !isAdding && onAdd(item)} + disabled={alreadyAdded || isAdding} + > + {({ pressed }) => ( + + + + {stock_name} + + + + {stock_code} + + {!!industry && ( + + {industry} + + )} + + + + {alreadyAdded ? ( + + + + 已添加 + + + ) : isAdding ? ( + + ) : ( + + + + + 加自选 + + + + )} + + )} + + ); +}; + +// 热门股票配置 +const HOT_STOCKS = [ + { stock_code: '600519', stock_name: '贵州茅台' }, + { stock_code: '000858', stock_name: '五粮液' }, + { stock_code: '300750', stock_name: '宁德时代' }, + { stock_code: '601318', stock_name: '中国平安' }, + { stock_code: '600036', stock_name: '招商银行' }, + { stock_code: '000333', stock_name: '美的集团' }, + { stock_code: '002594', stock_name: '比亚迪' }, + { stock_code: '601012', stock_name: '隆基绿能' }, +]; + +/** + * 添加自选股弹窗 + * @param {object} props + * @param {boolean} props.isOpen - 是否打开 + * @param {function} props.onClose - 关闭回调 + */ +const AddWatchlistModal = ({ isOpen, onClose }) => { + const [searchText, setSearchText] = useState(''); + const [searchResults, setSearchResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + const [addingCode, setAddingCode] = useState(null); + const searchTimeoutRef = useRef(null); + const toast = useToast(); + + const { addStock, isInWatchlist } = useWatchlist({ autoLoad: false }); + + // 搜索股票 + const handleSearch = useCallback(async (text) => { + setSearchText(text); + + // 清除之前的定时器 + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + + if (!text.trim()) { + setSearchResults([]); + return; + } + + // 防抖搜索 + searchTimeoutRef.current = setTimeout(async () => { + setIsSearching(true); + try { + const response = await stockDetailService.searchStocks(text, 20); + if (response.success && Array.isArray(response.data)) { + setSearchResults(response.data); + } else { + setSearchResults([]); + } + } catch (error) { + console.error('搜索股票失败:', error); + setSearchResults([]); + } finally { + setIsSearching(false); + } + }, 300); + }, []); + + // 添加到自选 + const handleAdd = useCallback(async (stock) => { + const { stock_code, stock_name } = stock; + setAddingCode(stock_code); + + try { + const result = await addStock(stock_code, stock_name); + if (result.success) { + toast.show({ + description: `已添加 ${stock_name} 到自选`, + placement: 'top', + duration: 2000, + }); + } else { + toast.show({ + description: result.error || '添加失败', + placement: 'top', + duration: 2000, + }); + } + } catch (error) { + toast.show({ + description: '添加失败,请重试', + placement: 'top', + duration: 2000, + }); + } finally { + setAddingCode(null); + } + }, [addStock, toast]); + + // 关闭弹窗 + const handleClose = useCallback(() => { + Keyboard.dismiss(); + setSearchText(''); + setSearchResults([]); + onClose?.(); + }, [onClose]); + + // 渲染列表数据 + const displayData = searchText.trim() ? searchResults : HOT_STOCKS; + const listTitle = searchText.trim() ? '搜索结果' : '热门股票'; + + return ( + + + + + {/* 头部 */} + + + 添加自选股 + + + + + + + + + {/* 搜索框 */} + + + } + InputRightElement={ + searchText ? ( + handleSearch('')} mr={3}> + + + ) : null + } + /> + + + {/* 列表标题 */} + + + {listTitle} + + {isSearching && ( + + )} + + + {/* 股票列表 */} + item.stock_code} + renderItem={({ item }) => ( + + )} + ListEmptyComponent={ + + + + {searchText ? '未找到相关股票' : '输入关键词搜索'} + + + } + keyboardShouldPersistTaps="handled" + showsVerticalScrollIndicator={false} + /> + + + + ); +}; + +const styles = StyleSheet.create({}); + +export default AddWatchlistModal; diff --git a/MeAgent/src/screens/Watchlist/WatchlistEventItem.js b/MeAgent/src/screens/Watchlist/WatchlistEventItem.js new file mode 100644 index 00000000..8a71e1bf --- /dev/null +++ b/MeAgent/src/screens/Watchlist/WatchlistEventItem.js @@ -0,0 +1,286 @@ +/** + * 自选事件列表项组件 + * 显示单个关注事件的信息 + */ + +import React, { memo } from 'react'; +import { StyleSheet } from 'react-native'; +import { + Box, + HStack, + VStack, + Text, + Icon, + Pressable, + Badge, +} from 'native-base'; +import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; + +// 事件类型配置 +const EVENT_TYPE_CONFIG = { + policy: { icon: 'bank', color: '#3B82F6', label: '政策' }, + industry: { icon: 'factory', color: '#8B5CF6', label: '产业' }, + company: { icon: 'domain', color: '#F59E0B', label: '公司' }, + market: { icon: 'chart-line', color: '#10B981', label: '市场' }, + tech: { icon: 'rocket-launch', color: '#EC4899', label: '科技' }, + default: { icon: 'newspaper', color: '#6B7280', label: '资讯' }, +}; + +// 重要性等级配置 +const IMPORTANCE_CONFIG = { + S: { color: '#EF4444', bgColor: 'rgba(239, 68, 68, 0.2)' }, + A: { color: '#F59E0B', bgColor: 'rgba(245, 158, 11, 0.2)' }, + B: { color: '#3B82F6', bgColor: 'rgba(59, 130, 246, 0.2)' }, + C: { color: '#6B7280', bgColor: 'rgba(107, 114, 128, 0.2)' }, +}; + +// 格式化日期 +const formatDate = (dateStr) => { + if (!dateStr) return ''; + const date = new Date(dateStr); + const now = new Date(); + const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24)); + + if (diffDays === 0) { + return '今天'; + } else if (diffDays === 1) { + return '昨天'; + } else if (diffDays < 7) { + return `${diffDays}天前`; + } else { + const month = date.getMonth() + 1; + const day = date.getDate(); + return `${month}月${day}日`; + } +}; + +// 格式化热度 +const formatHotScore = (score) => { + if (!score) return ''; + if (score >= 10000) { + return `${(score / 10000).toFixed(1)}万`; + } + return String(score); +}; + +/** + * 自选事件列表项 + * @param {object} props + * @param {object} props.event - 事件信息 + * @param {function} props.onPress - 点击回调 + * @param {function} props.onUnfollow - 取消关注回调 + * @param {boolean} props.isEditing - 是否处于编辑模式 + */ +const WatchlistEventItem = memo(({ + event, + onPress, + onUnfollow, + isEditing = false, +}) => { + const { + id, + title, + event_type, + importance, + hot_score, + publish_time, + related_stocks = [], + follower_count, + } = event; + + const typeConfig = EVENT_TYPE_CONFIG[event_type] || EVENT_TYPE_CONFIG.default; + const importanceConfig = IMPORTANCE_CONFIG[importance] || IMPORTANCE_CONFIG.C; + + return ( + onPress?.(event)} disabled={isEditing}> + {({ pressed }) => ( + + + + + {/* 左侧:删除按钮(编辑模式)或事件类型图标 */} + {isEditing ? ( + onUnfollow?.(id)} + hitSlop={10} + > + + + + + ) : ( + + + + )} + + {/* 中间:事件信息 */} + + {/* 标题行 */} + + {!!importance && ( + + {importance} + + )} + + {title} + + + + {/* 相关股票 */} + {related_stocks.length > 0 && ( + + {related_stocks.slice(0, 3).map((stock, index) => ( + + + {stock.stock_name || stock.name || stock} + + + ))} + {related_stocks.length > 3 && ( + + + +{related_stocks.length - 3} + + + )} + + )} + + {/* 底部信息行 */} + + {/* 事件类型标签 */} + + + + {typeConfig.label} + + + + {/* 热度 */} + {hot_score > 0 && ( + + + + {formatHotScore(hot_score)} + + + )} + + {/* 关注人数 */} + {follower_count > 0 && ( + + + + {follower_count}人关注 + + + )} + + {/* 时间 */} + + {formatDate(publish_time)} + + + + + {/* 最右侧:箭头(非编辑模式) */} + {!isEditing && ( + + )} + + + + )} + + ); +}); + +WatchlistEventItem.displayName = 'WatchlistEventItem'; + +export default WatchlistEventItem; diff --git a/MeAgent/src/screens/Watchlist/WatchlistScreen.js b/MeAgent/src/screens/Watchlist/WatchlistScreen.js new file mode 100644 index 00000000..c8491b87 --- /dev/null +++ b/MeAgent/src/screens/Watchlist/WatchlistScreen.js @@ -0,0 +1,432 @@ +/** + * 自选股/自选事件主页面 + * 包含股票和事件两个 Tab + */ + +import React, { useState, useCallback, useEffect } from 'react'; +import { StyleSheet, RefreshControl } from 'react-native'; +import { + Box, + VStack, + HStack, + Text, + Icon, + Pressable, + FlatList, + Spinner, + useToast, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; +import { BlurView } from 'expo-blur'; +import { useNavigation, useFocusEffect } from '@react-navigation/native'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +import WatchlistStockItem from './WatchlistStockItem'; +import WatchlistEventItem from './WatchlistEventItem'; +import AddWatchlistModal from './AddWatchlistModal'; +import { useWatchlist } from '../../hooks/useWatchlist'; +import { useWatchlistRealtimeQuotes } from '../../hooks/useRealtimeQuote'; + +// Tab 配置 +const TABS = [ + { key: 'stocks', label: '自选股', icon: 'trending-up' }, + { key: 'events', label: '自选事件', icon: 'newspaper-outline' }, +]; + +/** + * 自选股主页面 + */ +const WatchlistScreen = () => { + const navigation = useNavigation(); + const toast = useToast(); + + const [activeTab, setActiveTab] = useState('stocks'); + const [isEditing, setIsEditing] = useState(false); + const [isAddModalOpen, setIsAddModalOpen] = useState(false); + const [refreshing, setRefreshing] = useState(false); + + const { + stocks, + stocksWithQuotes, + events, + isLoadingStocks, + isLoadingEvents, + removeStock, + toggleEventFollow, + refreshAll, + loadWatchlist, + loadRealtimeQuotes, + loadFollowingEvents, + } = useWatchlist(); + + // WebSocket 实时行情 + const { + isConnected: wsConnected, + connectionState, + updateSubscription, + } = useWatchlistRealtimeQuotes(); + + // 页面获取焦点时刷新数据 + useFocusEffect( + useCallback(() => { + loadWatchlist(); + loadRealtimeQuotes(); + loadFollowingEvents(); + }, [loadWatchlist, loadRealtimeQuotes, loadFollowingEvents]) + ); + + // 当股票列表变化时更新 WebSocket 订阅 + useEffect(() => { + if (stocks.length > 0) { + updateSubscription(stocks); + } + }, [stocks, updateSubscription]); + + // 下拉刷新 + const handleRefresh = useCallback(async () => { + setRefreshing(true); + await refreshAll(); + setRefreshing(false); + }, [refreshAll]); + + // 点击股票 + const handleStockPress = useCallback((stock) => { + navigation.navigate('StockDetail', { + stockCode: stock.stock_code, + stockName: stock.stock_name, + }); + }, [navigation]); + + // 点击事件 + const handleEventPress = useCallback((event) => { + navigation.navigate('EventDetail', { + eventId: event.id, + }); + }, [navigation]); + + // 删除自选股 + const handleRemoveStock = useCallback(async (stockCode) => { + const result = await removeStock(stockCode); + if (result.success) { + toast.show({ + description: '已从自选中移除', + placement: 'top', + duration: 2000, + }); + } else { + toast.show({ + description: '移除失败,请重试', + placement: 'top', + duration: 2000, + }); + } + }, [removeStock, toast]); + + // 取消关注事件 + const handleUnfollowEvent = useCallback(async (eventId) => { + const result = await toggleEventFollow(eventId); + if (result.success) { + toast.show({ + description: '已取消关注', + placement: 'top', + duration: 2000, + }); + } else { + toast.show({ + description: '操作失败,请重试', + placement: 'top', + duration: 2000, + }); + } + }, [toggleEventFollow, toast]); + + // 获取连接状态显示 + const getConnectionStatus = () => { + if (wsConnected) { + return { color: '#22C55E', text: '实时' }; + } + if (connectionState === 'connecting' || connectionState === 'reconnecting') { + return { color: '#F59E0B', text: '连接中' }; + } + return { color: '#6B7280', text: '离线' }; + }; + + // 渲染头部 + const renderHeader = () => { + const connStatus = getConnectionStatus(); + return ( + + {/* 顶部栏 */} + + + + 我的自选 + + {/* WebSocket 连接状态指示器 */} + {activeTab === 'stocks' && stocks.length > 0 && ( + + + + {connStatus.text} + + + )} + + + {/* 编辑按钮 */} + setIsEditing(!isEditing)}> + + + + + {/* 添加按钮 */} + {activeTab === 'stocks' && ( + setIsAddModalOpen(true)}> + + + + + )} + + + + {/* Tab 切换 */} + + {TABS.map((tab) => { + const isActive = activeTab === tab.key; + return ( + { + setActiveTab(tab.key); + setIsEditing(false); + }} + flex={1} + > + + + + + {tab.label} + + + + {tab.key === 'stocks' ? stocksWithQuotes.length : events.length} + + + + + + ); + })} + + + ); + }; + + // 渲染空状态 + const renderEmptyState = () => { + const isStocks = activeTab === 'stocks'; + return ( + + + + + + {isStocks ? '还没有自选股' : '还没有关注事件'} + + + {isStocks + ? '点击右上角添加按钮,搜索并添加你感兴趣的股票' + : '在事件中心浏览事件,点击关注按钮添加到这里'} + + {isStocks && ( + setIsAddModalOpen(true)} mt={6}> + + + + + 添加自选股 + + + + + )} + + ); + }; + + // 渲染股票列表项 + const renderStockItem = ({ item }) => ( + + ); + + // 渲染事件列表项 + const renderEventItem = ({ item }) => ( + + ); + + // 当前显示的数据 + const currentData = activeTab === 'stocks' ? stocksWithQuotes : events; + const isLoading = activeTab === 'stocks' ? isLoadingStocks : isLoadingEvents; + + return ( + + + {/* 背景渐变 */} + + + {renderHeader()} + + {/* 加载状态 */} + {isLoading && currentData.length === 0 ? ( + + + + 加载中... + + + ) : ( + + activeTab === 'stocks' ? item.stock_code : String(item.id) + } + renderItem={activeTab === 'stocks' ? renderStockItem : renderEventItem} + ListEmptyComponent={renderEmptyState} + refreshControl={ + + } + contentContainerStyle={ + currentData.length === 0 ? styles.emptyContainer : styles.listContainer + } + showsVerticalScrollIndicator={false} + /> + )} + + {/* 添加自选股弹窗 */} + setIsAddModalOpen(false)} + /> + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + headerGradient: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: 200, + }, + listContainer: { + paddingBottom: 100, + }, + emptyContainer: { + flex: 1, + }, +}); + +export default WatchlistScreen; diff --git a/MeAgent/src/screens/Watchlist/WatchlistStockItem.js b/MeAgent/src/screens/Watchlist/WatchlistStockItem.js new file mode 100644 index 00000000..5101f53d --- /dev/null +++ b/MeAgent/src/screens/Watchlist/WatchlistStockItem.js @@ -0,0 +1,225 @@ +/** + * 自选股列表项组件 + * 显示单只股票的行情信息 + */ + +import React, { memo } from 'react'; +import { StyleSheet } from 'react-native'; +import { + Box, + HStack, + VStack, + Text, + Icon, + Pressable, +} from 'native-base'; +import { Ionicons } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; + +// 涨跌颜色 +const getChangeColor = (change) => { + if (change > 0) return '#EF4444'; // 红色 + if (change < 0) return '#22C55E'; // 绿色 + return '#94A3B8'; // 灰色 +}; + +// 背景渐变色 +const getGradientColors = (change) => { + if (change > 0) return ['rgba(239, 68, 68, 0.08)', 'rgba(239, 68, 68, 0.02)']; + if (change < 0) return ['rgba(34, 197, 94, 0.08)', 'rgba(34, 197, 94, 0.02)']; + return ['rgba(148, 163, 184, 0.08)', 'rgba(148, 163, 184, 0.02)']; +}; + +// 格式化价格 +const formatPrice = (price) => { + if (price === undefined || price === null) return '--'; + return Number(price).toFixed(2); +}; + +// 格式化涨跌幅 +const formatChange = (change) => { + if (change === undefined || change === null) return '--'; + const sign = change > 0 ? '+' : ''; + return `${sign}${Number(change).toFixed(2)}%`; +}; + +// 格式化成交量 +const formatVolume = (volume) => { + if (!volume) return '--'; + if (volume >= 100000000) { + return `${(volume / 100000000).toFixed(2)}亿`; + } + if (volume >= 10000) { + return `${(volume / 10000).toFixed(0)}万`; + } + return String(volume); +}; + +/** + * 自选股列表项 + * @param {object} props + * @param {object} props.stock - 股票信息 { stock_code, stock_name } + * @param {object} props.quote - 实时行情 { current_price, change_percent, volume, ... } + * @param {function} props.onPress - 点击回调 + * @param {function} props.onRemove - 删除回调 + * @param {boolean} props.isEditing - 是否处于编辑模式 + */ +const WatchlistStockItem = memo(({ + stock, + quote, + onPress, + onRemove, + isEditing = false, +}) => { + const { stock_code, stock_name } = stock; + const { + current_price, + change_percent, + volume, + high, + low, + } = quote || {}; + + const changeColor = getChangeColor(change_percent); + const gradientColors = getGradientColors(change_percent); + + return ( + onPress?.(stock)} disabled={isEditing}> + {({ pressed }) => ( + + + + + {/* 左侧:删除按钮(编辑模式)或涨跌指示条 */} + {isEditing ? ( + onRemove?.(stock_code)} + hitSlop={10} + mr={3} + > + + + + + ) : ( + + )} + + {/* 中间:股票信息 */} + + + {stock_name || stock_code} + + + + {stock_code} + + {volume > 0 && ( + + 成交 {formatVolume(volume)} + + )} + + + + {/* 右侧:价格和涨跌幅 */} + + + {formatPrice(current_price)} + + + {change_percent !== undefined && change_percent !== 0 && ( + 0 ? 'caret-up' : 'caret-down'} + size="xs" + color={changeColor} + /> + )} + + {formatChange(change_percent)} + + + + + {/* 最右侧:箭头(非编辑模式) */} + {!isEditing && ( + + )} + + + {/* 额外信息行:最高/最低价 */} + {(high > 0 || low > 0) && !isEditing && ( + + + 最高 + {formatPrice(high)} + + + 最低 + {formatPrice(low)} + + + )} + + + )} + + ); +}); + +WatchlistStockItem.displayName = 'WatchlistStockItem'; + +export default WatchlistStockItem; diff --git a/MeAgent/src/screens/Watchlist/index.js b/MeAgent/src/screens/Watchlist/index.js new file mode 100644 index 00000000..dd8e361a --- /dev/null +++ b/MeAgent/src/screens/Watchlist/index.js @@ -0,0 +1,8 @@ +/** + * 自选股模块导出 + */ + +export { default as WatchlistScreen } from './WatchlistScreen'; +export { default as WatchlistStockItem } from './WatchlistStockItem'; +export { default as WatchlistEventItem } from './WatchlistEventItem'; +export { default as AddWatchlistModal } from './AddWatchlistModal'; diff --git a/argon-pro-react-native/src/services/api.js b/MeAgent/src/services/api.js similarity index 96% rename from argon-pro-react-native/src/services/api.js rename to MeAgent/src/services/api.js index 49fc9ab9..aa1ffc31 100644 --- a/argon-pro-react-native/src/services/api.js +++ b/MeAgent/src/services/api.js @@ -3,7 +3,7 @@ * 复用 Web 端的 API 逻辑,适配 React Native */ -const API_BASE_URL = 'https://api.valuefrontier.cn'; +export const API_BASE_URL = 'https://api.valuefrontier.cn'; // 静态数据存储地址(腾讯 COS) export const API_BASE = 'https://valuefrontier-1308417363.cos-website.ap-shanghai.myqcloud.com'; diff --git a/argon-pro-react-native/src/services/authService.js b/MeAgent/src/services/authService.js similarity index 100% rename from argon-pro-react-native/src/services/authService.js rename to MeAgent/src/services/authService.js diff --git a/MeAgent/src/services/companyService.js b/MeAgent/src/services/companyService.js new file mode 100644 index 00000000..3766b31b --- /dev/null +++ b/MeAgent/src/services/companyService.js @@ -0,0 +1,158 @@ +/** + * 公司/股票相关信息服务 + * 获取股票相关事件、概念、公告等 + */ + +import { apiRequest, API_BASE_URL } from './api'; + +// Concept API 基础地址 +const CONCEPT_API_BASE = `${API_BASE_URL}/concept-api`; + +/** + * 公司信息服务 + */ +export const companyService = { + /** + * 获取股票相关事件 + * @param {string} stockName - 股票名称 + * @param {number} page - 页码 + * @param {number} perPage - 每页条数 + * @returns {Promise} + */ + getRelatedEvents: async (stockName, page = 1, perPage = 10) => { + try { + if (!stockName) { + return { success: false, data: [], error: '缺少股票名称' }; + } + + console.log('[CompanyService] 获取相关事件:', { stockName, page }); + + const url = `/api/events?sort=new&importance=all&q=${encodeURIComponent(stockName)}&page=${page}&mode=vertical&per_page=${perPage}`; + const response = await apiRequest(url); + + if (response.success && response.data) { + return { + success: true, + data: response.data.events || [], + pagination: response.pagination, + }; + } + + return { success: false, data: [], error: '获取事件失败' }; + } catch (error) { + console.error('[CompanyService] getRelatedEvents 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 获取股票相关概念 + * @param {string} stockCode - 股票代码(6位数字) + * @returns {Promise} + */ + getRelatedConcepts: async (stockCode) => { + try { + if (!stockCode) { + return { success: false, data: [], error: '缺少股票代码' }; + } + + // 提取6位数字代码 + const pureCode = String(stockCode).match(/\d{6}/)?.[0]; + if (!pureCode) { + return { success: false, data: [], error: '无效的股票代码' }; + } + + console.log('[CompanyService] 获取相关概念:', pureCode); + + const url = `${CONCEPT_API_BASE}/stock/${pureCode}/concepts?size=50`; + const response = await fetch(url); + const data = await response.json(); + + if (data && data.concepts) { + return { + success: true, + data: data.concepts, + }; + } + + return { success: false, data: [], error: '获取概念失败' }; + } catch (error) { + console.error('[CompanyService] getRelatedConcepts 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 获取股票公告 + * @param {string} stockCode - 股票代码 + * @param {number} limit - 条数限制 + * @returns {Promise} + */ + getAnnouncements: async (stockCode, limit = 20) => { + try { + if (!stockCode) { + return { success: false, data: [], error: '缺少股票代码' }; + } + + // 提取6位数字代码 + const pureCode = String(stockCode).match(/\d{6}/)?.[0]; + if (!pureCode) { + return { success: false, data: [], error: '无效的股票代码' }; + } + + console.log('[CompanyService] 获取公告:', pureCode); + + const url = `/api/stock/${pureCode}/announcements?limit=${limit}`; + const response = await apiRequest(url); + + if (response.success && response.data) { + return { + success: true, + data: response.data, + }; + } + + return { success: false, data: [], error: '获取公告失败' }; + } catch (error) { + console.error('[CompanyService] getAnnouncements 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 获取股票基本信息 + * @param {string} stockCode - 股票代码 + * @returns {Promise} + */ + getStockInfo: async (stockCode) => { + try { + if (!stockCode) { + return { success: false, data: null, error: '缺少股票代码' }; + } + + const pureCode = String(stockCode).match(/\d{6}/)?.[0]; + if (!pureCode) { + return { success: false, data: null, error: '无效的股票代码' }; + } + + console.log('[CompanyService] 获取股票信息:', pureCode); + + const url = `/api/financial/stock-info/${pureCode}`; + const response = await apiRequest(url); + + if (response.success && response.data) { + return { + success: true, + data: response.data, + }; + } + + return { success: false, data: null, error: '获取股票信息失败' }; + } catch (error) { + console.error('[CompanyService] getStockInfo 错误:', error); + return { success: false, data: null, error: error.message }; + } + }, +}; + +export default companyService; diff --git a/argon-pro-react-native/src/services/eventService.js b/MeAgent/src/services/eventService.js similarity index 100% rename from argon-pro-react-native/src/services/eventService.js rename to MeAgent/src/services/eventService.js diff --git a/argon-pro-react-native/src/services/pushService.js b/MeAgent/src/services/pushService.js similarity index 100% rename from argon-pro-react-native/src/services/pushService.js rename to MeAgent/src/services/pushService.js diff --git a/MeAgent/src/services/stockService.js b/MeAgent/src/services/stockService.js new file mode 100644 index 00000000..22527824 --- /dev/null +++ b/MeAgent/src/services/stockService.js @@ -0,0 +1,537 @@ +/** + * 股票详情服务层 + * 获取股票详情、K线数据、分时数据等 + */ + +import { apiRequest, API_BASE_URL } from './api'; + +/** + * 股票详情服务 + */ +export const stockDetailService = { + /** + * 批量获取股票报价(含5档盘口)- 使用 flex-screen API 获取完整数据 + * @param {string[]} codes - 股票代码数组 + * @param {string} eventTime - 可选的事件时间 + * @returns {Promise} { [code]: { name, price, change, bidPrices, askPrices, ... } } + */ + getQuotes: async (codes, eventTime = null) => { + try { + // 确保所有股票代码都带有交易所后缀 + const formattedCodes = codes.map(code => stockDetailService.formatStockCode(code)); + + const requestBody = { + codes: formattedCodes, + include_order_book: true, // 请求5档盘口数据 + }; + if (eventTime) { + requestBody.event_time = eventTime; + } + + console.log('[StockDetailService] 获取股票报价:', formattedCodes.slice(0, 5)); + + // 使用 flex-screen API 获取更完整的行情数据(与Web端一致) + const response = await apiRequest('/api/flex-screen/quotes', { + method: 'POST', + body: JSON.stringify(requestBody), + }); + + if (response.success && response.data) { + console.log('[StockDetailService] 报价响应成功:', Object.keys(response.data).length, '只股票'); + // 转换字段名以兼容现有组件 + const normalizedData = {}; + Object.entries(response.data).forEach(([code, quote]) => { + // 调试日志:打印原始 API 响应 + console.log('[StockDetailService] 原始报价数据:', code, { + last_px: quote.last_px, + prev_close_px: quote.prev_close_px, + change: quote.change, + change_pct: quote.change_pct, + }); + + normalizedData[code] = { + name: quote.name, + price: quote.last_px, + current_price: quote.last_px, + pre_close: quote.prev_close_px, + open: quote.open_px, + high: quote.high_px, + low: quote.low_px, + volume: quote.total_volume_trade, + amount: quote.total_value_trade, + change: quote.change, + change_percent: quote.change_pct, + change_amount: quote.change, + bid_prices: quote.bid_prices || [], + bid_volumes: quote.bid_volumes || [], + ask_prices: quote.ask_prices || [], + ask_volumes: quote.ask_volumes || [], + update_time: quote.update_time, + }; + }); + return { success: true, data: normalizedData }; + } else { + console.warn('[StockDetailService] 报价响应格式异常:', response); + return { success: false, data: {} }; + } + } catch (error) { + console.error('[StockDetailService] getQuotes 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 获取单只股票详情 + * 使用 /api/stock/{code}/quote-detail API 获取完整行情数据 + * @param {string} code - 股票代码 + * @returns {Promise} 股票详情 + */ + getStockDetail: async (code) => { + try { + const pureCode = stockDetailService.getPureCode(code); + const formattedCode = stockDetailService.formatStockCode(code); + console.log('[StockDetailService] 获取股票详情:', { code, pureCode, formattedCode }); + + // 并行请求 quote-detail 和 market/trade(用于获取成交量成交额) + const [quoteResponse, tradeResponse] = await Promise.all([ + apiRequest(`/api/stock/${pureCode}/quote-detail`), + apiRequest(`/api/market/trade/${pureCode}?limit=60`), + ]); + + // 从 market/trade 获取最新一天的成交量成交额 + let latestTrade = null; + if (tradeResponse.success && tradeResponse.data && tradeResponse.data.length > 0) { + latestTrade = tradeResponse.data[tradeResponse.data.length - 1]; + } + + if (quoteResponse.success && quoteResponse.data) { + const quote = quoteResponse.data; + + console.log('[StockDetailService] quote-detail 响应:', { + current_price: quote.current_price, + yesterday_close: quote.yesterday_close, + change_percent: quote.change_percent, + turnover_rate: quote.turnover_rate, + }); + + const normalizedData = { + stock_code: formattedCode, + stock_name: quote.name || '', + // 价格字段 + current_price: quote.current_price || 0, + price: quote.current_price || 0, + pre_close: quote.yesterday_close || 0, + open: quote.today_open || 0, + high: quote.today_high || 0, + low: quote.today_low || 0, + // 涨跌 + change_amount: (quote.current_price - quote.yesterday_close) || 0, + change_percent: quote.change_percent || 0, + // 成交量/成交额(从 market/trade 获取) + volume: latestTrade?.volume || 0, + amount: latestTrade?.amount || 0, + // 换手率/市盈率/市值 + turnover_rate: quote.turnover_rate || latestTrade?.turnover_rate || 0, + pe_ratio: quote.pe || 0, + market_cap: quote.market_cap || quote.circ_mv || 0, + // 52周高低 + week52_high: quote.week52_high || 0, + week52_low: quote.week52_low || 0, + // 资金流向 + net_inflow: quote.net_inflow || 0, + main_inflow_ratio: quote.main_inflow_ratio || 0, + net_active_buy_ratio: quote.net_active_buy_ratio || 0, + // 行业信息 + industry: quote.industry || quote.sw_industry_l2 || '', + sw_industry_l1: quote.sw_industry_l1 || '', + // 5档盘口(quote-detail 不提供,置空) + bidPrices: [], + bidVolumes: [], + askPrices: [], + askVolumes: [], + // 更新时间 + update_time: quote.update_time, + }; + + return { success: true, data: normalizedData }; + } + + // 如果 quote-detail 失败,只使用 market/trade 数据 + if (latestTrade) { + console.log('[StockDetailService] quote-detail 无数据,使用 market/trade'); + const normalizedData = { + stock_code: formattedCode, + stock_name: latestTrade.stock_name || '', + current_price: latestTrade.close || 0, + price: latestTrade.close || 0, + pre_close: latestTrade.pre_close || 0, + open: latestTrade.open || 0, + high: latestTrade.high || 0, + low: latestTrade.low || 0, + change_amount: (latestTrade.close - latestTrade.pre_close) || 0, + change_percent: latestTrade.change_percent || 0, + volume: latestTrade.volume || 0, + amount: latestTrade.amount || 0, + turnover_rate: latestTrade.turnover_rate || 0, + pe_ratio: latestTrade.pe_ratio || 0, + bidPrices: [], + bidVolumes: [], + askPrices: [], + askVolumes: [], + trade_date: latestTrade.date, + }; + return { success: true, data: normalizedData }; + } + + return { success: false, error: '获取股票信息失败' }; + } catch (error) { + console.error('[StockDetailService] getStockDetail 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 获取K线数据 + * @param {string} code - 股票代码 + * @param {string} type - K线类型: minute | daily | weekly | monthly + * @param {string} eventTime - 可选的事件时间 + * @returns {Promise} { success: true, data: [...klineData] } + */ + getKlineData: async (code, type = 'daily', eventTime = null) => { + try { + // 分时数据需要带后缀(ClickHouse 存储格式),日/周/月K线用纯数字 + const isMinuteType = type === 'minute' || type === 'timeline'; + const apiCode = isMinuteType + ? stockDetailService.formatStockCode(code) // 带后缀:000001.SZ + : stockDetailService.getPureCode(code); // 纯数字:000001 + + let url = `/api/stock/${apiCode}/kline?type=${type}`; + if (eventTime) { + url += `&event_time=${eventTime}`; + } + + console.log('[StockDetailService] 获取K线数据:', { code, apiCode, type, url }); + + const response = await apiRequest(url); + + // API 返回格式: { code, name, data, trade_date, type, prev_close }(不是 {success, data} 格式) + if (response && response.data) { + console.log('[StockDetailService] K线数据点数:', response.data?.length || 0); + // 标准化数据字段名称 + const normalizedData = stockDetailService.normalizeKlineData(response.data, type); + return { + success: true, + data: normalizedData, + stockName: response.name, + tradeDate: response.trade_date, + prevClose: response.prev_close, // timeline 类型返回昨收价 + }; + } + + // 处理错误响应 + if (response && response.error) { + return { success: false, data: [], error: response.error }; + } + + return { success: false, data: [], error: '获取K线数据失败' }; + } catch (error) { + console.error('[StockDetailService] getKlineData 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 获取分时数据(使用 latest-minute API 获取最新交易日数据) + * 同时获取昨收价用于涨跌幅计算 + * @param {string} code - 股票代码 + * @returns {Promise} { success: true, data: [...minuteData], prevClose: number } + */ + getMinuteData: async (code) => { + try { + const pureCode = stockDetailService.getPureCode(code); + + console.log('[StockDetailService] 获取分时数据:', { code, pureCode }); + + // 并行获取分时数据和昨收价 + // 注意:market/trade API 返回数据按日期升序排列,最新数据在数组末尾 + const [minuteResponse, tradeResponse] = await Promise.all([ + apiRequest(`/api/stock/${pureCode}/latest-minute`), + apiRequest(`/api/market/trade/${pureCode}?limit=60`), // 获取最近60天交易数据 + ]); + + // 从 market/trade 获取昨收价(取最新一天的 pre_close) + let prevClose = 0; + if (tradeResponse.success && tradeResponse.data && tradeResponse.data.length > 0) { + // 取数组最后一条(最新的交易数据),其 pre_close 就是昨收价 + const latestTrade = tradeResponse.data[tradeResponse.data.length - 1]; + prevClose = latestTrade.pre_close || 0; + console.log('[StockDetailService] 昨收价:', prevClose, '日期:', latestTrade.date); + } + + if (minuteResponse && minuteResponse.data) { + console.log('[StockDetailService] 分时数据点数:', minuteResponse.data?.length || 0); + + // 转换分钟K线数据为分时格式,并计算累计均价 + let totalAmount = 0; + let totalVolume = 0; + + const minuteData = minuteResponse.data.map(item => { + const price = item.close || 0; + const volume = item.volume || 0; + const amount = item.amount || (price * volume); + + totalAmount += amount; + totalVolume += volume; + const avgPrice = totalVolume > 0 ? totalAmount / totalVolume : price; + + return { + time: item.time || '', + price: price, + avg_price: avgPrice, + volume: volume, + open: item.open || 0, + high: item.high || 0, + low: item.low || 0, + close: item.close || 0, + prev_close: prevClose, // 每条数据都带上昨收价 + }; + }); + + return { + success: true, + data: minuteData, + stockName: minuteResponse.name, + tradeDate: minuteResponse.trade_date, + prevClose: prevClose, // 返回昨收价 + }; + } + + if (minuteResponse && minuteResponse.error) { + return { success: false, data: [], error: minuteResponse.error }; + } + + return { success: false, data: [], error: '获取分时数据失败' }; + } catch (error) { + console.error('[StockDetailService] getMinuteData 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 获取日K线数据 + * @param {string} code - 股票代码 + * @param {string} eventTime - 可选的事件时间 + * @returns {Promise} { success: true, data: [...klineData] } + */ + getDailyKline: async (code, eventTime = null) => { + return stockDetailService.getKlineData(code, 'daily', eventTime); + }, + + /** + * 获取周K线数据 + * @param {string} code - 股票代码 + * @returns {Promise} { success: true, data: [...klineData] } + */ + getWeeklyKline: async (code) => { + return stockDetailService.getKlineData(code, 'weekly'); + }, + + /** + * 获取月K线数据 + * @param {string} code - 股票代码 + * @returns {Promise} { success: true, data: [...klineData] } + */ + getMonthlyKline: async (code) => { + return stockDetailService.getKlineData(code, 'monthly'); + }, + + /** + * 搜索股票 + * @param {string} keyword - 搜索关键词(代码或名称) + * @returns {Promise} { success: true, data: [...stocks] } + */ + searchStocks: async (keyword) => { + try { + if (!keyword || keyword.trim().length < 1) { + return { success: true, data: [] }; + } + + console.log('[StockDetailService] 搜索股票:', keyword); + + const response = await apiRequest(`/api/stock/search?q=${encodeURIComponent(keyword)}`); + + if (response.success) { + console.log('[StockDetailService] 搜索结果:', response.data?.length || 0); + } + + return response; + } catch (error) { + console.error('[StockDetailService] searchStocks 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 格式化股票代码 + * @param {string} code - 原始股票代码 + * @returns {string} 格式化后的代码(如 000001.SZ) + */ + formatStockCode: (code) => { + const pureCode = String(code).match(/\d{6}/)?.[0]; + if (!pureCode) return code; + + // 根据首位判断交易所 + const firstDigit = pureCode[0]; + if (['6', '5'].includes(firstDigit)) { + return `${pureCode}.SH`; // 上交所 + } else if (['0', '3', '1', '2'].includes(firstDigit)) { + return `${pureCode}.SZ`; // 深交所 + } else if (['8', '4'].includes(firstDigit)) { + return `${pureCode}.BJ`; // 北交所 + } + + return pureCode; + }, + + /** + * 获取纯股票代码(不含交易所后缀) + * @param {string} code - 股票代码 + * @returns {string} 6位数字代码 + */ + getPureCode: (code) => { + const match = String(code).match(/\d{6}/); + return match ? match[0] : code; + }, + + /** + * 获取交易所标识 + * @param {string} code - 股票代码 + * @returns {string} SH | SZ | BJ + */ + getExchange: (code) => { + const pureCode = stockDetailService.getPureCode(code); + const firstDigit = pureCode[0]; + + if (['6', '5'].includes(firstDigit)) return 'SH'; + if (['0', '3', '1', '2'].includes(firstDigit)) return 'SZ'; + if (['8', '4'].includes(firstDigit)) return 'BJ'; + + return 'SH'; + }, + + /** + * 判断是否为指数 + * @param {string} code - 代码 + * @returns {boolean} + */ + isIndex: (code) => { + const pureCode = stockDetailService.getPureCode(code); + // 常见指数代码 + const indexCodes = ['000001', '399001', '399006', '000300', '000016', '000905']; + return indexCodes.includes(pureCode) || pureCode.startsWith('88'); + }, + + /** + * 获取涨幅分析数据 + * 返回涨幅超过5%的日期的异动分析 + * @param {string} code - 股票代码 + * @param {string} startDate - 开始日期(可选) + * @param {string} endDate - 结束日期(可选) + * @returns {Promise} { success: true, data: [...analysisData] } + */ + getRiseAnalysis: async (code, startDate = null, endDate = null) => { + try { + const pureCode = stockDetailService.getPureCode(code); + console.log('[StockDetailService] 获取涨幅分析:', { code, pureCode }); + + let url = `/api/market/rise-analysis/${pureCode}`; + const params = []; + if (startDate) params.push(`start_date=${startDate}`); + if (endDate) params.push(`end_date=${endDate}`); + if (params.length > 0) url += `?${params.join('&')}`; + + const response = await apiRequest(url); + + if (response.success && response.data) { + console.log('[StockDetailService] 涨幅分析数据:', response.data.length, '条'); + return { + success: true, + data: response.data.map(item => ({ + stock_code: item.stock_code, + stock_name: item.stock_name, + trade_date: item.trade_date, + rise_rate: item.rise_rate || item.change_pct, + close_price: item.close_price || item.close, + volume: item.volume, + amount: item.amount, + main_business: item.main_business, + rise_reason_brief: item.rise_reason_brief, + rise_reason_detail: item.rise_reason_detail, + announcements: item.announcements, + update_time: item.update_time, + })), + }; + } + + return { success: false, data: [], error: response.error || '获取涨幅分析失败' }; + } catch (error) { + console.error('[StockDetailService] getRiseAnalysis 错误:', error); + return { success: false, data: [], error: error.message }; + } + }, + + /** + * 标准化K线数据字段名称 + * 将API返回的不同字段名统一为组件期望的格式 + * @param {Array} data - 原始数据 + * @param {string} type - 数据类型 minute | timeline | daily | weekly | monthly + * @returns {Array} 标准化后的数据 + */ + normalizeKlineData: (data, type) => { + if (!data || !Array.isArray(data)) { + return []; + } + + // timeline 类型:分时走势数据(包含均价线) + if (type === 'timeline') { + return data.map(item => ({ + time: item.time || '', + price: item.price ?? 0, + avg_price: item.avg_price ?? 0, + volume: item.volume ?? 0, + change_percent: item.change_percent ?? 0, + })); + } + + // minute 类型:分钟K线数据 + if (type === 'minute') { + // 分时数据标准化 + return data.map(item => ({ + time: item.time || '', + price: item.price ?? item.close ?? item.current_price ?? 0, + avg_price: item.avg_price ?? item.average_price ?? 0, + volume: item.volume ?? item.vol ?? 0, + high: item.high ?? item.high_price ?? 0, + low: item.low ?? item.low_price ?? 0, + open: item.open ?? item.open_price ?? 0, + close: item.close ?? item.close_price ?? 0, + prev_close: item.prev_close ?? item.pre_close ?? 0, + change_percent: item.change_percent ?? item.changePct ?? 0, + })); + } + + // K线数据标准化(日/周/月) + return data.map(item => ({ + date: item.date || item.trade_date || item.time || '', + open: item.open ?? item.open_price ?? 0, + close: item.close ?? item.close_price ?? 0, + high: item.high ?? item.high_price ?? 0, + low: item.low ?? item.low_price ?? 0, + volume: item.volume ?? item.vol ?? 0, + prev_close: item.prev_close ?? item.pre_close ?? 0, + change_percent: item.change_percent ?? item.changePct ?? 0, + })); + }, +}; + +export default stockDetailService; diff --git a/MeAgent/src/services/watchlistService.js b/MeAgent/src/services/watchlistService.js new file mode 100644 index 00000000..28fcfe03 --- /dev/null +++ b/MeAgent/src/services/watchlistService.js @@ -0,0 +1,184 @@ +/** + * 自选股服务层 + * 管理用户的自选股和自选事件 + */ + +import { apiRequest } from './api'; + +/** + * 自选股服务 + */ +export const watchlistService = { + // ============ 自选股相关 ============ + + /** + * 获取用户自选股列表 + * @returns {Promise} { success: true, data: [{ id, stock_code, stock_name, created_at }] } + */ + getWatchlist: async () => { + try { + console.log('[WatchlistService] 获取自选股列表'); + const response = await apiRequest('/api/account/watchlist'); + + if (response.success) { + console.log('[WatchlistService] 自选股数量:', response.data?.length || 0); + } + + return response; + } catch (error) { + console.error('[WatchlistService] getWatchlist 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 添加股票到自选股 + * @param {string} stockCode - 股票代码 + * @param {string} stockName - 股票名称(可选) + * @returns {Promise} { success: true, data: { id } } + */ + addToWatchlist: async (stockCode, stockName = '') => { + try { + console.log('[WatchlistService] 添加自选股:', stockCode, stockName); + + const response = await apiRequest('/api/account/watchlist', { + method: 'POST', + body: JSON.stringify({ + stock_code: stockCode, + stock_name: stockName, + }), + }); + + if (response.success) { + console.log('[WatchlistService] 添加成功:', response.data); + } + + return response; + } catch (error) { + console.error('[WatchlistService] addToWatchlist 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 从自选股移除股票 + * @param {string} stockCode - 股票代码 + * @returns {Promise} { success: true } + */ + removeFromWatchlist: async (stockCode) => { + try { + console.log('[WatchlistService] 移除自选股:', stockCode); + + const response = await apiRequest(`/api/account/watchlist/${stockCode}`, { + method: 'DELETE', + }); + + if (response.success) { + console.log('[WatchlistService] 移除成功'); + } + + return response; + } catch (error) { + console.error('[WatchlistService] removeFromWatchlist 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 获取自选股实时行情 + * @returns {Promise} + * { success: true, data: [{ stock_code, stock_name, current_price, change_percent, ... }] } + */ + getWatchlistRealtime: async () => { + try { + console.log('[WatchlistService] 获取自选股实时行情'); + + const response = await apiRequest('/api/account/watchlist/realtime'); + + if (response.success) { + console.log('[WatchlistService] 实时行情数量:', response.data?.length || 0); + } + + return response; + } catch (error) { + console.error('[WatchlistService] getWatchlistRealtime 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 检查股票是否在自选中 + * @param {string} stockCode - 股票代码 + * @param {Array} watchlist - 自选股列表 + * @returns {boolean} + */ + isInWatchlist: (stockCode, watchlist = []) => { + // 标准化股票代码(提取6位数字) + const normalizeCode = (code) => { + const match = String(code).match(/\d{6}/); + return match ? match[0] : code; + }; + + const normalizedCode = normalizeCode(stockCode); + return watchlist.some(item => normalizeCode(item.stock_code) === normalizedCode); + }, + + // ============ 自选事件相关 ============ + + /** + * 获取用户关注的事件列表 + * @returns {Promise} { success: true, data: [...events] } + */ + getFollowingEvents: async () => { + try { + console.log('[WatchlistService] 获取关注事件列表'); + + const response = await apiRequest('/api/account/events/following'); + + if (response.success) { + console.log('[WatchlistService] 关注事件数量:', response.data?.length || 0); + } + + return response; + } catch (error) { + console.error('[WatchlistService] getFollowingEvents 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 切换事件关注状态 + * @param {number} eventId - 事件ID + * @returns {Promise} { success: true, data: { is_following, follower_count } } + */ + toggleEventFollow: async (eventId) => { + try { + console.log('[WatchlistService] 切换事件关注:', eventId); + + const response = await apiRequest(`/api/events/${eventId}/follow`, { + method: 'POST', + }); + + if (response.success) { + console.log('[WatchlistService] 关注状态:', response.data); + } + + return response; + } catch (error) { + console.error('[WatchlistService] toggleEventFollow 错误:', error); + return { success: false, error: error.message }; + } + }, + + /** + * 检查事件是否被关注 + * @param {number} eventId - 事件ID + * @param {Array} followingEvents - 关注的事件列表 + * @returns {boolean} + */ + isEventFollowed: (eventId, followingEvents = []) => { + return followingEvents.some(event => event.id === eventId); + }, +}; + +export default watchlistService; diff --git a/MeAgent/src/services/websocketService.js b/MeAgent/src/services/websocketService.js new file mode 100644 index 00000000..0c8e8cf7 --- /dev/null +++ b/MeAgent/src/services/websocketService.js @@ -0,0 +1,556 @@ +/** + * WebSocket 实时行情服务 + * 支持上交所(SSE)和深交所(SZSE)双通道 + */ + +import { AppState } from 'react-native'; + +// WebSocket 服务器地址 +const WS_ENDPOINTS = { + sse: 'wss://api.valuefrontier.cn/ws/sse', // 上交所 + szse: 'wss://api.valuefrontier.cn/ws/szse', // 深交所 +}; + +// 心跳间隔 (毫秒) +const HEARTBEAT_INTERVAL = 30000; + +// 重连延迟 (毫秒) +const RECONNECT_DELAYS = [1000, 2000, 5000, 10000, 30000]; + +// 连接状态 +const ConnectionState = { + DISCONNECTED: 'disconnected', + CONNECTING: 'connecting', + CONNECTED: 'connected', + RECONNECTING: 'reconnecting', +}; + +/** + * WebSocket 连接管理器 + */ +class WebSocketManager { + constructor(exchange) { + this.exchange = exchange; + this.url = WS_ENDPOINTS[exchange]; + this.ws = null; + this.state = ConnectionState.DISCONNECTED; + this.reconnectAttempts = 0; + this.heartbeatTimer = null; + this.reconnectTimer = null; + this.subscriptions = new Set(); + this.messageHandlers = new Set(); + this.stateHandlers = new Set(); + this.lastPong = null; + } + + /** + * 连接 WebSocket + */ + connect() { + if (this.state === ConnectionState.CONNECTING || this.state === ConnectionState.CONNECTED) { + return; + } + + this.state = ConnectionState.CONNECTING; + this._notifyStateChange(); + + try { + this.ws = new WebSocket(this.url); + + this.ws.onopen = () => { + console.log(`[WS-${this.exchange}] 连接成功`); + this.state = ConnectionState.CONNECTED; + this.reconnectAttempts = 0; + this._notifyStateChange(); + this._startHeartbeat(); + this._resubscribe(); + }; + + this.ws.onmessage = (event) => { + this._handleMessage(event.data); + }; + + this.ws.onerror = (error) => { + console.error(`[WS-${this.exchange}] 错误:`, error.message); + }; + + this.ws.onclose = (event) => { + console.log(`[WS-${this.exchange}] 连接关闭:`, event.code, event.reason); + this._stopHeartbeat(); + this.ws = null; + + if (this.state !== ConnectionState.DISCONNECTED) { + this.state = ConnectionState.RECONNECTING; + this._notifyStateChange(); + this._scheduleReconnect(); + } + }; + } catch (error) { + console.error(`[WS-${this.exchange}] 创建连接失败:`, error); + this._scheduleReconnect(); + } + } + + /** + * 断开连接 + */ + disconnect() { + this.state = ConnectionState.DISCONNECTED; + this._stopHeartbeat(); + this._cancelReconnect(); + + if (this.ws) { + this.ws.close(1000, 'Client disconnect'); + this.ws = null; + } + + this._notifyStateChange(); + } + + /** + * 订阅股票行情 + * @param {string[]} codes - 股票代码列表 + */ + subscribe(codes) { + if (!Array.isArray(codes)) { + codes = [codes]; + } + + codes.forEach(code => this.subscriptions.add(code)); + + if (this.state === ConnectionState.CONNECTED && this.ws) { + this._sendSubscribe(codes); + } + } + + /** + * 取消订阅 + * @param {string[]} codes - 股票代码列表 + */ + unsubscribe(codes) { + if (!Array.isArray(codes)) { + codes = [codes]; + } + + codes.forEach(code => this.subscriptions.delete(code)); + + if (this.state === ConnectionState.CONNECTED && this.ws) { + this._sendUnsubscribe(codes); + } + } + + /** + * 添加消息处理器 + * @param {function} handler - 消息处理函数 + */ + addMessageHandler(handler) { + this.messageHandlers.add(handler); + return () => this.messageHandlers.delete(handler); + } + + /** + * 添加状态变化处理器 + * @param {function} handler - 状态处理函数 + */ + addStateHandler(handler) { + this.stateHandlers.add(handler); + return () => this.stateHandlers.delete(handler); + } + + /** + * 获取当前连接状态 + */ + getState() { + return this.state; + } + + /** + * 是否已连接 + */ + isConnected() { + return this.state === ConnectionState.CONNECTED; + } + + // ============ 私有方法 ============ + + _sendSubscribe(codes) { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return; + + const message = JSON.stringify({ + action: 'subscribe', + codes: codes, + }); + this.ws.send(message); + console.log(`[WS-${this.exchange}] 订阅:`, codes); + } + + _sendUnsubscribe(codes) { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return; + + const message = JSON.stringify({ + action: 'unsubscribe', + codes: codes, + }); + this.ws.send(message); + console.log(`[WS-${this.exchange}] 取消订阅:`, codes); + } + + _resubscribe() { + if (this.subscriptions.size > 0) { + const codes = Array.from(this.subscriptions); + this._sendSubscribe(codes); + } + } + + _handleMessage(data) { + try { + // 处理 pong 响应 + if (data === 'pong') { + this.lastPong = Date.now(); + return; + } + + const message = JSON.parse(data); + + // 通知所有处理器 + this.messageHandlers.forEach(handler => { + try { + handler(message, this.exchange); + } catch (error) { + console.error(`[WS-${this.exchange}] 消息处理器错误:`, error); + } + }); + } catch (error) { + console.error(`[WS-${this.exchange}] 解析消息失败:`, error); + } + } + + _startHeartbeat() { + this._stopHeartbeat(); + this.heartbeatTimer = setInterval(() => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.send('ping'); + } + }, HEARTBEAT_INTERVAL); + } + + _stopHeartbeat() { + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer); + this.heartbeatTimer = null; + } + } + + _scheduleReconnect() { + this._cancelReconnect(); + + const delay = RECONNECT_DELAYS[ + Math.min(this.reconnectAttempts, RECONNECT_DELAYS.length - 1) + ]; + + console.log(`[WS-${this.exchange}] ${delay}ms 后重连 (第${this.reconnectAttempts + 1}次)`); + + this.reconnectTimer = setTimeout(() => { + this.reconnectAttempts++; + this.connect(); + }, delay); + } + + _cancelReconnect() { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + this.reconnectTimer = null; + } + } + + _notifyStateChange() { + this.stateHandlers.forEach(handler => { + try { + handler(this.state, this.exchange); + } catch (error) { + console.error(`[WS-${this.exchange}] 状态处理器错误:`, error); + } + }); + } +} + +/** + * 统一的 WebSocket 行情服务 + * 自动管理上交所和深交所双通道 + */ +class RealtimeQuoteService { + constructor() { + this.managers = { + sse: new WebSocketManager('sse'), + szse: new WebSocketManager('szse'), + }; + this.quoteHandlers = new Set(); + this.stateHandlers = new Set(); + this.appStateSubscription = null; + this._initialized = false; + } + + /** + * 初始化服务 + */ + initialize() { + if (this._initialized) return; + this._initialized = true; + + // 监听 App 前后台切换 + this.appStateSubscription = AppState.addEventListener('change', (nextAppState) => { + if (nextAppState === 'active') { + console.log('[RealtimeQuote] App 进入前台,重连 WebSocket'); + this.connect(); + } else if (nextAppState === 'background') { + console.log('[RealtimeQuote] App 进入后台,断开 WebSocket'); + this.disconnect(); + } + }); + + // 设置消息处理器 + Object.values(this.managers).forEach(manager => { + manager.addMessageHandler((message, exchange) => { + this._handleQuoteMessage(message, exchange); + }); + + manager.addStateHandler((state, exchange) => { + this._notifyStateChange(); + }); + }); + } + + /** + * 连接所有通道 + */ + connect() { + this.initialize(); + Object.values(this.managers).forEach(manager => manager.connect()); + } + + /** + * 断开所有通道 + */ + disconnect() { + Object.values(this.managers).forEach(manager => manager.disconnect()); + } + + /** + * 订阅股票行情 + * @param {string[]} codes - 股票代码列表 + */ + subscribe(codes) { + if (!Array.isArray(codes)) { + codes = [codes]; + } + + // 按交易所分类 + const sseCodes = []; + const szseCodes = []; + + codes.forEach(code => { + const exchange = this._getExchange(code); + if (exchange === 'sse') { + sseCodes.push(code); + } else { + szseCodes.push(code); + } + }); + + if (sseCodes.length > 0) { + this.managers.sse.subscribe(sseCodes); + } + if (szseCodes.length > 0) { + this.managers.szse.subscribe(szseCodes); + } + } + + /** + * 取消订阅 + * @param {string[]} codes - 股票代码列表 + */ + unsubscribe(codes) { + if (!Array.isArray(codes)) { + codes = [codes]; + } + + const sseCodes = []; + const szseCodes = []; + + codes.forEach(code => { + const exchange = this._getExchange(code); + if (exchange === 'sse') { + sseCodes.push(code); + } else { + szseCodes.push(code); + } + }); + + if (sseCodes.length > 0) { + this.managers.sse.unsubscribe(sseCodes); + } + if (szseCodes.length > 0) { + this.managers.szse.unsubscribe(szseCodes); + } + } + + /** + * 添加行情数据处理器 + * @param {function} handler - 处理函数 (quotes) => void + * @returns {function} 取消订阅函数 + */ + addQuoteHandler(handler) { + this.quoteHandlers.add(handler); + return () => this.quoteHandlers.delete(handler); + } + + /** + * 添加连接状态处理器 + * @param {function} handler - 处理函数 (state) => void + * @returns {function} 取消订阅函数 + */ + addStateHandler(handler) { + this.stateHandlers.add(handler); + return () => this.stateHandlers.delete(handler); + } + + /** + * 获取连接状态 + */ + getConnectionState() { + const sseState = this.managers.sse.getState(); + const szseState = this.managers.szse.getState(); + + if (sseState === ConnectionState.CONNECTED && szseState === ConnectionState.CONNECTED) { + return 'connected'; + } + if (sseState === ConnectionState.CONNECTING || szseState === ConnectionState.CONNECTING) { + return 'connecting'; + } + if (sseState === ConnectionState.RECONNECTING || szseState === ConnectionState.RECONNECTING) { + return 'reconnecting'; + } + return 'disconnected'; + } + + /** + * 是否已连接 + */ + isConnected() { + return this.managers.sse.isConnected() || this.managers.szse.isConnected(); + } + + /** + * 销毁服务 + */ + destroy() { + this.disconnect(); + if (this.appStateSubscription) { + this.appStateSubscription.remove(); + this.appStateSubscription = null; + } + this.quoteHandlers.clear(); + this.stateHandlers.clear(); + this._initialized = false; + } + + // ============ 私有方法 ============ + + /** + * 根据股票代码判断交易所 + */ + _getExchange(code) { + // 提取纯数字代码 + const numericCode = String(code).replace(/\D/g, ''); + + // 上交所: 6开头 + // 深交所: 0、3开头 + if (numericCode.startsWith('6')) { + return 'sse'; + } + return 'szse'; + } + + /** + * 处理行情消息 + */ + _handleQuoteMessage(message, exchange) { + // 消息格式转换 + let quotes = {}; + + if (message.type === 'quote' && message.data) { + // 单条行情 + const data = message.data; + const code = data.stock_code || data.code; + if (code) { + quotes[code] = this._normalizeQuote(data); + } + } else if (message.type === 'quotes' && Array.isArray(message.data)) { + // 批量行情 + message.data.forEach(item => { + const code = item.stock_code || item.code; + if (code) { + quotes[code] = this._normalizeQuote(item); + } + }); + } else if (message.stock_code || message.code) { + // 直接是行情数据 + const code = message.stock_code || message.code; + quotes[code] = this._normalizeQuote(message); + } + + // 通知处理器 + if (Object.keys(quotes).length > 0) { + this.quoteHandlers.forEach(handler => { + try { + handler(quotes); + } catch (error) { + console.error('[RealtimeQuote] 处理器错误:', error); + } + }); + } + } + + /** + * 标准化行情数据 + */ + _normalizeQuote(data) { + return { + stock_code: data.stock_code || data.code, + stock_name: data.stock_name || data.name, + current_price: parseFloat(data.current_price || data.price || data.current || 0), + change_percent: parseFloat(data.change_percent || data.pct_chg || data.change_pct || 0), + change_amount: parseFloat(data.change_amount || data.change || 0), + volume: parseInt(data.volume || data.vol || 0, 10), + amount: parseFloat(data.amount || data.turnover || 0), + open: parseFloat(data.open || data.open_price || 0), + high: parseFloat(data.high || data.high_price || 0), + low: parseFloat(data.low || data.low_price || 0), + pre_close: parseFloat(data.pre_close || data.prev_close || 0), + bid_prices: data.bid_prices || data.bidPrices || [], + bid_volumes: data.bid_volumes || data.bidVolumes || [], + ask_prices: data.ask_prices || data.askPrices || [], + ask_volumes: data.ask_volumes || data.askVolumes || [], + update_time: data.update_time || data.time || new Date().toISOString(), + }; + } + + _notifyStateChange() { + const state = this.getConnectionState(); + this.stateHandlers.forEach(handler => { + try { + handler(state); + } catch (error) { + console.error('[RealtimeQuote] 状态处理器错误:', error); + } + }); + } +} + +// 导出单例 +export const realtimeQuoteService = new RealtimeQuoteService(); + +// 导出连接状态常量 +export { ConnectionState }; + +export default realtimeQuoteService; diff --git a/argon-pro-react-native/src/services/ztService.js b/MeAgent/src/services/ztService.js similarity index 100% rename from argon-pro-react-native/src/services/ztService.js rename to MeAgent/src/services/ztService.js diff --git a/argon-pro-react-native/src/store/index.js b/MeAgent/src/store/index.js similarity index 67% rename from argon-pro-react-native/src/store/index.js rename to MeAgent/src/store/index.js index 6516e53a..2554458b 100644 --- a/argon-pro-react-native/src/store/index.js +++ b/MeAgent/src/store/index.js @@ -4,10 +4,14 @@ import { configureStore } from '@reduxjs/toolkit'; import eventsReducer from './slices/eventsSlice'; +import watchlistReducer from './slices/watchlistSlice'; +import stockReducer from './slices/stockSlice'; const store = configureStore({ reducer: { events: eventsReducer, + watchlist: watchlistReducer, + stock: stockReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ diff --git a/argon-pro-react-native/src/store/slices/eventsSlice.js b/MeAgent/src/store/slices/eventsSlice.js similarity index 100% rename from argon-pro-react-native/src/store/slices/eventsSlice.js rename to MeAgent/src/store/slices/eventsSlice.js diff --git a/MeAgent/src/store/slices/stockSlice.js b/MeAgent/src/store/slices/stockSlice.js new file mode 100644 index 00000000..33f72ad4 --- /dev/null +++ b/MeAgent/src/store/slices/stockSlice.js @@ -0,0 +1,280 @@ +/** + * 股票详情状态管理 Slice + * 管理当前查看的股票详情、K线数据、盘口数据 + */ + +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; +import { stockDetailService } from '../../services/stockService'; + +// 初始状态 +const initialState = { + // 当前查看的股票 + currentStock: null, + // 分时数据 + minuteData: [], + // 分时数据对应的昨收价 + minutePrevClose: 0, + // K线数据 + klineData: { + daily: [], + weekly: [], + monthly: [], + }, + // 当前图表类型 + chartType: 'minute', // minute | daily | weekly | monthly + // 5档盘口 + orderBook: { + bidPrices: [], + bidVolumes: [], + askPrices: [], + askVolumes: [], + }, + // 加载状态 + loading: { + stock: false, + minute: false, + kline: false, + }, + // 错误信息 + error: null, + // 最后更新时间 + lastUpdate: null, +}; + +/** + * 获取股票详情 + */ +export const fetchStockDetail = createAsyncThunk( + 'stock/fetchStockDetail', + async (stockCode, { rejectWithValue }) => { + try { + const response = await stockDetailService.getStockDetail(stockCode); + + if (response.success) { + return response.data; + } else { + return rejectWithValue(response.error || '获取股票详情失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 获取分时数据 + */ +export const fetchMinuteData = createAsyncThunk( + 'stock/fetchMinuteData', + async (stockCode, { rejectWithValue }) => { + try { + console.log('[stockSlice] fetchMinuteData 开始:', stockCode); + const response = await stockDetailService.getMinuteData(stockCode); + + console.log('[stockSlice] fetchMinuteData 响应:', { + success: response.success, + dataLength: response.data?.length || 0, + prevClose: response.prevClose, + firstItem: response.data?.[0], + }); + + if (response.success) { + return { + data: response.data || [], + prevClose: response.prevClose || 0, + }; + } else { + return rejectWithValue(response.error || '获取分时数据失败'); + } + } catch (error) { + console.error('[stockSlice] fetchMinuteData 错误:', error); + return rejectWithValue(error.message); + } + } +); + +/** + * 获取K线数据 + */ +export const fetchKlineData = createAsyncThunk( + 'stock/fetchKlineData', + async ({ stockCode, type = 'daily', eventTime = null }, { rejectWithValue }) => { + try { + console.log('[stockSlice] fetchKlineData 开始:', { stockCode, type, eventTime }); + const response = await stockDetailService.getKlineData(stockCode, type, eventTime); + + console.log('[stockSlice] fetchKlineData 响应:', { + success: response.success, + dataLength: response.data?.length || 0, + firstItem: response.data?.[0], + }); + + if (response.success) { + return { type, data: response.data || [] }; + } else { + return rejectWithValue(response.error || '获取K线数据失败'); + } + } catch (error) { + console.error('[stockSlice] fetchKlineData 错误:', error); + return rejectWithValue(error.message); + } + } +); + +/** + * 加载股票详情页全部数据 + */ +export const loadStockPage = createAsyncThunk( + 'stock/loadStockPage', + async (stockCode, { dispatch }) => { + // 并行加载股票详情和分时数据 + await Promise.all([ + dispatch(fetchStockDetail(stockCode)), + dispatch(fetchMinuteData(stockCode)), + ]); + return stockCode; + } +); + +// Slice 定义 +const stockSlice = createSlice({ + name: 'stock', + initialState, + reducers: { + // 清除当前股票数据 + clearCurrentStock: (state) => { + state.currentStock = null; + state.minuteData = []; + state.minutePrevClose = 0; + state.klineData = { daily: [], weekly: [], monthly: [] }; + state.orderBook = { bidPrices: [], bidVolumes: [], askPrices: [], askVolumes: [] }; + state.error = null; + }, + // 设置图表类型 + setChartType: (state, action) => { + state.chartType = action.payload; + }, + // 更新实时报价(用于 WebSocket) + updateStockQuote: (state, action) => { + const quote = action.payload; + if (state.currentStock) { + state.currentStock = { + ...state.currentStock, + ...quote, + }; + // 更新盘口数据 + if (quote.bidPrices) { + state.orderBook.bidPrices = quote.bidPrices; + state.orderBook.bidVolumes = quote.bidVolumes || []; + } + if (quote.askPrices) { + state.orderBook.askPrices = quote.askPrices; + state.orderBook.askVolumes = quote.askVolumes || []; + } + state.lastUpdate = new Date().toISOString(); + } + }, + // 追加分时数据点 + appendMinuteData: (state, action) => { + const newPoint = action.payload; + if (state.minuteData.length > 0) { + // 检查是否是同一分钟的数据(更新)还是新分钟(追加) + const lastPoint = state.minuteData[state.minuteData.length - 1]; + if (lastPoint.time === newPoint.time) { + state.minuteData[state.minuteData.length - 1] = newPoint; + } else { + state.minuteData.push(newPoint); + } + } else { + state.minuteData.push(newPoint); + } + }, + // 清除错误 + clearError: (state) => { + state.error = null; + }, + }, + extraReducers: (builder) => { + builder + // 获取股票详情 + .addCase(fetchStockDetail.pending, (state) => { + state.loading.stock = true; + state.error = null; + }) + .addCase(fetchStockDetail.fulfilled, (state, action) => { + state.loading.stock = false; + state.currentStock = action.payload; + // 更新盘口数据 + if (action.payload) { + const { bidPrices, bidVolumes, askPrices, askVolumes } = action.payload; + state.orderBook = { + bidPrices: bidPrices || [], + bidVolumes: bidVolumes || [], + askPrices: askPrices || [], + askVolumes: askVolumes || [], + }; + } + state.lastUpdate = new Date().toISOString(); + }) + .addCase(fetchStockDetail.rejected, (state, action) => { + state.loading.stock = false; + state.error = action.payload; + }) + // 获取分时数据 + .addCase(fetchMinuteData.pending, (state) => { + state.loading.minute = true; + }) + .addCase(fetchMinuteData.fulfilled, (state, action) => { + state.loading.minute = false; + state.minuteData = action.payload.data; + state.minutePrevClose = action.payload.prevClose; + }) + .addCase(fetchMinuteData.rejected, (state, action) => { + state.loading.minute = false; + state.error = action.payload; + }) + // 获取K线数据 + .addCase(fetchKlineData.pending, (state) => { + state.loading.kline = true; + }) + .addCase(fetchKlineData.fulfilled, (state, action) => { + state.loading.kline = false; + const { type, data } = action.payload; + state.klineData[type] = data; + }) + .addCase(fetchKlineData.rejected, (state, action) => { + state.loading.kline = false; + state.error = action.payload; + }); + }, +}); + +// 导出 actions +export const { + clearCurrentStock, + setChartType, + updateStockQuote, + appendMinuteData, + clearError, +} = stockSlice.actions; + +// Selectors +export const selectCurrentStock = (state) => state.stock.currentStock; +export const selectMinuteData = (state) => state.stock.minuteData; +export const selectMinutePrevClose = (state) => state.stock.minutePrevClose; +export const selectKlineData = (state) => state.stock.klineData; +export const selectChartType = (state) => state.stock.chartType; +export const selectOrderBook = (state) => state.stock.orderBook; +export const selectStockLoading = (state) => state.stock.loading; +export const selectStockError = (state) => state.stock.error; + +// 获取当前图表类型的K线数据 +export const selectCurrentKlineData = (state) => { + const { chartType, minuteData, klineData } = state.stock; + if (chartType === 'minute') { + return minuteData; + } + return klineData[chartType] || []; +}; + +export default stockSlice.reducer; diff --git a/MeAgent/src/store/slices/watchlistSlice.js b/MeAgent/src/store/slices/watchlistSlice.js new file mode 100644 index 00000000..ab8d53f2 --- /dev/null +++ b/MeAgent/src/store/slices/watchlistSlice.js @@ -0,0 +1,325 @@ +/** + * 自选股状态管理 Slice + * 管理用户的自选股和自选事件 + */ + +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; +import { watchlistService } from '../../services/watchlistService'; + +// 初始状态 +const initialState = { + // 自选股列表 + stocks: [], + // 自选事件列表 + events: [], + // 实时行情 { code: QuoteData } + realtimeQuotes: {}, + // 加载状态 + loading: { + stocks: false, + events: false, + realtime: false, + adding: false, + removing: false, + }, + // 错误信息 + error: null, + // 最后更新时间 + lastUpdate: null, +}; + +/** + * 获取自选股列表 + */ +export const fetchWatchlist = createAsyncThunk( + 'watchlist/fetchWatchlist', + async (_, { rejectWithValue }) => { + try { + const response = await watchlistService.getWatchlist(); + + if (response.success) { + return response.data || []; + } else { + return rejectWithValue(response.error || '获取自选股列表失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 获取自选股实时行情 + */ +export const fetchWatchlistRealtime = createAsyncThunk( + 'watchlist/fetchWatchlistRealtime', + async (_, { rejectWithValue }) => { + try { + const response = await watchlistService.getWatchlistRealtime(); + + if (response.success) { + return response.data || []; + } else { + return rejectWithValue(response.error || '获取实时行情失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 添加股票到自选股 + */ +export const addToWatchlist = createAsyncThunk( + 'watchlist/addToWatchlist', + async ({ stockCode, stockName }, { rejectWithValue, dispatch }) => { + try { + const response = await watchlistService.addToWatchlist(stockCode, stockName); + + if (response.success) { + // 添加成功后刷新列表 + dispatch(fetchWatchlist()); + return { stockCode, stockName, id: response.data?.id }; + } else { + return rejectWithValue(response.error || '添加自选股失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 从自选股移除股票 + */ +export const removeFromWatchlist = createAsyncThunk( + 'watchlist/removeFromWatchlist', + async (stockCode, { rejectWithValue }) => { + try { + const response = await watchlistService.removeFromWatchlist(stockCode); + + if (response.success) { + return stockCode; + } else { + return rejectWithValue(response.error || '移除自选股失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 获取关注的事件列表 + */ +export const fetchFollowingEvents = createAsyncThunk( + 'watchlist/fetchFollowingEvents', + async (_, { rejectWithValue }) => { + try { + const response = await watchlistService.getFollowingEvents(); + + if (response.success) { + return response.data || []; + } else { + return rejectWithValue(response.error || '获取关注事件列表失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +/** + * 切换事件关注状态 + */ +export const toggleEventFollow = createAsyncThunk( + 'watchlist/toggleEventFollow', + async (eventId, { rejectWithValue, dispatch }) => { + try { + const response = await watchlistService.toggleEventFollow(eventId); + + if (response.success) { + // 刷新关注事件列表 + dispatch(fetchFollowingEvents()); + return { + eventId, + isFollowing: response.data?.is_following, + followerCount: response.data?.follower_count, + }; + } else { + return rejectWithValue(response.error || '操作失败'); + } + } catch (error) { + return rejectWithValue(error.message); + } + } +); + +// Slice 定义 +const watchlistSlice = createSlice({ + name: 'watchlist', + initialState, + reducers: { + // 清除错误 + clearError: (state) => { + state.error = null; + }, + // 更新实时行情(用于 WebSocket 推送) + updateRealtimeQuote: (state, action) => { + const { code, quote } = action.payload; + state.realtimeQuotes[code] = { + ...state.realtimeQuotes[code], + ...quote, + updateTime: new Date().toISOString(), + }; + }, + // 批量更新实时行情 + updateRealtimeQuotes: (state, action) => { + const quotes = action.payload; + Object.entries(quotes).forEach(([code, quote]) => { + state.realtimeQuotes[code] = { + ...state.realtimeQuotes[code], + ...quote, + updateTime: new Date().toISOString(), + }; + }); + state.lastUpdate = new Date().toISOString(); + }, + // 乐观更新 - 添加自选股 + optimisticAddStock: (state, action) => { + const { stockCode, stockName } = action.payload; + if (!state.stocks.find(s => s.stock_code === stockCode)) { + state.stocks.unshift({ + stock_code: stockCode, + stock_name: stockName, + created_at: new Date().toISOString(), + }); + } + }, + // 乐观更新 - 移除自选股 + optimisticRemoveStock: (state, action) => { + const stockCode = action.payload; + state.stocks = state.stocks.filter(s => { + const code1 = String(s.stock_code).match(/\d{6}/)?.[0]; + const code2 = String(stockCode).match(/\d{6}/)?.[0]; + return code1 !== code2; + }); + }, + }, + extraReducers: (builder) => { + builder + // 获取自选股列表 + .addCase(fetchWatchlist.pending, (state) => { + state.loading.stocks = true; + state.error = null; + }) + .addCase(fetchWatchlist.fulfilled, (state, action) => { + state.loading.stocks = false; + state.stocks = action.payload; + }) + .addCase(fetchWatchlist.rejected, (state, action) => { + state.loading.stocks = false; + state.error = action.payload; + }) + // 获取自选股实时行情 + .addCase(fetchWatchlistRealtime.pending, (state) => { + state.loading.realtime = true; + }) + .addCase(fetchWatchlistRealtime.fulfilled, (state, action) => { + state.loading.realtime = false; + // 将数组转换为对象 + const quotes = {}; + action.payload.forEach(item => { + quotes[item.stock_code] = item; + }); + state.realtimeQuotes = quotes; + state.lastUpdate = new Date().toISOString(); + }) + .addCase(fetchWatchlistRealtime.rejected, (state, action) => { + state.loading.realtime = false; + state.error = action.payload; + }) + // 添加自选股 + .addCase(addToWatchlist.pending, (state) => { + state.loading.adding = true; + }) + .addCase(addToWatchlist.fulfilled, (state) => { + state.loading.adding = false; + }) + .addCase(addToWatchlist.rejected, (state, action) => { + state.loading.adding = false; + state.error = action.payload; + }) + // 移除自选股 + .addCase(removeFromWatchlist.pending, (state) => { + state.loading.removing = true; + }) + .addCase(removeFromWatchlist.fulfilled, (state, action) => { + state.loading.removing = false; + // 从列表中移除 + const stockCode = action.payload; + state.stocks = state.stocks.filter(s => { + const code1 = String(s.stock_code).match(/\d{6}/)?.[0]; + const code2 = String(stockCode).match(/\d{6}/)?.[0]; + return code1 !== code2; + }); + }) + .addCase(removeFromWatchlist.rejected, (state, action) => { + state.loading.removing = false; + state.error = action.payload; + }) + // 获取关注事件列表 + .addCase(fetchFollowingEvents.pending, (state) => { + state.loading.events = true; + }) + .addCase(fetchFollowingEvents.fulfilled, (state, action) => { + state.loading.events = false; + state.events = action.payload; + }) + .addCase(fetchFollowingEvents.rejected, (state, action) => { + state.loading.events = false; + state.error = action.payload; + }) + // 切换事件关注 + .addCase(toggleEventFollow.fulfilled, (state, action) => { + const { eventId, isFollowing } = action.payload; + if (!isFollowing) { + // 取消关注时从列表移除 + state.events = state.events.filter(e => e.id !== eventId); + } + }); + }, +}); + +// 导出 actions +export const { + clearError, + updateRealtimeQuote, + updateRealtimeQuotes, + optimisticAddStock, + optimisticRemoveStock, +} = watchlistSlice.actions; + +// Selectors +export const selectWatchlistStocks = (state) => state.watchlist.stocks; +export const selectWatchlistEvents = (state) => state.watchlist.events; +export const selectRealtimeQuotes = (state) => state.watchlist.realtimeQuotes; +export const selectWatchlistLoading = (state) => state.watchlist.loading; +export const selectWatchlistError = (state) => state.watchlist.error; + +// 检查股票是否在自选中 +export const selectIsInWatchlist = (stockCode) => (state) => { + const normalizeCode = (code) => String(code).match(/\d{6}/)?.[0] || code; + const normalizedCode = normalizeCode(stockCode); + return state.watchlist.stocks.some( + item => normalizeCode(item.stock_code) === normalizedCode + ); +}; + +// 检查事件是否被关注 +export const selectIsEventFollowed = (eventId) => (state) => { + return state.watchlist.events.some(event => event.id === eventId); +}; + +export default watchlistSlice.reducer; diff --git a/argon-pro-react-native/src/theme/index.js b/MeAgent/src/theme/index.js similarity index 100% rename from argon-pro-react-native/src/theme/index.js rename to MeAgent/src/theme/index.js diff --git a/argon-pro-react-native/src/types/event.js b/MeAgent/src/types/event.js similarity index 100% rename from argon-pro-react-native/src/types/event.js rename to MeAgent/src/types/event.js diff --git a/argon-pro-react-native/src/utils/tradingDayUtils.js b/MeAgent/src/utils/tradingDayUtils.js similarity index 100% rename from argon-pro-react-native/src/utils/tradingDayUtils.js rename to MeAgent/src/utils/tradingDayUtils.js diff --git a/argon-pro-react-native/navigation/Menu.js b/argon-pro-react-native/navigation/Menu.js deleted file mode 100644 index 264ea381..00000000 --- a/argon-pro-react-native/navigation/Menu.js +++ /dev/null @@ -1,255 +0,0 @@ -import React from "react"; -import { - ScrollView, - StyleSheet, - Dimensions, - Image, - TouchableOpacity, - Linking, -} from "react-native"; -import { Block, Text, theme } from "galio-framework"; -import { useSafeArea } from "react-native-safe-area-context"; -import { Box, HStack, VStack, Icon, Pressable, Spinner } from "native-base"; -import { Ionicons } from "@expo/vector-icons"; -import { LinearGradient } from "expo-linear-gradient"; -import Images from "../constants/Images"; -import { DrawerItem as DrawerCustomItem } from "../components/index"; -import { useAuth } from "../src/contexts/AuthContext"; - -const { width } = Dimensions.get("screen"); - -// 金色主题色 -const GOLD_PRIMARY = '#D4AF37'; - -// 用户卡片组件 -const UserCard = ({ navigation }) => { - const { user, isLoggedIn, isLoading, subscription, logout } = useAuth(); - - const handleLoginPress = () => { - navigation.closeDrawer(); - // 使用 getParent 获取根导航器来导航到 Login - navigation.getParent()?.navigate("Login"); - }; - - const handleLogoutPress = async () => { - await logout(); - }; - - // 获取订阅显示文本 - const getSubscriptionText = () => { - if (!subscription || !subscription.is_active) { - return "免费用户"; - } - const typeMap = { pro: "Pro 会员", max: "Max 会员" }; - return typeMap[subscription.type] || "免费用户"; - }; - - if (isLoading) { - return ( - - - - - 加载中... - - - - ); - } - - if (!isLoggedIn) { - return ( - - - - - - - - - 登录/注册 - - - 登录解锁更多功能 - - - - - - - ); - } - - // 已登录状态 - return ( - - - - - {(user?.username || user?.nickname || "U").charAt(0).toUpperCase()} - - - - - {user?.nickname || user?.username || "用户"} - - - - - {getSubscriptionText()} - - - - - - - - - - ); -}; - -function CustomDrawerContent({ - drawerPosition, - navigation, - profile, - focused, - state, - ...rest -}) { - const insets = useSafeArea(); - const screens = [ - { title: "事件中心", navigateTo: "EventsDrawer" }, - { title: "市场热点", navigateTo: "MarketDrawer" }, - { title: "Home", navigateTo: "HomeDrawer" }, - { title: "Profile", navigateTo: "ProfileDrawer" }, - { title: "Account", navigateTo: "AccountDrawer" }, - { title: "Elements", navigateTo: "ElementsDrawer" }, - { title: "Articles", navigateTo: "ArticlesDrawer" }, - { title: "Settings", navigateTo: "SettingsDrawer" }, - ]; - return ( - - {/* 品牌头部 - 黑金主题 */} - - - - - - 价值前沿 - - - VALUE FRONTIER - - - - - - - {/* 用户卡片 */} - - - - - {screens.map((item, index) => { - return ( - - ); - })} - - - - DOCUMENTATION - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - paddingHorizontal: 28, - paddingBottom: theme.SIZES.BASE, - paddingTop: theme.SIZES.BASE * 3, - justifyContent: "center", - }, -}); - -export default CustomDrawerContent;