diff --git a/%ProgramData%/Microsoft/Windows/UUS/State/_active.uusver b/%ProgramData%/Microsoft/Windows/UUS/State/_active.uusver
new file mode 100644
index 0000000..8a2fff5
--- /dev/null
+++ b/%ProgramData%/Microsoft/Windows/UUS/State/_active.uusver
@@ -0,0 +1 @@
+1301.2403.14011.0
\ No newline at end of file
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..6e099d1
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,3 @@
+EXPO_PUBLIC_REACT_APP_GEMINI_KEY=your_gemini_key
+EXPO_PUBLIC_REACT_APP_SUPABASE_URL=your_supabase_url
+EXPO_PUBLIC_REACT_APP_ANON_KEY=your_app_anon_key
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 05647d5..dbe5e16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ yarn-error.*
# local env files
.env*.local
+.env
# typescript
*.tsbuildinfo
diff --git a/.husky/pre-commit 2 b/.husky/pre-commit 2
new file mode 100644
index 0000000..6ab4e0e
--- /dev/null
+++ b/.husky/pre-commit 2
@@ -0,0 +1 @@
+npx lint-staged .
diff --git a/App.js b/App.js
index f79587c..7f39931 100644
--- a/App.js
+++ b/App.js
@@ -1,20 +1,159 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ Image,
+ Text,
+ View,
+ TouchableOpacity,
+ StyleSheet,
+ Animated,
+ Dimensions,
+ PanResponder,
+} from 'react-native';
import { StatusBar } from 'expo-status-bar';
-import { StyleSheet, Text, View } from 'react-native';
+import Navigator from './src/navigator/Navigator';
+import { supabase } from './src/config/supabaseClient';
+import SignupScreen from './src/screens/SignupScreen';
+import { NavigationContainer } from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+import { Provider } from 'react-redux';
+import store from './src/store/storeConfig';
+import { userLogin } from './src/store/ducks/user';
+import ChatBotScreen from './src/screens/ChatbotScreen';
+import Modal from 'react-native-modal';
+
+
+const Stack = createStackNavigator();
+
+const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
+const TOOLTIP_SIZE = 60;
export default function App() {
+ const [session, setSession] = useState(null);
+ const [isChatVisible, setIsChatVisible] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+ const pan = useRef(new Animated.ValueXY()).current;
+
+
+ useEffect(() => {
+ const checkSession = async () => {
+ const {
+ data: { session },
+ } = await supabase.auth.getSession();
+ if (session) {
+ setSession(session);
+ store.dispatch(userLogin(session));
+ setIsLoggedIn(true);
+ setIsLoading(false);
+ } else {
+ setIsLoading(false);
+ }
+ };
+
+ checkSession();
+
+ const { data: listener } = supabase.auth.onAuthStateChange(
+ (_event, session) => {
+ setSession(session);
+ }
+ );
+
+ return () => {
+ listener.unsubscribe();
+ };
+ }, []);
+
+ const handlePress = () => {
+ setIsChatVisible(true);
+ };
+ return (
+
+
+
+
+ {isLoggedIn ? (
+
+ {() => ()}
+
+ ) : (isLoading ? (
+ ) : (
+
+ {() => ()}
+
+ ))}
+
+ {session && (
+ <>
+ setIsChatVisible(false)}
+ style={styles.modal}>
+
+ setIsChatVisible(false) }}
+ />
+
+
+ setIsChatVisible(true)}>
+
+
+
+
+ >
+ )}
+
+
+ );
+}
+
+
+export function LoadingScreen() {
return (
-
- Initial commit for Habit Tracker
-
+
+ Loading...
);
}
const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#fff',
+ tooltipButton: {
+ position: 'absolute',
+ bottom: 80,
+ right: 30,
+ zIndex: 1000,
+ },
+ tooltipCircle: {
+ width: 60,
+ height: 60,
+ borderRadius: 30,
+ backgroundColor: 'lightblue',
+ justifyContent: 'center',
alignItems: 'center',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.8,
+ shadowRadius: 2,
+ elevation: 5,
+ },
+ tooltipImage: {
+ width: 50,
+ height: 50,
+ resizeMode: 'contain',
+ },
+ modal: {
justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalContent: {
+ width: '100%',
+ height: '80%',
+ backgroundColor: 'white',
+ borderRadius: 10,
+ padding: 20,
},
});
diff --git a/README.md b/README.md
index fd7e8bc..b10398b 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,113 @@
-## **Project Description**
+# Live Timeless
+- [**Project Overview**](#project-overview)
+- [**Status**](#status)
+- [**Installation**](#installation)
+- [Project Structure](#project-structure)
+- [**Architecture and Design**](#architecture-and-design)
+- [**Usage**](#usage)
-We are excited to embark on an ambitious initiative to create a mobile app designed to promote healthy living and wellness optimization through:
+# **Project Overview**
-- Habit logging
-- Fitness trackers
-- Medical test analysis
-- Personalized AI coaching
+- **Description:** Live Timeless is a mobile application for IOS and Android designed to engage users in maintaining and tracking healthy habits. Live Timeless motivates consistent health habits with social engagement, helping users manage their health efficiently and potentially reduce life insurance premiums. The app streamlines health management with a user-friendly interface and AI-driven suggestions, saving time and enhancing habit adherence. This app was developed in response to the need for a more engaging and effective habit management tool.
-With a foundation in the CST program's Web and Mobile option, this app will be a fresh greenfield project iterating on an existing concept prototype called Live Timeless. Students will be encouraged to:
+# **Status:**
-- Reassess and redesign this app from the ground up
-- Pick an appropriate and modern tech stack
-- Integrate with public APIs
+As of the latest project update (May 27th, 2024), "Live Timeless" has successfully implemented the majority of its planned features, aligning closely with our initial scope and objectives:
-This initiative will serve as a cornerstone for a broader corporate wellness program, blending financial incentives with health optimization. The goal is to empower users with actionable insights into their wellness journey, guided by:
+- **Coaching Chatbot:** Fully integrated with Google's gemini pro model, the AI coaching chatbot is operational, offering personalized plans and actionable steps to help users achieve their health goals.
-- Fitness tracker data
-- Regular blood-work
-- Social groups
-- A personalized AI chatbot coach
-- A corporate wellness program
+- **User Account Management:**: All user account management features are complete and functioning as intended, making it GDPR (General data protection regulation) compliant. This includes:
-## **Programming Language(s)**
+- Secure registration and login processes.
+- Functionalities to delete accounts and download user data.
+- Email-based authentication with enhanced security through OAuth (SupaAuth).
-Students will be expected to assess the project and pick an appropriate tech stack based on the project requirements and their capabilities. There is a strong preference for modern, popular, and open-source technologies. Examples include:
+- **Habit Setting and Tracking**:
-- Flutter
-- React Native
-- Django
-- Laravel
+- Users can set and track various habit goals such as water intake and daily pushups.
+- The application supports the upload of progress photos and descriptions.
+- Users can create, post, view, and track the progress of their habits and see detailed reports including optional progress photos and descriptions.
-(All technologies chosen must be free and open-source)
+- **System Performance and Scalability**:
-Students will also be given the opportunity to use APIs of existing services to augment their app's capabilities. Examples include:
+The backend architecture, powered by Supabase, effectively handles real-time data management and user interactions, ensuring scalability and robust performance.
-- OAuth / Firebase for authentication
-- Apple HealthKit / Google Fit for fitness trackers
-- OpenAI GPT for AI customized coaching chatbots
-- Tesseract for document OCR
+- **User Experience and Interface**:
-Additionally, students will have the chance to build their own DevOps pipeline, striving for continuous integration and deployment to the app store and to the cloud. We will be using GitHub Workflows and Actions. The use of a Mono-Repo is encouraged.
+The user interface is clean, intuitive, and fully responsive, designed to cater to a broad demographic across various devices and platforms.
-## **Hardware/Software Requirements**
+- **Pending Implementation**:
-Development will utilize open-source tools, with students using their own computing resources. Fitness wearables and health data will be provided as needed.
+The only two components not fully implemented as planned is the community interaction feature and instagram posting. While users can share and interact with friends' habit feeds, the broader community functionalities such as searching for and joining community groups around specific health habits are still under development. The API for posting habits to instagram also requires a few weeks for approval, ultimately causing it to fall out of scope for this project's timeline
-## **Current Work/Arrangement**
+# **Installation**
-Transitioning from manual and fragmented wellness tracking, this project aims for a technological leap. The existing mobile app, built with React Native & Laravel, provides a valuable learning platform, but the goal is to innovate beyond its current capabilities, creating a new, more sophisticated solution. The project aims to digitize and enhance the wellness tracking process, currently reliant on manual inputs and disparate systems. By leveraging Agile methodologies and a robust DevOps pipeline, we intend to streamline development and deployment, ensuring a seamless and scalable solution.
+- **Prerequisites:** Make sure you have the latest version of Node.js installed
+- **Setup:** Build instructions are here: https://docs.google.com/document/d/1zsOa7byMlx2hp6cp83-3GDG_W2bQ26CHtWwud7Ewx5s/edit?usp=sharing
+- **Configuration:** the .env file is to be placed in the root folder and is of the following format:
+
+- API key for Gemini
+EXPO_PUBLIC_REACT_APP_GEMINI_KEY=your_gemini_api_key_here
+
+- Supabase URL for connecting to your database
+EXPO_PUBLIC_REACT_APP_SUPABASE_URL=https://your_supabase_url_here
+
+- Anonymous key for accessing Supabase services
+EXPO_PUBLIC_REACT_APP_ANON_KEY=your_supabase_anon_key_here
+
+# Project Structure
+
+- /src: Holds most of the files making up the application
+- /navigator: Contains the bottom navigation bar that helps navigate between screens
+- /assets: Contains all static resources like images, logos, and icon files.
+- /components: Reusable components used throughout the application. These components are mostly from the frontend of the old repo
+- /screens: Contains all the individual screens of the app, each representing a different UI section.
+- /services: Contains an example service for Stripe implementation (is not used in app yet since it is out of scope. It's there for future development if needed)
+- /utils: Utility functions and helpers to run in the app
+- App.js: The root component that houses the overall app structure.
+
+# **Architecture and Design**
+
+![Live Timeless ERD](assets/images/ERDSupabase.png)
+
+- **Technologies Used:** List of main technologies, frameworks, and libraries.
+
+- AI-Model: Google Generative AI (Gemini Pro): This AI model is used to generate Habit plans/schedules for Users to follow to reach their health goals.
+
+- Frontend Development: React Native (version 0.74): Is used for developing UI components which provide a seamless user experience across Android and IOS devices.
+
+- Backend Development: Supabase (in General Availability, version 2.43.1): This is a firebase alternative that provides a database to store and sync data in real-time among users which tracks habit data. Supabase also handles backend logic and API integrations. Lastly, supabase has built-in support for authentication including OAuth.
+
+- Emulation Framework:Expo Go (SDK 51): Will be used to emulate the app on both android and ios devices during development
+
+# **Usage**
+
+Below are some use cases among many for the application
+
+Creating a habit:
+- Navigate to the 'Habit' section in the app and click 'Add Habit'.
+- Enter details about the habit, such as 'Habit Name' (e.g., 'Drink Water'), 'Habit Description' (e.g., 'Stay hydrated by drinking 8 glasses of water daily'), and set an 'End Date' for the habit.
+
+Interacting with the AI Coach:
+- Open the 'AI Coach' by clicking on the circle with the chatbot icon in the bottom right
+- Input your current habit goals and receive guidance from Gemini AI
+- The AI is accessible when creating a habit as well, click 'generate habit schedule' to see a suggestion based on the habit you're creating
+
+Updating Posting Progress:
+- Access the 'Checklist' dashboard.
+- Select the habit for the day you completed the habit (this will cross it off the list).
+- Navigate to the 'Timeline' and create a new post. Select the habit you did for the day
+- Upload a description and progress photo optionally and submit the update.
+
+Viewing Friend Feeds:
+- Go to the 'Profile' section and select 'Find User'
+- search a user by their username and press 'Follow'
+- Go to the 'Timeline' tab.
+- View posts from friends including their habit updates and progress photos.
+- React with a heart or comment on their posts to engage with their progress.
+
+Registering and Managing Account:
+- Register for a new account by providing an email and creating a password.
+- Verify your email through the link sent to your email inbox.
+- Log in to access your new account.
+- Click the gear icon in the top right of 'Profile' to get your data, sign out, or delete your account
\ No newline at end of file
diff --git a/assets/icons/activity-selected.png b/assets/icons/activity-selected.png
new file mode 100644
index 0000000..bde3d83
Binary files /dev/null and b/assets/icons/activity-selected.png differ
diff --git a/assets/icons/activity.png b/assets/icons/activity.png
new file mode 100644
index 0000000..b9b0266
Binary files /dev/null and b/assets/icons/activity.png differ
diff --git a/assets/icons/add-photo.png b/assets/icons/add-photo.png
new file mode 100644
index 0000000..d78745e
Binary files /dev/null and b/assets/icons/add-photo.png differ
diff --git a/assets/icons/alert 2.png b/assets/icons/alert 2.png
new file mode 100644
index 0000000..ea9cdcb
Binary files /dev/null and b/assets/icons/alert 2.png differ
diff --git a/assets/icons/alert.png b/assets/icons/alert.png
new file mode 100644
index 0000000..ea9cdcb
Binary files /dev/null and b/assets/icons/alert.png differ
diff --git a/assets/icons/arrow-right 2.png b/assets/icons/arrow-right 2.png
new file mode 100644
index 0000000..712f921
Binary files /dev/null and b/assets/icons/arrow-right 2.png differ
diff --git a/assets/icons/arrow-right.png b/assets/icons/arrow-right.png
new file mode 100644
index 0000000..712f921
Binary files /dev/null and b/assets/icons/arrow-right.png differ
diff --git a/assets/icons/arrow-up-right.png b/assets/icons/arrow-up-right.png
new file mode 100644
index 0000000..28b6cc9
Binary files /dev/null and b/assets/icons/arrow-up-right.png differ
diff --git a/assets/icons/battery 2.png b/assets/icons/battery 2.png
new file mode 100644
index 0000000..51700e2
Binary files /dev/null and b/assets/icons/battery 2.png differ
diff --git a/assets/icons/battery.png b/assets/icons/battery.png
new file mode 100644
index 0000000..51700e2
Binary files /dev/null and b/assets/icons/battery.png differ
diff --git a/assets/icons/bed 2.png b/assets/icons/bed 2.png
new file mode 100644
index 0000000..3e60cfe
Binary files /dev/null and b/assets/icons/bed 2.png differ
diff --git a/assets/icons/bed.png b/assets/icons/bed.png
new file mode 100644
index 0000000..3e60cfe
Binary files /dev/null and b/assets/icons/bed.png differ
diff --git a/assets/icons/bolt 2.png b/assets/icons/bolt 2.png
new file mode 100644
index 0000000..e3775bd
Binary files /dev/null and b/assets/icons/bolt 2.png differ
diff --git a/assets/icons/bolt.png b/assets/icons/bolt.png
new file mode 100644
index 0000000..e3775bd
Binary files /dev/null and b/assets/icons/bolt.png differ
diff --git a/assets/icons/bookmark-bordered.png b/assets/icons/bookmark-bordered.png
new file mode 100644
index 0000000..c8bb887
Binary files /dev/null and b/assets/icons/bookmark-bordered.png differ
diff --git a/assets/icons/bookmark-selected 2.png b/assets/icons/bookmark-selected 2.png
new file mode 100644
index 0000000..ca4d1b0
Binary files /dev/null and b/assets/icons/bookmark-selected 2.png differ
diff --git a/assets/icons/bookmark-selected.png b/assets/icons/bookmark-selected.png
new file mode 100644
index 0000000..ca4d1b0
Binary files /dev/null and b/assets/icons/bookmark-selected.png differ
diff --git a/assets/icons/bookmark-white.png b/assets/icons/bookmark-white.png
new file mode 100644
index 0000000..407f967
Binary files /dev/null and b/assets/icons/bookmark-white.png differ
diff --git a/assets/icons/bookmark.png b/assets/icons/bookmark.png
new file mode 100644
index 0000000..e15fbc5
Binary files /dev/null and b/assets/icons/bookmark.png differ
diff --git a/assets/icons/bullseye 2.png b/assets/icons/bullseye 2.png
new file mode 100644
index 0000000..6a1f949
Binary files /dev/null and b/assets/icons/bullseye 2.png differ
diff --git a/assets/icons/bullseye.png b/assets/icons/bullseye.png
new file mode 100644
index 0000000..6a1f949
Binary files /dev/null and b/assets/icons/bullseye.png differ
diff --git a/assets/icons/calendar 2.png b/assets/icons/calendar 2.png
new file mode 100644
index 0000000..2d7c1d4
Binary files /dev/null and b/assets/icons/calendar 2.png differ
diff --git a/assets/icons/calendar-selected 2.png b/assets/icons/calendar-selected 2.png
new file mode 100644
index 0000000..f27d542
Binary files /dev/null and b/assets/icons/calendar-selected 2.png differ
diff --git a/assets/icons/calendar-selected.png b/assets/icons/calendar-selected.png
new file mode 100644
index 0000000..f27d542
Binary files /dev/null and b/assets/icons/calendar-selected.png differ
diff --git a/assets/icons/calendar.png b/assets/icons/calendar.png
new file mode 100644
index 0000000..2d7c1d4
Binary files /dev/null and b/assets/icons/calendar.png differ
diff --git a/assets/icons/check.png b/assets/icons/check.png
new file mode 100644
index 0000000..5a67216
Binary files /dev/null and b/assets/icons/check.png differ
diff --git a/assets/icons/circle-selected.png b/assets/icons/circle-selected.png
new file mode 100644
index 0000000..d299316
Binary files /dev/null and b/assets/icons/circle-selected.png differ
diff --git a/assets/icons/circle.png b/assets/icons/circle.png
new file mode 100644
index 0000000..a7de561
Binary files /dev/null and b/assets/icons/circle.png differ
diff --git a/assets/icons/clipboard-check-selected.png b/assets/icons/clipboard-check-selected.png
new file mode 100644
index 0000000..d5587c9
Binary files /dev/null and b/assets/icons/clipboard-check-selected.png differ
diff --git a/assets/icons/clipboard-check.png b/assets/icons/clipboard-check.png
new file mode 100644
index 0000000..3f3eb7b
Binary files /dev/null and b/assets/icons/clipboard-check.png differ
diff --git a/assets/icons/close-image.png b/assets/icons/close-image.png
new file mode 100644
index 0000000..89d8ef0
Binary files /dev/null and b/assets/icons/close-image.png differ
diff --git a/assets/icons/close.png b/assets/icons/close.png
new file mode 100644
index 0000000..dcc06ae
Binary files /dev/null and b/assets/icons/close.png differ
diff --git a/assets/icons/cog.png b/assets/icons/cog.png
new file mode 100644
index 0000000..97d3be2
Binary files /dev/null and b/assets/icons/cog.png differ
diff --git a/assets/icons/comment.png b/assets/icons/comment.png
new file mode 100644
index 0000000..8d7f172
Binary files /dev/null and b/assets/icons/comment.png differ
diff --git a/assets/icons/delete.png b/assets/icons/delete.png
new file mode 100644
index 0000000..32b417f
Binary files /dev/null and b/assets/icons/delete.png differ
diff --git a/assets/icons/edit.png b/assets/icons/edit.png
new file mode 100644
index 0000000..fff533b
Binary files /dev/null and b/assets/icons/edit.png differ
diff --git a/assets/icons/ellipse-separator.png b/assets/icons/ellipse-separator.png
new file mode 100644
index 0000000..5bb6b24
Binary files /dev/null and b/assets/icons/ellipse-separator.png differ
diff --git a/assets/icons/ellipse-vertical.png b/assets/icons/ellipse-vertical.png
new file mode 100644
index 0000000..56d5ca3
Binary files /dev/null and b/assets/icons/ellipse-vertical.png differ
diff --git a/assets/icons/ellipse.png b/assets/icons/ellipse.png
new file mode 100644
index 0000000..bfd971d
Binary files /dev/null and b/assets/icons/ellipse.png differ
diff --git a/assets/icons/envelope-blue 2.png b/assets/icons/envelope-blue 2.png
new file mode 100644
index 0000000..0dc5a25
Binary files /dev/null and b/assets/icons/envelope-blue 2.png differ
diff --git a/assets/icons/envelope-blue.png b/assets/icons/envelope-blue.png
new file mode 100644
index 0000000..0dc5a25
Binary files /dev/null and b/assets/icons/envelope-blue.png differ
diff --git a/assets/icons/envelope-white 2.png b/assets/icons/envelope-white 2.png
new file mode 100644
index 0000000..47bf0d5
Binary files /dev/null and b/assets/icons/envelope-white 2.png differ
diff --git a/assets/icons/envelope-white.png b/assets/icons/envelope-white.png
new file mode 100644
index 0000000..47bf0d5
Binary files /dev/null and b/assets/icons/envelope-white.png differ
diff --git a/assets/icons/exercise.png b/assets/icons/exercise.png
new file mode 100644
index 0000000..5e7311d
Binary files /dev/null and b/assets/icons/exercise.png differ
diff --git a/assets/icons/exit 2.png b/assets/icons/exit 2.png
new file mode 100644
index 0000000..499e50f
Binary files /dev/null and b/assets/icons/exit 2.png differ
diff --git a/assets/icons/exit.png b/assets/icons/exit.png
new file mode 100644
index 0000000..499e50f
Binary files /dev/null and b/assets/icons/exit.png differ
diff --git a/assets/icons/eye.png b/assets/icons/eye.png
new file mode 100644
index 0000000..b588640
Binary files /dev/null and b/assets/icons/eye.png differ
diff --git a/assets/icons/film-disabled.png b/assets/icons/film-disabled.png
new file mode 100644
index 0000000..f1ad635
Binary files /dev/null and b/assets/icons/film-disabled.png differ
diff --git a/assets/icons/film.png b/assets/icons/film.png
new file mode 100644
index 0000000..60170d1
Binary files /dev/null and b/assets/icons/film.png differ
diff --git a/assets/icons/full-ellipse.png b/assets/icons/full-ellipse.png
new file mode 100644
index 0000000..6145ef5
Binary files /dev/null and b/assets/icons/full-ellipse.png differ
diff --git a/assets/icons/heart-full.png b/assets/icons/heart-full.png
new file mode 100644
index 0000000..ec91fbb
Binary files /dev/null and b/assets/icons/heart-full.png differ
diff --git a/assets/icons/heart.png b/assets/icons/heart.png
new file mode 100644
index 0000000..903a560
Binary files /dev/null and b/assets/icons/heart.png differ
diff --git a/assets/icons/home-selected.png b/assets/icons/home-selected.png
new file mode 100644
index 0000000..c2870eb
Binary files /dev/null and b/assets/icons/home-selected.png differ
diff --git a/assets/icons/home.png b/assets/icons/home.png
new file mode 100644
index 0000000..ec30647
Binary files /dev/null and b/assets/icons/home.png differ
diff --git a/assets/icons/icon-comment.png b/assets/icons/icon-comment.png
new file mode 100644
index 0000000..a34ba35
Binary files /dev/null and b/assets/icons/icon-comment.png differ
diff --git a/assets/icons/icon-habit.png b/assets/icons/icon-habit.png
new file mode 100644
index 0000000..fa2f0ac
Binary files /dev/null and b/assets/icons/icon-habit.png differ
diff --git a/assets/icons/icon-privacy.png b/assets/icons/icon-privacy.png
new file mode 100644
index 0000000..e91c63c
Binary files /dev/null and b/assets/icons/icon-privacy.png differ
diff --git a/assets/icons/image-plus-disabled.png b/assets/icons/image-plus-disabled.png
new file mode 100644
index 0000000..a3d59eb
Binary files /dev/null and b/assets/icons/image-plus-disabled.png differ
diff --git a/assets/icons/image-plus.png b/assets/icons/image-plus.png
new file mode 100644
index 0000000..58d9947
Binary files /dev/null and b/assets/icons/image-plus.png differ
diff --git a/assets/icons/info 2.png b/assets/icons/info 2.png
new file mode 100644
index 0000000..788938d
Binary files /dev/null and b/assets/icons/info 2.png differ
diff --git a/assets/icons/info-selected 2.png b/assets/icons/info-selected 2.png
new file mode 100644
index 0000000..25ad122
Binary files /dev/null and b/assets/icons/info-selected 2.png differ
diff --git a/assets/icons/info-selected.png b/assets/icons/info-selected.png
new file mode 100644
index 0000000..25ad122
Binary files /dev/null and b/assets/icons/info-selected.png differ
diff --git a/assets/icons/info.png b/assets/icons/info.png
new file mode 100644
index 0000000..788938d
Binary files /dev/null and b/assets/icons/info.png differ
diff --git a/assets/icons/like.png b/assets/icons/like.png
new file mode 100644
index 0000000..0f078fa
Binary files /dev/null and b/assets/icons/like.png differ
diff --git a/assets/icons/liked.png b/assets/icons/liked.png
new file mode 100644
index 0000000..161bde6
Binary files /dev/null and b/assets/icons/liked.png differ
diff --git a/assets/icons/list 2.png b/assets/icons/list 2.png
new file mode 100644
index 0000000..1702bb0
Binary files /dev/null and b/assets/icons/list 2.png differ
diff --git a/assets/icons/list-selected 2.png b/assets/icons/list-selected 2.png
new file mode 100644
index 0000000..3ba592e
Binary files /dev/null and b/assets/icons/list-selected 2.png differ
diff --git a/assets/icons/list-selected.png b/assets/icons/list-selected.png
new file mode 100644
index 0000000..3ba592e
Binary files /dev/null and b/assets/icons/list-selected.png differ
diff --git a/assets/icons/list.png b/assets/icons/list.png
new file mode 100644
index 0000000..1702bb0
Binary files /dev/null and b/assets/icons/list.png differ
diff --git a/assets/icons/lock 2.png b/assets/icons/lock 2.png
new file mode 100644
index 0000000..d852669
Binary files /dev/null and b/assets/icons/lock 2.png differ
diff --git a/assets/icons/lock.png b/assets/icons/lock.png
new file mode 100644
index 0000000..d852669
Binary files /dev/null and b/assets/icons/lock.png differ
diff --git a/assets/icons/medal-achievements.png b/assets/icons/medal-achievements.png
new file mode 100644
index 0000000..6bfed41
Binary files /dev/null and b/assets/icons/medal-achievements.png differ
diff --git a/assets/icons/medal-bronze.png b/assets/icons/medal-bronze.png
new file mode 100644
index 0000000..d8697b0
Binary files /dev/null and b/assets/icons/medal-bronze.png differ
diff --git a/assets/icons/medal-gold.png b/assets/icons/medal-gold.png
new file mode 100644
index 0000000..8c76f41
Binary files /dev/null and b/assets/icons/medal-gold.png differ
diff --git a/assets/icons/medal-post.png b/assets/icons/medal-post.png
new file mode 100644
index 0000000..407f967
Binary files /dev/null and b/assets/icons/medal-post.png differ
diff --git a/assets/icons/medal-score.png b/assets/icons/medal-score.png
new file mode 100644
index 0000000..dd7b2b2
Binary files /dev/null and b/assets/icons/medal-score.png differ
diff --git a/assets/icons/medal-silver.png b/assets/icons/medal-silver.png
new file mode 100644
index 0000000..65988cf
Binary files /dev/null and b/assets/icons/medal-silver.png differ
diff --git a/assets/icons/medal.png b/assets/icons/medal.png
new file mode 100644
index 0000000..8c76f41
Binary files /dev/null and b/assets/icons/medal.png differ
diff --git a/assets/icons/message-dots.png b/assets/icons/message-dots.png
new file mode 100644
index 0000000..458fe3e
Binary files /dev/null and b/assets/icons/message-dots.png differ
diff --git a/assets/icons/omega 2.png b/assets/icons/omega 2.png
new file mode 100644
index 0000000..31c7a17
Binary files /dev/null and b/assets/icons/omega 2.png differ
diff --git a/assets/icons/omega.png b/assets/icons/omega.png
new file mode 100644
index 0000000..31c7a17
Binary files /dev/null and b/assets/icons/omega.png differ
diff --git a/assets/icons/pencil 2.png b/assets/icons/pencil 2.png
new file mode 100644
index 0000000..a19a078
Binary files /dev/null and b/assets/icons/pencil 2.png differ
diff --git a/assets/icons/pencil.png b/assets/icons/pencil.png
new file mode 100644
index 0000000..a19a078
Binary files /dev/null and b/assets/icons/pencil.png differ
diff --git a/assets/icons/play-button.png b/assets/icons/play-button.png
new file mode 100644
index 0000000..1ed4384
Binary files /dev/null and b/assets/icons/play-button.png differ
diff --git a/assets/icons/ray.png b/assets/icons/ray.png
new file mode 100644
index 0000000..21134cb
Binary files /dev/null and b/assets/icons/ray.png differ
diff --git a/assets/icons/score 2.png b/assets/icons/score 2.png
new file mode 100644
index 0000000..e994cd6
Binary files /dev/null and b/assets/icons/score 2.png differ
diff --git a/assets/icons/score.png b/assets/icons/score.png
new file mode 100644
index 0000000..e994cd6
Binary files /dev/null and b/assets/icons/score.png differ
diff --git a/assets/icons/share-profile.png b/assets/icons/share-profile.png
new file mode 100644
index 0000000..33178c3
Binary files /dev/null and b/assets/icons/share-profile.png differ
diff --git a/assets/icons/smartphone 2.png b/assets/icons/smartphone 2.png
new file mode 100644
index 0000000..c33d041
Binary files /dev/null and b/assets/icons/smartphone 2.png differ
diff --git a/assets/icons/smartphone.png b/assets/icons/smartphone.png
new file mode 100644
index 0000000..c33d041
Binary files /dev/null and b/assets/icons/smartphone.png differ
diff --git a/assets/icons/stakes 2.png b/assets/icons/stakes 2.png
new file mode 100644
index 0000000..3eb1ac5
Binary files /dev/null and b/assets/icons/stakes 2.png differ
diff --git a/assets/icons/stakes-selected.jpeg b/assets/icons/stakes-selected.jpeg
new file mode 100644
index 0000000..43bd339
Binary files /dev/null and b/assets/icons/stakes-selected.jpeg differ
diff --git a/assets/icons/stakes-selected.png b/assets/icons/stakes-selected.png
new file mode 100644
index 0000000..31554c4
Binary files /dev/null and b/assets/icons/stakes-selected.png differ
diff --git a/assets/icons/stakes.jpeg b/assets/icons/stakes.jpeg
new file mode 100644
index 0000000..558c1a9
Binary files /dev/null and b/assets/icons/stakes.jpeg differ
diff --git a/assets/icons/stakes.png b/assets/icons/stakes.png
new file mode 100644
index 0000000..3eb1ac5
Binary files /dev/null and b/assets/icons/stakes.png differ
diff --git a/assets/icons/star 2.png b/assets/icons/star 2.png
new file mode 100644
index 0000000..a9d184f
Binary files /dev/null and b/assets/icons/star 2.png differ
diff --git a/assets/icons/star.png b/assets/icons/star.png
new file mode 100644
index 0000000..a9d184f
Binary files /dev/null and b/assets/icons/star.png differ
diff --git a/assets/icons/store 2.png b/assets/icons/store 2.png
new file mode 100644
index 0000000..56d5263
Binary files /dev/null and b/assets/icons/store 2.png differ
diff --git a/assets/icons/store.png b/assets/icons/store.png
new file mode 100644
index 0000000..56d5263
Binary files /dev/null and b/assets/icons/store.png differ
diff --git a/assets/icons/timeline-selected.png b/assets/icons/timeline-selected.png
new file mode 100644
index 0000000..a1f9c70
Binary files /dev/null and b/assets/icons/timeline-selected.png differ
diff --git a/assets/icons/timeline.png b/assets/icons/timeline.png
new file mode 100644
index 0000000..bcbd2d1
Binary files /dev/null and b/assets/icons/timeline.png differ
diff --git a/assets/icons/trash 2.png b/assets/icons/trash 2.png
new file mode 100644
index 0000000..7f880ed
Binary files /dev/null and b/assets/icons/trash 2.png differ
diff --git a/assets/icons/trash.png b/assets/icons/trash.png
new file mode 100644
index 0000000..7f880ed
Binary files /dev/null and b/assets/icons/trash.png differ
diff --git a/assets/icons/user-plus.png b/assets/icons/user-plus.png
new file mode 100644
index 0000000..bf01acc
Binary files /dev/null and b/assets/icons/user-plus.png differ
diff --git a/assets/icons/user-selected.png b/assets/icons/user-selected.png
new file mode 100644
index 0000000..493c322
Binary files /dev/null and b/assets/icons/user-selected.png differ
diff --git a/assets/icons/user.png b/assets/icons/user.png
new file mode 100644
index 0000000..439ab56
Binary files /dev/null and b/assets/icons/user.png differ
diff --git a/assets/icons/users-selected.png b/assets/icons/users-selected.png
new file mode 100644
index 0000000..26a96ca
Binary files /dev/null and b/assets/icons/users-selected.png differ
diff --git a/assets/icons/users-white.png b/assets/icons/users-white.png
new file mode 100644
index 0000000..26e0813
Binary files /dev/null and b/assets/icons/users-white.png differ
diff --git a/assets/icons/users.png b/assets/icons/users.png
new file mode 100644
index 0000000..8b518c9
Binary files /dev/null and b/assets/icons/users.png differ
diff --git a/assets/icons/warning.png b/assets/icons/warning.png
new file mode 100644
index 0000000..b960ec2
Binary files /dev/null and b/assets/icons/warning.png differ
diff --git a/assets/icons/wrong.png b/assets/icons/wrong.png
new file mode 100644
index 0000000..45604d3
Binary files /dev/null and b/assets/icons/wrong.png differ
diff --git a/assets/images/Chatbot.png b/assets/images/Chatbot.png
new file mode 100644
index 0000000..5e90609
Binary files /dev/null and b/assets/images/Chatbot.png differ
diff --git a/assets/images/Click.png b/assets/images/Click.png
new file mode 100644
index 0000000..f22f06e
Binary files /dev/null and b/assets/images/Click.png differ
diff --git a/assets/images/ERDSupabase.png b/assets/images/ERDSupabase.png
new file mode 100644
index 0000000..fae23d5
Binary files /dev/null and b/assets/images/ERDSupabase.png differ
diff --git a/assets/images/ExpoLogo.png b/assets/images/ExpoLogo.png
new file mode 100644
index 0000000..6d3425a
Binary files /dev/null and b/assets/images/ExpoLogo.png differ
diff --git a/assets/images/background-onboarding.png b/assets/images/background-onboarding.png
new file mode 100644
index 0000000..8b33f46
Binary files /dev/null and b/assets/images/background-onboarding.png differ
diff --git a/assets/images/bg.png b/assets/images/bg.png
new file mode 100644
index 0000000..aa0baec
Binary files /dev/null and b/assets/images/bg.png differ
diff --git a/assets/images/click-blue.png b/assets/images/click-blue.png
new file mode 100644
index 0000000..013e052
Binary files /dev/null and b/assets/images/click-blue.png differ
diff --git a/assets/images/detail-login.png b/assets/images/detail-login.png
new file mode 100644
index 0000000..71ddc35
Binary files /dev/null and b/assets/images/detail-login.png differ
diff --git a/assets/images/image-tip.png b/assets/images/image-tip.png
new file mode 100644
index 0000000..6782ac6
Binary files /dev/null and b/assets/images/image-tip.png differ
diff --git a/assets/images/imagem-teste.png b/assets/images/imagem-teste.png
new file mode 100644
index 0000000..f9716ec
Binary files /dev/null and b/assets/images/imagem-teste.png differ
diff --git a/assets/images/lines-down.png b/assets/images/lines-down.png
new file mode 100644
index 0000000..2bb13e4
Binary files /dev/null and b/assets/images/lines-down.png differ
diff --git a/assets/images/logo-onboarding.png b/assets/images/logo-onboarding.png
new file mode 100644
index 0000000..1078a3c
Binary files /dev/null and b/assets/images/logo-onboarding.png differ
diff --git a/assets/images/logo-white.png b/assets/images/logo-white.png
new file mode 100644
index 0000000..8c8a929
Binary files /dev/null and b/assets/images/logo-white.png differ
diff --git a/assets/images/medal-ranking.png b/assets/images/medal-ranking.png
new file mode 100644
index 0000000..71f3059
Binary files /dev/null and b/assets/images/medal-ranking.png differ
diff --git a/assets/images/modal-header.png b/assets/images/modal-header.png
new file mode 100644
index 0000000..f9716ec
Binary files /dev/null and b/assets/images/modal-header.png differ
diff --git a/assets/images/no-habits-photo.png b/assets/images/no-habits-photo.png
new file mode 100644
index 0000000..018b9b2
Binary files /dev/null and b/assets/images/no-habits-photo.png differ
diff --git a/assets/images/no-profile.png b/assets/images/no-profile.png
new file mode 100644
index 0000000..99c8cec
Binary files /dev/null and b/assets/images/no-profile.png differ
diff --git a/assets/images/titan-bg.png b/assets/images/titan-bg.png
new file mode 100644
index 0000000..eec8d5d
Binary files /dev/null and b/assets/images/titan-bg.png differ
diff --git a/assets/styles/Colors.js b/assets/styles/Colors.js
new file mode 100644
index 0000000..5a8c16b
--- /dev/null
+++ b/assets/styles/Colors.js
@@ -0,0 +1,32 @@
+import { ActivityIndicator } from "react-native-paper";
+
+export default {
+ primary: '#082139',
+ primary2: '#03111F',
+ primary3: '#9F2436',
+ primary4: '#9CC6FF',
+ primary5: '#00ACEE',
+ primary6: '#051626',
+ primary7: '#1D4369',
+ primary8: '#1B7BFC',
+ primary9: '#B5BCC4',
+ primary10: '#44648A',
+ grey1: '#212121',
+ grey2: '#424242',
+ grey3: '#616161',
+ grey4: '#757575',
+ grey5: '#9e9e9e',
+ grey6: '#bdbdbd',
+ grey7: '#e0e0e0',
+ dkGreyBg: '#232323',
+ greyOutline: '#cbd2d9',
+ background: '#082139',
+ text: '#FCFCFC',
+ text2: '#F8F7F9',
+ success: '#32a852',
+ error: '#cf3434',
+ warning: '#e86c00',
+ navigator: '#091725',
+ white: '#FFFFFF',
+ ActivityIndicator: '#90E0EF',
+};
\ No newline at end of file
diff --git a/assets/styles/Default.js b/assets/styles/Default.js
new file mode 100644
index 0000000..41c2b94
--- /dev/null
+++ b/assets/styles/Default.js
@@ -0,0 +1,141 @@
+import { StyleSheet, Dimensions } from "react-native";
+import Colors from "./Colors";
+
+export default StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get("window").width,
+ backgroundColor: Colors.primary,
+ },
+ containerScrollview: {
+ paddingVertical: 60,
+ alignItems: "center",
+ justifyContent: "center",
+ flexGrow: 1,
+ },
+ centerContainer: {
+ flex: 1,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ contentContainer: {
+ flex: 1,
+ alignItems: "center",
+ width: Dimensions.get("window").width,
+ },
+ containerStyle: {
+ paddingHorizontal: 0,
+ marginHorizontal: 0,
+ },
+ loginButtonTitle: {
+ color: "white",
+ fontSize: 16,
+ fontWeight: "400",
+ },
+ loginButton: {
+ height: 60,
+ borderRadius: 4,
+ marginBottom: 16,
+ backgroundColor: "transparent",
+ borderWidth: 2,
+ borderColor: "white",
+ width: Dimensions.get("window").width - 100,
+ justifyContent: "flex-start",
+ },
+ disabledLoginButton: {
+ justifyContent: "center",
+ },
+ loginButtonEmail: {
+ backgroundColor: "rgba(0, 75, 125, 1)",
+ borderColor: "rgba(0, 75, 125, 1)",
+ },
+ loginButtonGoogle: {
+ backgroundColor: "#9F2436",
+ borderColor: "#9F2436",
+ },
+ loginButtonApple: {
+ backgroundColor: "#000",
+ borderColor: "#000",
+ },
+ loginIconButtonContainer: {
+ marginHorizontal: 24,
+ },
+ loginInput: {
+ width: Dimensions.get("window").width - 44,
+ color: "#9CC6FF",
+ fontSize: 16,
+ },
+ loginInputContainer: {
+ borderColor: "#455c8a",
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ width: Dimensions.get("window").width - 44,
+ padding: 0,
+ marginLeft: 0,
+ },
+ containerInput: {
+ paddingLeft: 0,
+ },
+ loginInputLabel: {
+ color: "#FCFCFC",
+ fontSize: 16,
+ paddingLeft: 0,
+ fontWeight: "300",
+ },
+ loginNextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: "#982538",
+ width: Dimensions.get("window").width - 44,
+ },
+ loginCreateAccountButton: {
+ height: 64,
+ borderRadius: 8,
+ marginTop: 16,
+ backgroundColor: "transparent",
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: "#9CC6FF",
+ width: Dimensions.get("window").width - 44,
+ },
+ loginButtonBoldTitle: {
+ color: "#FCFCFC",
+ fontSize: 16,
+ fontWeight: "600",
+ },
+ actionSheetButtonBox: {
+ height: 50,
+ marginTop: StyleSheet.hairlineWidth,
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: "#292929",
+ },
+ actionSheetBody: {
+ flex: 1,
+ alignSelf: "flex-end",
+ backgroundColor: "#4a4a4a",
+ },
+ actionSheetCancelButtonBox: {
+ height: 50,
+ marginTop: 6,
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: "#292929",
+ },
+ loginButtonApple: {
+ backgroundColor: "#111",
+ borderColor: "#111",
+ },
+ buttonIconStyle: {
+ width: 24,
+ height: 24,
+ marginHorizontal: 20,
+ },
+ inputIconStyle: {
+ width: 16,
+ height: 16,
+ opacity: 0.4,
+ },
+ iconStyle: {
+ width: 24,
+ height: 24,
+ },
+});
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 3b9c05e..0fd38cb 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,9 +1,67 @@
+// import globals from 'globals';
+// import pluginJs from '@eslint/js';
+// import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js';
+
+// export default [
+// { languageOptions: { globals: globals.node } },
+// pluginJs.configs.recommended,
+// pluginReactConfig,
+// ];
+
+// To address temporary errors of pushing initial skeleton frontend code, May 13th 9:30PM
import globals from 'globals';
import pluginJs from '@eslint/js';
import pluginReactConfig from 'eslint-plugin-react/configs/recommended.js';
export default [
- { languageOptions: { globals: globals.browser } },
+ {
+ languageOptions: {
+ globals: globals.node,
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ },
pluginJs.configs.recommended,
pluginReactConfig,
+ {
+ settings: {
+ react: {
+ version: 'detect', // automatically detect the React version
+ },
+ },
+ },
+ {
+ files: [
+ 'App.js',
+ 'src/components/CustomPicker.js',
+ 'src/navigator/Navigator.js',
+ 'src/screens/community/ViewCommunity.js',
+ 'src/screens/profile/HealthHabitReport.js',
+ 'src/screens/profile/HealthHabitReportDetails.js',
+ 'src/screens/profile/Profile.js',
+ 'src/screens/profile/SavedPost.js',
+ 'src/screens/profile/UpdateFavoriteBook.js',
+ 'src/screens/profile/UpdateFavoriteFood.js',
+ 'src/screens/profile/UpdateProfile.js',
+ 'src/screens/profile/UserCommunity.js',
+ 'src/screens/profile/UserHabit.js',
+ 'src/screens/profile/UserProfile.js',
+ 'src/screens/profile/HealthHabitReportUtils.js',
+ ],
+ rules: {
+ 'no-unused-vars': 'off',
+ 'react/react-in-jsx-scope': 'off',
+ 'react/prop-types': 'off',
+ 'no-undef': 'off',
+ 'react/no-unescaped-entities': 'off',
+ 'no-empty': 'off',
+ 'no-dupe-keys': 'off',
+ 'no-warning-comments': 'off',
+ },
+ },
];
diff --git a/package-lock.json b/package-lock.json
index 523fb49..29552fd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,70 @@
"version": "1.0.0",
"hasInstallScript": true,
"dependencies": {
- "expo": "~51.0.5",
- "expo-status-bar": "~1.11.1",
+
+ "@emailjs/react-native": "^4.2.2",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-brands-svg-icons": "^6.5.2",
+ "@fortawesome/free-regular-svg-icons": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-native-fontawesome": "^0.3.1",
+ "@google/generative-ai": "^0.11.3",
+ "@react-native-async-storage/async-storage": "1.23.1",
+ "@react-native-community/blur": "^4.4.0",
+ "@react-native-community/datetimepicker": "^8.0.1",
+ "@react-native-picker/picker": "^2.7.5",
+ "@react-native-vector-icons/fontawesome5": "^5.15.4-alpha.12",
+ "@react-navigation/bottom-tabs": "^6.5.20",
+ "@react-navigation/native": "^6.1.17",
+ "@react-navigation/native-stack": "^6.9.26",
+ "@react-navigation/stack": "^6.3.29",
+ "@reduxjs/toolkit": "^2.2.5",
+ "@stripe/stripe-react-native": "^0.37.3",
+ "@supabase/supabase-js": "^2.43.1",
+ "axios": "^1.6.8",
+ "base64-arraybuffer": "^1.0.2",
+ "emailjs-com": "^3.2.0",
+ "expo": "^51.0.0",
+ "expo-av": "~14.0.3",
+ "expo-blur": "~13.0.2",
+ "expo-contacts": "~13.0.3",
+ "expo-file-system": "~17.0.1",
+ "expo-image-manipulator": "~12.0.3",
+ "expo-image-picker": "~15.0.5",
+ "expo-linear-gradient": "^13.0.2",
+ "expo-notifications": "~0.28.1",
+ "expo-permissions": "^14.4.0",
+ "expo-sharing": "~12.0.1",
+ "expo-status-bar": "~1.12.1",
+ "moment": "^2.30.1",
+ "prop-types": "^15.8.1",
"react": "18.2.0",
- "react-native": "0.73.6"
+ "react-native": "0.74.1",
+ "react-native-actionsheet": "^2.4.2",
+ "react-native-chart-kit": "^6.12.0",
+ "react-native-collapsible": "^1.6.1",
+ "react-native-elements": "^3.4.3",
+ "react-native-gesture-handler": "^2.16.2",
+ "react-native-get-random-values": "^1.11.0",
+ "react-native-keyboard-aware-scroll-view": "^0.9.5",
+ "react-native-mime-types": "^2.5.0",
+ "react-native-modal": "^13.0.1",
+ "react-native-modal-datetime-picker": "^17.1.0",
+ "react-native-modalize": "^2.1.1",
+ "react-native-paper": "^5.12.3",
+ "react-native-raw-bottom-sheet": "^3.0.0",
+ "react-native-reanimated": "^3.11.0",
+ "react-native-select-dropdown": "^4.0.1",
+ "react-native-step-indicator": "^1.0.3",
+ "react-native-svg": "^15.3.0",
+ "react-native-swipeable": "^0.6.0",
+ "react-native-tooltip-menu": "^3.0.7",
+ "react-native-typography": "^1.4.1",
+ "react-native-url-polyfill": "^2.0.0",
+ "react-native-vector-icons": "^10.1.0",
+ "react-redux": "^9.1.2",
+ "redux": "^5.0.1",
+ "stripe": "^15.5.0"
},
"devDependencies": {
"@babel/plugin-transform-numeric-separator": "^7.18.6",
@@ -190,6 +250,22 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/@babel/helper-define-polyfill-provider/node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
@@ -2105,6 +2181,60 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@callstack/react-theme-provider": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.9.tgz",
+ "integrity": "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==",
+ "dependencies": {
+ "deepmerge": "^3.2.0",
+ "hoist-non-react-statics": "^3.3.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0"
+ }
+ },
+ "node_modules/@callstack/react-theme-provider/node_modules/deepmerge": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
+ "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@egjs/hammerjs": {
+ "version": "2.0.17",
+ "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
+ "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
+ "dependencies": {
+ "@types/hammerjs": "^2.0.36"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@emailjs/browser": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.3.3.tgz",
+ "integrity": "sha512-ltpt2S/WVREIBXptxYAVYBvXb2O6yTUYiRUWF8OLikMxlmiGsIgKpgHppikNd4Df0uAav7jCsQKcOJ3TJFUx5g==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@emailjs/react-native": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@emailjs/react-native/-/react-native-4.2.2.tgz",
+ "integrity": "sha512-jXjEliE8zMWsOkOfbCA/jA16usjV0SdhQvZ6Cw3PDBxsFbInIMPKhxexHG4/bXxX7cjAhdBK4lfDHL4P+dZfNw==",
+ "dependencies": {
+ "@emailjs/browser": "^4.3.3",
+ "@react-native-async-storage/async-storage": "^1.22.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react-native": ">=0.60 <1.0"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -2152,12 +2282,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/@eslint/eslintrc/node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
"node_modules/@eslint/eslintrc/node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
@@ -2173,55 +2297,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@eslint/eslintrc/node_modules/import-fresh": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
- "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
- "dev": true,
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -2235,9 +2310,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.1.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.1.1.tgz",
- "integrity": "sha512-5WoDz3Y19Bg2BnErkZTp0en+c/i9PvgFS7MBe1+m60HjFr0hrphlAGp4yzI7pxpt4xShln4ZyYp4neJm8hmOkQ==",
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.2.0.tgz",
+ "integrity": "sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2259,9 +2334,9 @@
}
},
"node_modules/@expo/cli": {
- "version": "0.18.11",
- "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.11.tgz",
- "integrity": "sha512-2xIfvj5RnQbQqZdkYa9a7Roll1ywBER2omCUKdbJazRcJTkkN3HMv/jILztdZ2uKlcfIqPq4VTbKEhV/IkewYg==",
+ "version": "0.18.9",
+ "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.18.9.tgz",
+ "integrity": "sha512-CoxiISJqI7bymGzIflm8JxGkSg8hoZ2r7wfAN5bD6rKTQ83m8LiYGCZ/AQKT2sTNrnHSA+tvjuqwycvxGzIyVA==",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/code-signing-certificates": "0.0.5",
@@ -2420,6 +2495,14 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/@expo/cli/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
"node_modules/@expo/cli/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2451,6 +2534,19 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/@expo/cli/node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/@expo/cli/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -2459,15 +2555,38 @@
"node": ">=8"
}
},
- "node_modules/@expo/cli/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "node_modules/@expo/cli/node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@expo/cli/node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
},
"node_modules/@expo/cli/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -2608,9 +2727,9 @@
}
},
"node_modules/@expo/config-plugins/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -2662,9 +2781,9 @@
}
},
"node_modules/@expo/config/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -2888,9 +3007,9 @@
}
},
"node_modules/@expo/image-utils/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -3142,6 +3261,14 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/@expo/package-manager/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
"node_modules/@expo/package-manager/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -3181,6 +3308,18 @@
"node": ">=8"
}
},
+ "node_modules/@expo/package-manager/node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/@expo/package-manager/node_modules/sudo-prompt": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz",
@@ -3259,9 +3398,9 @@
}
},
"node_modules/@expo/prebuild-config/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -3354,11 +3493,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/@expo/xcpretty/node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
- },
"node_modules/@expo/xcpretty/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -3398,17 +3532,6 @@
"node": ">=8"
}
},
- "node_modules/@expo/xcpretty/node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
"node_modules/@expo/xcpretty/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -3420,11 +3543,90 @@
"node": ">=8"
}
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz",
+ "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz",
+ "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz",
+ "integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-regular-svg-icons": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz",
+ "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz",
+ "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.5.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-native-fontawesome": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-native-fontawesome/-/react-native-fontawesome-0.3.1.tgz",
+ "integrity": "sha512-fWkCW/Et+5YSV0gJffDci6zuHNtIST5/QFOhyqgxaepqod28cRZwsrKhx68PxPEnUlF/K6RVxQ7SvQyWURbDPQ==",
+ "dependencies": {
+ "humps": "^2.0.1",
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react-native": ">= 0.67",
+ "react-native-svg": ">= 11.x"
+ }
+ },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
"integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="
},
+ "node_modules/@google/generative-ai": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.11.3.tgz",
+ "integrity": "sha512-QtQ1hz6rcybbw35uxXlFF26KNnaTVr2oWwnmDkC1M35KdzN4tVc4wakgJp8uXbY9KDCNHksyp11DbFg0HPckZQ==",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/@graphql-typed-document-node/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
@@ -3479,6 +3681,11 @@
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"dev": true
},
+ "node_modules/@ide/backoff": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz",
+ "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g=="
+ },
"node_modules/@isaacs/ttlcache": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz",
@@ -3713,9 +3920,9 @@
}
},
"node_modules/@npmcli/fs/node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -3761,20 +3968,39 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@react-native-async-storage/async-storage": {
+ "version": "1.23.1",
+ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz",
+ "integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==",
+ "dependencies": {
+ "merge-options": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react-native": "^0.0.0-0 || >=0.60 <1.0"
+ }
+ },
+ "node_modules/@react-native-community/blur": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@react-native-community/blur/-/blur-4.4.0.tgz",
+ "integrity": "sha512-P+xdT2LIq1ewOsF3zx7C0nu4dj7nxl2NVTsMXEzRDjM3bWMdrrEbTRA7uwPV5ngn7/BXIommBPlT/JW4SAedrw==",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/@react-native-community/cli": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-12.3.6.tgz",
- "integrity": "sha512-647OSi6xBb8FbwFqX9zsJxOzu685AWtrOUWHfOkbKD+5LOpGORw+GQo0F9rWZnB68rLQyfKUZWJeaD00pGv5fw==",
- "dependencies": {
- "@react-native-community/cli-clean": "12.3.6",
- "@react-native-community/cli-config": "12.3.6",
- "@react-native-community/cli-debugger-ui": "12.3.6",
- "@react-native-community/cli-doctor": "12.3.6",
- "@react-native-community/cli-hermes": "12.3.6",
- "@react-native-community/cli-plugin-metro": "12.3.6",
- "@react-native-community/cli-server-api": "12.3.6",
- "@react-native-community/cli-tools": "12.3.6",
- "@react-native-community/cli-types": "12.3.6",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.6.tgz",
+ "integrity": "sha512-IqclB7VQ84ye8Fcs89HOpOscY4284VZg2pojHNl8H0Lzd4DadXJWQoxC7zWm8v2f8eyeX2kdhxp2ETD5tceIgA==",
+ "dependencies": {
+ "@react-native-community/cli-clean": "13.6.6",
+ "@react-native-community/cli-config": "13.6.6",
+ "@react-native-community/cli-debugger-ui": "13.6.6",
+ "@react-native-community/cli-doctor": "13.6.6",
+ "@react-native-community/cli-hermes": "13.6.6",
+ "@react-native-community/cli-server-api": "13.6.6",
+ "@react-native-community/cli-tools": "13.6.6",
+ "@react-native-community/cli-types": "13.6.6",
"chalk": "^4.1.2",
"commander": "^9.4.1",
"deepmerge": "^4.3.0",
@@ -3793,13 +4019,14 @@
}
},
"node_modules/@react-native-community/cli-clean": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-12.3.6.tgz",
- "integrity": "sha512-gUU29ep8xM0BbnZjwz9MyID74KKwutq9x5iv4BCr2im6nly4UMf1B1D+V225wR7VcDGzbgWjaezsJShLLhC5ig==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.6.tgz",
+ "integrity": "sha512-cBwJTwl0NyeA4nyMxbhkWZhxtILYkbU3TW3k8AXLg+iGphe0zikYMGB3T+haTvTc6alTyEFwPbimk9bGIqkjAQ==",
"dependencies": {
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
- "execa": "^5.0.0"
+ "execa": "^5.0.0",
+ "fast-glob": "^3.3.2"
}
},
"node_modules/@react-native-community/cli-clean/node_modules/ansi-styles": {
@@ -3944,15 +4171,15 @@
}
},
"node_modules/@react-native-community/cli-config": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-12.3.6.tgz",
- "integrity": "sha512-JGWSYQ9EAK6m2v0abXwFLEfsqJ1zkhzZ4CV261QZF9MoUNB6h57a274h1MLQR9mG6Tsh38wBUuNfEPUvS1vYew==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.6.tgz",
+ "integrity": "sha512-mbG425zCKr8JZhv/j11382arezwS/70juWMsn8j2lmrGTrP1cUdW0MF15CCIFtJsqyK3Qs+FTmqttRpq81QfSg==",
"dependencies": {
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
"cosmiconfig": "^5.1.0",
"deepmerge": "^4.3.0",
- "glob": "^7.1.3",
+ "fast-glob": "^3.3.2",
"joi": "^17.2.1"
}
},
@@ -4021,22 +4248,23 @@
}
},
"node_modules/@react-native-community/cli-debugger-ui": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-12.3.6.tgz",
- "integrity": "sha512-SjUKKsx5FmcK9G6Pb6UBFT0s9JexVStK5WInmANw75Hm7YokVvHEgtprQDz2Uvy5znX5g2ujzrkIU//T15KQzA==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.6.tgz",
+ "integrity": "sha512-Vv9u6eS4vKSDAvdhA0OiQHoA7y39fiPIgJ6biT32tN4avHDtxlc6TWZGiqv7g98SBvDWvoVAmdPLcRf3kU+c8g==",
"dependencies": {
"serve-static": "^1.13.1"
}
},
"node_modules/@react-native-community/cli-doctor": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-12.3.6.tgz",
- "integrity": "sha512-fvBDv2lTthfw4WOQKkdTop2PlE9GtfrlNnpjB818MhcdEnPjfQw5YaTUcnNEGsvGomdCs1MVRMgYXXwPSN6OvQ==",
- "dependencies": {
- "@react-native-community/cli-config": "12.3.6",
- "@react-native-community/cli-platform-android": "12.3.6",
- "@react-native-community/cli-platform-ios": "12.3.6",
- "@react-native-community/cli-tools": "12.3.6",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.6.tgz",
+ "integrity": "sha512-TWZb5g6EmQe2Ua2TEWNmyaEayvlWH4GmdD9ZC+p8EpKFpB1NpDGMK6sXbpb42TDvwZg5s4TDRplK0PBEA/SVDg==",
+ "dependencies": {
+ "@react-native-community/cli-config": "13.6.6",
+ "@react-native-community/cli-platform-android": "13.6.6",
+ "@react-native-community/cli-platform-apple": "13.6.6",
+ "@react-native-community/cli-platform-ios": "13.6.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
"command-exists": "^1.2.8",
"deepmerge": "^4.3.0",
@@ -4174,17 +4402,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-doctor/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@react-native-community/cli-doctor/node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -4264,12 +4481,9 @@
}
},
"node_modules/@react-native-community/cli-doctor/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -4277,29 +4491,48 @@
"node": ">=10"
}
},
- "node_modules/@react-native-community/cli-doctor/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dependencies": {
- "has-flag": "^4.0.0"
+ "ansi-regex": "^4.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=6"
}
},
- "node_modules/@react-native-community/cli-doctor/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ "node_modules/@react-native-community/cli-doctor/node_modules/strip-ansi/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@react-native-community/cli-doctor/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@react-native-community/cli-doctor/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@react-native-community/cli-hermes": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-12.3.6.tgz",
- "integrity": "sha512-sNGwfOCl8OAIjWCkwuLpP8NZbuO0dhDI/2W7NeOGDzIBsf4/c4MptTrULWtGIH9okVPLSPX0NnRyGQ+mSwWyuQ==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.6.tgz",
+ "integrity": "sha512-La5Ie+NGaRl3klei6WxKoOxmCUSGGxpOk6vU5pEGf0/O7ky+Ay0io+zXYUZqlNMi/cGpO7ZUijakBYOB/uyuFg==",
"dependencies": {
- "@react-native-community/cli-platform-android": "12.3.6",
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-platform-android": "13.6.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
"hermes-profile-transformer": "^0.0.6"
}
@@ -4369,15 +4602,15 @@
}
},
"node_modules/@react-native-community/cli-platform-android": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-12.3.6.tgz",
- "integrity": "sha512-DeDDAB8lHpuGIAPXeeD9Qu2+/wDTFPo99c8uSW49L0hkmZJixzvvvffbGQAYk32H0TmaI7rzvzH+qzu7z3891g==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.6.tgz",
+ "integrity": "sha512-/tMwkBeNxh84syiSwNlYtmUz/Ppc+HfKtdopL/5RB+fd3SV1/5/NPNjMlyLNgFKnpxvKCInQ7dnl6jGHJjeHjg==",
"dependencies": {
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
+ "fast-glob": "^3.3.2",
"fast-xml-parser": "^4.2.4",
- "glob": "^7.1.3",
"logkitty": "^0.7.1"
}
},
@@ -4522,20 +4755,20 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-platform-ios": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-12.3.6.tgz",
- "integrity": "sha512-3eZ0jMCkKUO58wzPWlvAPRqezVKm9EPZyaPyHbRPWU8qw7JqkvnRlWIaYDGpjCJgVW4k2hKsEursLtYKb188tg==",
+ "node_modules/@react-native-community/cli-platform-apple": {
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.6.tgz",
+ "integrity": "sha512-bOmSSwoqNNT3AmCRZXEMYKz1Jf1l2F86Nhs7qBcXdY/sGiJ+Flng564LOqvdAlVLTbkgz47KjNKCS2pP4Jg0Mg==",
"dependencies": {
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-tools": "13.6.6",
"chalk": "^4.1.2",
"execa": "^5.0.0",
+ "fast-glob": "^3.3.2",
"fast-xml-parser": "^4.0.12",
- "glob": "^7.1.3",
"ora": "^5.4.1"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/ansi-styles": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
@@ -4549,7 +4782,7 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/chalk": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
@@ -4564,7 +4797,7 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/cli-cursor": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
@@ -4575,7 +4808,7 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/color-convert": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
@@ -4586,12 +4819,12 @@
"node": ">=7.0.0"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/color-name": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/execa": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
@@ -4613,7 +4846,7 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/get-stream": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
@@ -4624,7 +4857,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/has-flag": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
@@ -4632,7 +4865,7 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/is-stream": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
@@ -4643,7 +4876,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/log-symbols": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
@@ -4658,7 +4891,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/mimic-fn": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
@@ -4666,7 +4899,7 @@
"node": ">=6"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/npm-run-path": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
@@ -4677,7 +4910,7 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/onetime": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
@@ -4691,7 +4924,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/ora": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/ora": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
"integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
@@ -4713,7 +4946,7 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/restore-cursor": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
@@ -4725,18 +4958,7 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-platform-ios/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/supports-color": {
+ "node_modules/@react-native-community/cli-platform-apple/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
@@ -4747,54 +4969,46 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-plugin-metro": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-12.3.6.tgz",
- "integrity": "sha512-3jxSBQt4fkS+KtHCPSyB5auIT+KKIrPCv9Dk14FbvOaEh9erUWEm/5PZWmtboW1z7CYeNbFMeXm9fM2xwtVOpg=="
+ "node_modules/@react-native-community/cli-platform-ios": {
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.6.tgz",
+ "integrity": "sha512-vjDnRwhlSN5ryqKTas6/DPkxuouuyFBAqAROH4FR1cspTbn6v78JTZKDmtQy9JMMo7N5vZj1kASU5vbFep9IOQ==",
+ "dependencies": {
+ "@react-native-community/cli-platform-apple": "13.6.6"
+ }
},
"node_modules/@react-native-community/cli-server-api": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-12.3.6.tgz",
- "integrity": "sha512-80NIMzo8b2W+PL0Jd7NjiJW9mgaT8Y8wsIT/lh6mAvYH7mK0ecDJUYUTAAv79Tbo1iCGPAr3T295DlVtS8s4yQ==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.6.tgz",
+ "integrity": "sha512-ZtCXxoFlM7oDv3iZ3wsrT3SamhtUJuIkX2WePLPlN5bcbq7zimbPm2lHyicNJtpcGQ5ymsgpUWPCNZsWQhXBqQ==",
"dependencies": {
- "@react-native-community/cli-debugger-ui": "12.3.6",
- "@react-native-community/cli-tools": "12.3.6",
+ "@react-native-community/cli-debugger-ui": "13.6.6",
+ "@react-native-community/cli-tools": "13.6.6",
"compression": "^1.7.1",
"connect": "^3.6.5",
"errorhandler": "^1.5.1",
"nocache": "^3.0.1",
"pretty-format": "^26.6.2",
"serve-static": "^1.13.1",
- "ws": "^7.5.1"
+ "ws": "^6.2.2"
}
},
"node_modules/@react-native-community/cli-server-api/node_modules/ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
- "engines": {
- "node": ">=8.3.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+ "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+ "dependencies": {
+ "async-limiter": "~1.0.0"
}
},
"node_modules/@react-native-community/cli-tools": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-12.3.6.tgz",
- "integrity": "sha512-FPEvZn19UTMMXUp/piwKZSh8cMEfO8G3KDtOwo53O347GTcwNrKjgZGtLSPELBX2gr+YlzEft3CoRv2Qmo83fQ==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.6.tgz",
+ "integrity": "sha512-ptOnn4AJczY5njvbdK91k4hcYazDnGtEPrqIwEI+k/CTBHNdb27Rsm2OZ7ye6f7otLBqF8gj/hK6QzJs8CEMgw==",
"dependencies": {
"appdirsjs": "^1.2.4",
"chalk": "^4.1.2",
+ "execa": "^5.0.0",
"find-up": "^5.0.0",
"mime": "^2.4.1",
"node-fetch": "^2.6.0",
@@ -4861,6 +5075,39 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/@react-native-community/cli-tools/node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/@react-native-community/cli-tools/node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@react-native-community/cli-tools/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4869,6 +5116,17 @@
"node": ">=8"
}
},
+ "node_modules/@react-native-community/cli-tools/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/@react-native-community/cli-tools/node_modules/is-wsl": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
@@ -4892,17 +5150,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@react-native-community/cli-tools/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@react-native-community/cli-tools/node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -4911,6 +5158,17 @@
"node": ">=6"
}
},
+ "node_modules/@react-native-community/cli-tools/node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@react-native-community/cli-tools/node_modules/onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
@@ -4971,12 +5229,9 @@
}
},
"node_modules/@react-native-community/cli-tools/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -4984,17 +5239,6 @@
"node": ">=10"
}
},
- "node_modules/@react-native-community/cli-tools/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/@react-native-community/cli-tools/node_modules/sudo-prompt": {
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
@@ -5011,15 +5255,10 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli-tools/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
"node_modules/@react-native-community/cli-types": {
- "version": "12.3.6",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-12.3.6.tgz",
- "integrity": "sha512-xPqTgcUtZowQ8WKOkI9TLGBwH2bGggOC4d2FFaIRST3gTcjrEeGRNeR5aXCzJFIgItIft8sd7p2oKEdy90+01Q==",
+ "version": "13.6.6",
+ "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.6.tgz",
+ "integrity": "sha512-733iaYzlmvNK7XYbnWlMjdE+2k0hlTBJW071af/xb6Bs+hbJqBP9c03FZuYH2hFFwDDntwj05bkri/P7VgSxug==",
"dependencies": {
"joi": "^17.2.1"
}
@@ -5152,17 +5391,6 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@react-native-community/cli/node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -5222,12 +5450,9 @@
}
},
"node_modules/@react-native-community/cli/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.1.tgz",
+ "integrity": "sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==",
"bin": {
"semver": "bin/semver.js"
},
@@ -5246,15 +5471,69 @@
"node": ">=8"
}
},
- "node_modules/@react-native-community/cli/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ "node_modules/@react-native-community/datetimepicker": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.0.1.tgz",
+ "integrity": "sha512-4BO0t3geMNNw9cIIm9p9FNUzwMXexdzD4pAH0AaUAycs3BS71HLrX8jHbrI7nzq/+8O7cLAXn5Gudte+YpTV8Q==",
+ "dependencies": {
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*",
+ "react-native-windows": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-windows": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-native-picker/picker": {
+ "version": "2.7.5",
+ "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.7.5.tgz",
+ "integrity": "sha512-vhMaOLkXSUb+YKVbukMJToU4g+89VMhBG2U9+cLYF8X8HtFRidrHjohGqT8/OyesDuKIXeLIP+UFYI9Q9CRA9Q==",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/@react-native-vector-icons/common": {
+ "version": "0.0.1-alpha.6",
+ "resolved": "https://registry.npmjs.org/@react-native-vector-icons/common/-/common-0.0.1-alpha.6.tgz",
+ "integrity": "sha512-3LgnZIGyhCJz/BaOLCwqWlZkFRgxLCb9+y0maWBAkq2BYw/FzXTyMjpM6nYZ2wSE9FVuY1NfwHRZvNDxa5IYUQ==",
+ "dependencies": {
+ "@react-native-community/cli-tools": "^13.6.5",
+ "prop-types": "^15.8.1",
+ "yargs": "^17.7.2"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/@react-native-vector-icons/fontawesome5": {
+ "version": "5.15.4-alpha.12",
+ "resolved": "https://registry.npmjs.org/@react-native-vector-icons/fontawesome5/-/fontawesome5-5.15.4-alpha.12.tgz",
+ "integrity": "sha512-1xg+OLgdg9lfOsFtP2RiD/Rm+XsxacfpPmyFUi2QGJWLZ3zUsjipAkd5hjVSkHtzYyt5mUsDXrR2ir0g7DHb7Q==",
+ "dependencies": {
+ "@react-native-vector-icons/common": "^0.0.1-alpha.6"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
},
"node_modules/@react-native/assets-registry": {
- "version": "0.73.1",
- "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.73.1.tgz",
- "integrity": "sha512-2FgAbU7uKM5SbbW9QptPPZx8N9Ke2L7bsHb+EhAanZjFZunA9PaYtyjUQ1s7HD+zDVqOQIvjkpXSv7Kejd2tqg==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.83.tgz",
+ "integrity": "sha512-2vkLMVnp+YTZYTNSDIBZojSsjz8sl5PscP3j4GcV6idD8V978SZfwFlk8K0ti0BzRs11mzL0Pj17km597S/eTQ==",
"engines": {
"node": ">=18"
}
@@ -5347,20 +5626,21 @@
}
},
"node_modules/@react-native/community-cli-plugin": {
- "version": "0.73.17",
- "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.73.17.tgz",
- "integrity": "sha512-F3PXZkcHg+1ARIr6FRQCQiB7ZAA+MQXGmq051metRscoLvgYJwj7dgC8pvgy0kexzUkHu5BNKrZeySzUft3xuQ==",
- "dependencies": {
- "@react-native-community/cli-server-api": "12.3.6",
- "@react-native-community/cli-tools": "12.3.6",
- "@react-native/dev-middleware": "0.73.8",
- "@react-native/metro-babel-transformer": "0.73.15",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.83.tgz",
+ "integrity": "sha512-7GAFjFOg1mFSj8bnFNQS4u8u7+QtrEeflUIDVZGEfBZQ3wMNI5ycBzbBGycsZYiq00Xvoc6eKFC7kvIaqeJpUQ==",
+ "dependencies": {
+ "@react-native-community/cli-server-api": "13.6.6",
+ "@react-native-community/cli-tools": "13.6.6",
+ "@react-native/dev-middleware": "0.74.83",
+ "@react-native/metro-babel-transformer": "0.74.83",
"chalk": "^4.0.0",
"execa": "^5.1.1",
"metro": "^0.80.3",
"metro-config": "^0.80.3",
"metro-core": "^0.80.3",
"node-fetch": "^2.2.0",
+ "querystring": "^0.2.1",
"readline": "^1.3.0"
},
"engines": {
@@ -5509,26 +5789,28 @@
}
},
"node_modules/@react-native/debugger-frontend": {
- "version": "0.73.3",
- "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.73.3.tgz",
- "integrity": "sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.83.tgz",
+ "integrity": "sha512-RGQlVUegBRxAUF9c1ss1ssaHZh6CO+7awgtI9sDeU0PzDZY/40ImoPD5m0o0SI6nXoVzbPtcMGzU+VO590pRfA==",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/dev-middleware": {
- "version": "0.73.8",
- "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.73.8.tgz",
- "integrity": "sha512-oph4NamCIxkMfUL/fYtSsE+JbGOnrlawfQ0kKtDQ5xbOjPKotKoXqrs1eGwozNKv7FfQ393stk1by9a6DyASSg==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.83.tgz",
+ "integrity": "sha512-UH8iriqnf7N4Hpi20D7M2FdvSANwTVStwFCSD7VMU9agJX88Yk0D1T6Meh2RMhUu4kY2bv8sTkNRm7LmxvZqgA==",
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
- "@react-native/debugger-frontend": "0.73.3",
+ "@react-native/debugger-frontend": "0.74.83",
+ "@rnx-kit/chromium-edge-launcher": "^1.0.0",
"chrome-launcher": "^0.15.2",
- "chromium-edge-launcher": "^1.0.0",
"connect": "^3.6.5",
"debug": "^2.2.0",
"node-fetch": "^2.2.0",
+ "nullthrows": "^1.1.1",
"open": "^7.0.3",
+ "selfsigned": "^2.4.1",
"serve-static": "^1.13.1",
"temp-dir": "^2.0.0",
"ws": "^6.2.2"
@@ -5574,29 +5856,29 @@
}
},
"node_modules/@react-native/gradle-plugin": {
- "version": "0.73.4",
- "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.73.4.tgz",
- "integrity": "sha512-PMDnbsZa+tD55Ug+W8CfqXiGoGneSSyrBZCMb5JfiB3AFST3Uj5e6lw8SgI/B6SKZF7lG0BhZ6YHZsRZ5MlXmg==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.83.tgz",
+ "integrity": "sha512-Pw2BWVyOHoBuJVKxGVYF6/GSZRf6+v1Ygc+ULGz5t20N8qzRWPa2fRZWqoxsN7TkNLPsECYY8gooOl7okOcPAQ==",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/js-polyfills": {
- "version": "0.73.1",
- "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.73.1.tgz",
- "integrity": "sha512-ewMwGcumrilnF87H4jjrnvGZEaPFCAC4ebraEK+CurDDmwST/bIicI4hrOAv+0Z0F7DEK4O4H7r8q9vH7IbN4g==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.83.tgz",
+ "integrity": "sha512-/t74n8r6wFhw4JEoOj3bN71N1NDLqaawB75uKAsSjeCwIR9AfCxlzZG0etsXtOexkY9KMeZIQ7YwRPqUdNXuqw==",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/metro-babel-transformer": {
- "version": "0.73.15",
- "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.73.15.tgz",
- "integrity": "sha512-LlkSGaXCz+xdxc9819plmpsl4P4gZndoFtpjN3GMBIu6f7TBV0GVbyJAU4GE8fuAWPVSVL5ArOcdkWKSbI1klw==",
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.83.tgz",
+ "integrity": "sha512-hGdx5N8diu8y+GW/ED39vTZa9Jx1di2ZZ0aapbhH4egN1agIAusj5jXTccfNBwwWF93aJ5oVbRzfteZgjbutKg==",
"dependencies": {
"@babel/core": "^7.20.0",
- "@react-native/babel-preset": "0.73.21",
- "hermes-parser": "0.15.0",
+ "@react-native/babel-preset": "0.74.83",
+ "hermes-parser": "0.19.1",
"nullthrows": "^1.1.1"
},
"engines": {
@@ -5606,131 +5888,196 @@
"@babel/core": "*"
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": {
- "version": "0.73.4",
- "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.73.4.tgz",
- "integrity": "sha512-XzRd8MJGo4Zc5KsphDHBYJzS1ryOHg8I2gOZDAUCGcwLFhdyGu1zBNDJYH2GFyDrInn9TzAbRIf3d4O+eltXQQ==",
+ "node_modules/@react-native/normalize-colors": {
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.83.tgz",
+ "integrity": "sha512-jhCY95gRDE44qYawWVvhTjTplW1g+JtKTKM3f8xYT1dJtJ8QWv+gqEtKcfmOHfDkSDaMKG0AGBaDTSK8GXLH8Q=="
+ },
+ "node_modules/@react-native/virtualized-lists": {
+ "version": "0.74.83",
+ "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.83.tgz",
+ "integrity": "sha512-rmaLeE34rj7py4FxTod7iMTC7BAsm+HrGA8WxYmEJeyTV7WSaxAkosKoYBz8038mOiwnG9VwA/7FrB6bEQvn1A==",
"dependencies": {
- "@react-native/codegen": "0.73.3"
+ "invariant": "^2.2.4",
+ "nullthrows": "^1.1.1"
},
"engines": {
"node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.6",
+ "react": "*",
+ "react-native": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": {
- "version": "0.73.21",
- "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.73.21.tgz",
- "integrity": "sha512-WlFttNnySKQMeujN09fRmrdWqh46QyJluM5jdtDNrkl/2Hx6N4XeDUGhABvConeK95OidVO7sFFf7sNebVXogA==",
+ "node_modules/@react-navigation/bottom-tabs": {
+ "version": "6.5.20",
+ "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-6.5.20.tgz",
+ "integrity": "sha512-ow6Z06iS4VqBO8d7FP+HsGjJLWt2xTWIvuWjpoCvsM/uQXzCRDIjBv9HaKcXbF0yTW7IMir0oDAbU5PFzEDdgA==",
"dependencies": {
- "@babel/core": "^7.20.0",
- "@babel/plugin-proposal-async-generator-functions": "^7.0.0",
- "@babel/plugin-proposal-class-properties": "^7.18.0",
- "@babel/plugin-proposal-export-default-from": "^7.0.0",
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0",
- "@babel/plugin-proposal-numeric-separator": "^7.0.0",
- "@babel/plugin-proposal-object-rest-spread": "^7.20.0",
- "@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
- "@babel/plugin-proposal-optional-chaining": "^7.20.0",
- "@babel/plugin-syntax-dynamic-import": "^7.8.0",
- "@babel/plugin-syntax-export-default-from": "^7.0.0",
- "@babel/plugin-syntax-flow": "^7.18.0",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0",
- "@babel/plugin-syntax-optional-chaining": "^7.0.0",
- "@babel/plugin-transform-arrow-functions": "^7.0.0",
- "@babel/plugin-transform-async-to-generator": "^7.20.0",
- "@babel/plugin-transform-block-scoping": "^7.0.0",
- "@babel/plugin-transform-classes": "^7.0.0",
- "@babel/plugin-transform-computed-properties": "^7.0.0",
- "@babel/plugin-transform-destructuring": "^7.20.0",
- "@babel/plugin-transform-flow-strip-types": "^7.20.0",
- "@babel/plugin-transform-function-name": "^7.0.0",
- "@babel/plugin-transform-literals": "^7.0.0",
- "@babel/plugin-transform-modules-commonjs": "^7.0.0",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0",
- "@babel/plugin-transform-parameters": "^7.0.0",
- "@babel/plugin-transform-private-methods": "^7.22.5",
- "@babel/plugin-transform-private-property-in-object": "^7.22.11",
- "@babel/plugin-transform-react-display-name": "^7.0.0",
- "@babel/plugin-transform-react-jsx": "^7.0.0",
- "@babel/plugin-transform-react-jsx-self": "^7.0.0",
- "@babel/plugin-transform-react-jsx-source": "^7.0.0",
- "@babel/plugin-transform-runtime": "^7.0.0",
- "@babel/plugin-transform-shorthand-properties": "^7.0.0",
- "@babel/plugin-transform-spread": "^7.0.0",
- "@babel/plugin-transform-sticky-regex": "^7.0.0",
- "@babel/plugin-transform-typescript": "^7.5.0",
- "@babel/plugin-transform-unicode-regex": "^7.0.0",
- "@babel/template": "^7.0.0",
- "@react-native/babel-plugin-codegen": "0.73.4",
- "babel-plugin-transform-flow-enums": "^0.0.2",
- "react-refresh": "^0.14.0"
- },
- "engines": {
- "node": ">=18"
+ "@react-navigation/elements": "^1.3.30",
+ "color": "^4.2.3",
+ "warn-once": "^0.1.0"
},
"peerDependencies": {
- "@babel/core": "*"
+ "@react-navigation/native": "^6.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 3.0.0",
+ "react-native-screens": ">= 3.0.0"
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": {
- "version": "0.73.3",
- "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.73.3.tgz",
- "integrity": "sha512-sxslCAAb8kM06vGy9Jyh4TtvjhcP36k/rvj2QE2Jdhdm61KvfafCATSIsOfc0QvnduWFcpXUPvAVyYwuv7PYDg==",
+ "node_modules/@react-navigation/core": {
+ "version": "6.4.16",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.16.tgz",
+ "integrity": "sha512-UDTJBsHxnzgFETR3ZxhctP+RWr4SkyeZpbhpkQoIGOuwSCkt1SE0qjU48/u6r6w6XlX8OqVudn1Ab0QFXTHxuQ==",
"dependencies": {
- "@babel/parser": "^7.20.0",
- "flow-parser": "^0.206.0",
- "glob": "^7.1.1",
- "invariant": "^2.2.4",
- "jscodeshift": "^0.14.0",
- "mkdirp": "^0.5.1",
- "nullthrows": "^1.1.1"
+ "@react-navigation/routers": "^6.1.9",
+ "escape-string-regexp": "^4.0.0",
+ "nanoid": "^3.1.23",
+ "query-string": "^7.1.3",
+ "react-is": "^16.13.0",
+ "use-latest-callback": "^0.1.9"
},
- "engines": {
- "node": ">=18"
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/@react-navigation/elements": {
+ "version": "1.3.30",
+ "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.30.tgz",
+ "integrity": "sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ==",
+ "peerDependencies": {
+ "@react-navigation/native": "^6.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 3.0.0"
+ }
+ },
+ "node_modules/@react-navigation/native": {
+ "version": "6.1.17",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.17.tgz",
+ "integrity": "sha512-mer3OvfwWOHoUSMJyLa4vnBH3zpFmCwuzrBPlw7feXklurr/ZDiLjLxUScOot6jLRMz/67GyilEYMmP99LL0RQ==",
+ "dependencies": {
+ "@react-navigation/core": "^6.4.16",
+ "escape-string-regexp": "^4.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "nanoid": "^3.1.23"
},
"peerDependencies": {
- "@babel/preset-env": "^7.1.6"
+ "react": "*",
+ "react-native": "*"
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/flow-parser": {
- "version": "0.206.0",
- "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.206.0.tgz",
- "integrity": "sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w==",
- "engines": {
- "node": ">=0.4.0"
+ "node_modules/@react-navigation/native-stack": {
+ "version": "6.9.26",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.26.tgz",
+ "integrity": "sha512-++dueQ+FDj2XkZ902DVrK79ub1vp19nSdAZWxKRgd6+Bc0Niiesua6rMCqymYOVaYh+dagwkA9r00bpt/U5WLw==",
+ "dependencies": {
+ "@react-navigation/elements": "^1.3.30",
+ "warn-once": "^0.1.0"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^6.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 3.0.0",
+ "react-native-screens": ">= 3.0.0"
}
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-estree": {
- "version": "0.15.0",
- "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.15.0.tgz",
- "integrity": "sha512-lLYvAd+6BnOqWdnNbP/Q8xfl8LOGw4wVjfrNd9Gt8eoFzhNBRVD95n4l2ksfMVOoxuVyegs85g83KS9QOsxbVQ=="
+ "node_modules/@react-navigation/routers": {
+ "version": "6.1.9",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
+ "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==",
+ "dependencies": {
+ "nanoid": "^3.1.23"
+ }
},
- "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-parser": {
- "version": "0.15.0",
- "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.15.0.tgz",
- "integrity": "sha512-Q1uks5rjZlE9RjMMjSUCkGrEIPI5pKJILeCtK1VmTj7U4pf3wVPoo+cxfu+s4cBAPy2JzikIIdCZgBoR6x7U1Q==",
+ "node_modules/@react-navigation/stack": {
+ "version": "6.3.29",
+ "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.3.29.tgz",
+ "integrity": "sha512-tzlGkoRgB6P7vgw7rHuWo3TL7Gzu6xh5LMf+zSdCuEiKp/qASzxYfnTEr9tOLbVs/gf+qeukEDheCSAJKVpBXw==",
"dependencies": {
- "hermes-estree": "0.15.0"
+ "@react-navigation/elements": "^1.3.30",
+ "color": "^4.2.3",
+ "warn-once": "^0.1.0"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^6.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-gesture-handler": ">= 1.0.0",
+ "react-native-safe-area-context": ">= 3.0.0",
+ "react-native-screens": ">= 3.0.0"
}
},
- "node_modules/@react-native/normalize-colors": {
- "version": "0.73.2",
- "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.73.2.tgz",
- "integrity": "sha512-bRBcb2T+I88aG74LMVHaKms2p/T8aQd8+BZ7LuuzXlRfog1bMWWn/C5i0HVuvW4RPtXQYgIlGiXVDy9Ir1So/w=="
+ "node_modules/@reduxjs/toolkit": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.5.tgz",
+ "integrity": "sha512-aeFA/s5NCG7NoJe/MhmwREJxRkDs0ZaSqt0MxhWUrwCf1UQXpwR87RROJEql0uAkLI6U7snBOYOcKw83ew3FPg==",
+ "dependencies": {
+ "immer": "^10.0.3",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "reselect": "^5.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
},
- "node_modules/@react-native/virtualized-lists": {
- "version": "0.73.4",
- "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.73.4.tgz",
- "integrity": "sha512-HpmLg1FrEiDtrtAbXiwCgXFYyloK/dOIPIuWW3fsqukwJEWAiTzm1nXGJ7xPU5XTHiWZ4sKup5Ebaj8z7iyWog==",
+ "node_modules/@rnx-kit/chromium-edge-launcher": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz",
+ "integrity": "sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==",
"dependencies": {
- "invariant": "^2.2.4",
- "nullthrows": "^1.1.1"
+ "@types/node": "^18.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "is-wsl": "^2.2.0",
+ "lighthouse-logger": "^1.0.0",
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=14.15"
+ }
+ },
+ "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
},
- "peerDependencies": {
- "react-native": "*"
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@rnx-kit/chromium-edge-launcher/node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@rnx-kit/chromium-edge-launcher": {
@@ -5841,6 +6188,93 @@
"@sinonjs/commons": "^3.0.0"
}
},
+ "node_modules/@stripe/stripe-react-native": {
+ "version": "0.37.3",
+ "resolved": "https://registry.npmjs.org/@stripe/stripe-react-native/-/stripe-react-native-0.37.3.tgz",
+ "integrity": "sha512-9OReixY4bP3ogHUoAmDs+FRKxTCCDz6APep6fn8LAzs5xG5IGDetFb7UlZkigFYqtJgbe3+n4kMS5wIcxKgnLQ==",
+ "peerDependencies": {
+ "expo": ">=46.0.9",
+ "react": "*",
+ "react-native": "*"
+ },
+ "peerDependenciesMeta": {
+ "expo": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@supabase/auth-js": {
+ "version": "2.64.2",
+ "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.64.2.tgz",
+ "integrity": "sha512-s+lkHEdGiczDrzXJ1YWt2y3bxRi+qIUnXcgkpLSrId7yjBeaXBFygNjTaoZLG02KNcYwbuZ9qkEIqmj2hF7svw==",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/functions-js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.3.1.tgz",
+ "integrity": "sha512-QyzNle/rVzlOi4BbVqxLSH828VdGY1RElqGFAj+XeVypj6+PVtMlD21G8SDnsPQDtlqqTtoGRgdMlQZih5hTuw==",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/node-fetch": {
+ "version": "2.6.15",
+ "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
+ "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ }
+ },
+ "node_modules/@supabase/postgrest-js": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.15.2.tgz",
+ "integrity": "sha512-9/7pUmXExvGuEK1yZhVYXPZnLEkDTwxgMQHXLrN5BwPZZm4iUCL1YEyep/Z2lIZah8d8M433mVAUEGsihUj5KQ==",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/realtime-js": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.9.5.tgz",
+ "integrity": "sha512-TEHlGwNGGmKPdeMtca1lFTYCedrhTAv3nZVoSjrKQ+wkMmaERuCe57zkC5KSWFzLYkb5FVHW8Hrr+PX1DDwplQ==",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14",
+ "@types/phoenix": "^1.5.4",
+ "@types/ws": "^8.5.10",
+ "ws": "^8.14.2"
+ }
+ },
+ "node_modules/@supabase/storage-js": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.5.tgz",
+ "integrity": "sha512-OpLoDRjFwClwc2cjTJZG8XviTiQH4Ik8sCiMK5v7et0MDu2QlXjCAW3ljxJB5+z/KazdMOTnySi+hysxWUPu3w==",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/supabase-js": {
+ "version": "2.43.1",
+ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.43.1.tgz",
+ "integrity": "sha512-A+RV50mWNtyKo6M0u4G6AOqEifQD+MoOjZcpRkPMPpEAFgMsc2dt3kBlBlR/MgZizWQgUKhsvrwKk0efc8g6Ug==",
+ "dependencies": {
+ "@supabase/auth-js": "2.64.2",
+ "@supabase/functions-js": "2.3.1",
+ "@supabase/node-fetch": "2.6.15",
+ "@supabase/postgrest-js": "1.15.2",
+ "@supabase/realtime-js": "2.9.5",
+ "@supabase/storage-js": "2.5.5"
+ }
+ },
+ "node_modules/@types/hammerjs": {
+ "version": "2.0.45",
+ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.45.tgz",
+ "integrity": "sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ=="
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -5863,9 +6297,9 @@
}
},
"node_modules/@types/node": {
- "version": "20.12.8",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
- "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
+ "version": "18.19.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz",
+ "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -5878,11 +6312,60 @@
"@types/node": "*"
}
},
+ "node_modules/@types/phoenix": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz",
+ "integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA=="
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
+ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-native": {
+ "version": "0.70.19",
+ "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.19.tgz",
+ "integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-native-vector-icons": {
+ "version": "6.4.18",
+ "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz",
+ "integrity": "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw==",
+ "dependencies": {
+ "@types/react": "*",
+ "@types/react-native": "^0.70"
+ }
+ },
"node_modules/@types/stack-utils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
+ "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@@ -6045,6 +6528,25 @@
"strip-ansi": "^5.0.0"
}
},
+ "node_modules/ansi-fragments/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-fragments/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -6108,12 +6610,9 @@
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.1",
@@ -6265,6 +6764,18 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
},
+ "node_modules/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
"node_modules/ast-types": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz",
@@ -6316,6 +6827,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/babel-core": {
"version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
@@ -6374,9 +6895,9 @@
}
},
"node_modules/babel-preset-expo": {
- "version": "11.0.6",
- "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.6.tgz",
- "integrity": "sha512-jRi9I5/jT+dnIiNJDjDg+I/pV+AlxrIW/DNbdqYoRWPZA/LHDqD6IJnJXLxbuTcQ+llp+0LWcU7f/kC/PgGpkw==",
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-11.0.5.tgz",
+ "integrity": "sha512-IjqR4B7wnBU55pofLeLGjwUGrWJE1buamgzE9CYpYCNicZmJcNjXUcinQiurXCMuClF2hOff3QfZsLxnGj1UaA==",
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.12.9",
"@babel/plugin-transform-export-namespace-from": "^7.22.11",
@@ -6389,11 +6910,24 @@
"react-refresh": "^0.14.2"
}
},
+ "node_modules/badgin": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz",
+ "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw=="
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -6455,6 +6989,11 @@
"node": ">= 6"
}
},
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+ },
"node_modules/bplist-creator": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz",
@@ -6691,6 +7230,14 @@
"node": ">=4"
}
},
+ "node_modules/caller-callsite/node_modules/callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/caller-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
@@ -6703,11 +7250,12 @@
}
},
"node_modules/callsites": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
- "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"engines": {
- "node": ">=4"
+ "node": ">=6"
}
},
"node_modules/camelcase": {
@@ -6722,9 +7270,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001614",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz",
- "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==",
+ "version": "1.0.30001616",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
+ "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
"funding": [
{
"type": "opencollective",
@@ -6753,6 +7301,14 @@
"node": ">=4"
}
},
+ "node_modules/chalk/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
@@ -6786,86 +7342,26 @@
"node": ">=12.13.0"
}
},
- "node_modules/chrome-launcher/node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/chromium-edge-launcher": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz",
- "integrity": "sha512-pgtgjNKZ7i5U++1g1PWv75umkHvhVTDOQIZ+sjeUX9483S7Y6MUvO0lrd7ShGlQlFHMN4SwKTCq/X8hWrbv2KA==",
- "dependencies": {
- "@types/node": "*",
- "escape-string-regexp": "^4.0.0",
- "is-wsl": "^2.2.0",
- "lighthouse-logger": "^1.0.0",
- "mkdirp": "^1.0.4",
- "rimraf": "^3.0.2"
+ "node": ">=8"
}
},
- "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/chromium-edge-launcher/node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "bin": {
- "mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/chromium-edge-launcher/node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/ci-info": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
- "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/sibiraj-s"
- }
- ],
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "engines": {
- "node": ">=6"
+ "node": ">=6"
}
},
"node_modules/cli-cursor": {
@@ -6903,17 +7399,6 @@
"node": ">=12"
}
},
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
@@ -6935,6 +7420,18 @@
"node": ">=6"
}
},
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -6948,6 +7445,31 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/color/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
"node_modules/colorette": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
@@ -7099,6 +7621,46 @@
"node": ">=4"
}
},
+ "node_modules/cosmiconfig/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "dependencies": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/cosmiconfig/node_modules/resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/cross-fetch": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
@@ -7136,6 +7698,57 @@
"node": ">=8"
}
},
+ "node_modules/css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+ "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+ "dependencies": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/css-tree/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
"node_modules/dag-map": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz",
@@ -7218,6 +7831,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/decode-uri-component": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
@@ -7367,19 +7988,6 @@
"node": ">= 0.8"
}
},
- "node_modules/deprecated-react-native-prop-types": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-5.0.0.tgz",
- "integrity": "sha512-cIK8KYiiGVOFsKdPMmm1L3tA/Gl+JopXL6F5+C7x39MyPsQYnP57Im/D6bNUzcborD7fcMwiwZqcBdBXXZucYQ==",
- "dependencies": {
- "@react-native/normalize-colors": "^0.73.0",
- "invariant": "^2.2.4",
- "prop-types": "^15.8.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -7423,6 +8031,57 @@
"node": ">=6.0.0"
}
},
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+ "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
@@ -7454,9 +8113,18 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
- "version": "1.4.752",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz",
- "integrity": "sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q=="
+ "version": "1.4.759",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.759.tgz",
+ "integrity": "sha512-qZJc+zsuI+/5UjOSFnpkJBwwLMH1AZgyKqJ7LUNnRsB7v/cDjMu9DvXgp9kH6PTTZxjnPXGp2Uhurw+2Ll4Hjg=="
+ },
+ "node_modules/emailjs-com": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/emailjs-com/-/emailjs-com-3.2.0.tgz",
+ "integrity": "sha512-Prbz3E1usiAwGjMNYRv6EsJ5c373cX7/AGnZQwOfrpNJrygQJ15+E9OOq4pU8yC977Z5xMetRfc3WmDX6RcjAA==",
+ "deprecated": "The SDK name changed to @emailjs/browser",
+ "engines": {
+ "node": ">=12.0.0"
+ }
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -7479,6 +8147,17 @@
"once": "^1.4.0"
}
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/env-editor": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/env-editor/-/env-editor-0.4.2.tgz",
@@ -7697,11 +8376,14 @@
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"engines": {
- "node": ">=0.8.0"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint": {
@@ -7803,23 +8485,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/eslint-plugin-react/node_modules/resolve": {
- "version": "2.0.0-next.5",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
- "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
- "dev": true,
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -7872,12 +8537,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/eslint/node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
"node_modules/eslint/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -7912,6 +8571,7 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -7960,6 +8620,7 @@
"node": ">=8"
}
},
+
"node_modules/eslint/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -8178,23 +8839,23 @@
}
},
"node_modules/expo": {
- "version": "51.0.5",
- "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.5.tgz",
- "integrity": "sha512-i+AfkfToYPFCobX0NUT9PmDtsB3L19vhCB/XUE1Ph6FVrBF6GipkHzTADuRLMaVg5u/2sm/fdzGzBcMjXSHqmA==",
+ "version": "51.0.0",
+ "resolved": "https://registry.npmjs.org/expo/-/expo-51.0.0.tgz",
+ "integrity": "sha512-qY4gECM+YDWgmv0rTzdlrbvGKYLMy/xQ6FtYp2/HG+yF+XpqpKTCNQ2RZN97DRIXlPmxhPd/S5IUD46kW3TQaQ==",
"dependencies": {
"@babel/runtime": "^7.20.0",
- "@expo/cli": "0.18.11",
+ "@expo/cli": "0.18.9",
"@expo/config": "9.0.1",
"@expo/config-plugins": "8.0.4",
"@expo/metro-config": "0.18.3",
"@expo/vector-icons": "^14.0.0",
- "babel-preset-expo": "~11.0.6",
+ "babel-preset-expo": "~11.0.5",
"expo-asset": "~10.0.6",
"expo-file-system": "~17.0.1",
- "expo-font": "~12.0.5",
+ "expo-font": "~12.0.4",
"expo-keep-awake": "~13.0.1",
"expo-modules-autolinking": "1.11.1",
- "expo-modules-core": "1.12.10",
+ "expo-modules-core": "1.12.9",
"fbemitter": "^3.0.0",
"whatwg-url-without-unicode": "8.0.0-3"
},
@@ -8202,6 +8863,14 @@
"expo": "bin/cli"
}
},
+ "node_modules/expo-application": {
+ "version": "5.9.1",
+ "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-5.9.1.tgz",
+ "integrity": "sha512-uAfLBNZNahnDZLRU41ZFmNSKtetHUT9Ua557/q189ua0AWV7pQjoVAx49E4953feuvqc9swtU3ScZ/hN1XO/FQ==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-asset": {
"version": "10.0.6",
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-10.0.6.tgz",
@@ -8216,12 +8885,21 @@
"expo": "*"
}
},
- "node_modules/expo-asset/node_modules/@react-native/assets-registry": {
- "version": "0.74.83",
- "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.83.tgz",
- "integrity": "sha512-2vkLMVnp+YTZYTNSDIBZojSsjz8sl5PscP3j4GcV6idD8V978SZfwFlk8K0ti0BzRs11mzL0Pj17km597S/eTQ==",
- "engines": {
- "node": ">=18"
+ "node_modules/expo-av": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-14.0.3.tgz",
+ "integrity": "sha512-gOsTBEUxpg6IG+IGgRPA195vAzU7bI/8CyR35/gJUZ7IMBlymn1gVFFhLjCnDxcj7IE4L+c6p8poFgim4j5pHw==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-blur": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.2.tgz",
+ "integrity": "sha512-t2p7BChO3Reykued++QJRMZ/og6J3aXtSQ+bU31YcBeXhZLkHwjWEhiPKPnJka7J2/yTs4+jOCNDY0kCZmcE3w==",
+ "peerDependencies": {
+ "expo": "*"
+
}
},
"node_modules/expo-constants": {
@@ -8235,6 +8913,14 @@
"expo": "*"
}
},
+ "node_modules/expo-contacts": {
+ "version": "13.0.3",
+ "resolved": "https://registry.npmjs.org/expo-contacts/-/expo-contacts-13.0.3.tgz",
+ "integrity": "sha512-TRB3+en0u/cwmlc1ENcB9dxJY+6rxs9C8uEte1WfpzZLcyRk5ifP48LCLatYQdTlQqtuF262uFRPjrvmzrygwg==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-file-system": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-17.0.1.tgz",
@@ -8244,9 +8930,10 @@
}
},
"node_modules/expo-font": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.5.tgz",
- "integrity": "sha512-h/VkN4jlHYDJ6T6pPgOYTVoDEfBY0CTKQe4pxnPDGQiE6H+DFdDgk+qWVABGpRMH0+zXoHB+AEi3OoQjXIynFA==",
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-12.0.4.tgz",
+ "integrity": "sha512-VtOQB7MEeFMVwo46/9/ntqzrgraTE7gAsnfi2NukFcCpDmyAU3G1R7m287LUXltE46SmGkMgAvM6+fflXFjaJA==",
+
"dependencies": {
"fontfaceobserver": "^2.1.0"
},
@@ -8254,22 +8941,61 @@
"expo": "*"
}
},
- "node_modules/expo-keep-awake": {
- "version": "13.0.1",
- "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.1.tgz",
- "integrity": "sha512-Kqv8Bf1f5Jp7YMUgTTyKR9GatgHJuAcC8vVWDEkgVhB3O7L3pgBy5MMSMUhkTmRRV6L8TZe/rDmjiBoVS/soFA==",
+ "node_modules/expo-image-loader": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.7.0.tgz",
+ "integrity": "sha512-cx+MxxsAMGl9AiWnQUzrkJMJH4eNOGlu7XkLGnAXSJrRoIiciGaKqzeaD326IyCTV+Z1fXvIliSgNW+DscvD8g==",
"peerDependencies": {
"expo": "*"
}
},
- "node_modules/expo-modules-autolinking": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.1.tgz",
- "integrity": "sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw==",
+ "node_modules/expo-image-manipulator": {
+ "version": "12.0.3",
+ "resolved": "https://registry.npmjs.org/expo-image-manipulator/-/expo-image-manipulator-12.0.3.tgz",
+ "integrity": "sha512-gosW32roHbXRKPiBVbQDFpxaZf8sjOJ9aaqbe085Qfcenvvr1lNFMx9M9BFYhAoKd23oEWlyvNHDnAayV4gAFA==",
"dependencies": {
- "chalk": "^4.1.0",
- "commander": "^7.2.0",
- "fast-glob": "^3.2.5",
+ "expo-image-loader": "~4.7.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-image-picker": {
+ "version": "15.0.5",
+ "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-15.0.5.tgz",
+ "integrity": "sha512-Qqp16udsadx/YpNcNaWzfbmO0tbMxyX9bS1aFiDVC+Zffh8LY8S4HJJcnWqSC2TeuAl+9SxUwTloJagvPeMBBw==",
+ "dependencies": {
+ "expo-image-loader": "~4.7.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-keep-awake": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-13.0.1.tgz",
+ "integrity": "sha512-Kqv8Bf1f5Jp7YMUgTTyKR9GatgHJuAcC8vVWDEkgVhB3O7L3pgBy5MMSMUhkTmRRV6L8TZe/rDmjiBoVS/soFA==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-linear-gradient": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-13.0.2.tgz",
+ "integrity": "sha512-EDcILUjRKu4P1rtWcwciN6CSyGtH7Bq4ll3oTRV7h3h8oSzSilH1g6z7kTAMlacPBKvMnkkWOGzW6KtgMKEiTg==",
+
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-modules-autolinking": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.1.tgz",
+ "integrity": "sha512-2dy3lTz76adOl7QUvbreMCrXyzUiF8lygI7iFJLjgIQIVH+43KnFWE5zBumpPbkiaq0f0uaFpN9U0RGQbnKiMw==",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "commander": "^7.2.0",
+ "fast-glob": "^3.2.5",
"find-up": "^5.0.0",
"fs-extra": "^9.1.0"
},
@@ -8375,23 +9101,94 @@
}
},
"node_modules/expo-modules-core": {
- "version": "1.12.10",
- "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.10.tgz",
- "integrity": "sha512-aS4imfr7fuUtcx+j/CHuG6ohNSThyCzGRh1kKjQTDcO0/CqDO2cSFnxf7n2vpiRFgyoMFJvFFtW/zIzVXiC2Tw==",
+ "version": "1.12.9",
+ "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-1.12.9.tgz",
+ "integrity": "sha512-t0HrPwelNFqGiaa9RsDt2ttDekAbgHjcq4PBovNS0jyhRwBbDDb465xoMxG+V4eNLBYTP+BVgxMHK+TPxT2QgQ==",
"dependencies": {
"invariant": "^2.2.4"
}
},
+ "node_modules/expo-notifications": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.28.1.tgz",
+ "integrity": "sha512-qBVcq3lc+FIvcYt/8M+JB1c60g0hVuyGY4MVGTY56ciU6nMOCiBiz4XPc3DeiZA16jVtfriooWA26wqBkQfkHg==",
+ "dependencies": {
+ "@expo/image-utils": "^0.5.0",
+ "@ide/backoff": "^1.0.0",
+ "abort-controller": "^3.0.0",
+ "assert": "^2.0.0",
+ "badgin": "^1.1.5",
+ "expo-application": "~5.9.0",
+ "expo-constants": "~16.0.0",
+ "fs-extra": "^9.1.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/expo-notifications/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/expo-permissions": {
+ "version": "14.4.0",
+ "resolved": "https://registry.npmjs.org/expo-permissions/-/expo-permissions-14.4.0.tgz",
+ "integrity": "sha512-oAcnJ7dlZhpBydK73cwomA2xofizayVUz+FW5REl7dMu7MYyeN/3aqhlpZ3mYddrxvG161bqu97MQr01UixUnw==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-sharing": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-12.0.1.tgz",
+ "integrity": "sha512-wBT+WeXwapj/9NWuLJO01vi9bdlchYu/Q/xD8slL/Ls4vVYku8CPqzkTtDFcjLrjtlJqyeHsdQXwKLvORmBIew==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-status-bar": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.11.1.tgz",
- "integrity": "sha512-ddQEtCOgYHTLlFUe/yH67dDBIoct5VIULthyT3LRJbEwdpzAgueKsX2FYK02ldh440V87PWKCamh7R9evk1rrg=="
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.12.1.tgz",
+ "integrity": "sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA=="
+ },
+ "node_modules/fast-base64-decode": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
+ "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-glob": {
"version": "3.3.2",
@@ -8408,6 +9205,17 @@
"node": ">=8.6.0"
}
},
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -8512,6 +9320,14 @@
"node": ">=8"
}
},
+ "node_modules/filter-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+ "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -8626,6 +9442,25 @@
"node": ">=0.4.0"
}
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fontfaceobserver": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz",
@@ -8640,9 +9475,9 @@
}
},
"node_modules/form-data": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
- "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -8840,14 +9675,15 @@
}
},
"node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
"dependencies": {
- "is-glob": "^4.0.1"
+ "is-glob": "^4.0.3"
},
"engines": {
- "node": ">= 6"
+ "node": ">=10.13.0"
}
},
"node_modules/globals": {
@@ -9038,6 +9874,14 @@
"node": ">=8"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
"node_modules/hosted-git-info": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz",
@@ -9108,6 +9952,11 @@
"node": ">=10.17.0"
}
},
+ "node_modules/humps": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
+ "integrity": "sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g=="
+ },
"node_modules/husky": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
@@ -9164,22 +10013,36 @@
"node": ">=16.x"
}
},
+ "node_modules/immer": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
- "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
"dependencies": {
- "caller-path": "^2.0.0",
- "resolve-from": "^3.0.0"
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
},
"engines": {
- "node": ">=4"
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/import-fresh/node_modules/resolve-from": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
"engines": {
"node": ">=4"
}
@@ -9273,6 +10136,21 @@
"node": ">= 0.10"
}
},
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
@@ -9443,7 +10321,6 @@
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
- "dev": true,
"dependencies": {
"has-tostringtag": "^1.0.0"
},
@@ -9515,6 +10392,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-negative-zero": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
@@ -9564,6 +10456,14 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -9739,9 +10639,9 @@
}
},
"node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
},
"node_modules/isexe": {
"version": "2.0.0",
@@ -10160,9 +11060,9 @@
"integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww=="
},
"node_modules/joi": {
- "version": "17.13.0",
- "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.0.tgz",
- "integrity": "sha512-9qcrTyoBmFZRNHeVP4edKqIUEgFzq7MHvTNSDuHSqkpOPtiBkgNgcmTSqmiw1kw9tdKaiddvIDv/eCJDxmqWCA==",
+ "version": "17.13.1",
+ "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz",
+ "integrity": "sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==",
"dependencies": {
"@hapi/hoek": "^9.3.0",
"@hapi/topo": "^5.1.0",
@@ -10182,12 +11082,11 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
+ "argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
@@ -10669,6 +11568,11 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -10800,17 +11704,6 @@
"node": ">=8"
}
},
- "node_modules/logkitty/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/logkitty/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -10943,6 +11836,11 @@
"resolved": "https://registry.npmjs.org/md5hex/-/md5hex-1.0.0.tgz",
"integrity": "sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ=="
},
+ "node_modules/mdn-data": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
+ },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
@@ -10953,6 +11851,17 @@
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
"integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA=="
},
+ "node_modules/merge-options": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
+ "dependencies": {
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -10967,9 +11876,9 @@
}
},
"node_modules/metro": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.8.tgz",
- "integrity": "sha512-in7S0W11mg+RNmcXw+2d9S3zBGmCARDxIwoXJAmLUQOQoYsRP3cpGzyJtc7WOw8+FXfpgXvceD0u+PZIHXEL7g==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.80.9.tgz",
+ "integrity": "sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg==",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"@babel/core": "^7.20.0",
@@ -10992,18 +11901,18 @@
"jest-worker": "^29.6.3",
"jsc-safe-url": "^0.2.2",
"lodash.throttle": "^4.1.1",
- "metro-babel-transformer": "0.80.8",
- "metro-cache": "0.80.8",
- "metro-cache-key": "0.80.8",
- "metro-config": "0.80.8",
- "metro-core": "0.80.8",
- "metro-file-map": "0.80.8",
- "metro-resolver": "0.80.8",
- "metro-runtime": "0.80.8",
- "metro-source-map": "0.80.8",
- "metro-symbolicate": "0.80.8",
- "metro-transform-plugins": "0.80.8",
- "metro-transform-worker": "0.80.8",
+ "metro-babel-transformer": "0.80.9",
+ "metro-cache": "0.80.9",
+ "metro-cache-key": "0.80.9",
+ "metro-config": "0.80.9",
+ "metro-core": "0.80.9",
+ "metro-file-map": "0.80.9",
+ "metro-resolver": "0.80.9",
+ "metro-runtime": "0.80.9",
+ "metro-source-map": "0.80.9",
+ "metro-symbolicate": "0.80.9",
+ "metro-transform-plugins": "0.80.9",
+ "metro-transform-worker": "0.80.9",
"mime-types": "^2.1.27",
"node-fetch": "^2.2.0",
"nullthrows": "^1.1.1",
@@ -11023,9 +11932,9 @@
}
},
"node_modules/metro-babel-transformer": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.8.tgz",
- "integrity": "sha512-TTzNwRZb2xxyv4J/+yqgtDAP2qVqH3sahsnFu6Xv4SkLqzrivtlnyUbaeTdJ9JjtADJUEjCbgbFgUVafrXdR9Q==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.9.tgz",
+ "integrity": "sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ==",
"dependencies": {
"@babel/core": "^7.20.0",
"hermes-parser": "0.20.1",
@@ -11049,11 +11958,11 @@
}
},
"node_modules/metro-cache": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.8.tgz",
- "integrity": "sha512-5svz+89wSyLo7BxdiPDlwDTgcB9kwhNMfNhiBZPNQQs1vLFXxOkILwQiV5F2EwYT9DEr6OPZ0hnJkZfRQ8lDYQ==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.9.tgz",
+ "integrity": "sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w==",
"dependencies": {
- "metro-core": "0.80.8",
+ "metro-core": "0.80.9",
"rimraf": "^3.0.2"
},
"engines": {
@@ -11061,9 +11970,9 @@
}
},
"node_modules/metro-cache-key": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.8.tgz",
- "integrity": "sha512-qWKzxrLsRQK5m3oH8ePecqCc+7PEhR03cJE6Z6AxAj0idi99dHOSitTmY0dclXVB9vP2tQIAE8uTd8xkYGk8fA==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.9.tgz",
+ "integrity": "sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg==",
"engines": {
"node": ">=18"
}
@@ -11083,38 +11992,38 @@
}
},
"node_modules/metro-config": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.8.tgz",
- "integrity": "sha512-VGQJpfJawtwRzGzGXVUoohpIkB0iPom4DmSbAppKfumdhtLA8uVeEPp2GM61kL9hRvdbMhdWA7T+hZFDlo4mJA==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.80.9.tgz",
+ "integrity": "sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg==",
"dependencies": {
"connect": "^3.6.5",
"cosmiconfig": "^5.0.5",
"jest-validate": "^29.6.3",
- "metro": "0.80.8",
- "metro-cache": "0.80.8",
- "metro-core": "0.80.8",
- "metro-runtime": "0.80.8"
+ "metro": "0.80.9",
+ "metro-cache": "0.80.9",
+ "metro-core": "0.80.9",
+ "metro-runtime": "0.80.9"
},
"engines": {
"node": ">=18"
}
},
"node_modules/metro-core": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.8.tgz",
- "integrity": "sha512-g6lud55TXeISRTleW6SHuPFZHtYrpwNqbyFIVd9j9Ofrb5IReiHp9Zl8xkAfZQp8v6ZVgyXD7c130QTsCz+vBw==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.80.9.tgz",
+ "integrity": "sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg==",
"dependencies": {
"lodash.throttle": "^4.1.1",
- "metro-resolver": "0.80.8"
+ "metro-resolver": "0.80.9"
},
"engines": {
"node": ">=18"
}
},
"node_modules/metro-file-map": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.8.tgz",
- "integrity": "sha512-eQXMFM9ogTfDs2POq7DT2dnG7rayZcoEgRbHPXvhUWkVwiKkro2ngcBE++ck/7A36Cj5Ljo79SOkYwHaWUDYDw==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.9.tgz",
+ "integrity": "sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ==",
"dependencies": {
"anymatch": "^3.0.3",
"debug": "^2.2.0",
@@ -11148,9 +12057,9 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/metro-minify-terser": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.8.tgz",
- "integrity": "sha512-y8sUFjVvdeUIINDuW1sejnIjkZfEF+7SmQo0EIpYbWmwh+kq/WMj74yVaBWuqNjirmUp1YNfi3alT67wlbBWBQ==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.9.tgz",
+ "integrity": "sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A==",
"dependencies": {
"terser": "^5.15.0"
},
@@ -11159,17 +12068,17 @@
}
},
"node_modules/metro-resolver": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.8.tgz",
- "integrity": "sha512-JdtoJkP27GGoZ2HJlEsxs+zO7jnDUCRrmwXJozTlIuzLHMRrxgIRRby9fTCbMhaxq+iA9c+wzm3iFb4NhPmLbQ==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.9.tgz",
+ "integrity": "sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w==",
"engines": {
"node": ">=18"
}
},
"node_modules/metro-runtime": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.8.tgz",
- "integrity": "sha512-2oScjfv6Yb79PelU1+p8SVrCMW9ZjgEiipxq7jMRn8mbbtWzyv3g8Mkwr+KwOoDFI/61hYPUbY8cUnu278+x1g==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.9.tgz",
+ "integrity": "sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg==",
"dependencies": {
"@babel/runtime": "^7.0.0"
},
@@ -11178,16 +12087,16 @@
}
},
"node_modules/metro-source-map": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.8.tgz",
- "integrity": "sha512-+OVISBkPNxjD4eEKhblRpBf463nTMk3KMEeYS8Z4xM/z3qujGJGSsWUGRtH27+c6zElaSGtZFiDMshEb8mMKQg==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.9.tgz",
+ "integrity": "sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw==",
"dependencies": {
"@babel/traverse": "^7.20.0",
"@babel/types": "^7.20.0",
"invariant": "^2.2.4",
- "metro-symbolicate": "0.80.8",
+ "metro-symbolicate": "0.80.9",
"nullthrows": "^1.1.1",
- "ob1": "0.80.8",
+ "ob1": "0.80.9",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
},
@@ -11204,12 +12113,12 @@
}
},
"node_modules/metro-symbolicate": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.8.tgz",
- "integrity": "sha512-nwhYySk79jQhwjL9QmOUo4wS+/0Au9joEryDWw7uj4kz2yvw1uBjwmlql3BprQCBzRdB3fcqOP8kO8Es+vE31g==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.9.tgz",
+ "integrity": "sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA==",
"dependencies": {
"invariant": "^2.2.4",
- "metro-source-map": "0.80.8",
+ "metro-source-map": "0.80.9",
"nullthrows": "^1.1.1",
"source-map": "^0.5.6",
"through2": "^2.0.1",
@@ -11231,9 +12140,9 @@
}
},
"node_modules/metro-transform-plugins": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.8.tgz",
- "integrity": "sha512-sSu8VPL9Od7w98MftCOkQ1UDeySWbsIAS5I54rW22BVpPnI3fQ42srvqMLaJUQPjLehUanq8St6OMBCBgH/UWw==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.9.tgz",
+ "integrity": "sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg==",
"dependencies": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
@@ -11246,21 +12155,21 @@
}
},
"node_modules/metro-transform-worker": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.8.tgz",
- "integrity": "sha512-+4FG3TQk3BTbNqGkFb2uCaxYTfsbuFOCKMMURbwu0ehCP8ZJuTUramkaNZoATS49NSAkRgUltgmBa4YaKZ5mqw==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.9.tgz",
+ "integrity": "sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ==",
"dependencies": {
"@babel/core": "^7.20.0",
"@babel/generator": "^7.20.0",
"@babel/parser": "^7.20.0",
"@babel/types": "^7.20.0",
- "metro": "0.80.8",
- "metro-babel-transformer": "0.80.8",
- "metro-cache": "0.80.8",
- "metro-cache-key": "0.80.8",
- "metro-minify-terser": "0.80.8",
- "metro-source-map": "0.80.8",
- "metro-transform-plugins": "0.80.8",
+ "metro": "0.80.9",
+ "metro-babel-transformer": "0.80.9",
+ "metro-cache": "0.80.9",
+ "metro-cache-key": "0.80.9",
+ "metro-minify-terser": "0.80.9",
+ "metro-source-map": "0.80.9",
+ "metro-transform-plugins": "0.80.9",
"nullthrows": "^1.1.1"
},
"engines": {
@@ -11373,17 +12282,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/metro/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/metro/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -11572,6 +12470,14 @@
"mkdirp": "bin/cmd.js"
}
},
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -11803,15 +12709,27 @@
"node": ">=4"
}
},
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+
"node_modules/nullthrows": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
"integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
},
"node_modules/ob1": {
- "version": "0.80.8",
- "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.8.tgz",
- "integrity": "sha512-QHJQk/lXMmAW8I7AIM3in1MSlwe1umR72Chhi8B7Xnq6mzjhBKkA6Fy/zAhQnGkA4S912EPCEvTij5yh+EQTAA==",
+ "version": "0.80.9",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.80.9.tgz",
+ "integrity": "sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==",
"engines": {
"node": ">=18"
}
@@ -11832,6 +12750,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -11977,9 +12910,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/optionator": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "node_modules/opencollective-postinstall": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+ "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+ "bin": {
+ "opencollective-postinstall": "index.js"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"dependencies": {
@@ -12010,6 +12951,25 @@
"node": ">=6"
}
},
+ "node_modules/ora/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ora/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -12105,15 +13065,6 @@
"node": ">=6"
}
},
- "node_modules/parent-module/node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
@@ -12191,6 +13142,14 @@
"node": ">=8"
}
},
+ "node_modules/paths-js": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
+ "integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==",
+ "engines": {
+ "node": ">=0.11.0"
+ }
+ },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -12327,6 +13286,11 @@
"node": ">=4.0.0"
}
},
+ "node_modules/point-in-polygon": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
+ "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="
+ },
"node_modules/possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -12551,6 +13515,11 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@@ -12576,6 +13545,46 @@
"qrcode-terminal": "bin/qrcode-terminal.js"
}
},
+ "node_modules/qs": {
+ "version": "6.12.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
+ "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
+ "dependencies": {
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/query-string": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
+ "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
+ "dependencies": {
+ "decode-uri-component": "^0.2.2",
+ "filter-obj": "^1.1.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/querystring": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz",
+ "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/queue": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
@@ -12603,145 +13612,526 @@
}
]
},
- "node_modules/range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "engines": {
- "node": ">= 0.6"
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-devtools-core": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.2.0.tgz",
+ "integrity": "sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A==",
+ "dependencies": {
+ "shell-quote": "^1.6.1",
+ "ws": "^7"
+ }
+ },
+ "node_modules/react-devtools-core/node_modules/ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-freeze": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
+ "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==",
+ "peer": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=17.0.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-native": {
+ "version": "0.74.1",
+ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.1.tgz",
+ "integrity": "sha512-0H2XpmghwOtfPpM2LKqHIN7gxy+7G/r1hwJHKLV6uoyXGC/gCojRtoo5NqyKrWpFC8cqyT6wTYCLuG7CxEKilg==",
+ "dependencies": {
+ "@jest/create-cache-key-function": "^29.6.3",
+ "@react-native-community/cli": "13.6.6",
+ "@react-native-community/cli-platform-android": "13.6.6",
+ "@react-native-community/cli-platform-ios": "13.6.6",
+ "@react-native/assets-registry": "0.74.83",
+ "@react-native/codegen": "0.74.83",
+ "@react-native/community-cli-plugin": "0.74.83",
+ "@react-native/gradle-plugin": "0.74.83",
+ "@react-native/js-polyfills": "0.74.83",
+ "@react-native/normalize-colors": "0.74.83",
+ "@react-native/virtualized-lists": "0.74.83",
+ "abort-controller": "^3.0.0",
+ "anser": "^1.4.9",
+ "ansi-regex": "^5.0.0",
+ "base64-js": "^1.5.1",
+ "chalk": "^4.0.0",
+ "event-target-shim": "^5.0.1",
+ "flow-enums-runtime": "^0.0.6",
+ "invariant": "^2.2.4",
+ "jest-environment-node": "^29.6.3",
+ "jsc-android": "^250231.0.0",
+ "memoize-one": "^5.0.0",
+ "metro-runtime": "^0.80.3",
+ "metro-source-map": "^0.80.3",
+ "mkdirp": "^0.5.1",
+ "nullthrows": "^1.1.1",
+ "pretty-format": "^26.5.2",
+ "promise": "^8.3.0",
+ "react-devtools-core": "^5.0.0",
+ "react-refresh": "^0.14.0",
+ "react-shallow-renderer": "^16.15.0",
+ "regenerator-runtime": "^0.13.2",
+ "scheduler": "0.24.0-canary-efb381bbf-20230505",
+ "stacktrace-parser": "^0.1.10",
+ "whatwg-fetch": "^3.0.0",
+ "ws": "^6.2.2",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "react-native": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.6",
+ "react": "18.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-native-actionsheet": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/react-native-actionsheet/-/react-native-actionsheet-2.4.2.tgz",
+ "integrity": "sha512-DBoWIvVwuWXuptF4t46pBqkFxaUxS+rsIdHiA05t0n4BdTIDV2R4s9bLEUVOGzb94D7VxIamsXZPA/3mmw+SXg==",
+ "peerDependencies": {
+ "prop-types": ">=15.4.0",
+ "react": ">=15.4.0",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-animatable": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
+ "integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ }
+ },
+ "node_modules/react-native-chart-kit": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-6.12.0.tgz",
+ "integrity": "sha512-nZLGyCFzZ7zmX0KjYeeSV1HKuPhl1wOMlTAqa0JhlyW62qV/1ZPXHgT8o9s8mkFaGxdqbspOeuaa6I9jUQDgnA==",
+ "dependencies": {
+ "lodash": "^4.17.13",
+ "paths-js": "^0.4.10",
+ "point-in-polygon": "^1.0.1"
+ },
+ "peerDependencies": {
+ "react": "> 16.7.0",
+ "react-native": ">= 0.50.0",
+ "react-native-svg": "> 6.4.1"
+ }
+ },
+ "node_modules/react-native-collapsible": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.6.1.tgz",
+ "integrity": "sha512-orF4BeiXd2hZW7fu9YcqIJXzN6TJcFcddY807D3MAOVktLuW9oQ+RIkrTJ5DR3v9ZOFfREkOjEmS79qeUTvkBQ==",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-elements": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.3.tgz",
+ "integrity": "sha512-VtZc25EecPZyUBER85zFK9ZbY6kkUdcm1ZwJ9hdoGSCr1R/GFgxor4jngOcSYeMvQ+qimd5No44OVJW3rSJECA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@types/react-native-vector-icons": "^6.4.6",
+ "color": "^3.1.2",
+ "deepmerge": "^4.2.2",
+ "hoist-non-react-statics": "^3.3.2",
+ "lodash.isequal": "^4.5.0",
+ "opencollective-postinstall": "^2.0.3",
+ "react-native-ratings": "8.0.4",
+ "react-native-size-matters": "^0.3.1"
+ },
+ "peerDependencies": {
+ "react-native-safe-area-context": ">= 3.0.0",
+ "react-native-vector-icons": ">7.0.0"
+ }
+ },
+ "node_modules/react-native-elements/node_modules/color": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+ "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+ "dependencies": {
+ "color-convert": "^1.9.3",
+ "color-string": "^1.6.0"
+ }
+ },
+ "node_modules/react-native-gesture-handler": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz",
+ "integrity": "sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==",
+ "dependencies": {
+ "@egjs/hammerjs": "^2.0.17",
+ "hoist-non-react-statics": "^3.3.0",
+ "invariant": "^2.2.4",
+ "lodash": "^4.17.21",
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-get-random-values": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz",
+ "integrity": "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==",
+ "dependencies": {
+ "fast-base64-decode": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react-native": ">=0.56"
+ }
+ },
+ "node_modules/react-native-iphone-x-helper": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
+ "integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==",
+ "peerDependencies": {
+ "react-native": ">=0.42.0"
+ }
+ },
+ "node_modules/react-native-keyboard-aware-scroll-view": {
+ "version": "0.9.5",
+ "resolved": "https://registry.npmjs.org/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.9.5.tgz",
+ "integrity": "sha512-XwfRn+T/qBH9WjTWIBiJD2hPWg0yJvtaEw6RtPCa5/PYHabzBaWxYBOl0usXN/368BL1XktnZPh8C2lmTpOREA==",
+ "dependencies": {
+ "prop-types": "^15.6.2",
+ "react-native-iphone-x-helper": "^1.0.3"
+ },
+ "peerDependencies": {
+ "react-native": ">=0.48.4"
+ }
+ },
+ "node_modules/react-native-mime-types": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/react-native-mime-types/-/react-native-mime-types-2.5.0.tgz",
+ "integrity": "sha512-l1NIGxa0MBFPGBFDd3yeGe2f8+Qb+U4M1FEK7l3Fob0R7kOecwgRau2CCCmu6W5QzyhKdBxRC8SOTuTIEKKAUw==",
+ "dependencies": {
+ "mime-db": "~1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/react-native-modal": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-13.0.1.tgz",
+ "integrity": "sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==",
+ "dependencies": {
+ "prop-types": "^15.6.2",
+ "react-native-animatable": "1.3.3"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": ">=0.65.0"
+ }
+ },
+ "node_modules/react-native-modal-datetime-picker": {
+ "version": "17.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-modal-datetime-picker/-/react-native-modal-datetime-picker-17.1.0.tgz",
+ "integrity": "sha512-jfTwfaCLtBffYbQ+pOGFLM+J5HmUh3vb9rT0JrrQPjxzecdc8pNYreB1c96+mVuq8bDCvaCdIeuEsslTqLJL0Q==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "@react-native-community/datetimepicker": ">=6.7.0",
+ "react-native": ">=0.65.0"
+ }
+ },
+ "node_modules/react-native-modalize": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/react-native-modalize/-/react-native-modalize-2.1.1.tgz",
+ "integrity": "sha512-4/7EZWsrUqAAkkAVEnOsSdpAPQaEBewX7TvwFuzgvGDzxKpq3O58I9SnSeU8QtG/r91XYHJNaU5dAuDrcLjUaQ==",
+ "peerDependencies": {
+ "react": "> 15.0.0",
+ "react-native": "> 0.50.0",
+ "react-native-gesture-handler": "> 1.0.0"
+ }
+ },
+ "node_modules/react-native-paper": {
+ "version": "5.12.3",
+ "resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.12.3.tgz",
+ "integrity": "sha512-nH1e1pGPE/aOE5YR2GRX7CfMHFA9cAfrAfgCtwL4amJPDZCoVjc5yt2VDiUE1rT+JUfk0qdICMP3UggxvjMgug==",
+ "dependencies": {
+ "@callstack/react-theme-provider": "^3.0.9",
+ "color": "^3.1.2",
+ "use-latest-callback": "^0.1.5"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*",
+ "react-native-safe-area-context": "*",
+ "react-native-vector-icons": "*"
+ }
+ },
+ "node_modules/react-native-paper/node_modules/color": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+ "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+ "dependencies": {
+ "color-convert": "^1.9.3",
+ "color-string": "^1.6.0"
+ }
+ },
+ "node_modules/react-native-ratings": {
+ "version": "8.0.4",
+ "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-8.0.4.tgz",
+ "integrity": "sha512-Xczu5lskIIRD6BEdz9A0jDRpEck/SFxRqiglkXi0u67yAtI1/pcJC76P4MukCbT8K4BPVl+42w83YqXBoBRl7A==",
+ "dependencies": {
+ "lodash": "^4.17.15"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-raw-bottom-sheet": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/react-native-raw-bottom-sheet/-/react-native-raw-bottom-sheet-3.0.0.tgz",
+ "integrity": "sha512-kHR7j2ExCLqf/AO3MECozMJXi48O1+YxUYSRgRo/5Ftm7mEcrxJEzvjqMmqUbVhhKlfk5hLCGFnEQ5Z9OHCUtg=="
+ },
+ "node_modules/react-native-reanimated": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.11.0.tgz",
+ "integrity": "sha512-BNw/XDgUfs8UhfY1X6IniU8kWpnotWGyt8qmQviaHisTi5lvwnaOdXQKfN1KGONx6ekdFRHRP5EFwLi0UajwKA==",
+ "dependencies": {
+ "@babel/plugin-transform-arrow-functions": "^7.0.0-0",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
+ "@babel/plugin-transform-optional-chaining": "^7.0.0-0",
+ "@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
+ "@babel/plugin-transform-template-literals": "^7.0.0-0",
+ "@babel/preset-typescript": "^7.16.7",
+ "convert-source-map": "^2.0.0",
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-safe-area-context": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.1.tgz",
+ "integrity": "sha512-w8tCuowDorUkPoWPXmhqosovBr33YsukkwYCDERZFHAxIkx6qBadYxfeoaJ91nCQKjkNzGrK5qhoNOeSIcYSpA==",
+ "peer": true,
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-screens": {
+ "version": "3.31.1",
+ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.31.1.tgz",
+ "integrity": "sha512-8fRW362pfZ9y4rS8KY5P3DFScrmwo/vu1RrRMMx0PNHbeC9TLq0Kw1ubD83591yz64gLNHFLTVkTJmWeWCXKtQ==",
+ "peer": true,
+ "dependencies": {
+ "react-freeze": "^1.0.0",
+ "warn-once": "^0.1.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-select-dropdown": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/react-native-select-dropdown/-/react-native-select-dropdown-4.0.1.tgz",
+ "integrity": "sha512-t4se17kALFcPb9wMbxig5dS1BE3pWRC6HPuFlM0J2Y6yhB1GsLqboy6an6R9rML8pRuGIJIxL29cbwEvPQwKxQ=="
+ },
+ "node_modules/react-native-size-matters": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz",
+ "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==",
+ "peerDependencies": {
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-step-indicator": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/react-native-step-indicator/-/react-native-step-indicator-1.0.3.tgz",
+ "integrity": "sha512-WwTb6lpJl0T7MV59sus1utoEQuqbuWHq6V0R0iDWmlPRFjM0qirhTNTS5on1+V9Qmc9dsUCT380/mRgDF+U6zw==",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
}
},
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "node_modules/react-native-svg": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.3.0.tgz",
+ "integrity": "sha512-mBHu/fdlzUbpGX8SZFxgbKvK/sgqLfDLP8uh8G7Us+zJgdjO8OSEeqHQs+kPRdQmdLJQiqPJX2WXgCl7ToTWqw==",
"dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
+ "css-select": "^5.1.0",
+ "css-tree": "^1.1.3"
},
- "bin": {
- "rc": "cli.js"
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
}
},
- "node_modules/react": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
- "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "node_modules/react-native-swipeable": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/react-native-swipeable/-/react-native-swipeable-0.6.0.tgz",
+ "integrity": "sha512-OJUmOtPAZ3s6OHdbmGdGBq3oNZaUFOV81UMQfO2fvIF2cUOMbjUKQCRU7EhijEyufiaMAlSu/VguCRAdLRLk3w==",
"dependencies": {
- "loose-envify": "^1.1.0"
+ "prop-types": "^15.5.10"
},
- "engines": {
- "node": ">=0.10.0"
+ "peerDependencies": {
+ "react": ">=15.2.0",
+ "react-native": ">=0.44.0"
}
},
- "node_modules/react-devtools-core": {
- "version": "4.28.5",
- "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz",
- "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==",
- "dependencies": {
- "shell-quote": "^1.6.1",
- "ws": "^7"
+ "node_modules/react-native-tooltip-menu": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/react-native-tooltip-menu/-/react-native-tooltip-menu-3.0.7.tgz",
+ "integrity": "sha512-d6+MlilDXfygwyMY3LvDPp9J6tLIIgwTwrzyz7g5zez70J4/EfaqYFZJXlxyfH6EQdQrnIz2YhS7jj8BmzsYBA==",
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
}
},
- "node_modules/react-devtools-core/node_modules/ws": {
- "version": "7.5.9",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
- "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
- "engines": {
- "node": ">=8.3.0"
- },
+ "node_modules/react-native-typography": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/react-native-typography/-/react-native-typography-1.4.1.tgz",
+ "integrity": "sha512-dc9Zfs4jUdq4ygx4/KwO6jKTERBu6cRrfPJGntw/pA+D6BMjlWfMNuhZ/69vf4Zpsnt9s4AGe+Z/V1QFYaCXAA==",
"peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
+ "react": "*",
+ "react-native": "*"
}
},
- "node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ "node_modules/react-native-url-polyfill": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz",
+ "integrity": "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==",
+ "dependencies": {
+ "whatwg-url-without-unicode": "8.0.0-3"
+ },
+ "peerDependencies": {
+ "react-native": "*"
+ }
},
- "node_modules/react-native": {
- "version": "0.73.6",
- "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.73.6.tgz",
- "integrity": "sha512-oqmZe8D2/VolIzSPZw+oUd6j/bEmeRHwsLn1xLA5wllEYsZ5zNuMsDus235ONOnCRwexqof/J3aztyQswSmiaA==",
+ "node_modules/react-native-vector-icons": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.1.0.tgz",
+ "integrity": "sha512-fdQjCHIdoXmRoTZ5gvN1FmT4sGLQ2wmQiNZHKJQUYnE2tkIwjGnxNch+6Nd4lHAACvMWO7LOzBNot2u/zlOmkw==",
"dependencies": {
- "@jest/create-cache-key-function": "^29.6.3",
- "@react-native-community/cli": "12.3.6",
- "@react-native-community/cli-platform-android": "12.3.6",
- "@react-native-community/cli-platform-ios": "12.3.6",
- "@react-native/assets-registry": "0.73.1",
- "@react-native/codegen": "0.73.3",
- "@react-native/community-cli-plugin": "0.73.17",
- "@react-native/gradle-plugin": "0.73.4",
- "@react-native/js-polyfills": "0.73.1",
- "@react-native/normalize-colors": "0.73.2",
- "@react-native/virtualized-lists": "0.73.4",
- "abort-controller": "^3.0.0",
- "anser": "^1.4.9",
- "ansi-regex": "^5.0.0",
- "base64-js": "^1.5.1",
- "chalk": "^4.0.0",
- "deprecated-react-native-prop-types": "^5.0.0",
- "event-target-shim": "^5.0.1",
- "flow-enums-runtime": "^0.0.6",
- "invariant": "^2.2.4",
- "jest-environment-node": "^29.6.3",
- "jsc-android": "^250231.0.0",
- "memoize-one": "^5.0.0",
- "metro-runtime": "^0.80.3",
- "metro-source-map": "^0.80.3",
- "mkdirp": "^0.5.1",
- "nullthrows": "^1.1.1",
- "pretty-format": "^26.5.2",
- "promise": "^8.3.0",
- "react-devtools-core": "^4.27.7",
- "react-refresh": "^0.14.0",
- "react-shallow-renderer": "^16.15.0",
- "regenerator-runtime": "^0.13.2",
- "scheduler": "0.24.0-canary-efb381bbf-20230505",
- "stacktrace-parser": "^0.1.10",
- "whatwg-fetch": "^3.0.0",
- "ws": "^6.2.2",
- "yargs": "^17.6.2"
+ "prop-types": "^15.7.2",
+ "yargs": "^16.1.1"
},
"bin": {
- "react-native": "cli.js"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "react": "18.2.0"
+ "fa-upgrade.sh": "bin/fa-upgrade.sh",
+ "fa5-upgrade": "bin/fa5-upgrade.sh",
+ "fa6-upgrade": "bin/fa6-upgrade.sh",
+ "generate-icon": "bin/generate-icon.js"
}
},
- "node_modules/react-native/node_modules/@react-native/codegen": {
- "version": "0.73.3",
- "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.73.3.tgz",
- "integrity": "sha512-sxslCAAb8kM06vGy9Jyh4TtvjhcP36k/rvj2QE2Jdhdm61KvfafCATSIsOfc0QvnduWFcpXUPvAVyYwuv7PYDg==",
+ "node_modules/react-native-vector-icons/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dependencies": {
- "@babel/parser": "^7.20.0",
- "flow-parser": "^0.206.0",
- "glob": "^7.1.1",
- "invariant": "^2.2.4",
- "jscodeshift": "^0.14.0",
- "mkdirp": "^0.5.1",
- "nullthrows": "^1.1.1"
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
},
"engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "@babel/preset-env": "^7.1.6"
+ "node": ">=10"
+ }
+ },
+ "node_modules/react-native-vector-icons/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/react-native/node_modules/ansi-styles": {
@@ -12789,14 +14179,6 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
- "node_modules/react-native/node_modules/flow-parser": {
- "version": "0.206.0",
- "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.206.0.tgz",
- "integrity": "sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w==",
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/react-native/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -12837,6 +14219,28 @@
"async-limiter": "~1.0.0"
}
},
+ "node_modules/react-redux": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
+ "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "dependencies": {
+ "@types/use-sync-external-store": "^0.0.3",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.2.25",
+ "react": "^18.0",
+ "redux": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -12871,6 +14275,11 @@
"util-deprecate": "~1.0.1"
}
},
+ "node_modules/readable-stream/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
"node_modules/readline": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
@@ -12898,6 +14307,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
@@ -13048,10 +14470,16 @@
"path-parse": "^1.0.5"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.0.tgz",
+ "integrity": "sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg=="
+ },
"node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "version": "2.0.0-next.5",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz",
+ "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -13151,11 +14579,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/safe-array-concat/node_modules/isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
- },
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -13435,6 +14858,19 @@
"node": ">= 5.10.0"
}
},
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-swizzle/node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+ },
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -13513,6 +14949,14 @@
"node": "*"
}
},
+ "node_modules/split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -13588,6 +15032,14 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/strict-uri-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -13617,17 +15069,6 @@
"node": ">=8"
}
},
- "node_modules/string-width/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/string.prototype.matchall": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz",
@@ -13701,22 +15142,14 @@
}
},
"node_modules/strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
- "ansi-regex": "^4.1.0"
+ "ansi-regex": "^5.0.1"
},
"engines": {
- "node": ">=6"
- }
- },
- "node_modules/strip-ansi/node_modules/ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
- "engines": {
- "node": ">=6"
+ "node": ">=8"
}
},
"node_modules/strip-eof": {
@@ -13736,11 +15169,27 @@
}
},
"node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
"engines": {
- "node": ">=0.10.0"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stripe": {
+ "version": "15.5.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-15.5.0.tgz",
+ "integrity": "sha512-c04ToET4ZUzoeSh2rWarXCPNa2+6YzkwNAcWaT4axYRlN/u1XMkz9+inouNsXWjeT6ttBrp1twz10x/sCbWLpQ==",
+ "dependencies": {
+ "@types/node": ">=8.1.0",
+ "qs": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=12.*"
}
},
"node_modules/strnum": {
@@ -14363,9 +15812,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.0.14",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.14.tgz",
- "integrity": "sha512-JixKH8GR2pWYshIPUg/NujK3JO7JiqEEUiNArE86NQyrgUuZeTlZQN3xuS/yiV5Kb48ev9K6RqNkaJjXsdg7Jw==",
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz",
+ "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==",
"funding": [
{
"type": "opencollective",
@@ -14405,6 +15854,34 @@
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.0.tgz",
"integrity": "sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA=="
},
+ "node_modules/use-latest-callback": {
+ "version": "0.1.9",
+ "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.9.tgz",
+ "integrity": "sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==",
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
+ "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -14460,6 +15937,11 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/warn-once": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz",
+ "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q=="
+ },
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
@@ -14563,12 +16045,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/which-builtin-type/node_modules/isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
- "dev": true
- },
"node_modules/which-collection": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
@@ -14670,17 +16146,6 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
- "node_modules/wrap-ansi/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/package.json b/package.json
index 706185b..e22ea03 100644
--- a/package.json
+++ b/package.json
@@ -18,10 +18,69 @@
}
},
"dependencies": {
- "expo": "~51.0.5",
- "expo-status-bar": "~1.11.1",
+ "@emailjs/react-native": "^4.2.2",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-brands-svg-icons": "^6.5.2",
+ "@fortawesome/free-regular-svg-icons": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-native-fontawesome": "^0.3.1",
+ "@google/generative-ai": "^0.11.3",
+ "@react-native-async-storage/async-storage": "1.23.1",
+ "@react-native-community/blur": "^4.4.0",
+ "@react-native-community/datetimepicker": "^8.0.1",
+ "@react-native-picker/picker": "^2.7.5",
+ "@react-native-vector-icons/fontawesome5": "^5.15.4-alpha.12",
+ "@react-navigation/bottom-tabs": "^6.5.20",
+ "@react-navigation/native": "^6.1.17",
+ "@react-navigation/native-stack": "^6.9.26",
+ "@react-navigation/stack": "^6.3.29",
+ "@reduxjs/toolkit": "^2.2.5",
+ "@stripe/stripe-react-native": "^0.37.3",
+ "@supabase/supabase-js": "^2.43.1",
+ "axios": "^1.6.8",
+ "base64-arraybuffer": "^1.0.2",
+ "emailjs-com": "^3.2.0",
+ "expo": "^51.0.0",
+ "expo-av": "~14.0.3",
+ "expo-blur": "~13.0.2",
+ "expo-contacts": "~13.0.3",
+ "expo-file-system": "~17.0.1",
+ "expo-image-manipulator": "~12.0.3",
+ "expo-image-picker": "~15.0.5",
+ "expo-linear-gradient": "^13.0.2",
+ "expo-notifications": "~0.28.1",
+ "expo-permissions": "^14.4.0",
+ "expo-sharing": "~12.0.1",
+ "expo-status-bar": "~1.12.1",
+ "moment": "^2.30.1",
+ "prop-types": "^15.8.1",
"react": "18.2.0",
- "react-native": "0.73.6"
+ "react-native": "0.74.1",
+ "react-native-actionsheet": "^2.4.2",
+ "react-native-chart-kit": "^6.12.0",
+ "react-native-collapsible": "^1.6.1",
+ "react-native-elements": "^3.4.3",
+ "react-native-gesture-handler": "^2.16.2",
+ "react-native-get-random-values": "^1.11.0",
+ "react-native-keyboard-aware-scroll-view": "^0.9.5",
+ "react-native-mime-types": "^2.5.0",
+ "react-native-modal": "^13.0.1",
+ "react-native-modal-datetime-picker": "^17.1.0",
+ "react-native-modalize": "^2.1.1",
+ "react-native-paper": "^5.12.3",
+ "react-native-raw-bottom-sheet": "^3.0.0",
+ "react-native-reanimated": "^3.11.0",
+ "react-native-select-dropdown": "^4.0.1",
+ "react-native-step-indicator": "^1.0.3",
+ "react-native-svg": "^15.3.0",
+ "react-native-swipeable": "^0.6.0",
+ "react-native-tooltip-menu": "^3.0.7",
+ "react-native-typography": "^1.4.1",
+ "react-native-url-polyfill": "^2.0.0",
+ "react-native-vector-icons": "^10.1.0",
+ "react-redux": "^9.1.2",
+ "redux": "^5.0.1",
+ "stripe": "^15.5.0"
},
"devDependencies": {
"@babel/plugin-transform-numeric-separator": "^7.18.6",
diff --git a/src/components/CardPost.js b/src/components/CardPost.js
new file mode 100644
index 0000000..e61d0eb
--- /dev/null
+++ b/src/components/CardPost.js
@@ -0,0 +1,405 @@
+import React, { useState, useEffect } from 'react';
+import { StyleSheet, View, Text, TouchableOpacity, Image, Alert, Modal, FlatList } from 'react-native';
+import { LinearGradient } from 'expo-linear-gradient';
+import { TapGestureHandler } from 'react-native-gesture-handler';
+import { useNavigation } from '@react-navigation/native';
+import moment from 'moment';
+import { useSelector } from 'react-redux';
+import Colors from '../../assets/styles/Colors';
+import PropTypes from 'prop-types';
+import { supabase } from '../config/supabaseClient';
+
+const CardPost = (props) => {
+ const session = useSelector(({ user }) => user.session);
+ const user = session?.user;
+ const [showModalCardOptions, setShowModalCardOptions] = useState(false);
+ const [isDeletingPost, setIsDeletingPost] = useState(false);
+ const [successDeleting, setSuccessDeleting] = useState(false);
+ const isPostFromUserLoggedIn = props.postUser.id === user.id;
+ const navigation = useNavigation();
+ const [likeFromUser, setLikeFromUser] = useState(props.likeFromUser);
+ const [countLikes, setCountLikes] = useState(props.countLikes);
+ const [showLikesModal, setShowLikesModal] = useState(false);
+ const [likedUsers, setLikedUsers] = useState([]);
+
+ useEffect(() => {
+ if (!showModalCardOptions && successDeleting) {
+ Alert.alert('Success!', 'Your post was deleted!');
+ props.actions.onDeletePostSuccess(props.postId);
+ }
+ }, [showModalCardOptions, successDeleting]);
+
+ const viewUser = () => {
+ navigation.navigate('UserProfile', { userId: props.postUser.id });
+ };
+
+ const renderCardTop = () => (
+
+
+ {props.postUser?.imageUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+ {props.postUser?.name ?? 'Unknown User'}
+
+
+ {props.createdAt ? moment(props.createdAt).local().startOf('second').fromNow() : 'Unknown posted time'}
+
+
+ {isPostFromUserLoggedIn && (
+
+ {/* */}
+
+ )}
+
+ );
+
+ const fetchLikedUsers = async () => {
+ try {
+ // Get user_ids who liked the post
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('user_id')
+ .eq('post_id', props.postId);
+
+ if (likeError) throw likeError;
+
+ if (likeData.length === 0) {
+ setLikedUsers([]);
+ setShowLikesModal(true);
+ return;
+ }
+
+ const userIds = likeData.map(like => like.user_id);
+
+ // Get user details from User table
+ const { data: usersData, error: usersError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIds);
+
+ if (usersError) throw usersError;
+
+ setLikedUsers(usersData);
+ setShowLikesModal(true);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch users who liked the post');
+ }
+ };
+
+ const renderCardContent = () => (
+ fetchLikeCount(),
+ fetchLikeStatus(),
+
+ {props.post.habit_photo && (
+
+ )}
+
+
+ {countLikes} likes
+
+
+
+ {props.postUser?.name ?? 'Unknown User'}
+ {props.postDescription}
+
+
+ );
+
+ const renderCardActions = () => (
+
+
+
+ {countLikes > 0 ? countLikes : null}
+
+
+
+ {props.actions.countComments > 0 ? props.actions.countComments : null}
+
+
+ );
+
+ const onPressLike = async () => {
+ try {
+ if (!user || !user.id) {
+ throw new Error('User is not defined or does not have an id');
+ }
+
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('*')
+ .eq('post_id', props.postId)
+ .eq('user_id', user.id);
+
+ if (likeError) throw likeError;
+
+ if (likeData.length > 0) {
+ const { error: deleteError } = await supabase
+ .from('Like')
+ .delete()
+ .eq('post_id', props.postId)
+ .eq('user_id', user.id);
+
+ if (deleteError) throw deleteError;
+ } else {
+ const { error: insertError } = await supabase
+ .from('Like')
+ .insert([{ post_id: props.postId, user_id: user.id }]);
+
+ if (insertError) throw insertError;
+ }
+
+ const { data: likeCountData, error: likeCountError } = await supabase
+ .from('Like')
+ .select('*', { count: 'exact' })
+ .eq('post_id', props.postId);
+
+ if (likeCountError) throw likeCountError;
+
+ const updatedLikeCount = likeCountData.length;
+
+ setCountLikes(updatedLikeCount);
+ setLikeFromUser(likeData.length === 0);
+ props.actions.onLikePostSuccess(props.postId);
+ } catch (error) {
+ Alert.alert('Error', 'Something went wrong with liking the post');
+ }
+ };
+
+ const fetchLikeCount = async () => {
+ const { data: likeCountData, error: likeCountError } = await supabase
+ .from('Like')
+ .select('*', { count: 'exact' })
+ .eq('post_id', props.postId);
+
+ if (likeCountError) throw likeCountError;
+
+ const updatedLikeCount = likeCountData.length;
+
+ setCountLikes(updatedLikeCount);
+ };
+
+ const fetchLikeStatus = async () => {
+ if (!user || !user.id) return;
+ try {
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('*')
+ .eq('post_id', props.postId)
+ .eq('user_id', user.id);
+
+ if (likeError) throw likeError;
+
+ setLikeFromUser(likeData.length > 0);
+ } catch (error) {
+ Alert.alert('Error', 'Failed to fetch like status');
+ }
+ };
+
+ const onPressComment = () => {
+ var userData = { id: props.postUser.id, name: props.postUser.name, imageUrl: props.postUser.imageUrl };
+ var postData = { description: props.postDescription, id: props.postId };
+ props.navigation.navigate('Comments', {
+ navigation: props.navigation,
+ postId: props.postId,
+ userData,
+ postData
+ });
+ };
+
+ const toggleModalOptions = () => {
+ if (isPostFromUserLoggedIn) {
+ props.setBlurActive(!showModalCardOptions);
+ setShowModalCardOptions(!showModalCardOptions);
+ return;
+ }
+ onPressPost();
+ };
+
+ const onPressPost = () => {
+ props.navigation.navigate('PostDetails', { postId: props.postId });
+ };
+
+ return (
+
+
+ {renderCardTop()}
+ {renderCardContent()}
+
+
+
+ {countLikes > 0 ? countLikes : null}
+
+
+
+ {props.actions.countComments > 0 ? props.actions.countComments : null}
+
+
+
+ setShowLikesModal(false)}
+ >
+
+
+ Liked by
+ item.user_id.toString()}
+ renderItem={({ item }) => (
+
+
+ {item.username}
+
+ )}
+ />
+ setShowLikesModal(false)} style={styles.closeButton}>
+ Close
+
+
+
+
+
+
+ );
+};
+
+CardPost.propTypes = {
+ postId: PropTypes.string.isRequired,
+ post: PropTypes.object.isRequired,
+ postUser: PropTypes.object.isRequired,
+ createdAt: PropTypes.string.isRequired,
+ postDescription: PropTypes.string.isRequired,
+ postType: PropTypes.string.isRequired,
+ actions: PropTypes.object.isRequired,
+ navigation: PropTypes.object.isRequired,
+ likeFromUser: PropTypes.bool.isRequired,
+ countLikes: PropTypes.number.isRequired,
+ user: PropTypes.object.isRequired,
+};
+
+const styles = StyleSheet.create({
+ cardPostHeaderContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ padding: 10,
+ },
+ cardPostIconPhoto: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ },
+ cardPostHeaderTextContainer: {
+ flex: 1,
+ flexDirection: 'column',
+ marginLeft: 10,
+ },
+ textUserName: {
+ fontWeight: 'bold',
+ color: Colors.text,
+ },
+ textSubtitle: {
+ color: '#FFFFFF',
+ },
+ cardPostVerticalEllipsis: {
+ width: 24,
+ height: 24,
+ },
+ habitImage: {
+ width: '100%',
+ height: 400,
+ },
+ cardPostDescriptionContainer: {
+ flexDirection: 'row',
+ alignItems: 'flex-start',
+ padding: 10,
+ },
+ postText: {
+ color: Colors.text,
+ },
+ containerActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 10,
+ paddingHorizontal: 10,
+ },
+ buttonActions: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginRight: 20,
+ },
+ textPostActions: {
+ marginLeft: 5,
+ color: Colors.text,
+ },
+ icon: {
+ width: 29,
+ height: 29,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ },
+ modalContent: {
+ width: '80%',
+ backgroundColor: 'white',
+ borderRadius: 10,
+ padding: 20,
+ alignItems: 'center',
+ },
+ modalTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ },
+ userContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginBottom: 10,
+ },
+ userImage: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ marginRight: 10,
+ },
+ userName: {
+ fontSize: 16,
+ },
+ closeButton: {
+ marginTop: 20,
+ padding: 10,
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ },
+ closeButtonText: {
+ color: 'white',
+ fontWeight: 'bold',
+ },
+});
+
+export default CardPost;
diff --git a/src/components/CheckoutScreen.js b/src/components/CheckoutScreen.js
new file mode 100644
index 0000000..b27d03f
--- /dev/null
+++ b/src/components/CheckoutScreen.js
@@ -0,0 +1,64 @@
+import React, { useEffect } from 'react';
+import { Button } from 'react-native';
+import { CardField } from '@stripe/stripe-react-native'; // Import CardField from stripe-react-native to enter credit card info securely
+import {
+ initializeStripe /*, handlePayment */,
+} from '../services/PaymentService';
+import { useStripe } from '@stripe/stripe-react-native';
+
+const CheckoutScreen = () => {
+ useEffect(() => {
+ initializeStripe();
+ }, []);
+
+ // const onSubmit = async () => {
+ // // Example usage of handlePayment
+ // const paymentResult = await handlePayment('pm_1Example', 'pi_1Example');
+ // console.log(paymentResult);
+ // };
+
+ const handlePaymentPress = async () => {
+ //Example payment method ID and payment intent ID - these would be fetched or generated
+ const paymentMethodId = 'your_payment_method_id';
+ const paymentIntentId = 'your_payment_intent_id';
+ const { confirmPayment } = useStripe();
+ const result = await confirmPayment(paymentIntentId, {
+ type: 'Card',
+ paymentMethodId: paymentMethodId,
+ });
+
+ if (result.error) {
+ console.error('Payment Confirmation Error', result.error);
+ } else {
+ console.log('Payment Successful', result.paymentIntent);
+ }
+ };
+
+ return (
+ <>
+ {
+ console.log('Card Details: ', cardDetails);
+ }}
+ />
+ {/* Commented command uses onSubmit function instead of handlePayment function */}
+ {/* */}
+
+ >
+ );
+};
+
+export default CheckoutScreen;
diff --git a/src/components/CommentCard.js b/src/components/CommentCard.js
new file mode 100644
index 0000000..d3e92e3
--- /dev/null
+++ b/src/components/CommentCard.js
@@ -0,0 +1,93 @@
+import React from 'react';
+import { View } from 'react-native';
+import PropTypes from 'prop-types';
+import { StyleSheet } from 'react-native';
+import { Text } from 'react-native';
+import Colors from '../../assets/styles/Colors';
+import { Image } from 'react-native';
+import moment from 'moment';
+
+export default function CommentCard({ commentData }) {
+ return (
+
+
+
+
+ {commentData.user_id.username}
+
+ {moment(commentData.createdAt).local().startOf('second').fromNow()}
+
+
+
+ {commentData.content}
+
+ );
+}
+
+CommentCard.propTypes = {
+ commentData: PropTypes.object,
+};
+
+const styles = StyleSheet.create({
+ cardShadow: {
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.25,
+ shadowRadius: 6,
+ elevation: 7,
+ },
+ container: {
+ backgroundColor: 'rgba(156, 198, 255, 0.042)',
+ display: 'flex',
+ flex: 1,
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.25,
+ shadowRadius: 6,
+ elevation: 7,
+ padding: 10,
+ borderRadius: 8,
+ margin: 5,
+ },
+ comment: {
+ justifyContent: 'flex-start',
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 16,
+ color: Colors.text,
+ width: '100%',
+ flexShrink: 1,
+ },
+ cardPostLinearGradientContainer: {
+ flex: 1,
+ backgroundColor: 'rgba(156, 198, 255, 0.042)',
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ headerContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ padding: 10,
+ },
+ userIconPhoto: {
+ width: 38,
+ height: 38,
+ borderRadius: 32,
+ marginRight: 8,
+ },
+ textSubtitle: {
+ fontSize: 14,
+ lineHeight: 19,
+ fontWeight: '400',
+ color: Colors.text,
+ },
+});
+
diff --git a/src/components/CustomPicker.js b/src/components/CustomPicker.js
new file mode 100644
index 0000000..728ef0e
--- /dev/null
+++ b/src/components/CustomPicker.js
@@ -0,0 +1,189 @@
+import React, { useRef, useState } from 'react';
+import {
+ View,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ Dimensions,
+ Platform,
+} from 'react-native';
+import Colors from '../../assets/styles/Colors';
+import { Picker } from '@react-native-picker/picker';
+import RBSheet from 'react-native-raw-bottom-sheet';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+
+const CustomPicker = props => {
+ const values = props.placeholder
+ ? [{ label: props.placeholder, value: '' }, ...props.options]
+ : [...props.options];
+
+ const RBSCustomPicker = useRef();
+
+ const [valueIOS, setValueIOS] = useState(props.selectedValue ?? '');
+
+ const closeModalIOS = change => {
+ if (change) {
+ props.onValueChange(valueIOS);
+ }
+
+ RBSCustomPicker.current.close();
+ };
+
+ const CustomPickerComponent = () => {
+ let customProperties;
+
+ if (Platform.OS === 'ios') {
+ customProperties = {
+ style: styles.pickerStyleIOS,
+ selectedValue: valueIOS,
+ onValueChange: (itemValue, itemIndex) => setValueIOS(itemValue),
+ itemStyle: styles.pickerText,
+ dropdownIconColor: Colors.text,
+ };
+ } else {
+ customProperties = {
+ style: styles.pickerText,
+ selectedValue: props.selectedValue ?? '',
+ onValueChange: (itemValue, itemIndex) => props.onValueChange(itemValue),
+ placeholder: props.placeholder ?? null,
+ dropdownIconColor: Colors.primary4,
+ };
+ }
+
+ return (
+
+ {values
+ ? values.map(option => (
+
+ ))
+ : null}
+
+ );
+ };
+
+ const AndroidPicker = () => (
+
+ {props.label}
+
+
+ );
+
+ return Platform.OS === 'ios' ? (
+ <>
+ RBSCustomPicker.current.open()}>
+ {props.label}
+
+ RBSCustomPicker.current.open()}>
+
+ {values.find(item => item.value === props.selectedValue)?.label ??
+ ''}
+
+
+
+
+
+ setValueIOS(props.selectedValue)}>
+
+ closeModalIOS(false)}>
+ Cancel
+
+
+ closeModalIOS(true)}>
+ Confirm
+
+
+
+
+
+ >
+ ) : (
+
+ );
+};
+
+const styles = StyleSheet.create({
+ customPickerContainer: {
+ flexDirection: 'column',
+ borderBottomColor: 'rgba(156, 198, 255, 0.4)',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ },
+ touchableOpacityContainerIOS: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ paddingRight: 8,
+ paddingBottom: 10,
+ },
+ pickerLabel: {
+ fontSize: 16,
+ color: Colors.text,
+ lineHeight: 16,
+ fontWeight: '400',
+ marginBottom: 8,
+ },
+ pickerText: {
+ fontSize: 16,
+ color: Colors.primary4,
+ opacity: 0.4,
+ },
+ containerHeaderBottomSheet: {
+ backgroundColor: '#282828',
+ paddingHorizontal: 16,
+ paddingVertical: 15,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: Dimensions.get('window').width,
+ },
+ textHeaderBottomSheet: {
+ fontSize: 16,
+ color: '#d7892b',
+ },
+ containerBottomSheet: {
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ backgroundColor: '#1c1c1e',
+ },
+ pickerStyleIOS: {
+ width: Dimensions.get('window').width - 44,
+ height: 200,
+ backgroundColor: '#1c1c1e',
+ borderRadius: 2,
+ borderWidth: 0,
+ color: Colors.text,
+ borderColor: '#455c8a',
+ marginHorizontal: 10,
+ paddingHorizontal: 16,
+ marginVertical: 0,
+ paddingVertical: 0,
+ marginBottom: 32,
+ fontSize: 16,
+ },
+});
+
+export default CustomPicker;
diff --git a/src/components/CustomSwipeable.js b/src/components/CustomSwipeable.js
new file mode 100644
index 0000000..fe935ff
--- /dev/null
+++ b/src/components/CustomSwipeable.js
@@ -0,0 +1,106 @@
+import React, { useRef } from "react";
+import { StyleSheet, View, TouchableOpacity } from "react-native";
+import { Text } from "react-native-elements";
+import Icon from "react-native-vector-icons/FontAwesome5";
+import Colors from "../../assets/styles/Colors";
+import Swipeable from "react-native-gesture-handler/Swipeable";
+
+const CustomSwipeable = (props) => {
+ const swipeableRef = useRef();
+
+ const rightContent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const leftContent = () => {
+ return (
+
+
+
+ );
+ };
+
+ const getTextCheckedStyle = (obj) => {
+ // if (moment(obj.ush_last_checked).format('MM/DD/YYYY') === moment(new Date()).format('MM/DD/YYYY')) {
+ if (obj.user_habit_check) {
+ if (obj.user_habit_check.uhc_checked) {
+ return styles.textCheked;
+ }
+ }
+ // }
+ };
+
+ const onLeftOpen = () => {
+ swipeableRef.current.close();
+ props.onSwipeableLeftOpen();
+ };
+
+ const onRightOpen = () => {
+ swipeableRef.current.close();
+ props.onSwipeableRightOpen();
+ };
+
+ return (
+ {
+ if (direction === 'left') {
+ onLeftOpen();
+ } else if (direction === 'right') {
+ onRightOpen();
+ }
+ }}
+ >
+
+
+
+ {props.user_habit.habit.hab_name}
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ containerCheckItemRight: {
+ backgroundColor: Colors.primary4,
+ justifyContent: "center",
+ alignItems: "flex-end",
+ paddingRight: 22,
+ flex: 1,
+ },
+ containerCheckItemLeft: {
+ backgroundColor: Colors.primary3,
+ justifyContent: "center",
+ alignItems: "flex-start",
+ paddingLeft: 22,
+ flex: 1,
+ },
+ textCheked: {
+ textDecorationLine: "line-through",
+ color: Colors.primary4,
+ },
+ textAccordionContent: {
+ fontSize: 14,
+ color: Colors.text,
+ },
+ habitItem: {
+ paddingVertical: 22,
+ paddingHorizontal: 22,
+ backgroundColor: Colors.primary6,
+ },
+});
+
+export default CustomSwipeable;
diff --git a/src/components/EmptyContent.js b/src/components/EmptyContent.js
new file mode 100644
index 0000000..14017b5
--- /dev/null
+++ b/src/components/EmptyContent.js
@@ -0,0 +1,57 @@
+import React from "react";
+import { StyleSheet, View, Dimensions } from "react-native";
+import { Text, Divider } from "react-native-elements";
+import Icon from "react-native-vector-icons/FontAwesome5";
+import Colors from "../../assets/styles/Colors";
+import { systemWeights } from "react-native-typography";
+
+const EmptyContent = () => {
+ let offset = props.offset ? props.offset : 0;
+
+ return (
+
+
+ {props.title}
+
+
+
+ {props.subtitle}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ content: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ subtitle: {
+ color: Colors.grey3,
+ textAlign: "center",
+ marginTop: 1,
+ marginHorizontal: 30,
+ fontSize: 17,
+ },
+ title: {
+ color: Colors.grey3,
+ marginTop: 25,
+ marginHorizontal: 30,
+ marginBottom: 10,
+ fontSize: 30,
+ textAlign: "center",
+ },
+});
+
+export default EmptyContent;
diff --git a/src/components/FadeInView.js b/src/components/FadeInView.js
new file mode 100644
index 0000000..46865fc
--- /dev/null
+++ b/src/components/FadeInView.js
@@ -0,0 +1,22 @@
+import React, { useState, useEffect } from "react";
+import { Animated } from "react-native";
+
+const FadeInView = (props) => {
+ const [fadeAnim, setFadeAnim] = useState(new Animated.Value(0));
+
+ useEffect(() => {
+ Animated.timing(fadeAnim, {
+ toValue: 1,
+ duration: (props.pos / 3) * 1000,
+ useNativeDriver: true,
+ }).start();
+ }, []);
+
+ return (
+
+ {props.children}
+
+ );
+};
+
+export default FadeInView;
diff --git a/src/components/Fetching.js b/src/components/Fetching.js
new file mode 100644
index 0000000..fd11bd2
--- /dev/null
+++ b/src/components/Fetching.js
@@ -0,0 +1,31 @@
+import React from "react";
+import { StyleSheet, View, Dimensions, ActivityIndicator } from "react-native";
+import Colors from "../../assets/styles/Colors";
+
+const Fetching = (props) => {
+ return (
+
+ {props.isFetching ? (
+
+
+
+ ) : (
+ {props.children}
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ content: {
+ height: Dimensions.get("window").height - 250,
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+});
+
+export default Fetching;
diff --git a/src/components/GradientText.js b/src/components/GradientText.js
new file mode 100644
index 0000000..9ec96f5
--- /dev/null
+++ b/src/components/GradientText.js
@@ -0,0 +1,21 @@
+import React from "react";
+import { Text } from "react-native";
+import { LinearGradient } from "expo-linear-gradient";
+import MaskedView from "@react-native-masked-view/masked-view";
+
+const GradientText = (props) => {
+ return (
+ }>
+
+
+
+
+ );
+};
+
+export default GradientText;
diff --git a/src/components/Header.js b/src/components/Header.js
new file mode 100644
index 0000000..f542843
--- /dev/null
+++ b/src/components/Header.js
@@ -0,0 +1,138 @@
+import React from "react";
+import {
+ StyleSheet,
+ View,
+ Dimensions,
+ TouchableOpacity,
+ Text,
+ Image,
+} from "react-native";
+import Icon from "react-native-vector-icons/FontAwesome5";
+import Colors from "../../assets/styles/Colors";
+import { systemWeights } from "react-native-typography";
+import { useSelector } from "react-redux";
+
+const Header = (props) => {
+ const user = useSelector(({ user }) => user);
+
+ return (
+
+ {props.backButton ? (
+ props.navigation.pop()}
+ >
+
+
+ ) : null}
+
+ {props.showBackgroundImage ? (
+
+ ) : null}
+
+
+ {props.title ? (
+ props.navigation.goBack()}
+ >
+
+ {props.title}
+
+
+ ) : null}
+
+ {props.showMenu ? (
+ user.image ? (
+ props.navigation.navigate("Menu")}>
+
+
+ ) : (
+ props.navigation.navigate("Menu")}>
+
+
+ )
+ ) : null}
+
+ {props.backMenu ? (
+
+ props.navigation.navigate("Home", {
+ screen: "Habit",
+ params: { screen: "HabitsIndex" },
+ })
+ }
+ >
+
+
+ ) : null}
+
+ {props.customRightIcon ? props.customRightIcon : null}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ headerContainer: {
+ paddingTop: 58,
+ width: Dimensions.get("window").width,
+ backgroundColor: Colors.primary,
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ paddingHorizontal: 22,
+ },
+ containerBackButton: {
+ justifyContent: "flex-start",
+ },
+ backButtonStyle: {
+ paddingVertical: 10,
+ paddingRight: 17,
+ },
+ title: {
+ fontSize: 24,
+ color: Colors.text,
+ },
+ containerContent: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ flex: 1,
+ alignItems: "center",
+ },
+ userPhoto: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ },
+ image: {
+ position: "absolute",
+ top: 0,
+ left: 120,
+ width: Dimensions.get("window").width,
+ aspectRatio: 2.5 / 1,
+ height: undefined,
+ },
+});
+
+export default Header;
diff --git a/src/components/UserHabit.js b/src/components/UserHabit.js
new file mode 100644
index 0000000..0aa2a25
--- /dev/null
+++ b/src/components/UserHabit.js
@@ -0,0 +1,142 @@
+import React, { useState } from "react";
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ TouchableOpacity,
+} from "react-native";
+import Colors from "../../assets/styles/Colors";
+import Accordion from "react-native-collapsible/Accordion";
+import Icon from "react-native-vector-icons/FontAwesome5";
+
+const UserHabit = (props) => {
+ const [activeSections, setActiveSections] = useState([]);
+
+ const updateActiveSections = (indexNumber) => {
+ let activeSectionsAux = [...activeSections];
+ const index = activeSections.indexOf(indexNumber);
+
+ if (index !== -1) {
+ activeSectionsAux.splice(index, 1);
+ } else {
+ activeSectionsAux.push(indexNumber);
+ }
+
+ setActiveSections(activeSectionsAux);
+ };
+
+ const _renderHeader = (section, index, isActive) => {
+ return (
+ updateActiveSections(index)}>
+
+ {section.hac_name}
+
+
+
+
+ );
+ };
+
+ const _renderContent = (section) => {
+ return (
+
+ {section.habits.map((obj, i) => {
+ return (
+
+ props.navigation.navigate("Home", {
+ screen: "Habits",
+ params: {
+ screen: "ViewHabit",
+ params: { user_habit_id: obj.id },
+ },
+ })
+ }
+ >
+
+
+ {obj.habit.hab_name}
+
+
+
+
+
+ );
+ })}
+
+ );
+ };
+
+ return (
+
+ null}
+ />
+
+ );
+};
+
+const styles = StyleSheet.create({
+ containerAccordionHeader: {
+ width: Dimensions.get("window").width - 44,
+ backgroundColor: "rgba(156, 198, 255, 0.084)",
+ paddingVertical: 17,
+ paddingHorizontal: 17,
+ borderRadius: 4,
+ marginTop: 8,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ textAccordionHeader: {
+ fontSize: 16,
+ color: Colors.text,
+ },
+ containerAccordionContent: {
+ width: Dimensions.get("window").width - 44,
+ backgroundColor: "rgba(156, 198, 255, 0.084)",
+ borderBottomLeftRadius: 4,
+ borderBottomRightRadius: 4,
+ paddingHorizontal: 14,
+ // paddingVertical: 15,
+ },
+ textAccordionContent: {
+ flex: 1,
+ fontSize: 14,
+ color: Colors.text,
+ },
+ habitItem: {
+ borderTopWidth: StyleSheet.hairlineWidth,
+ borderColor: Colors.primary4,
+ flexDirection: "row",
+ paddingVertical: 16,
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+ headerOpened: {
+ borderBottomLeftRadius: 0,
+ borderBottomRightRadius: 0,
+ },
+});
+
+export default UserHabit;
diff --git a/src/components/community/CardCommunity.js b/src/components/community/CardCommunity.js
new file mode 100644
index 0000000..7570fb2
--- /dev/null
+++ b/src/components/community/CardCommunity.js
@@ -0,0 +1,114 @@
+import React from "react";
+import {
+ StyleSheet,
+ View,
+ Text,
+ TouchableOpacity,
+ Image,
+ Dimensions,
+} from "react-native";
+import Colors from "../../../assets/styles/Colors";
+
+const CardCommunity = (props) => {
+ return (
+
+
+ props.type === "My Communities" ||
+ props.community?.community_member?.length > 0
+ ? props.navigation.push("Home", {
+ screen: "Community",
+ params: {
+ screen: "FeedCommunity",
+ params: { community: { id: props.community.id } },
+ },
+ })
+ : props.navigation.navigate("Home", {
+ screen: "Community",
+ params: {
+ screen: "ViewCommunity",
+ params: { community: { id: props.community.id } },
+ },
+ })
+ }
+ >
+
+
+
+ {props.community.com_name}
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginLeft: 16,
+ marginTop: 16,
+ width: (Dimensions.get("window").width - 48) / 2,
+ height: ((Dimensions.get("window").width - 48) / 2) * 1.31,
+ },
+ community: {
+ flex: 1,
+ justifyContent: "flex-end",
+ width: "100%",
+ height: "100%",
+ },
+ containerShadow: {
+ backgroundColor: "rgba(0,0,0,0.5)",
+ width: (Dimensions.get("window").width - 48) / 2,
+ height: ((Dimensions.get("window").width - 48) / 2) * 1.31,
+ position: "absolute",
+ borderRadius: 4,
+ },
+ header: {
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ top: 0,
+ marginTop: 12,
+ marginLeft: 19,
+ zIndex: 3,
+ elevation: 3,
+ position: "absolute",
+ },
+ headerTitle: {
+ color: Colors.text,
+ fontWeight: "700",
+ fontSize: 14,
+ lineHeight: 19,
+ },
+ communityImage: {
+ width: "100%",
+ height: "100%",
+ borderRadius: 4,
+ },
+ footer: {
+ position: "absolute",
+ alignSelf: "center",
+ zIndex: 7,
+ elevation: 7,
+ width: 131,
+ marginHorizontal: 12,
+ paddingBottom: 12,
+ },
+ communityTitle: {
+ color: Colors.text,
+ fontWeight: "600",
+ fontSize: 13,
+ lineHeight: 16,
+ textAlign: "left",
+ },
+});
+
+export default CardCommunity;
diff --git a/src/components/community/CardHabits.js b/src/components/community/CardHabits.js
new file mode 100644
index 0000000..9728ce5
--- /dev/null
+++ b/src/components/community/CardHabits.js
@@ -0,0 +1,174 @@
+import React from "react";
+import {
+ StyleSheet,
+ View,
+ Text,
+ TouchableOpacity,
+ Image,
+ Dimensions,
+} from "react-native";
+import Colors from "../../../assets/styles/Colors";
+
+const CardHabits = (props) => {
+ const onCardPress = () => {
+ if (props.timeline) {
+ props.navigation.push("Home", {
+ screen: "Habits",
+ params: { screen: "HabitSelected", params: { hab_id: props.habit.id } },
+ });
+ return;
+ }
+
+ if (props.myHabit) {
+ props.navigation.push("Home", {
+ screen: "Habits",
+ params: {
+ screen: "ViewHabit",
+ params: { user_habit_id: props.userHabit },
+ },
+ });
+ return;
+ }
+
+ props.navigation.push("Home", {
+ screen: "Community",
+ params: {
+ screen: "ViewCommunityHabit",
+ params: {
+ habit: { id: props.communityHabit, admin: props.admin },
+ community: { id: props.community.id },
+ onlyViewMode: props.onlyViewMode,
+ },
+ },
+ });
+ };
+
+ return (
+
+
+
+
+ {props.isMomentum
+ ? props.habit?.category
+ : props.habit?.category?.hac_name}
+
+
+
+ {props.habit?.image ? (
+
+ ) : (
+
+ )}
+
+
+
+ {props.habit?.hab_name}
+
+
+ {props.isMomentum ? (
+
+ {`${props.currentStreak}/30`}
+
+ ) : null}
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginLeft: 16,
+ marginTop: 16,
+ },
+ habit: {
+ flex: 1,
+ justifyContent: "flex-end",
+ width: (Dimensions.get("window").width - 48) / 2,
+ height: ((Dimensions.get("window").width - 48) / 2) * 1.31,
+ },
+ containerShadow: {
+ backgroundColor: "rgba(0,0,0,0.5)",
+ width: (Dimensions.get("window").width - 48) / 2,
+ height: ((Dimensions.get("window").width - 48) / 2) * 1.31,
+ position: "absolute",
+ borderRadius: 4,
+ },
+ containerStreak: {
+ marginRight: 12,
+ height: 36,
+ width: 36,
+ borderRadius: 18,
+ borderWidth: 2,
+ borderColor: "#318FC5",
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ textStreak: {
+ fontSize: 9,
+ color: Colors.text,
+ },
+ header: {
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ top: 0,
+ marginTop: 12,
+ marginLeft: 12,
+ zIndex: 3,
+ elevation: 3,
+ position: "absolute",
+ },
+ headerTitle: {
+ color: Colors.text,
+ fontWeight: "700",
+ fontSize: 14,
+ lineHeight: 19,
+ },
+ habitImage: {
+ width: "100%",
+ height: "100%",
+ borderRadius: 4,
+ },
+ noHabitImage: {
+ width: "60%",
+ height: "60%",
+ alignSelf: "center",
+ justifyContent: "center",
+ marginBottom: 48,
+ borderRadius: 4,
+ },
+ footer: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ position: "absolute",
+ alignSelf: "center",
+ zIndex: 7,
+ elevation: 7,
+ width: 131,
+ marginHorizontal: 12,
+ paddingBottom: 12,
+ },
+ habitTitle: {
+ color: Colors.text,
+ fontWeight: "600",
+ fontSize: 13,
+ lineHeight: 16,
+ textAlign: "left",
+ width: "100%",
+ },
+});
+
+export default CardHabits;
diff --git a/src/components/community/PostComment.js b/src/components/community/PostComment.js
new file mode 100644
index 0000000..a553bac
--- /dev/null
+++ b/src/components/community/PostComment.js
@@ -0,0 +1,210 @@
+import React, { useRef, useState, useEffect } from "react";
+import {
+ StyleSheet,
+ Dimensions,
+ View,
+ Image,
+ Text,
+ ActivityIndicator,
+ Platform,
+} from "react-native";
+import Colors from "../../../assets/styles/Colors";
+import { Video } from "expo-av";
+import { LinearGradient } from "expo-linear-gradient";
+import moment from "moment";
+import * as mime from "react-native-mime-types";
+
+const PostComment = (props) => {
+ const videoRef = useRef(null);
+ const [statusVideoComment, setStatusVideoComment] = useState({});
+ const [isPreloading, setIsPreloading] = useState(false);
+
+ useEffect(() => {
+ return () => {
+ setStatusVideoComment({});
+ };
+ }, []);
+
+ useEffect(() => {
+ if (props.comment?.file) {
+ mime.lookup(props.comment?.file?.att_name).includes("video")
+ ? loadVideo(props.comment?.file?.url)
+ : null;
+ }
+ }, [props.comment]);
+
+ const loadVideo = async (source) => {
+ if (source && videoRef.current) {
+ await unloadVideo();
+ await videoRef.current.loadAsync({ uri: source });
+ }
+ };
+
+ async function unloadVideo() {
+ if (videoRef.current !== null) {
+ await videoRef.current.unloadAsync();
+ }
+ }
+
+ const renderData = () => {
+ return (
+ <>
+
+ {props.comment?.user?.image ? (
+
+ ) : (
+
+ )}
+ {props.comment?.user?.name}
+
+
+ {props.comment?.file ? (
+ mime.lookup(props.comment?.file?.att_name).includes("image") ? (
+
+ ) : mime.lookup(props.comment?.file?.att_name).includes("video") ? (
+
+ {isPreloading && (
+
+ )}
+
+ ) : null
+ ) : null}
+
+ {props.comment?.cpc_text !== "" && props.comment?.cpc_text ? (
+ {props.comment?.cpc_text}
+ ) : null}
+
+ {moment(props.comment?.created_at).format("HH:mm - MM/DD/YYYY")}
+
+
+ >
+ );
+ };
+
+ return Platform.OS === "android" ? (
+
+ {renderData()}
+
+ ) : (
+ {renderData()}
+ );
+};
+
+const styles = StyleSheet.create({
+ containerGradientSendPost: {
+ marginBottom: 8,
+ zIndex: 1,
+ elevation: 1,
+ backgroundColor:
+ Platform.OS === "ios" ? "rgba(0, 37, 68, 0.75)" : "transparent",
+ width: Dimensions.get("window").width,
+ },
+ containerInfoComment: {
+ flex: 1,
+ flexDirection: "column",
+ justifyContent: "flex-start",
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 48,
+ marginBottom: 19,
+ },
+ containerComment: {
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ paddingVertical: 3,
+ alignItems: "center",
+ width: Dimensions.get("window").width,
+ marginBottom: 16,
+ marginHorizontal: 24,
+ paddingTop: 16,
+ },
+ textCreatedAtComment: {
+ fontWeight: "400",
+ fontSize: 12,
+ lineHeight: 18,
+ color: "rgba(255, 255, 255, 0.64)",
+ marginTop: 12,
+ },
+ attachmentComments: {
+ width: Dimensions.get("window").width - 48,
+ resizeMode: "cover",
+ borderRadius: 16,
+ marginBottom: 12,
+ height: 326,
+ },
+ textComment: {
+ fontSize: 16,
+ lineHeight: 16,
+ fontWeight: "700",
+ color: Colors.text,
+ paddingTop: 2,
+ },
+ buttons: {
+ top: "40%",
+ alignSelf: "center",
+ position: "absolute",
+ },
+ buttonPlay: {
+ width: 72,
+ height: 72,
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: "700",
+ lineHeight: 22,
+ color: Colors.text,
+ },
+ userPhoto: {
+ width: 32,
+ height: 32,
+ borderRadius: 32,
+ marginRight: 12,
+ },
+});
+
+export default PostComment;
diff --git a/src/components/community/PostCommunity.js b/src/components/community/PostCommunity.js
new file mode 100644
index 0000000..5714920
--- /dev/null
+++ b/src/components/community/PostCommunity.js
@@ -0,0 +1,266 @@
+import React, { useRef, useState } from "react";
+import {
+ StyleSheet,
+ Dimensions,
+ View,
+ Text,
+ TouchableOpacity,
+ Image,
+ Platform,
+} from "react-native";
+import Colors from "../../../assets/styles/Colors";
+import { LinearGradient } from "expo-linear-gradient";
+import * as mime from "react-native-mime-types";
+import VideoCard from "../../components/community/VideoCard";
+import { useSelector } from "react-redux";
+
+const PostCommunity = (props) => {
+ const video = useRef(null);
+ const [status, setStatus] = useState({});
+ const [stopVideo, setStopVideo] = useState(false);
+ const user = useSelector(({ user }) => user);
+
+ const viewPost = () => {
+ setStopVideo(true);
+ props.navigation.navigate("ViewPostCommunity", {
+ post: { id_post: props.post.id, savedPost: props.savedPost ?? false },
+ });
+ };
+
+ const viewUser = (obj) => {
+ if (user.id === obj.id) {
+ props.navigation.navigate("Home", {
+ screen: "Profile",
+ params: { screen: "ProfileIndex" },
+ });
+ } else {
+ props.navigation.push("Home", {
+ screen: "Profile",
+ params: {
+ screen: "UserProfile",
+ params: { user: { id_user: obj.id } },
+ },
+ });
+ }
+ };
+
+ const renderData = () => {
+ return (
+ <>
+
+
+ viewUser(props.post?.user)}
+ >
+ {props.post?.user?.image ? (
+
+ ) : (
+
+ )}
+ {props.post?.user?.name}
+
+
+ {props.post?.cop_text ? (
+ {props.post?.cop_text}
+ ) : null}
+ {props.post?.file?.length > 0 ? (
+ mime
+ .lookup(props.post?.file[0]?.file?.att_name)
+ .includes("image") ? (
+
+
+
+ ) : mime
+ .lookup(props.post?.file[0]?.file?.att_name)
+ .includes("video") ? (
+
+
+
+ ) : null
+ ) : null}
+
+
+
+
+ props.like()}
+ >
+
+
+ {props.countLikes ? props.countLikes : null}
+
+
+
+ {
+ props.navigation.navigate("ViewPostCommunity", {
+ post: { id_post: props.post.id },
+ }),
+ video.current ? video.current.stopAsync() : null;
+ }}
+ >
+
+
+ {props.countComments ? props.countComments : null}
+
+
+
+ props.save()}
+ >
+
+
+
+
+
+ >
+ );
+ };
+
+ return Platform.OS === "android" ? (
+
+ {renderData()}
+
+ ) : (
+ {renderData()}
+ );
+};
+
+const styles = StyleSheet.create({
+ video: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width,
+ height: 230,
+ },
+ buttons: {
+ top: 75,
+ flex: 1,
+ flexDirection: "row",
+ justifyContent: "center",
+ alignSelf: "center",
+ alignItems: "center",
+ position: "absolute",
+ },
+ containerList: {
+ marginBottom: 8,
+ paddingTop: 16,
+ zIndex: 2,
+ elevation: 2,
+ backgroundColor:
+ Platform.OS === "ios" ? "rgba(0, 37, 68, 0.75)" : "transparent",
+ width: Dimensions.get("window").width,
+ },
+ infoCommunity: {
+ marginBottom: 21,
+ zIndex: 3,
+ elevation: 3,
+ },
+ containerItemPost: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ paddingBottom: 14,
+ marginHorizontal: 16,
+ },
+ containerTextPost: {
+ width: Dimensions.get("window").width,
+ borderBottomColor: "#264261",
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ //paddingBottom: 16,
+ },
+ textCounter: {
+ fontWeight: "500",
+ fontSize: 12,
+ lineHeight: 18,
+ color: "#FFFFFF",
+ //marginLeft: 7,
+ },
+ actionsIcons: {
+ width: 24,
+ height: 24,
+ marginRight: 4,
+ alignSelf: "center",
+ left: 0,
+ },
+ buttonPlay: {
+ alignSelf: "center",
+ width: 72,
+ height: 72,
+ },
+ containerActionsPost: {
+ flex: 1,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 74,
+ marginBottom: 19,
+ },
+ textPost: {
+ fontWeight: "400",
+ fontSize: 14,
+ lineHeight: 21,
+ color: "#FFFFFF",
+ marginHorizontal: 16,
+ marginBottom: 16,
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: "700",
+ lineHeight: 22,
+ color: Colors.text,
+ },
+ userPhoto: {
+ width: 32,
+ height: 32,
+ borderRadius: 32,
+ marginRight: 12,
+ },
+ file: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width,
+ height: 230,
+ },
+});
+
+export default PostCommunity;
diff --git a/src/components/community/PostHabits.js b/src/components/community/PostHabits.js
new file mode 100644
index 0000000..710f7c6
--- /dev/null
+++ b/src/components/community/PostHabits.js
@@ -0,0 +1,287 @@
+import React from "react";
+import {
+ StyleSheet,
+ Dimensions,
+ View,
+ Text,
+ TouchableOpacity,
+ Image,
+} from "react-native";
+import Colors from "../../../assets/styles/Colors";
+import { LinearGradient } from "expo-linear-gradient";
+import { useSelector } from "react-redux";
+
+const PostHabits = (props) => {
+ const user = useSelector(({ user }) => user);
+
+ const viewUser = (obj) => {
+ if (user.id === obj.id) {
+ props.navigation.navigate("Home", {
+ screen: "Profile",
+ params: { screen: "ProfileIndex" },
+ });
+ } else {
+ props.navigation.push("Home", {
+ screen: "Profile",
+ params: {
+ screen: "UserProfile",
+ params: { user: { id_user: obj.id } },
+ },
+ });
+ }
+ };
+
+ return (
+ <>
+
+ props.navigation.navigate("ViewPostCommunity", {
+ post: { id_post: props.post.id, savedPost: props.savedPost },
+ })
+ }
+ >
+
+
+ viewUser(props.post?.user)}
+ >
+ {props.post?.user?.image ? (
+
+ ) : (
+
+ )}
+
+
+ {props.post?.user?.name}
+
+ Automatic Posting
+
+
+
+ {props.post?.cop_type === "new_habit" ? (
+
+ {props.post?.user?.name} {props.post?.cop_text.slice(0, 19)}
+ {props.post?.cop_text.slice(34)}
+
+ ) : (
+ {props.post?.cop_text}
+ )}
+
+
+ {props.post?.cop_type === "new_habit" ? (
+ <>
+
+ {props.post?.cop_text.slice(0, 19)}
+
+
+ {props.post?.cop_text.slice(20)}
+
+ >
+ ) : (
+ <>
+
+ {props.post?.cop_text.slice(
+ 0,
+ props.post?.cop_text.indexOf(".") + 1,
+ )}
+
+
+ {props.post?.cop_text.slice(
+ props.post?.cop_text.indexOf(".") + 1,
+ )}
+
+ >
+ )}
+
+
+
+
+
+
+ props.like()}
+ >
+
+
+ {props.countLikes ? props.countLikes : null}
+
+
+
+ props.navigation.navigate("ViewPostCommunity", {
+ post: { id_post: props.post.id },
+ })
+ }
+ >
+
+
+ {props.countComments ? props.countComments : null}
+
+
+ props.save()}>
+
+
+
+
+
+ >
+ );
+};
+
+const styles = StyleSheet.create({
+ containerList: {
+ marginBottom: 8,
+ paddingTop: 16,
+ zIndex: 1,
+ elevation: 1,
+ width: Dimensions.get("window").width,
+ },
+ textSubtitle: {
+ fontSize: 14,
+ lineHeight: 19,
+ fontWeight: "400",
+ color: "rgba(255, 255, 255, 0.5)",
+ },
+ infoCommunity: {
+ marginBottom: 21,
+ },
+ containerItemPost: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ marginLeft: 16,
+ marginBottom: 13,
+ },
+ containerGradientPost: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ marginTop: 12,
+ paddingVertical: 16,
+ marginHorizontal: 16,
+ borderRadius: 4,
+ width: Dimensions.get("window").width - 32,
+ },
+ containerTextPost: {
+ width: Dimensions.get("window").width,
+ borderBottomColor: "#264261",
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ paddingBottom: 16,
+ },
+ textCounter: {
+ fontWeight: "500",
+ fontSize: 12,
+ lineHeight: 18,
+ color: "#FFFFFF",
+ },
+ actionsIcons: {
+ width: 24,
+ height: 24,
+ marginRight: 4,
+ alignSelf: "center",
+ left: 0,
+ },
+ containerActionsPost: {
+ flex: 1,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 74,
+ marginBottom: 19,
+ },
+ textCardPost: {
+ fontWeight: "700",
+ fontSize: 16,
+ lineHeight: 24,
+ color: "#FFFFFF",
+ marginHorizontal: 16,
+ },
+ textPost: {
+ fontWeight: "400",
+ fontSize: 14,
+ lineHeight: 21,
+ color: "#FFFFFF",
+ marginHorizontal: 16,
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: "700",
+ lineHeight: 22,
+ color: Colors.text,
+ },
+ userPhoto: {
+ width: 32,
+ height: 32,
+ borderRadius: 32,
+ marginRight: 12,
+ },
+ imagemContador: {
+ width: 40,
+ height: 40,
+ borderRadius: 32,
+ marginRight: 16,
+ },
+});
+
+export default PostHabits;
diff --git a/src/components/community/VideoCard.js b/src/components/community/VideoCard.js
new file mode 100644
index 0000000..fd4a97a
--- /dev/null
+++ b/src/components/community/VideoCard.js
@@ -0,0 +1,94 @@
+import React, { useRef, useState, useEffect } from "react";
+import {
+ StyleSheet,
+ TouchableOpacity,
+ Dimensions,
+ ActivityIndicator,
+} from "react-native";
+import { Video } from "expo-av";
+
+const VideoCard = (props) => {
+ const videoRef = useRef(null);
+ const [status, setStatus] = useState({});
+ const [isPreloading, setIsPreloading] = useState(false);
+
+ useEffect(() => {
+ videoRef.current ? videoRef.current.stopAsync() : null;
+ }, [props?.navigation]);
+
+ useEffect(() => {
+ return () => {
+ videoRef.current ? videoRef.current.stopAsync() : null;
+ setStatus({});
+ };
+ }, [props?.stop]);
+
+ useEffect(() => {
+ loadVideo(props.video);
+ }, [props?.video]);
+
+ const loadVideo = async (source) => {
+ if (source && videoRef.current) {
+ await unloadVideo();
+ await videoRef.current.loadAsync({ uri: source });
+ }
+ };
+
+ async function unloadVideo() {
+ if (videoRef.current !== null) {
+ await videoRef.current.unloadAsync();
+ }
+ }
+
+ return (
+
+ {isPreloading ? (
+
+ ) : null}
+ {props?.video ? (
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginLeft: 16,
+ marginTop: 16,
+ width: (Dimensions.get("window").width - 48) / 2,
+ height: ((Dimensions.get("window").width - 48) / 2) * 1.31,
+ },
+ buttons: {
+ top: 75,
+ flex: 1,
+ flexDirection: "row",
+ justifyContent: "center",
+ alignSelf: "center",
+ alignItems: "center",
+ position: "absolute",
+ },
+ file: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width,
+ height: 230,
+ },
+});
+
+export default VideoCard;
diff --git a/src/config/supabaseClient.js b/src/config/supabaseClient.js
new file mode 100644
index 0000000..42460e8
--- /dev/null
+++ b/src/config/supabaseClient.js
@@ -0,0 +1,15 @@
+import 'react-native-url-polyfill/auto';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { createClient } from '@supabase/supabase-js';
+
+const supabaseUrl = process.env.EXPO_PUBLIC_REACT_APP_SUPABASE_URL;
+const supabaseAnonKey = process.env.EXPO_PUBLIC_REACT_APP_ANON_KEY;
+
+export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
+ auth: {
+ storage: AsyncStorage,
+ autoRefreshToken: true,
+ persistSession: true,
+ detectSessionInUrl: false,
+ },
+});
diff --git a/src/constants/Constants.js b/src/constants/Constants.js
new file mode 100644
index 0000000..71c9d5c
--- /dev/null
+++ b/src/constants/Constants.js
@@ -0,0 +1,22 @@
+let url = "";
+
+let expoClientId =
+ "430719148489-6mp24nknv8biblbgs5hok6qdq8e5rr3m.apps.googleusercontent.com";
+let androidClientId =
+ "430719148489-6mp24nknv8biblbgs5hok6qdq8e5rr3m.apps.googleusercontent.com";
+let iosClientId =
+ "430719148489-melq8bpgaobfgagjpn9tp5krmvevb87f.apps.googleusercontent.com";
+
+if (__DEV__) {
+ url = "https://api.dev-livetimeless.com";
+} else {
+ url = "https://api.dev-livetimeless.com";
+}
+
+export const Constants = {
+ url: url,
+ baseUrl: url + "/api",
+ expoClientId,
+ iosClientId,
+ androidClientId,
+};
diff --git a/src/navigator/Navigator.js b/src/navigator/Navigator.js
new file mode 100644
index 0000000..c4c7a37
--- /dev/null
+++ b/src/navigator/Navigator.js
@@ -0,0 +1,182 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Image } from 'react-native';
+import Colors from '../../assets/styles/Colors';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { createStackNavigator } from '@react-navigation/stack';
+import Habits from '../screens/habits/Habits';
+import ViewHabit from '../screens/habits/ViewHabit';
+import AddHabit from '../screens/habits/AddHabit';
+import ProfileScreen from '../screens/ProfileScreen';
+import FollowScreen from '../screens/FollowScreen';
+import FollowersScreen from '../screens/FollowersScreen';
+import EditProfile from '../screens/EditProfile';
+import SettingsScreen from '../screens/SettingsScreen';
+import UserProfile from '../screens/UserProfile';
+import homeSelected from '../../assets/icons/home-selected.png';
+import home from '../../assets/icons/home.png';
+import infoSelected from '../../assets/icons/info-selected.png';
+import info from '../../assets/icons/info.png';
+
+import activitySelected from '../../assets/icons/activity-selected.png';
+import activity from '../../assets/icons/activity.png';
+import usersSelected from '../../assets/icons/users-selected.png';
+import users from '../../assets/icons/users.png';
+import userSelected from '../../assets/icons/user-selected.png';
+import user from '../../assets/icons/user.png';
+import clipboardCheckSelected from '../../assets/icons/clipboard-check-selected.png';
+import clipboardCheck from '../../assets/icons/clipboard-check.png';
+import Timeline from '../screens/timeline/Timeline';
+import Community from '../screens/community/Community';
+import CreateCommunity from '../screens/community/CreateCommunity';
+import Profile from '../screens/profile/Profile';
+import UpdateProfile from '../screens/profile/UpdateProfile';
+import ChatbotScreen from '../screens/ChatbotScreen';
+import UserDataScreen from '../screens/UserDataScreen';
+import ChecklistScreen from '../screens/checklist/Checklist';
+import CommentsScreen from '../screens/timeline/CommentsScreen';
+import AddPost from '../screens/timeline/AddPost';
+
+const Tab = createBottomTabNavigator();
+const CommunityStack = createStackNavigator();
+const ProfileStack = createStackNavigator();
+const HabitsStack = createStackNavigator();
+const TimelineStack = createStackNavigator();
+
+const CommunityScreen = () => {
+ return (
+
+
+
+
+ );
+};
+
+const HabitsScreen = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+const ProfilesScreen = ({ setIsLoggedIn }) => {
+ return (
+
+
+
+
+
+
+
+
+
+ {() => }
+
+
+ );
+};
+
+const TimelineScreen = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default function Navigator({ setIsLoggedIn }) {
+ const icons = (route, focused) => {
+ const sizeStyle = { width: 24, height: 24 };
+
+ switch (route.name) {
+ case 'Timeline':
+ return (
+
+ );
+ case 'ExtraTips':
+ return (
+
+ );
+ case 'Checklist':
+ return (
+
+ );
+ case 'Habits':
+ return (
+
+ );
+ case 'Community':
+ return (
+
+ );
+ case 'Profile':
+ return (
+
+ );
+ default:
+ return null;
+ }
+ };
+
+ return (
+ ({
+ tabBarIcon: ({ focused }) => icons(route, focused),
+ tabBarShowLabel: true,
+ tabBarActiveTintColor: Colors.primary8,
+ tabBarInactiveTintColor: Colors.primary9,
+ tabBarStyle: {
+ backgroundColor: Colors.navigator,
+ borderTopWidth: 0,
+ height: 82,
+ paddingTop: 20,
+ paddingBottom: 24,
+ },
+ })}>
+
+
+
+
+ {() => }
+
+
+ );
+}
+
+Navigator.propTypes = {
+ route: PropTypes.object,
+ setIsLoggedIn: PropTypes.func,
+};
diff --git a/src/screens/ChatbotScreen.js b/src/screens/ChatbotScreen.js
new file mode 100644
index 0000000..1ec9a8c
--- /dev/null
+++ b/src/screens/ChatbotScreen.js
@@ -0,0 +1,157 @@
+import React, { useState, useRef } from 'react';
+import {
+ View,
+ TextInput,
+ Button,
+ FlatList,
+ Text,
+ StyleSheet,
+ KeyboardAvoidingView,
+ Platform,
+} from 'react-native';
+const { GoogleGenerativeAI } = require('@google/generative-ai');
+const apikey = process.env.EXPO_PUBLIC_REACT_APP_GEMINI_KEY;
+const genAI = new GoogleGenerativeAI(apikey);
+
+const ChatbotScreen = () => {
+ const [messages, setMessages] = useState([]);
+ const [input, setInput] = useState('');
+ const flatListRef = useRef(null);
+
+ const handleSend = async () => {
+ if (input.trim().length === 0) return;
+ const newMessages = [...messages, { type: 'user', text: input }];
+ setMessages(newMessages);
+
+ try {
+ const model = genAI.getGenerativeModel({ model: 'gemini-pro' });
+
+ const chat = model.startChat({
+ history: [
+ {
+ role: 'user',
+ parts: [
+ {
+ text: 'Hello, I would like you to be my healthy habits coach and give me advice about meeting my habit goals',
+ },
+ ],
+ },
+ {
+ role: 'model',
+ parts: [
+ {
+ text: 'Great to meet you. I would love to help. What are your habit goals?',
+ },
+ ],
+ },
+ ],
+ generationConfig: {
+ maxOutputTokens: 6000,
+ },
+ });
+
+ const result = await chat.sendMessage(input);
+ const response = await result.response;
+ const text = await response.text();
+ setMessages([...newMessages, { type: 'bot', text }]);
+ } catch (error) {
+ console.error('Error handling send:', error);
+ setMessages([
+ ...newMessages,
+ {
+ type: 'bot',
+ text: 'Service is currently unavailable. Please try again later.',
+ },
+ ]);
+ }
+
+ setInput('');
+ };
+
+ return (
+
+ Talk to Your Habit Coach!
+
+ (
+
+ {item.text}
+
+ )}
+ keyExtractor={(item, index) => index.toString()}
+ style={styles.messageList}
+ onContentSizeChange={() =>
+ flatListRef.current.scrollToEnd({ animated: true })
+ }
+ />
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 16,
+ backgroundColor: '#fff',
+ },
+ messageList: {
+ flex: 1,
+ marginBottom: 16,
+ },
+ userMessage: {
+ textAlign: 'right',
+ marginVertical: 4,
+ padding: 8,
+ backgroundColor: 'lightblue',
+ borderRadius: 8,
+ },
+ botMessage: {
+ textAlign: 'left',
+ marginVertical: 4,
+ padding: 8,
+ backgroundColor: '#ECECEC',
+ borderRadius: 8,
+ },
+ inputContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: 8,
+ paddingTop: 8,
+ borderTopWidth: 1,
+ borderTopColor: '#ccc',
+ },
+ input: {
+ flex: 1,
+ borderColor: '#ccc',
+ borderWidth: 1,
+ padding: 8,
+ borderRadius: 4,
+ marginRight: 8,
+ },
+ title: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ marginBottom: 10,
+ textAlign: 'center',
+ color: 'navy',
+ },
+});
+
+export default ChatbotScreen;
diff --git a/src/screens/EditProfile.js b/src/screens/EditProfile.js
new file mode 100644
index 0000000..49f3d75
--- /dev/null
+++ b/src/screens/EditProfile.js
@@ -0,0 +1,136 @@
+import React, { useState, useEffect } from 'react';
+import { Alert, View, StyleSheet, Dimensions } from 'react-native';
+import { Button, Input } from 'react-native-elements';
+import { supabase } from '../config/supabaseClient';
+import Header from '../components/Header';
+import store from '../store/storeConfig';
+import Default from '../../assets/styles/Default';
+import Colors from '../../assets/styles/Colors';
+
+export default function EditProfile({ navigation }) {
+ const session = store.getState().user.session;
+ const [loading, setLoading] = useState(false);
+ const [username, setUsername] = useState('');
+ const [bio, setBio] = useState('');
+ const [profileImage, setProfileImage] = useState(null);
+
+ useEffect(() => {
+ if (session) {
+ getProfile();
+ }
+ }, [session]);
+
+ async function getProfile() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { data, error, status } = await supabase
+ .from('User')
+ .select(`username, bio, profile_image`)
+ .eq('user_id', session?.user.id)
+ .single();
+ if (error && status !== 406) {
+ throw error;
+ }
+
+ if (data) {
+ setUsername(data.username);
+ setBio(data.bio);
+ setProfileImage(data.profile_image);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching profile', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ async function updateProfile() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const updates = {
+ user_id: session?.user.id,
+ username,
+ bio,
+ profile_image: profileImage
+ };
+
+ let { error } = await supabase.from('User').upsert(updates);
+ if (error) throw error;
+ Alert.alert('Profile updated successfully!');
+ navigation.goBack();
+ } catch (error) {
+ Alert.alert('Error updating profile', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ return (
+
+
+
+ setUsername(text)}
+ containerStyle={styles.inputContainer}
+ inputStyle={styles.input}
+ labelStyle={styles.inputLabel}
+ />
+
+
+ setBio(text)}
+ containerStyle={styles.inputContainer}
+ inputStyle={styles.input}
+ labelStyle={styles.inputLabel}
+ />
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ inputWrapper: {
+ width: '100%',
+ paddingHorizontal: 15,
+ marginVertical: 10,
+ },
+ inputContainer: {
+ marginVertical: 10,
+ },
+ input: {
+ color: Colors.text,
+ },
+ inputLabel: {
+ color: Colors.text,
+ },
+ updateButton: {
+ backgroundColor: Colors.primary,
+ borderRadius: 5,
+ paddingVertical: 10,
+ marginHorizontal: 15,
+ },
+ updateButtonText: {
+ fontSize: 16,
+ },
+});
diff --git a/src/screens/FollowScreen.js b/src/screens/FollowScreen.js
new file mode 100644
index 0000000..7177e25
--- /dev/null
+++ b/src/screens/FollowScreen.js
@@ -0,0 +1,234 @@
+import React, { useState, useEffect } from 'react';
+import { StyleSheet, View, Text, TextInput, FlatList, TouchableOpacity, Image, Alert } from 'react-native';
+import { useNavigation, useRoute } from '@react-navigation/native';
+import { supabase } from '../config/supabaseClient';
+import store from '../store/storeConfig';
+import Colors from '../../assets/styles/Colors';
+import Header from '../components/Header';
+
+export default function FollowScreen() {
+ const route = useRoute();
+ const { userId } = route.params;
+ const session = store.getState().user.session;
+ const [searchQuery, setSearchQuery] = useState('');
+ const [searchResults, setSearchResults] = useState([]);
+ const [followingList, setFollowingList] = useState([]);
+ const navigation = useNavigation();
+
+ useEffect(() => {
+ if (userId) {
+ fetchFollowingList();
+ }
+ }, [userId]);
+
+ const fetchFollowingList = async () => {
+ try {
+ const { data, error } = await supabase
+ .from('Following')
+ .select('following(username, user_id, profile_image)')
+ .eq('follower', userId);
+
+ if (error) {
+ throw error;
+ }
+
+ setFollowingList(data.map(item => item.following));
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const handleSearch = async () => {
+ try {
+ if (searchQuery.trim() === '') {
+ setSearchResults([]);
+ return;
+ }
+
+ const { data, error } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .ilike('username', `%${searchQuery}%`);
+
+ if (error) {
+ throw error;
+ }
+
+ const updatedResults = data.map(user => ({
+ ...user,
+ isFollowing: followingList.some(following => following.user_id === user.user_id),
+ }));
+
+ setSearchResults(updatedResults);
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const followUser = async (userId) => {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error } = await supabase
+ .from('Following')
+ .insert({ follower: session.user.id, following: userId });
+
+ if (error) {
+ throw error;
+ }
+
+ Alert.alert('Success', 'You are now following this user');
+ fetchFollowingList();
+ } catch (error) {
+ Alert.alert('You are already following this person');
+ }
+ };
+
+ const unfollowUser = async (userId) => {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error } = await supabase
+ .from('Following')
+ .delete()
+ .eq('follower', session.user.id)
+ .eq('following', userId);
+
+ if (error) {
+ throw error;
+ }
+
+ Alert.alert('Success', 'You have unfollowed this user');
+ fetchFollowingList();
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const renderFollowingItem = ({ item }) => {
+ return (
+ navigation.navigate('UserProfile', { userId: item.user_id })}
+ >
+
+ {item.username}
+ unfollowUser(item.user_id)}>
+ Unfollow
+
+
+ );
+ };
+
+ const renderSearchResultItem = ({ item }) => {
+ return (
+ navigation.navigate('UserProfile', { userId: item.user_id })}
+ >
+
+ {item.username}
+ {item.isFollowing ? (
+ You already follow this user
+ ) : (
+ followUser(item.user_id)}>
+ Follow
+
+ )}
+
+ );
+ };
+
+ return (
+
+
+ {
+ setSearchQuery(text);
+ handleSearch();
+ }}
+ onSubmitEditing={handleSearch}
+ />
+ {searchQuery.trim() === '' ? (
+ String(item.user_id)}
+ renderItem={renderFollowingItem}
+ style={styles.followingList}
+ />
+ ) : (
+ <>
+ Search Results
+ String(item.user_id)}
+ renderItem={renderSearchResultItem}
+ />
+ >
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 20,
+ backgroundColor: Colors.background,
+ },
+ searchBar: {
+ height: 40,
+ borderColor: 'gray',
+ borderWidth: 1,
+ marginBottom: 20,
+ paddingHorizontal: 10,
+ backgroundColor: 'white',
+ borderRadius: 45,
+ },
+ followingList: {
+ marginBottom: 20,
+ },
+ resultItem: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 10,
+ borderBottomWidth: 1,
+ borderBottomColor: 'gray',
+ },
+ profileImage: {
+ width: 50,
+ height: 50,
+ borderRadius: 25,
+ marginRight: 15,
+ },
+ username: {
+ fontSize: 16,
+ color: Colors.text,
+ flex: 1,
+ },
+ followButton: {
+ color: Colors.primary8,
+ fontWeight: 'bold',
+ },
+ unfollowButton: {
+ color: Colors.secondary,
+ fontWeight: 'bold',
+ },
+ alreadyFollowing: {
+ color: 'gray',
+ fontStyle: 'italic',
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ color: Colors.primary8,
+ },
+});
diff --git a/src/screens/FollowersScreen.js b/src/screens/FollowersScreen.js
new file mode 100644
index 0000000..6af6a6e
--- /dev/null
+++ b/src/screens/FollowersScreen.js
@@ -0,0 +1,264 @@
+import React, { useState, useEffect } from 'react';
+import { StyleSheet, View, Text, TextInput, FlatList, TouchableOpacity, Image, ActivityIndicator, Alert } from 'react-native';
+import { useNavigation, useRoute } from '@react-navigation/native';
+import { supabase } from '../config/supabaseClient';
+import store from '../store/storeConfig';
+import Colors from '../../assets/styles/Colors';
+import Header from '../components/Header';
+
+export default function FollowersScreen() {
+ const route = useRoute();
+ const { userId } = route.params;
+ const session = store.getState().user.session;
+ const [searchQuery, setSearchQuery] = useState('');
+ const [searchResults, setSearchResults] = useState([]);
+ const [followersList, setFollowersList] = useState([]);
+ const [followingList, setFollowingList] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const navigation = useNavigation();
+
+ useEffect(() => {
+ if (userId) {
+ fetchFollowers();
+ fetchFollowing();
+ }
+ }, [userId]);
+
+ const fetchFollowers = async () => {
+ try {
+ const { data, error } = await supabase
+ .from('Following')
+ .select('follower(username, user_id, profile_image)')
+ .eq('following', userId); // Use userId prop instead of session user ID
+
+ if (error) {
+ throw error;
+ }
+
+ setFollowersList(data.map(item => item.follower));
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const fetchFollowing = async () => {
+ try {
+ const { data, error } = await supabase
+ .from('Following')
+ .select('following')
+ .eq('follower', userId); // Use userId prop instead of session user ID
+
+ if (error) {
+ throw error;
+ }
+
+ setFollowingList(data.map(item => item.following));
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const handleSearch = async () => {
+ try {
+ if (searchQuery.trim() === '') {
+ setSearchResults([]);
+ return;
+ }
+
+ const { data, error } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .ilike('username', `%${searchQuery}%`);
+
+ if (error) {
+ throw error;
+ }
+
+ const updatedResults = data.map(user => ({
+ ...user,
+ isFollower: followersList.some(follower => follower.user_id === user.user_id),
+ isFollowingBack: followingList.includes(user.user_id),
+ }));
+
+ setSearchResults(updatedResults);
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const followUser = async (userId) => {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error } = await supabase
+ .from('Following')
+ .insert({ follower: session.user.id, following: userId });
+
+ if (error) {
+ throw error;
+ }
+
+ Alert.alert('Success', 'You are now following this user');
+ fetchFollowers();
+ fetchFollowing();
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const unfollowUser = async (userId) => {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error } = await supabase
+ .from('Following')
+ .delete()
+ .eq('follower', session.user.id)
+ .eq('following', userId);
+
+ if (error) {
+ throw error;
+ }
+
+ Alert.alert('Success', 'You have unfollowed this user');
+ fetchFollowers();
+ fetchFollowing();
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+ const renderSearchResultItem = ({ item }) => (
+
+
+ {item.username}
+ {item.isFollowingBack ? (
+ unfollowUser(item.user_id)}>
+ Unfollow
+
+ ) : (
+ followUser(item.user_id)}>
+ Follow
+
+ )}
+
+ );
+
+ const renderFollowerItem = ({ item }) => (
+ navigation.navigate('UserProfile', { userId: item.user_id })}
+ >
+
+ {item.username}
+ {followingList.includes(item.user_id) ? (
+ unfollowUser(item.user_id)}>
+ Unfollow
+
+ ) : (
+ followUser(item.user_id)}>
+ Follow
+
+ )}
+
+ );
+
+ return (
+
+
+ {
+ setSearchQuery(text);
+ handleSearch();
+ }}
+ onSubmitEditing={handleSearch}
+ />
+ {loading ? (
+
+ ) : (
+ searchQuery.trim() === '' ? (
+ String(item.user_id)}
+ renderItem={renderFollowerItem}
+ style={styles.followersList}
+ />
+ ) : (
+ <>
+ Search Results
+ String(item.user_id)}
+ renderItem={renderSearchResultItem}
+ />
+ >
+ )
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 20,
+ backgroundColor: Colors.background,
+ },
+ searchBar: {
+ height: 40,
+ borderColor: 'gray',
+ borderWidth: 1,
+ marginBottom: 20,
+ paddingHorizontal: 10,
+ backgroundColor: 'white',
+ borderRadius: 45,
+ },
+ followersList: {
+ marginBottom: 20,
+ },
+ resultItem: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 10,
+ borderBottomWidth: 1,
+ borderBottomColor: 'gray',
+ },
+ profileImage: {
+ width: 50,
+ height: 50,
+ borderRadius: 25,
+ marginRight: 15,
+ },
+ username: {
+ fontSize: 16,
+ color: Colors.text,
+ flex: 1,
+ },
+ followButton: {
+ color: Colors.primary8,
+ fontWeight: 'bold',
+ },
+ unfollowButton: {
+ color: Colors.secondary,
+ fontWeight: 'bold',
+ },
+ alreadyFollowing: {
+ color: 'gray',
+ fontStyle: 'italic',
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ marginBottom: 20,
+ color: Colors.primary8,
+ },
+});
diff --git a/src/screens/ProfileScreen.js b/src/screens/ProfileScreen.js
new file mode 100644
index 0000000..e257973
--- /dev/null
+++ b/src/screens/ProfileScreen.js
@@ -0,0 +1,704 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import {
+ StyleSheet,
+ View,
+ Alert,
+ ScrollView,
+ Text,
+ Image,
+ Dimensions,
+ TouchableOpacity,
+ Modal,
+} from 'react-native';
+import { Button, Input } from 'react-native-elements';
+import { supabase } from '../config/supabaseClient';
+import { useNavigation } from '@react-navigation/native';
+import * as ImagePicker from 'expo-image-picker';
+import store from '../store/storeConfig';
+import Default from '../../assets/styles/Default';
+import Colors from '../../assets/styles/Colors';
+import { FlatList } from 'react-native-gesture-handler';
+
+const { width } = Dimensions.get('window');
+const imageSize = width / 3;
+
+export default function Account() {
+ const session = store.getState().user.session;
+ const navigation = useNavigation();
+ const [loading, setLoading] = useState(false);
+ const [username, setUsername] = useState('');
+ const [bio, setBio] = useState('');
+ const [profileImage, setProfileImage] = useState(null);
+ const [postsCount, setPostsCount] = useState(0);
+ const [followingCount, setFollowingCount] = useState(0);
+ const [followerCount, setFollowerCount] = useState(0);
+ const [habitImages, setHabitImages] = useState([]);
+ const [selectedImage, setSelectedImage] = useState(null);
+ const [modalVisible, setModalVisible] = useState(false);
+
+ useEffect(() => {
+ if (session) {
+ getProfile();
+ getPostsCount();
+ getFollowingCount();
+ getFollowerCount();
+ getHabitImages();
+ }
+ }, [session]);
+
+ async function getProfile() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { data, error, status } = await supabase
+ .from('User')
+ .select(`username, bio, profile_image`)
+ .eq('user_id', session?.user.id)
+ .single();
+ if (error && status !== 406) {
+ throw error;
+ }
+
+ if (data) {
+ setUsername(data.username);
+ setBio(data.bio);
+ setProfileImage(data.profile_image);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching profile', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ const getHabitImages = async () => {
+ const userId = session?.user.id;
+ try {
+ setLoading(true);
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .select('habit_id, schedule_id')
+ .eq('user_id', userId);
+
+ if (scheduleError) throw scheduleError;
+
+ const habitIds = scheduleData.map(schedule => schedule.habit_id);
+ const scheduleIds = scheduleData.map(schedule => schedule.schedule_id);
+
+ if (habitIds.length === 0 || scheduleIds.length === 0) {
+ Alert.alert('Error', 'No habits or schedules found for the user');
+ setLoading(false);
+ return;
+ }
+
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .select('habit_id, habit_title, habit_photo')
+ .in('habit_id', habitIds);
+
+ if (habitError) throw habitError;
+
+ const { data: imagesData, error: imagesError } = await supabase
+ .from('HabitImages')
+ .select('*')
+ .in('habit_id', habitIds);
+
+ if (imagesError) throw imagesError;
+
+ const { data: postsData, error: postsError } = await supabase
+ .from('Post')
+ .select('post_id, post_description, schedule_id')
+ .in('schedule_id', scheduleIds);
+
+ if (postsError) throw postsError;
+
+ const postIds = postsData.map(post => post.post_id);
+
+ const { data: postImagesData, error: postImagesError } = await supabase
+ .from('Image')
+ .select('image_photo, post_id')
+ .in('post_id', postIds);
+
+ if (postImagesError) throw postImagesError;
+
+ const combinedImages = habitData.map(habit => {
+ const habitImages = imagesData.filter(image => image.habit_id === habit.habit_id);
+ const habitPosts = postsData.filter(post =>
+ scheduleData.find(schedule => schedule.schedule_id === post.schedule_id)?.habit_id === habit.habit_id
+ );
+ const habitPostImages = postImagesData.filter(image =>
+ habitPosts.some(post => post.post_id === image.post_id)
+ );
+
+ const combined = [
+ ...(habit.habit_photo ? [{ image_photo: habit.habit_photo, id: 'habitPhoto' }] : []),
+ ...habitImages,
+ ...habitPostImages.map(image => ({
+ ...image,
+ post_description: habitPosts.find(post => post.post_id === image.post_id)?.post_description
+ }))
+ ].filter(image => image.image_photo !== null);
+
+ const postTexts = habitPosts
+ .filter(post => !postImagesData.some(image => image.post_id === post.post_id && image.image_photo))
+ .map(post => ({
+ post_description: post.post_description,
+ post_id: post.post_id
+ }));
+
+ return {
+ habit_title: habit.habit_title,
+ images: combined,
+ postTexts: postTexts
+ };
+ });
+
+ setHabitImages(combinedImages);
+ } catch (error) {
+ Alert.alert('Error fetching habit images', error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+
+
+
+
+ async function getPostsCount() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error, status, count } = await supabase
+ .from('Post')
+ .select('*', { count: 'exact' })
+ .eq('user_id', session?.user.id);
+
+ if (error && status !== 406) {
+ throw error;
+ }
+
+ if (count !== undefined) {
+ setPostsCount(count);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching posts count', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ async function getFollowingCount() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { count, error, status } = await supabase
+ .from('Following')
+ .select('*', { count: 'exact' })
+ .eq('follower', session?.user.id);
+
+ if (error && status !== 406) {
+ throw error;
+ }
+
+ if (count !== undefined) {
+ setFollowingCount(count);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching following count', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ async function getFollowerCount() {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { count, error, status } = await supabase
+ .from('Following')
+ .select('*', { count: 'exact' })
+ .eq('following', session?.user.id);
+
+ if (error && status !== 406) {
+ throw error;
+ }
+
+ if (count !== undefined) {
+ setFollowerCount(count);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching follower count', error.message);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ const goToFollowersScreen = () => {
+ navigation.navigate('FollowersScreen', { userId: session.user.id });
+ };
+
+ const goToFollowScreen = () => {
+ navigation.navigate('FollowScreen', { userId: session.user.id });
+ };
+
+ const goToSettingsScreen = () => {
+ navigation.navigate('SettingsScreen');
+ };
+
+ const handleImagePicker = async () => {
+ let result = await ImagePicker.launchImageLibraryAsync({
+ mediaTypes: ImagePicker.MediaTypeOptions.Images,
+ allowsEditing: true,
+ aspect: [4, 3],
+ quality: 1,
+ });
+
+ if (!result.cancelled) {
+ setProfileImage(result.assets[0]);
+ uploadProfileImage(result.assets[0]);
+ }
+ };
+
+ const base64ToArrayBuffer = (base64) => {
+ const binaryString = window.atob(base64);
+ const len = binaryString.length;
+ const bytes = new Uint8Array(len);
+ for (let i = 0; len > i; i++) {
+ bytes[i] = binaryString.charCodeAt(i);
+ }
+ return bytes.buffer;
+ };
+
+ const uploadProfileImage = async (image) => {
+ try {
+ setLoading(true);
+ const fileName = `${Date.now()}_${String(image.uri).replace('null', '').split('/').pop()}`;
+ console.log('Uploading file:', fileName);
+
+ const response = await fetch(image.uri);
+ const blob = await response.blob();
+
+ const reader = new FileReader();
+ reader.onloadend = async () => {
+ const base64data = reader.result.split(',')[1];
+ const arrayBuffer = base64ToArrayBuffer(base64data);
+
+ const { error: uploadError } = await supabase.storage
+ .from('profiles')
+ .upload(fileName, arrayBuffer, {
+ cacheControl: '3600',
+ upsert: false,
+ contentType: 'image/jpeg',
+ });
+
+ if (uploadError) {
+ console.error('Upload error:', uploadError);
+ Alert.alert('Error', uploadError.message);
+ setLoading(false);
+ return;
+ }
+
+ const { data } = supabase.storage.from('profiles').getPublicUrl(fileName);
+ const publicUrl = data.publicUrl;
+ setProfileImage(publicUrl);
+
+ const updates = {
+ user_id: session?.user.id,
+ username: username,
+ bio: bio,
+ profile_image: publicUrl,
+ };
+
+ const { error: updateError } = await supabase
+ .from('User')
+ .upsert(updates, { returning: 'minimal' });
+
+ if (updateError) {
+ console.error('Update error:', updateError);
+ Alert.alert('Error', updateError.message);
+ setLoading(false);
+ return;
+ }
+
+ Alert.alert('Success', 'Profile image updated successfully!');
+ };
+ reader.readAsDataURL(blob);
+ } catch (error) {
+ console.error('Upload error:', error);
+ Alert.alert('Error', 'Failed to upload image');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const openModal = (image) => {
+ setSelectedImage(image);
+ setModalVisible(true);
+ };
+
+ const closeModal = () => {
+ setModalVisible(false);
+ setSelectedImage(null);
+ };
+
+ const renderHabitImages = ({ item }) => (
+
+ {item.habit_title}
+ index.toString()}
+ numColumns={3}
+ renderItem={({ item: image }) => (
+ openModal(image)}>
+
+
+ )}
+ />
+ {item.postTexts.length > 0 && (
+
+ {item.postTexts.map((post) => (
+
+ {post.post_description}
+
+ ))}
+
+ )}
+
+ );
+
+
+
+
+
+
+ return (
+
+
+
+
+ {profileImage ? (
+
+ ) : (
+
+ )}
+
+
+ {`Hi, ${username || 'User'}`}
+
+
+
+ {followerCount}
+ followers
+
+
+ {followingCount}
+ following
+
+
+ {postsCount}
+ posts
+
+
+
+
+
+
+
+
+
+ navigation.navigate('EditProfile')}>
+
+
+ Edit Profile
+
+
+
+
+
+
+
+
+
+ Username
+ {username}
+
+
+ Bio
+ {bio}
+
+ >
+ }
+ data={habitImages}
+ renderItem={renderHabitImages}
+ keyExtractor={(item, index) => index.toString()}
+ />
+
+
+
+
+ {selectedImage && (
+ <>
+
+ {selectedImage.post_description}
+
+ >
+ )}
+
+
+
+
+ );
+
+
+
+}
+
+const styles = StyleSheet.create({
+ scrollViewContent: {
+ paddingHorizontal: 15,
+ paddingTop: 28,
+ width: Dimensions.get('window').width - 2,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerHeader: {
+ alignItems: 'center',
+ marginBottom: 32,
+ },
+ containerPhoto: {
+ marginBottom: 15,
+ },
+ userPhoto: {
+ width: 90,
+ height: 90,
+ borderRadius: 45,
+ },
+ textName: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginBottom: 20,
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ width: '100%',
+ marginBottom: 20,
+ },
+ stat: {
+ alignItems: 'center',
+ },
+ statCount: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: Colors.text,
+ },
+ statLabel: {
+ fontSize: 14,
+ color: Colors.text,
+ },
+ settingsIcon: {
+ position: 'absolute',
+ top: 40,
+ right: 20,
+ },
+ containerActionsHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ width: '100%',
+ marginBottom: 20,
+ },
+ editProfileButton: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 10,
+ backgroundColor: '#333333',
+ borderRadius: 5,
+ width: 150,
+ },
+ iconsHeader: {
+ width: 25,
+ height: 25,
+ marginRight: 2,
+ },
+ editProfileText: {
+ color: 'white',
+ fontWeight: '600',
+ },
+ buttonWrapper: {
+ alignItems: 'center',
+ marginBottom: 20,
+ },
+ findUserButton: {
+ backgroundColor: '#333333',
+ borderRadius: 45,
+ width: 150,
+ paddingVertical: 10,
+ },
+ findUserButtonText: {
+ fontSize: 16,
+ color: 'white',
+ fontWeight: '600',
+ },
+ inputWrapper: {
+ marginBottom: 10,
+ },
+ inputContainer: {
+ marginBottom: 10,
+ },
+ input: {
+ color: Colors.primary4,
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ fontSize: 16,
+ paddingHorizontal: 12,
+ paddingTop: 8,
+ paddingBottom: 8,
+ height: 40,
+ backgroundColor: Colors.primary,
+ textAlignVertical: 'top',
+ },
+ title: {
+ fontSize: 16,
+ fontWeight: 'bold',
+ color: Colors.white,
+ marginBottom: 8,
+ },
+ textContent: {
+ fontSize: 14,
+ color: Colors.white,
+ marginBottom: 16,
+ },
+ inputLabel: {
+ color: Colors.text,
+ fontWeight: 'normal',
+ marginBottom: 8,
+ },
+ verticallySpaced: {
+ marginTop: 4,
+ marginBottom: 4,
+ },
+ mt20: {
+ marginTop: 20,
+ },
+ updateButton: {
+ backgroundColor: Colors.primary8,
+ borderRadius: 20,
+ },
+ signOutButton: {
+ backgroundColor: Colors.primary8,
+ borderRadius: 20,
+ },
+ deleteButton: {
+ backgroundColor: Colors.error,
+ borderRadius: 20,
+ },
+ downloadButton: {
+ backgroundColor: Colors.success,
+ borderRadius: 20,
+ },
+ userDataContainer: {
+ marginTop: 20,
+ },
+ userDataText: {
+ fontSize: 16,
+ color: Colors.text,
+ },
+ habitSection: {
+ marginBottom: 20,
+ },
+ habitTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginBottom: 10,
+ marginLeft: 10,
+ },
+ gridImage: {
+ width: imageSize - 10,
+ height: imageSize - 10,
+ margin: 5,
+ },
+ textContainer: {
+ padding: 10,
+ },
+ textPost: {
+ fontSize: 16,
+ color: 'black',
+ },
+ textPostContainer: {
+ borderWidth: 1,
+ borderColor: Colors.lightGray,
+ borderRadius: 8,
+ padding: 10,
+ marginBottom: 10,
+ backgroundColor: Colors.white,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
+ },
+ modalContent: {
+ width: '90%',
+ backgroundColor: Colors.cardBackground,
+ borderRadius: 10,
+ padding: 16,
+ alignItems: 'center',
+ },
+ fullImage: {
+ width: '100%',
+ height: Dimensions.get('window').width,
+ borderRadius: 10,
+ marginBottom: 16,
+ },
+ imageDescription: {
+ color: Colors.white,
+ marginBottom: 16,
+ textAlign: 'center',
+ fontSize: 12,
+ },
+ textDescription: {
+ fontSize: 14,
+ color: Colors.text,
+ marginTop: 5,
+ marginBottom: 5,
+ paddingLeft: 10,
+ paddingRight: 10,
+ },
+});
+
+Account.propTypes = {
+ navigation: PropTypes.object,
+ setIsLoggedIn: PropTypes.func,
+};
diff --git a/src/screens/SettingsScreen.js b/src/screens/SettingsScreen.js
new file mode 100644
index 0000000..a177f45
--- /dev/null
+++ b/src/screens/SettingsScreen.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import { StyleSheet, View, Button, Alert } from 'react-native';
+import * as FileSystem from 'expo-file-system';
+import * as Sharing from 'expo-sharing';
+import { supabase } from '../config/supabaseClient';
+import store from '../store/storeConfig';
+import Colors from '../../assets/styles/Colors';
+
+export default function SettingsScreen({setIsLoggedIn}) {
+ const session = store.getState().user.session;
+
+ async function deleteUserAndData() {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { error } = await supabase.rpc('delete_user_data', {
+ user_id: session.user.id,
+ });
+
+ if (error) {
+ console.error('Error deleting user:', error);
+ return;
+ }
+
+ console.log('User and associated data deleted successfully');
+ await supabase.auth.signOut();
+ } catch (error) {
+ console.error('Error deleting user:', error);
+ Alert.alert('Error', error.message);
+ }
+ }
+
+ async function downloadUserData() {
+ try {
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { data, error } = await supabase.rpc('get_user_data', {
+ p_user_id: session.user.id,
+ });
+
+ if (error) {
+ console.error('Error fetching data:', error);
+ Alert.alert('Error', 'Error fetching data: ' + error.message);
+ return;
+ }
+
+ const jsonData = JSON.stringify(data, null, 2);
+ const fileUri = FileSystem.documentDirectory + 'user_data.json';
+
+ await FileSystem.writeAsStringAsync(fileUri, jsonData, {
+ encoding: FileSystem.EncodingType.UTF8,
+ });
+
+ Alert.alert('Success', 'User data saved as user_data.json');
+ Sharing.shareAsync(fileUri);
+ } catch (error) {
+ console.error('Error:', error);
+ Alert.alert('Error', 'Unexpected error: ' + error.message);
+ }
+ }
+
+ return (
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 20,
+ backgroundColor: Colors.background,
+ },
+});
diff --git a/src/screens/SignupScreen.js b/src/screens/SignupScreen.js
new file mode 100644
index 0000000..a8c8037
--- /dev/null
+++ b/src/screens/SignupScreen.js
@@ -0,0 +1,107 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import { Alert, StyleSheet, View, AppState } from 'react-native';
+import { supabase } from '../config/supabaseClient';
+import { Button, Input } from 'react-native-elements';
+import { userLogin } from '../store/ducks/user';
+import store from '../store/storeConfig';
+
+// Tells Supabase Auth to continuously refresh the session automatically if
+// the app is in the foreground. When this is added, you will continue to receive
+// `onAuthStateChange` events with the `TOKEN_REFRESHED` or `SIGNED_OUT` event
+// if the user's session is terminated. This should only be registered once.
+AppState.addEventListener('change', state => {
+ if (state === 'active') {
+ supabase.auth.startAutoRefresh();
+ } else {
+ supabase.auth.stopAutoRefresh();
+ }
+});
+
+export default function Auth( { setIsLoggedIn }) {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ async function signInWithEmail() {
+ setLoading(true);
+ const { data, error } = await supabase.auth.signInWithPassword({
+ email: email,
+ password: password,
+ });
+
+ setLoading(false);
+ if (error) {
+ Alert.alert('Login Error', error.message);
+ } else if (data.session) {
+ store.dispatch(userLogin(data.session));
+ setIsLoggedIn(true);
+ }
+ }
+
+ async function signUpWithEmail() {
+ setLoading(true);
+ const { error } = await supabase.auth.signUp({
+ email: email,
+ password: password,
+ });
+
+ if (error) {
+ Alert.alert('Signup Error', error.message);
+ setLoading(false);
+ } else {
+ Alert.alert(
+ 'Signup Notification',
+ 'Please check your inbox for email verification!'
+ );
+ setLoading(false);
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ marginTop: 40,
+ padding: 12,
+ },
+ verticallySpaced: {
+ paddingTop: 4,
+ paddingBottom: 4,
+ alignSelf: 'stretch',
+ },
+ mt20: {
+ marginTop: 20,
+ },
+});
+
+Auth.propTypes = {
+ setIsLoggedIn: PropTypes.func,
+};
diff --git a/src/screens/UserDataScreen.js b/src/screens/UserDataScreen.js
new file mode 100644
index 0000000..f815be4
--- /dev/null
+++ b/src/screens/UserDataScreen.js
@@ -0,0 +1,135 @@
+// Desc: This page works as is, but API calls are disabled for non-web applications.
+// This is because emailjs is not supported on mobile devices.
+
+import React, { useEffect, useState } from 'react';
+import {
+ View,
+ Text,
+ TextInput,
+ Button,
+ StyleSheet,
+ ScrollView,
+} from 'react-native';
+import emailjs from '@emailjs/react-native';
+import { supabase } from '../config/supabaseClient';
+import store from '../store/storeConfig';
+
+const serviceId = process.env.EXPO_PUBLIC_SERVICE_ID;
+const templateId = process.env.EXPO_PUBLIC_TEMPLATE_ID;
+const publicKey = process.env.EXPO_PUBLIC_PUBLIC_KEY;
+
+const ContactForm = () => {
+ const [email, setEmail] = useState();
+ const [name, setName] = useState();
+ const [message, setMessage] = useState();
+ const session = store.getState().user.session;
+
+ useEffect(() => {
+ const fetchUserData = async () => {
+ const { data } = await supabase.rpc('get_user_data', {
+ p_user_id: session.user.id,
+ });
+ setMessage(JSON.stringify(data, null, 2));
+ };
+
+ fetchUserData();
+ }, []);
+
+ const onSubmit = async () => {
+ emailjs
+ .send(
+ serviceId,
+ templateId,
+ {
+ from_name: 'Ozan',
+ to_email: 'sukhrajsidhu@live.ca',
+ message: 'it works',
+ },
+ {
+ publicKey: publicKey,
+ }
+ )
+ .then(
+ response => {
+ console.log('SUCCESS!', response.status, response.text);
+ },
+ err => {
+ console.log('FAILED...', err);
+ }
+ );
+ };
+
+ return (
+
+
+ Name
+
+
+
+
+ E-mail
+
+
+
+
+ Message
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: 20,
+ justifyContent: 'center',
+ backgroundColor: '#fff',
+ },
+ header: {
+ fontSize: 20,
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ formElement: {
+ marginBottom: 15,
+ },
+ label: {
+ marginBottom: 5,
+ },
+ input: {
+ borderWidth: 1,
+ borderColor: '#ccc',
+ padding: 10,
+ borderRadius: 5,
+ },
+ textarea: {
+ borderWidth: 1,
+ borderColor: '#ccc',
+ padding: 10,
+ borderRadius: 5,
+ height: 100,
+ },
+});
+
+export default ContactForm;
diff --git a/src/screens/UserProfile.js b/src/screens/UserProfile.js
new file mode 100644
index 0000000..fbdb44c
--- /dev/null
+++ b/src/screens/UserProfile.js
@@ -0,0 +1,325 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ FlatList,
+ StyleSheet,
+ Dimensions,
+ Text,
+ TouchableOpacity,
+ Alert,
+ Image,
+ Modal,
+} from 'react-native';
+import { useNavigation, useRoute } from '@react-navigation/native';
+import Default from '../../assets/styles/Default';
+import Colors from '../../assets/styles/Colors';
+import Header from '../components/Header';
+import { supabase } from '../config/supabaseClient';
+import { Button } from 'react-native-elements';
+
+const { width } = Dimensions.get('window');
+const imageSize = width / 3;
+
+const UserProfile = () => {
+ const [loading, setLoading] = useState(false);
+ const [username, setUsername] = useState('');
+ const [bio, setBio] = useState('');
+ const [profileImage, setProfileImage] = useState(null);
+ const [habitImages, setHabitImages] = useState([]);
+ const [selectedImage, setSelectedImage] = useState(null);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [followerCount, setFollowerCount] = useState(0);
+ const [followingCount, setFollowingCount] = useState(0);
+ const [postsCount, setPostsCount] = useState(0);
+
+ const navigation = useNavigation();
+ const route = useRoute();
+ const { userId } = route.params;
+
+ useEffect(() => {
+ getProfile();
+ getHabitImages();
+ getFollowStats();
+ getPostsCount();
+ }, [userId]);
+
+ const getProfile = async () => {
+ try {
+ setLoading(true);
+ const { data, error } = await supabase
+ .from('User')
+ .select('username, bio, profile_image')
+ .eq('user_id', userId)
+ .single();
+ if (error) throw error;
+ if (data) {
+ setUsername(data.username);
+ setBio(data.bio);
+ setProfileImage(data.profile_image);
+ }
+ } catch (error) {
+ Alert.alert('Error fetching profile', error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getHabitImages = async () => {
+ try {
+ setLoading(true);
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .select('habit_id')
+ .eq('user_id', userId);
+
+ if (scheduleError) throw scheduleError;
+
+ const habitIds = scheduleData.map(schedule => schedule.habit_id);
+
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .select('habit_id, habit_title')
+ .in('habit_id', habitIds);
+
+ if (habitError) throw habitError;
+
+ const { data: imagesData, error: imagesError } = await supabase
+ .from('HabitImages')
+ .select('*')
+ .in('habit_id', habitIds);
+
+ if (imagesError) throw imagesError;
+
+ const imagesByHabit = habitData.reduce((acc, habit) => {
+ acc[habit.habit_title] = imagesData.filter(image => image.habit_id === habit.habit_id);
+ return acc;
+ }, {});
+
+ setHabitImages(imagesByHabit);
+ } catch (error) {
+ Alert.alert('Error fetching habit images', error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getFollowStats = async () => {
+ try {
+ const { count: followerCount, error: followerError } = await supabase
+ .from('Following')
+ .select('*', { count: 'exact' })
+ .eq('following', userId);
+
+ const { count: followingCount, error: followingError } = await supabase
+ .from('Following')
+ .select('*', { count: 'exact' })
+ .eq('follower', userId);
+
+ if (followerError || followingError) {
+ throw new Error(followerError?.message || followingError?.message);
+ }
+
+ setFollowerCount(followerCount);
+ setFollowingCount(followingCount);
+ } catch (error) {
+ Alert.alert('Error fetching follow stats', error.message);
+ }
+ };
+
+ const getPostsCount = async () => {
+ try {
+ const { count: postsCount, error: postsError } = await supabase
+ .from('Post')
+ .select('*', { count: 'exact' })
+ .eq('user_id', userId);
+
+ if (postsError) {
+ throw postsError;
+ }
+
+ setPostsCount(postsCount);
+ } catch (error) {
+ Alert.alert('Error fetching posts count', error.message);
+ }
+ };
+
+ const goToFollowersScreen = () => {
+ navigation.navigate('FollowersScreen', { userId });
+ };
+
+ const goToFollowScreen = () => {
+ navigation.navigate('FollowScreen', { userId });
+ };
+
+ const openModal = (image) => {
+ setSelectedImage(image);
+ setModalVisible(true);
+ };
+
+ const closeModal = () => {
+ setModalVisible(false);
+ setSelectedImage(null);
+ };
+
+ const renderHabitImages = ({ item }) => (
+
+ {item.habit_title}
+ index.toString()}
+ numColumns={3}
+ renderItem={({ item: image }) => (
+ openModal(image)}>
+
+
+ )}
+ />
+
+ );
+
+ return (
+
+
+
+
+
+ {profileImage ? (
+
+ ) : (
+
+ )}
+
+ {username}
+ {bio}
+
+
+
+ {followerCount}
+ followers
+
+
+ {followingCount}
+ following
+
+
+ {postsCount}
+ posts
+
+
+ >
+ }
+ data={Object.keys(habitImages).map(habitTitle => ({
+ habit_title: habitTitle,
+ images: habitImages[habitTitle],
+ }))}
+ renderItem={renderHabitImages}
+ keyExtractor={(item, index) => index.toString()}
+ />
+
+
+
+
+ {selectedImage && (
+ <>
+
+ {selectedImage.description}
+
+ >
+ )}
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ profileHeader: {
+ alignItems: 'center',
+ marginVertical: 20,
+ },
+ profileImageContainer: {
+ marginBottom: 10,
+ },
+ profileImage: {
+ width: 100,
+ height: 100,
+ borderRadius: 50,
+ },
+ username: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: Colors.text,
+ },
+ bio: {
+ fontSize: 16,
+ color: Colors.text,
+ textAlign: 'center',
+ marginHorizontal: 20,
+ },
+ statsContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ marginVertical: 20,
+ },
+ stat: {
+ alignItems: 'center',
+ },
+ statCount: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: Colors.text,
+ },
+ statLabel: {
+ fontSize: 14,
+ color: Colors.text,
+ },
+ habitSection: {
+ marginBottom: 20,
+ },
+ habitTitle: {
+ fontSize: 18,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginBottom: 10,
+ marginLeft: 10,
+ },
+ gridImage: {
+ width: imageSize - 10,
+ height: imageSize - 10,
+ margin: 5,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
+ },
+ modalContent: {
+ width: '90%',
+ backgroundColor: Colors.cardBackground,
+ borderRadius: 10,
+ padding: 16,
+ alignItems: 'center',
+ },
+ fullImage: {
+ width: '100%',
+ height: Dimensions.get('window').width,
+ borderRadius: 10,
+ marginBottom: 16,
+ },
+ imageDescription: {
+ color: Colors.white,
+ marginBottom: 16,
+ },
+});
+
+export default UserProfile;
diff --git a/src/screens/checklist/Checklist.js b/src/screens/checklist/Checklist.js
new file mode 100644
index 0000000..5765635
--- /dev/null
+++ b/src/screens/checklist/Checklist.js
@@ -0,0 +1,336 @@
+import React, { useState, useCallback } from 'react';
+import {
+ View,
+ StyleSheet,
+ Text,
+ Alert,
+ FlatList,
+ TouchableOpacity,
+ Dimensions,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+import { Button } from 'react-native-elements';
+import { useNavigation, useFocusEffect } from '@react-navigation/native';
+import store from '../../store/storeConfig';
+import { supabase } from '../../config/supabaseClient';
+import moment from 'moment';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+
+const Checklist = () => {
+ const session = store.getState().user.session;
+ const navigation = useNavigation();
+ const [schedules, setSchedules] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [crossedOff, setCrossedOff] = useState({});
+ const [currentDay, setCurrentDay] = useState(moment().format('YYYY-MM-DD'));
+
+ useFocusEffect(
+ useCallback(() => {
+ fetchSchedules();
+ }, [currentDay])
+ );
+
+
+ // helper function determine the active day
+const getDayIndex = (day) => {
+ switch (day) {
+ case 'Sunday': return 0;
+ case 'Monday': return 1;
+ case 'Tuesday': return 2;
+ case 'Wednesday': return 3;
+ case 'Thursday': return 4;
+ case 'Friday': return 5;
+ case 'Saturday': return 6;
+ default: return -1;
+ }
+};
+
+const fetchSchedules = async () => {
+ try {
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .select('*')
+ .eq('user_id', session?.user.id);
+
+ console.log('Fetched schedule data:', scheduleData);
+
+ if (scheduleError) {
+ throw scheduleError;
+ }
+
+ if (scheduleData) {
+ const habitIds = scheduleData.map(schedule => schedule.habit_id);
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .select('*')
+ .in('habit_id', habitIds);
+
+ console.log('Fetched habit data:', habitData);
+
+ if (habitError) {
+ throw habitError;
+ }
+
+ if (habitData) {
+ const dayIndex = getDayIndex(moment(currentDay).format('dddd'));
+ const combinedData = scheduleData.map(schedule => {
+ const habit = habitData.find(h => h.habit_id === schedule.habit_id);
+ return {
+ ...schedule,
+ habit_title: habit?.habit_title,
+ habit_description: habit?.habit_description,
+ habit_photo: habit?.habit_photo,
+ is_active_today: (schedule.schedule_active_days & (1 << dayIndex)) !== 0,
+ };
+ }).filter(schedule => schedule.is_active_today);
+
+ setSchedules(combinedData);
+ console.log('Combined data:', combinedData);
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching schedules or habits:', error);
+ Alert.alert('Error fetching schedules or habits', error.message);
+ } finally {
+ setLoading(false);
+ }
+};
+
+
+
+
+
+ const addHabit = () => {
+ navigation.navigate('AddHabit');
+ };
+
+ const changeDay = (type) => {
+ let newDay = moment(currentDay);
+
+ if (type === 'sub') {
+ newDay = newDay.subtract(1, 'day');
+ }
+
+ if (type === 'add') {
+ newDay = newDay.add(1, 'day');
+ }
+
+ setCurrentDay(newDay.format('YYYY-MM-DD'));
+ };
+
+ const crossOffHabit = (habitId) => {
+ setCrossedOff(prev => ({
+ ...prev,
+ [currentDay]: {
+ ...prev[currentDay],
+ [habitId]: !prev[currentDay]?.[habitId]
+ }
+ }));
+ };
+
+ const handleCrossOff = (habitId) => {
+ Alert.alert(
+ "Cross off Habit",
+ "Would you like to cross this off your list today?",
+ [
+ {
+ text: "Cancel",
+ style: "cancel"
+ },
+ {
+ text: "Yes",
+ onPress: () => crossOffHabit(habitId)
+ }
+ ]
+ );
+ };
+
+ const renderSchedule = ({ item }) => (
+ handleCrossOff(item.habit_id)}
+ style={styles.scheduleItem}
+ >
+
+ {item.habit_title || 'N/A'}
+
+
+ {item.schedule_state === 'Open' ? 'ACTIVE' : 'INACTIVE'}
+
+
+ );
+
+ const rightContent = (
+
+
+
+ );
+
+ const leftContent = (
+
+
+
+ );
+
+ const headerComponent = (
+ <>
+ {/* */}
+
+ {/* What can we improve in */}
+ Daily Checklist
+
+
+
+ changeDay('sub')}>
+
+
+
+ {moment().isSame(currentDay, 'day') ? 'Today' : moment(currentDay).format('DD MMMM')}
+
+ changeDay('add')}>
+
+
+
+
+ >
+ );
+
+ return (
+
+
+ index.toString()}
+ renderItem={renderSchedule}
+ ListHeaderComponent={headerComponent}
+ contentContainerStyle={styles.list}
+ ListEmptyComponent={
+
+
+
+ }
+ />
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: Colors.background,
+ paddingHorizontal: 20,
+ },
+ headerContainer: {
+ alignItems: 'center',
+ marginVertical: 10,
+ },
+ text1: {
+ fontSize: 24,
+ color: Colors.text,
+ fontWeight: '400',
+ },
+ text2: {
+ fontSize: 32,
+ color: Colors.text,
+ fontWeight: '700',
+ },
+ separatorSubheader: {
+ borderBottomColor: 'rgba(156,198,255,0.2)',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ marginVertical: 10,
+ },
+ subheaderContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ width: '100%',
+ paddingHorizontal: 20,
+ },
+ textDate: {
+ color: Colors.text,
+ fontSize: 20,
+ fontWeight: '400',
+ paddingVertical: 10,
+ },
+ list: {
+ paddingVertical: 10,
+ },
+ scheduleItem: {
+ paddingVertical: 22,
+ paddingHorizontal: 22,
+ backgroundColor: Colors.primary6,
+ marginVertical: 10,
+ borderRadius: 8,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 3,
+ },
+ shadowOpacity: 0.27,
+ shadowRadius: 4.65,
+ elevation: 6,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ habitTitle: {
+ fontSize: 18,
+ color: Colors.primary5,
+ },
+ habitStatus: {
+ fontSize: 14,
+ },
+ crossedOff: {
+ textDecorationLine: 'line-through',
+ color: Colors.white,
+ },
+ buttonContainer: {
+ marginTop: 50,
+ width: '100%',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ },
+ containerCheckItemRight: {
+ backgroundColor: Colors.primary4,
+ paddingHorizontal: 22,
+ flex: 1,
+ justifyContent: 'center',
+ },
+ containerCheckItemLeft: {
+ backgroundColor: Colors.secondary3,
+ paddingHorizontal: 22,
+ flex: 1,
+ justifyContent: 'center',
+ },
+ icon: {
+ paddingHorizontal: 10,
+ },
+});
+
+export default Checklist;
diff --git a/src/screens/community/Community.js b/src/screens/community/Community.js
new file mode 100644
index 0000000..e937270
--- /dev/null
+++ b/src/screens/community/Community.js
@@ -0,0 +1,572 @@
+import React, { useState, useEffect, useRef, useCallback } from "react";
+import {
+ View,
+ StyleSheet,
+ Alert,
+ Dimensions,
+ Text,
+ FlatList,
+ ScrollView,
+ SafeAreaView,
+ Keyboard,
+ TouchableOpacity,
+ RefreshControl,
+ ActivityIndicator,
+} from "react-native";
+import Default from "../../../assets/styles/Default";
+import Colors from "../../../assets/styles/Colors";
+import Fetching from "../../components/Fetching";
+import CardCommunity from "../../components/community/CardCommunity";
+import { searchCommunity, getByCategory } from "../../store/ducks/community";
+import { TextInput } from "react-native-paper";
+// import { useSelector } from "react-redux";
+import { LinearGradient } from "expo-linear-gradient";
+import debounce from "lodash.debounce";
+import { Button } from "react-native-elements";
+
+const Community = (props) => {
+ const [fetching, setFetching] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [load_more, setLoadMore] = useState(true);
+ const [category, setCategory] = useState("");
+ const [communities, setCommunities] = useState([]);
+ const [section_community, setSectionCommunity] = useState([]);
+ const [see_all, setSeeAll] = useState(false);
+ const [page, setPage] = useState(0);
+ const [search, setSearch] = useState("");
+// const user = useSelector(({ user }) => user);
+
+ const RBSDelete = useRef();
+
+ useEffect(() => {
+ getCommunities(search, true, false);
+
+ setPage(0);
+ setSeeAll(false);
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = props.navigation.addListener("focus", () => {
+ if (see_all) {
+ fetchCategory(search, category);
+ return;
+ }
+
+ getCommunities("", false, false, false);
+ });
+
+ return unsubscribe;
+ }, [
+ props.navigation,
+ search,
+ category,
+ see_all,
+ fetchCategory,
+ getCommunities,
+ ]);
+
+ const getCommunities = (filter, is_fetching, is_refreshing, force) => {
+ if (filter === "" || force) {
+ is_refreshing
+ ? setRefreshing(true)
+ : is_fetching
+ ? setFetching(true)
+ : null;
+
+ searchCommunity({ search: filter })
+ .catch((err) => {
+ Alert.alert(
+ "Ops!",
+ "Something went wrong with our servers. Please contact us.",
+ );
+ })
+ .then((res) => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert("Ops!", res.data.errors[0]);
+ } else {
+ setCommunities(res.data);
+ }
+ }
+ })
+ .finally(() => {
+ setRefreshing(false);
+ setFetching(false);
+ });
+ }
+ };
+
+ const onSearch = useCallback(
+ debounce((filter) => {
+ if (see_all) {
+ fetchCategory(filter, category);
+ } else {
+ getCommunities(filter, false, false, true);
+ }
+ }, 700),
+ [],
+ );
+
+ const onChangeSearchText = (filter) => {
+ setSearch(filter);
+ onSearch(filter);
+ };
+
+ const addCommunity = () => {
+ props.navigation.navigate("CreateCommunity");
+ };
+
+ const verifySession = (session) => {
+ return session === 0
+ ? "my_communities"
+ : session === 1
+ ? "public_communities"
+ : session === 2
+ ? "private_communities"
+ : null;
+ };
+
+ const formatTextCategory = (text) => {
+ return text === "my_communities"
+ ? "My Communities"
+ : text === "public_communities"
+ ? "Public Communities"
+ : text === "private_communities"
+ ? "Private Communities"
+ : null;
+ };
+
+ const fetchCategory = (search, category) => {
+ setFetching(true);
+
+ let request = {
+ page: 0,
+ search: search,
+ category: category,
+ };
+
+ getByCategory(request)
+ .catch((err) => {
+ Alert.alert(
+ "Ops!",
+ "Something went wrong with our servers. Please contact us.",
+ );
+ })
+ .then((res) => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert("Ops!", res.data.errors[0]);
+ } else {
+ res.data.data.title = formatTextCategory(category);
+ setSectionCommunity(res.data.data);
+ }
+
+ if (res.data.current_page === res.data.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ } else {
+ setLoadMore(true);
+ }
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ });
+ };
+
+ const viewSectionCommunity = (session) => {
+ let option = verifySession(session);
+ setCategory(option);
+ setSeeAll(!see_all);
+ fetchCategory(search, option);
+ };
+
+ const handleDismiss = () => {
+ setSeeAll(!see_all);
+ setSectionCommunity([]);
+ getCommunities(search, true, false);
+ };
+
+ const loadMore = () => {
+ let community_aux = [];
+ let number_page = page + 1;
+
+ setPage(number_page);
+ setLoading(true);
+
+ let request = {
+ page: number_page,
+ search: search,
+ category: category,
+ };
+
+ getByCategory(request)
+ .catch((err) => {
+ Alert.alert(
+ "Ops!",
+ "Something went wrong with our servers. Please contact us.",
+ );
+ })
+ .then((res) => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert("Ops!", res.data.errors[0]);
+ } else {
+ community_aux = [...section_community];
+ community_aux = community_aux.concat(res.data.data);
+ community_aux.title = formatTextCategory(category);
+
+ setSectionCommunity(community_aux);
+
+ if (res.data.current_page === res.data.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ }
+ }
+ }
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ const inputSearchTheme = {
+ colors: {
+ primary: Colors.primary4,
+ text: "#FFFFFF",
+ placeholder: Colors.primary4,
+ },
+ fonts: {
+ regular: {
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: "normal",
+ },
+ },
+ roundness: 8,
+ };
+
+ const isCloseToBottom = ({
+ layoutMeasurement,
+ contentOffset,
+ contentSize,
+ }) => {
+ const paddingToBottom = 10;
+ return (
+ layoutMeasurement.height + contentOffset.y >=
+ contentSize.height - paddingToBottom
+ );
+ };
+
+ return (
+
+ {
+ if (
+ see_all &&
+ load_more &&
+ section_community.length > 0 &&
+ isCloseToBottom(nativeEvent) &&
+ !loading
+ ) {
+ loadMore();
+ }
+ }}
+ style={styles.container}
+ refreshControl={
+ getCommunities("", false, true, false)}
+ refreshing={refreshing}
+ />
+ }
+ >
+
+
+
+ Community
+
+
+
+
+
+
+
+
+
+ }
+ theme={inputSearchTheme}
+ onChangeText={(e) => onChangeSearchText(e)}
+ blurOnSubmit={false}
+ />
+
+
+ {!see_all ? (
+ communities.length > 0 ? (
+ communities.map((community, i) => {
+ return (
+
+
+
+ {community.title}
+
+ {community.data.length > 0 ? (
+ viewSectionCommunity(i)}
+ >
+
+ See All
+
+
+ ) : null}
+
+
+ {community.data.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ snapToAlignment={"start"}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+
+ {community.title === "My Communities"
+ ? "You don't have any communities yet"
+ : community.title === "Public Communities"
+ ? "We don't have any public communities yet."
+ : community.title === "Private Communities"
+ ? "We don't have any private communities yet"
+ : null}
+
+
+ )}
+
+ );
+ })
+ ) : null
+ ) : (
+
+
+
+ {section_community.title}
+
+
+
+ Dismiss
+
+
+
+
+
+ {section_community.length > 0 ? (
+ section_community.map((obj, i) => {
+ return (
+
+ );
+ })
+ ) : (
+
+
+ No communities found in this section
+
+
+ )}
+
+
+ {loading ? (
+
+
+
+ ) : null}
+
+ )}
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ //flex: 2,
+ width: Dimensions.get("window").width,
+ },
+ containerList: {
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerCom: {
+ flex: 1,
+ flexDirection: "row",
+ flexWrap: "wrap",
+ alignItems: "flex-start",
+ justifyContent: "space-between",
+ marginBottom: 16,
+ marginRight: 16,
+ },
+ containerViewSection: {
+ flex: 1,
+ paddingTop: 16,
+ paddingBottom: 32,
+ zIndex: 1,
+ elevation: 1,
+ },
+ textNotCommunities: {
+ fontSize: 12,
+ lineHeight: 16,
+ height: 16,
+ fontWeight: "bold",
+ color: Colors.text,
+ marginLeft: 16,
+ },
+ containerSearch: {
+ flexDirection: "row",
+ width: Dimensions.get("window").width,
+ alignSelf: "center",
+ },
+ containerButton: {
+ justifyContent: "flex-end",
+ alignItems: "center",
+ marginBottom: 8,
+ //marginHorizontal: 4,
+ },
+ inputSearch: {
+ flex: 1,
+ backgroundColor: "#002544",
+ borderRadius: 8,
+ marginHorizontal: 24,
+ marginBottom: 16,
+ height: 64,
+ },
+ containerHeaderTitle: {
+ alignSelf: "center",
+ },
+ containerSeeAll: {
+ flexDirection: "row",
+ justifyContent: "flex-end",
+ alignSelf: "flex-end",
+ marginLeft: 16,
+ zIndex: 4,
+ elevation: 4,
+ position: "absolute",
+ },
+ textName: {
+ fontSize: 24,
+ color: Colors.text,
+ alignSelf: "center",
+ lineHeight: 32,
+ },
+ image: {
+ alignSelf: "center",
+ height: 121,
+ marginLeft: 4,
+ marginBottom: 0,
+ },
+ containerHeader: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "center",
+ marginBottom: 18,
+ marginTop: 16,
+ },
+ textSeeAll: {
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ alignSelf: "flex-end",
+ marginRight: 16,
+ },
+ textEmpty: {
+ alignSelf: "flex-start",
+ marginRight: 16,
+ marginTop: 16,
+ },
+ sectionHeader: {
+ fontWeight: "700",
+ fontSize: 16,
+ lineHeight: 16,
+ color: "#FCFCFC",
+ marginLeft: 16,
+ zIndex: 2,
+ elevation: 2,
+ },
+});
+
+export default Community;
diff --git a/src/screens/community/CreateCommunity.js b/src/screens/community/CreateCommunity.js
new file mode 100644
index 0000000..dfed03a
--- /dev/null
+++ b/src/screens/community/CreateCommunity.js
@@ -0,0 +1,2107 @@
+import React, { useState, useEffect, useRef } from "react";
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ RefreshControl,
+ ScrollView,
+ Image,
+ SafeAreaView,
+ TouchableOpacity,
+ Modal,
+ Pressable,
+ Alert,
+ TextInput,
+ Keyboard,
+ Platform,
+} from "react-native";
+import { TextInput as TextInputPaper } from "react-native-paper";
+import Default from "../../../assets/styles/Default";
+import Colors from "../../../assets/styles/Colors";
+import Fetching from "../../components/Fetching";
+import Icon from "react-native-vector-icons/FontAwesome5";
+// import { useSelector } from "react-redux";
+import { LinearGradient } from "expo-linear-gradient";
+import * as ImagePicker from "expo-image-picker";
+import * as mime from "react-native-mime-types";
+import { Button, Input } from "react-native-elements";
+// import { listConnection } from "../../store/ducks/connection";
+import { store as storeCommunity } from "../../store/ducks/community";
+import RBSheet from "react-native-raw-bottom-sheet";
+import Header from "../../components/Header";
+import ActionSheet from "react-native-actionsheet";
+import { BlurView } from "expo-blur";
+import { Modalize } from "react-native-modalize";
+import { systemWeights } from "react-native-typography";
+import { takeCamera, takeGaleria } from "../../utils/Utils";
+
+const CreateCommunity = (props) => {
+ const [fetching, setFetching] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [step, setStep] = useState(1);
+
+ const [name, setName] = useState("");
+ const [description, setDescription] = useState("");
+ const [communityPhoto, setCommunityPhoto] = useState(null);
+
+ const [modal, setModal] = useState(false);
+ const [modalSuccess, setModalSuccess] = useState(false);
+ const [modal_type, setModalType] = useState("");
+
+ const [title, setTitle] = useState("");
+ const [ruleDescription, setRuleDescription] = useState("");
+ const [rules, setRules] = useState([]);
+
+ const [tagTitle, setTagTitle] = useState("");
+ const [tags, setTags] = useState([]);
+
+ const [community, setCommunity] = useState({});
+ const [privacy, setPrivacy] = useState(0);
+
+ const [viewSearch, setViewSearch] = useState(false);
+ const [list_connections, setListConnections] = useState([]);
+ const [list_invites, setListInvites] = useState([]);
+ const [list_aux_connections, setListAuxConnections] = useState([]);
+ const [search, setSearch] = useState("");
+ const [focusSearch, setFocusSearch] = useState(false);
+
+ const [index, setIndex] = useState(null);
+ const [permission, setPermission] = useState(null);
+ const [flag_admin, setFlagAdmin] = useState(false);
+ const [user_selected, setUserSelected] = useState({});
+ const [list_admins_moderators, setListAdminsModerators] = useState([]);
+ const modalizeRef = useRef(null);
+
+ const [remove, setRemove] = useState(null);
+ const [index_remove, setIndexRemove] = useState(null);
+
+// const user = useSelector(({ user }) => user);
+ const ASPhotoOptions = useRef();
+
+ const RBSDelete = useRef();
+ const RBSExit = useRef();
+
+// useEffect(() => {
+// fetchConnections(false, true, false);
+// }, []);
+
+// useEffect(() => {
+// let init_admin = [...list_admins_moderators];
+// user.permission = 0;
+// let check = false;
+
+// let found = init_admin.find((element) => element === user);
+
+// // !found ? init_admin.push(user) : null;
+// setListAdminsModerators(init_admin);
+// }, [step === 3]);
+
+ const fetchConnections = async (isRefresh, is_fetching, force) => {
+ if (search === "" || force) {
+ isRefresh ? setRefreshing(true) : is_fetching ? setFetching(true) : null;
+
+ await listConnection()
+ .catch((err) => {
+ Alert.alert(
+ "Ops!",
+ "Something went wrong with our servers. Please contact us.",
+ );
+ })
+ .then((res) => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert("Ops!", res.data.errors[0]);
+ } else {
+ setListConnections(res.data);
+ setListAuxConnections(res.data);
+ }
+ }
+ });
+
+ setRefreshing(false);
+ setFetching(false);
+ }
+ };
+
+ const createCommunity = () => {
+ setLoading(true);
+ let communityForm = new FormData();
+
+ communityForm.append("com_name", name);
+ communityForm.append("com_description", description);
+ communityForm.append("com_private", privacy);
+ communityForm.append("com_members", JSON.stringify(list_invites));
+ communityForm.append(
+ "com_admins_moderators",
+ JSON.stringify(list_admins_moderators),
+ );
+ communityForm.append("com_rules", JSON.stringify(rules));
+ communityForm.append("com_tags", JSON.stringify(tags));
+ communityForm.append("com_image", communityPhoto);
+
+ storeCommunity(communityForm)
+ .catch((err) => {
+ Alert.alert(
+ "Ops!",
+ "Something went wrong with our servers. Please contact us.",
+ );
+ })
+ .then((res) => {
+ if (res?.status === 200) {
+ if (res?.data?.errors) {
+ Alert.alert("Ops!", res.data.errors[0]);
+ } else {
+ setCommunity(res.data);
+ setLoading(false);
+ setModalSuccess(true);
+ }
+ }
+ });
+ };
+
+ const next = () => {
+ if (name === "") {
+ Alert.alert("Oops!", "You need insert name.");
+ return;
+ }
+
+ if (description === "") {
+ Alert.alert("Oops!", "You need to insert a description.");
+ return;
+ }
+
+ if (communityPhoto === null) {
+ Alert.alert("Oops!", "You need add a photo before continue.");
+ return;
+ }
+
+ setModalSuccess(!modalSuccess);
+ };
+
+ const addRule = () => {
+ if (title === "" || ruleDescription === "") {
+ Alert.alert("Oops!", "You need fill in all fields.");
+ return;
+ }
+
+ let rule = {};
+ let aux_rules = [];
+
+ rule.title = title;
+ rule.description = ruleDescription;
+
+ aux_rules = rules;
+ aux_rules.push(rule);
+ setRules(aux_rules);
+
+ setModal(!modal);
+ setTitle("");
+ setRuleDescription("");
+ };
+
+ const handleActionSheet = async (index) => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+
+ if (status == "granted") {
+ pickCamera();
+ } else {
+ Alert.alert("Ops", "You need to allow access to the camera first.");
+
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+
+ if (status === "granted") {
+ pickGaleria();
+ } else {
+ Alert.alert("Ops", "You need to allow access to the camera first.");
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await takeCamera();
+
+ if (result === "failed") {
+ Alert.alert("Ops", "An error ocurred when trying to open the camera.");
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const pickGaleria = async () => {
+ let result = await takeGaleria();
+
+ if (result === "failed") {
+ Alert.alert("Ops", "An error ocurred when trying to open the library.");
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const setAnexos = (foto) => {
+ let auxFoto = { ...foto };
+ auxFoto.url = foto.uri;
+
+ setCommunityPhoto(auxFoto);
+ };
+
+ const addTag = () => {
+ if (title === "") {
+ Alert.alert("Oops!", "You need fill in all fields.");
+ return;
+ }
+
+ let tag = {};
+ let aux_tags = [];
+
+ tag.title = title;
+
+ aux_tags = tags;
+ aux_tags.push(tag);
+ setTags(aux_tags);
+
+ setModal(!modal);
+ setTitle("");
+ };
+
+ const inputSearchTheme = {
+ colors: {
+ primary: Colors.primary4,
+ text: "#FFFFFF",
+ placeholder: Colors.primary4,
+ },
+ fonts: {
+ regular: {
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: "normal",
+ },
+ },
+ roundness: 8,
+ };
+
+ const handleConnect = (obj, index) => {
+ setFetching(true);
+
+ let aux_invites = [...list_invites];
+ let aux_connections = [...list_connections];
+
+ let position_list = aux_invites.indexOf(obj.connection);
+
+ if (position_list !== -1) {
+ aux_invites.splice(position_list, 1);
+
+ aux_connections.map((item, i) => {
+ if (item.id === obj.id) {
+ item.invited = false;
+ }
+ });
+ } else {
+ aux_invites.push(obj.connection);
+
+ aux_connections.map((item, i) => {
+ if (item.id === obj.id) {
+ item.invited = true;
+ }
+ });
+ }
+
+ setListInvites(aux_invites);
+ setListConnections(aux_connections);
+
+ setFetching(false);
+ };
+
+ const searchUsers = async (filter) => {
+ setFetching(true);
+
+ const user_filtered = list_aux_connections.filter((value) =>
+ value.connection.name.toLowerCase().includes(filter.toLowerCase()),
+ );
+ setListConnections(user_filtered);
+
+ setFetching(false);
+ };
+
+ const onChangeSearchText = (filter) => {
+ setSearch(filter);
+ searchUsers(filter);
+ };
+
+ const onChangePermission = (obj, index, admin = false) => {
+ setUserSelected(obj);
+ setIndex(index);
+ setFlagAdmin(admin);
+ modalizeRef.current?.open();
+ };
+
+ const handleChangePermission = (value) => {
+ setPermission(value);
+
+ if (flag_admin) {
+ list_admins_moderators.splice(index, 1);
+ }
+
+ user_selected.permission = value;
+
+ let aux_list_admins = list_admins_moderators;
+ aux_list_admins.push(user_selected);
+ list_invites.splice(index, 1);
+ setListAdminsModerators(aux_list_admins);
+
+ setPermission(null);
+ setUserSelected({});
+ setIndex(null);
+ setFlagAdmin(false);
+
+ modalizeRef.current?.close();
+ };
+
+ const onRemove = (section, i) => {
+ section === "rule" ? setRemove(0) : setRemove(1);
+ setIndexRemove(i);
+
+ RBSExit.current.open();
+ };
+
+ const doRemove = () => {
+ if (remove) {
+ let aux = [...tags];
+
+ aux.splice(index_remove, 1);
+ setTags(aux);
+ } else {
+ let aux = [...rules];
+
+ aux.splice(index_remove, 1);
+ setRules(aux);
+ }
+
+ RBSExit.current.close();
+ };
+
+ const removeConnection = () => {
+ if (list_admins_moderators.includes(user_selected)) {
+ list_admins_moderators.splice(index, 1);
+ } else {
+ list_invites.splice(index, 1);
+ }
+
+ setPermission(null);
+ setUserSelected({});
+ setIndex(null);
+ setFlagAdmin(false);
+
+ modalizeRef.current?.close();
+ };
+
+ return step === 1 ? (
+
+
+
+
+
+
+ {modal || modalSuccess ? (
+
+ ) : null}
+
+
+
+ {communityPhoto ? (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.communityImage}
+ >
+
+
+
+ Edit Photo
+
+
+ >
+ ) : (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.containerPhoto}
+ >
+
+ Add Photo
+
+ >
+ )}
+
+ props.navigation.pop()}
+ >
+
+
+
+
+ handleActionSheet(index)}
+ styles={{
+ buttonBox: Default.actionSheetButtonBox,
+ body: Default.actionSheetBody,
+ cancelButtonBox: Default.actionSheetCancelButtonBox,
+ }}
+ />
+
+
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ Privacy
+
+
+ setPrivacy(0)}>
+
+ Public
+ {privacy === 0 ? (
+
+ ) : (
+
+ )}
+
+
+
+ setPrivacy(1)}>
+
+ Private
+ {privacy === 1 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ Community Rules
+
+
+
+ Add Rule
+ {
+ setModal(!modal), setModalType("Rule");
+ }}
+ title={"New"}
+ />
+
+
+ {rules.length > 0
+ ? rules.map((obj, i) => {
+ return (
+
+
+ {i + 1 + ". " + obj.title}
+
+ onRemove("rule", i)}>
+
+
+
+ );
+ })
+ : null}
+
+
+ Community Tags
+
+
+
+ Add Tag
+ {
+ setModal(!modal), setModalType("Tag");
+ }}
+ title={"New"}
+ />
+
+
+ {tags.length > 0
+ ? tags.map((obj, i) => {
+ return (
+
+
+ {obj.title}
+
+ onRemove("tag", i)}>
+
+
+
+ );
+ })
+ : null}
+
+
+
+
+
+
+ {modal || modalSuccess ? (
+ setModal(!modal)}
+ style={styles.containerShadow}
+ >
+ ) : null}
+
+
+
+
+
+ Are you sure to remove this {remove ? "Tag ?" : "Rule ?"}
+
+
+
+
+
+
+ RBSExit.current.close()}
+ >
+
+
+ Cancel
+
+
+
+
+
+
+ {
+ setModal(!modal), setTitle(""), setRuleDescription("");
+ }}
+ >
+
+
+ {
+ setModal(!modal), setTitle(""), setRuleDescription("");
+ }}
+ >
+
+
+
+
+
+
+
+ {modal_type === "Rule" ? (
+
+
+ Rule Description
+
+
+
+ ) : null}
+
+
+
+
+
+
+
+ setModalSuccess(!modalSuccess)}
+ >
+
+
+
+ setModalSuccess(!modalSuccess)}
+ >
+
+
+
+
+
+
+ {privacy === 0
+ ? "Public community"
+ : "Private community"}
+
+
+
+
+
+ Your community has
+
+
+ been successfully created
+
+
+
+ {
+ setModalSuccess(!modalSuccess), setStep(2);
+ }}
+ >
+
+ Invite Your Friends
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : step === 2 ? (
+
+ fetchConnections(true, false, false)}
+ refreshing={refreshing}
+ />
+ }
+ >
+
+ {
+ !focusSearch ? (
+
+ props.navigation.pop()}
+ >
+
+
+
+
+
+ Invite your Connections
+
+
+
+ ) : null
+ //
+ }
+
+ setFocusSearch(true)}
+ onBlur={() => setFocusSearch(search === "" ? false : true)}
+ left={}
+ theme={inputSearchTheme}
+ onChangeText={(e) => onChangeSearchText(e)}
+ blurOnSubmit={false}
+ />
+
+
+
+ {list_connections.length > 0 ? (
+
+
+
+
+ {focusSearch ? "Search result" : "Connections"}
+
+
+ {list_connections.map((obj, i) => {
+ return (
+
+
+
+ {obj.connection?.image ? (
+
+ ) : (
+
+ )}
+
+ {obj.connection.name}
+
+ {obj.invited ? (
+ handleConnect(obj, i)}
+ title={"Undo"}
+ />
+ ) : (
+ handleConnect(obj, i)}
+ title={"Invite"}
+ />
+ )}
+
+
+
+ );
+ })}
+
+
+ ) : (
+
+
+ No contacts in live timeless.
+
+
+ )}
+
+ setStep(3)}
+ title={"Next"}
+ />
+
+
+
+
+ ) : step === 3 ? (
+
+ fetchContacts(true, false, false)}
+ refreshing={refreshing}
+ />
+ }
+ >
+
+
+
+ setStep(2)}
+ >
+
+
+
+
+
+ Community Permissions
+
+
+
+
+ {modalSuccess ? (
+ <>
+
+ setModal(!modal)}
+ style={styles.containerShadowStep3}
+ >
+ >
+ ) : null}
+
+ {list_admins_moderators.length > 0 ? (
+
+
+
+
+ Admins and Moderatos
+
+
+ {list_admins_moderators.map((obj, i) => {
+ return (
+
+
+
+ {obj.image ? (
+
+ ) : (
+
+ )}
+
+
+ {obj.name}
+
+
+ {obj.permission === 0 ? "Admin" : "Moderator"}
+
+
+ onChangePermission(obj, i, true)}
+ >
+
+
+
+
+
+ );
+ })}
+
+
+ ) : null}
+
+ {list_invites.length > 0 ? (
+
+
+
+ Connections
+
+ {list_invites.map((obj, i) => {
+ return (
+
+
+
+ {obj.image ? (
+
+ ) : (
+
+ )}
+
+ {obj.name}
+
+ onChangePermission(obj, i)}
+ >
+
+
+
+
+
+ );
+ })}
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+ {user_selected.image ? (
+
+ ) : (
+
+ )}
+ {user_selected.name}
+
+
+
+ handleChangePermission(0)}
+ style={styles.containerOptionPrivacyStep3}
+ >
+ ADD AS ADMIN
+ {permission === 0 ? (
+
+ ) : (
+
+ )}
+
+
+ handleChangePermission(1)}
+ style={styles.containerOptionPrivacyStep3}
+ >
+ ADD AS MODERATOR
+ {permission === 1 ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+ {
+ setModalSuccess(!modalSuccess),
+ props.navigation.push("FeedCommunity", {
+ community: { id: community.id },
+ });
+ }}
+ >
+
+
+
+ {
+ setModalSuccess(!modalSuccess),
+ props.navigation.push("FeedCommunity", {
+ community: { id: community.id },
+ });
+ }}
+ >
+
+
+
+
+
+
+ {privacy === 0 ? "Public community" : "Private community"}
+
+
+
+
+
+ Your community has been successfully created
+
+
+
+
+
+
+
+ ) : null;
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get("window").width,
+ zIndex: 1,
+ elevation: 1,
+ marginTop: -58,
+ },
+ containerBody: {
+ flex: 1,
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "flex-start",
+ paddingHorizontal: 24,
+ zIndex: 4,
+ elevation: 4,
+ },
+ containerBlur: {
+ zIndex: 1,
+ elevation: 1,
+ position: "absolute",
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ marginTop: -3,
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: "flex-end",
+ width: Dimensions.get("window").width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerPhoto: {
+ flex: 1,
+ flexDirection: "column",
+ alignSelf: "center",
+ justifyContent: "flex-start",
+ alignItems: "center",
+ zIndex: 3,
+ elevation: 3,
+ marginTop: 56,
+ },
+ containerShadow: {
+ backgroundColor: "rgba(0,0,0,0.6)",
+ zIndex: 5,
+ elevation: 5,
+ position: "absolute",
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ communityPhoto: {
+ width: 50,
+ height: 50,
+ },
+ circleIcon: {
+ width: 26,
+ height: 26,
+ },
+ containerTitle: {
+ paddingHorizontal: 24,
+ paddingTop: 16,
+ },
+ containerTitleOptions: {
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: "#264261",
+ width: Dimensions.get("window").width - 48,
+ paddingBottom: 16,
+ marginBottom: 16,
+ },
+ containerHeaderModal: {
+ flexDirection: "row",
+ alignSelf: "flex-end",
+ marginBottom: 12,
+ },
+ containerList: {
+ marginBottom: 24,
+ paddingHorizontal: 12,
+ paddingTop: 12,
+ backgroundColor: "rgba(0, 37, 68, 0.15)",
+ width: Dimensions.get("window").width - 48,
+ borderRadius: 8,
+ },
+ containerOptionPrivacy: {
+ marginBottom: 8,
+ paddingLeft: 16,
+ paddingRight: 22,
+ paddingVertical: 24,
+ backgroundColor: "rgba(0, 37, 68, 0.15)",
+ width: Dimensions.get("window").width - 48,
+ borderRadius: 8,
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+ containerButton: {
+ justifyContent: "flex-end",
+ alignItems: "center",
+ marginBottom: 16,
+ },
+ containerSectionModal: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 80,
+ zIndex: 5,
+ elevation: 5,
+ },
+ containerSectionSuccessModal: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 76,
+ },
+ centeredView: {
+ flex: 1,
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ //paddingHorizontal: 16,
+ //paddingVertical: 32,
+ },
+ modalView: {
+ borderRadius: 4,
+ marginHorizontal: 32,
+ alignItems: "flex-start",
+ shadowColor: "#000",
+ paddingBottom: 50,
+ paddingTop: 16,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ width: Dimensions.get("window").width - 44,
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: "#082139",
+ },
+ buttonContainer: {
+ marginBottom: 30,
+ alignItems: "center",
+ },
+ containerBottomSheet: {
+ alignItems: "center",
+ backgroundColor: Colors.primary,
+ },
+ containerTextBottomSheet: {
+ flex: 1,
+ alignItems: "center",
+ justifyContent: "center",
+ },
+ createAccountText: {
+ fontSize: 14,
+ color: "white",
+ },
+ warningIconStyle: {
+ width: 80,
+ height: 80,
+ },
+ textExit: {
+ marginTop: 26,
+ fontSize: 14,
+ color: Colors.text,
+ },
+ containerTextModal: {
+ alignContent: "center",
+ },
+ textInputStyle: {
+ color: Colors.primary4,
+ borderColor: "#455c8a",
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ fontSize: 16,
+ paddingHorizontal: 12,
+ paddingTop: 13,
+ height: 96,
+ backgroundColor: Colors.primary,
+ textAlignVertical: "top",
+ marginBottom: 26,
+ marginTop: 12,
+ },
+ textDescriptionStyle: {
+ color: Colors.text,
+ textAlignVertical: "top",
+ },
+ nextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: "#982538",
+ width: Dimensions.get("window").width - 48,
+ },
+ modalButton: {
+ height: 56,
+ borderRadius: 4,
+ backgroundColor: "#982538",
+ },
+ modalInput: {
+ borderColor: "#455c8a",
+ color: Colors.primary4,
+ fontSize: 16,
+ width: Dimensions.get("window").width - 80,
+ },
+ buttonRules: {
+ height: 32,
+ borderRadius: 4,
+ backgroundColor: "rgba(0, 75, 125, 1)",
+ width: 96,
+ alignItems: "flex-start",
+ paddingVertical: 7,
+ },
+ headerIcon: {
+ width: 18,
+ height: 18,
+ marginRight: 3,
+ },
+ titleButtonRule: {
+ fontSize: 13,
+ fontWeight: "700",
+ lineHeight: 18,
+ },
+ inviteText: {
+ fontSize: 16,
+ fontWeight: "700",
+ lineHeight: 24,
+ color: Colors.primary8,
+ },
+ backButton: {
+ marginLeft: 25,
+ marginTop: 69,
+ width: 60,
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ top: 0,
+ zIndex: 3,
+ elevation: 3,
+ position: "absolute",
+ },
+ addPhoto: {
+ marginLeft: 25,
+ marginTop: 32,
+ width: 60,
+ flexDirection: "row",
+ justifyContent: "flex-start",
+ top: 0,
+ zIndex: 3,
+ elevation: 3,
+ position: "absolute",
+ },
+ imageModalHeader: {
+ width: 24,
+ resizeMode: "contain",
+ height: 24,
+ },
+ typeCommunity: {
+ marginBottom: 16,
+ flexDirection: "row",
+ alignItems: "center",
+ alignSelf: "center",
+ borderRadius: 4,
+ padding: 9,
+ },
+ inputSearch: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ backgroundColor: "#002544",
+ borderRadius: 8,
+ marginHorizontal: 16,
+ marginBottom: 16,
+ },
+ textLabelModal: {
+ fontSize: 16,
+ fontWeight: "400",
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ textSuccessModal: {
+ textAlign: "center",
+ fontSize: 24,
+ fontWeight: "700",
+ lineHeight: 33,
+ color: Colors.text,
+ },
+ containerHeader: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "center",
+ marginBottom: 18,
+ marginTop: 16,
+ },
+ infoCommunity: {
+ height: 96,
+ },
+ textTitleSection: {
+ fontWeight: "700",
+ fontSize: 16,
+ lineHeight: 16,
+ color: "#FCFCFC",
+ },
+ textInfo: {
+ fontWeight: "400",
+ fontSize: 14,
+ lineHeight: 21,
+ color: "#FFFFFF",
+ },
+ textTitle: {
+ fontWeight: "700",
+ fontSize: 20,
+ lineHeight: 30,
+ color: "#FCFCFC",
+ },
+ textAddPhoto: {
+ fontWeight: "400",
+ fontSize: 16,
+ lineHeight: 19,
+ color: "#FCFCFC",
+ marginTop: 8,
+ },
+ typeCommunityText: {
+ fontWeight: "600",
+ fontSize: 13,
+ lineHeight: 16,
+ color: "#FCFCFC",
+ },
+ communityImage: {
+ width: "100%",
+ height: "100%",
+ position: "absolute",
+ },
+ containerStep2: {
+ flexDirection: "column",
+ flex: 1,
+ alignItems: "center",
+ width: Dimensions.get("window").width,
+ paddingHorizontal: 16,
+ zIndex: 0,
+ elevation: 0,
+ },
+ innerContainerStep2: {
+ flexDirection: "column",
+ flex: 1,
+ alignItems: "center",
+ },
+ containerActionsStep2: {
+ flexDirection: "row",
+ width: Dimensions.get("window").width - 48,
+ },
+ containerBlurStep2: {
+ flex: 1,
+ zIndex: 1,
+ elevation: 1,
+ position: "absolute",
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ containerButtonStep2: {
+ flex: 1,
+ justifyContent: "flex-end",
+ marginBottom: 32,
+ marginTop: 41,
+ alignItems: "center",
+ },
+ containerGroupStep2: {
+ flexDirection: "row",
+ marginBottom: 8,
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ containerPostStep2: {
+ flex: 1,
+ width: Dimensions.get("window").width - 48,
+ backgroundColor: "rgba(156, 198, 255, 0.042)",
+ borderRadius: 8,
+ paddingHorizontal: 16,
+ },
+ containerConnectionStep2: {
+ width: Dimensions.get("window").width - 32,
+ borderBottomColor: "#264261",
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ paddingBottom: 16,
+ marginBottom: 16,
+ },
+ containerNotConnectionsStep2: {
+ marginTop: 16,
+ alignSelf: "center",
+ },
+ textConnectionStep2: {
+ fontSize: 16,
+ lineHeight: 16,
+ width: 128,
+ height: 16,
+ fontWeight: "bold",
+ color: Colors.text,
+ width: Dimensions.get("window").width - 44,
+ },
+ titleInviteStep2: {
+ flex: 1,
+ flexDirection: "column",
+ alignSelf: "flex-start",
+ alignItems: "center",
+ },
+ textNoPeopleToConnectStep2: {
+ fontSize: 16,
+ lineHeight: 16,
+ fontWeight: "400",
+ color: Colors.text,
+ },
+ containerItemConnectionStep2: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ paddingVertical: 16,
+ },
+ textUserNameStep2: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ fontWeight: "700",
+ color: Colors.text,
+ },
+ userPhotoStep2: {
+ width: 32,
+ height: 32,
+ borderRadius: 32,
+ marginRight: 8,
+ },
+ containerSearchStep2: {
+ flexDirection: "row",
+ width: Dimensions.get("window").width - 32,
+ alignSelf: "center",
+ marginBottom: 16,
+ },
+ inputSearchStep2: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ backgroundColor: "#002544",
+ borderRadius: 8,
+ },
+ backButtonStyleStep2: {
+ width: 50,
+ height: 50,
+ marginTop: 5,
+ marginLeft: 24,
+ marginBottom: -22,
+ alignSelf: "flex-start",
+ },
+ textUserHeaderNameStep2: {
+ color: Colors.text,
+ fontWeight: "400",
+ fontSize: 20,
+ lineHeight: 27,
+ },
+ connectionButtonStep2: {
+ borderRadius: 8,
+ backgroundColor: "#004B7D",
+ width: 96,
+ paddingVertical: 7,
+ paddingHorizontal: 16,
+ },
+ textButtonConnectStep2: {
+ color: "#FCFCFC",
+ fontSize: 13,
+ fontWeight: "700",
+ lineHeight: 18,
+ },
+
+ containerStep3: {
+ flexDirection: "column",
+ flex: 1,
+ alignItems: "center",
+ width: Dimensions.get("window").width,
+ paddingHorizontal: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ innerContainerStep3: {
+ flexDirection: "column",
+ flex: 1,
+ alignItems: "center",
+ marginBottom: 24,
+ },
+ containerActionsStep3: {
+ flexDirection: "row",
+ width: Dimensions.get("window").width - 48,
+ marginTop: 0,
+ marginBottom: 16,
+ },
+ containerBlurStep3: {
+ zIndex: 1,
+ elevation: 1,
+ position: "absolute",
+ left: 0,
+ bottom: 0,
+ right: 0,
+ marginTop: -3,
+ },
+ containerButtonStep3: {
+ flex: 1,
+ justifyContent: "flex-end",
+ marginBottom: 32,
+ alignItems: "center",
+ },
+ containerShadowStep3: {
+ backgroundColor: "rgba(0,0,0,0.6)",
+ zIndex: 5,
+ elevation: 5,
+ position: "absolute",
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ containerOptionPrivacyStep3: {
+ marginBottom: 8,
+ paddingLeft: 16,
+ paddingRight: 22,
+ paddingVertical: 24,
+ backgroundColor: "#004B7D",
+ width: Dimensions.get("window").width - 48,
+ borderRadius: 4,
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+ containerTitleModalStep3: {
+ flex: 1,
+ alignSelf: "center",
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ marginTop: 40,
+ width: Dimensions.get("window").width - 48,
+ paddingBottom: 16,
+ },
+ textNameStep3: {
+ fontSize: 24,
+ color: Colors.text,
+ fontWeight: "400",
+ alignSelf: "center",
+ lineHeight: 32,
+ marginTop: 8,
+ },
+ centeredViewStep3: {
+ flex: 1,
+ flexDirection: "column",
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ modalViewStep3: {
+ borderRadius: 4,
+ marginHorizontal: 32,
+ alignItems: "flex-start",
+ shadowColor: "#000",
+ paddingBottom: 50,
+ paddingTop: 16,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ width: Dimensions.get("window").width - 44,
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: "#082139",
+ },
+ containerTextModalStep3: {
+ alignContent: "center",
+ },
+ containerSectionSuccessModalStep3: {
+ alignSelf: "center",
+ width: Dimensions.get("window").width - 76,
+ },
+ containerHeaderModalStep3: {
+ flexDirection: "column",
+ justifyContent: "center",
+ alignSelf: "flex-end",
+ marginLeft: 24,
+ },
+ containerModalStep3: {
+ flex: 1,
+ flexDirection: "column",
+ },
+ containerReportStep3: {
+ flexDirection: "column",
+ marginHorizontal: 24,
+ },
+ containerGroupStep3: {
+ flexDirection: "row",
+ marginBottom: 8,
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ containerUserStep3: {
+ flex: 1,
+ width: Dimensions.get("window").width - 48,
+ backgroundColor: "rgba(156, 198, 255, 0.042)",
+ borderRadius: 8,
+ paddingHorizontal: 16,
+ },
+ containerConnectionStep3: {
+ width: Dimensions.get("window").width - 32,
+ borderBottomColor: "#264261",
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ paddingBottom: 16,
+ marginBottom: 16,
+ },
+ modalizeStep3: {
+ backgroundColor: "rgba(0, 37, 68, 0.8)",
+ borderTopLeftRadius: 8,
+ borderTopRightRadius: 8,
+ },
+ containerNotConnectionsStep3: {
+ marginTop: 16,
+ alignSelf: "center",
+ },
+ textSuccessModalStep3: {
+ textAlign: "center",
+ fontSize: 24,
+ fontWeight: "700",
+ lineHeight: 33,
+ color: Colors.text,
+ },
+ typeCommunityStep3: {
+ marginBottom: 16,
+ flexDirection: "row",
+ alignItems: "center",
+ alignSelf: "center",
+ borderRadius: 4,
+ padding: 9,
+ },
+ headerIconStep3: {
+ width: 18,
+ height: 18,
+ marginRight: 3,
+ },
+ textSubtitleStep3: {
+ fontSize: 10,
+ lineHeight: 16,
+ width: 93,
+ height: 16,
+ color: Colors.text,
+ },
+ imageModalHeaderStep3: {
+ width: 24,
+ resizeMode: "contain",
+ height: 24,
+ },
+ typeCommunityTextStep3: {
+ fontWeight: "600",
+ fontSize: 13,
+ lineHeight: 16,
+ color: "#FCFCFC",
+ },
+ textConnectionStep3: {
+ fontSize: 16,
+ lineHeight: 16,
+ width: 128,
+ height: 16,
+ fontWeight: "bold",
+ color: Colors.text,
+ width: Dimensions.get("window").width - 44,
+ },
+ circleIconStep3: {
+ width: 26,
+ height: 26,
+ },
+ textTitleSectionStep3: {
+ fontWeight: "700",
+ fontSize: 16,
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ titleInviteStep3: {
+ flex: 1,
+ flexDirection: "column",
+ alignSelf: "flex-start",
+ alignItems: "center",
+ },
+ textNoPeopleToConnectStep3: {
+ fontSize: 16,
+ lineHeight: 16,
+ fontWeight: "400",
+ color: Colors.text,
+ },
+ containerItemConnectionStep3: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ paddingVertical: 16,
+ },
+ textUserNameStep3: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ fontWeight: "700",
+ color: Colors.text,
+ },
+ userPhotoStep3: {
+ width: 32,
+ height: 32,
+ borderRadius: 32,
+ marginRight: 8,
+ },
+ userPhotoModalStep3: {
+ width: 64,
+ height: 64,
+ borderRadius: 32,
+ },
+ inputSearchStep3: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ backgroundColor: "#002544",
+ borderRadius: 8,
+ },
+ backButtonStyleStep3: {
+ width: 50,
+ height: 50,
+ marginTop: 5,
+ marginLeft: 24,
+ marginBottom: -22,
+ alignSelf: "flex-start",
+ },
+ textUserHeaderNameStep3: {
+ color: Colors.text,
+ fontWeight: "400",
+ fontSize: 20,
+ lineHeight: 27,
+ },
+});
+
+export default CreateCommunity;
diff --git a/src/screens/community/ViewCommunity.js b/src/screens/community/ViewCommunity.js
new file mode 100644
index 0000000..d1b5dd4
--- /dev/null
+++ b/src/screens/community/ViewCommunity.js
@@ -0,0 +1,1295 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ RefreshControl,
+ ScrollView,
+ Image,
+ FlatList,
+ SectionList,
+ SafeAreaView,
+ Keyboard,
+ TouchableOpacity,
+ Alert,
+ Modal,
+ Share,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import moment from 'moment';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+// import {
+// get as getCommunity,
+// sendRequest as joinCommunity,
+// updateAutomaticPublish,
+// listHabits,
+// } from "../../store/ducks/community";
+import { useSelector } from 'react-redux';
+import { LinearGradient } from 'expo-linear-gradient';
+import { Button } from 'react-native-elements';
+import { BlurView } from 'expo-blur';
+import CardHabits from '../../components/community/CardHabits';
+
+const ViewCommunity = props => {
+ const [fetching, setFetching] = useState(false);
+ const [fetching_habits, setFetchingHabits] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+ const [modalSucess, setModalSucess] = useState(false);
+ const [modalSucessPrivate, setModalSucessPrivate] = useState(false);
+ const [publish_automatic, setPublishAutomatic] = useState(false);
+ const [community, setCommunity] = useState({});
+ const [admins_info, setAdminsInfo] = useState('');
+ const [moderators_info, setModeratorsInfo] = useState('');
+ const [option_bar, setOptionBar] = useState(2);
+ const [see_all, setSeeAll] = useState(false);
+ const user = useSelector(({ user }) => user);
+ const [list_habits, setListHabits] = useState('');
+
+ const RBSDelete = useRef();
+
+ useEffect(() => {
+ fetchCommunity(true, false);
+ }, [props]);
+
+ const fetchCommunity = (is_fetching, is_refreshing) => {
+ is_fetching
+ ? setFetching(true)
+ : is_refreshing
+ ? setRefreshing(true)
+ : null;
+
+ getCommunity(props.route.params.community.id)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setCommunity(res.data);
+
+ let admins = res.data?.admins_moderators.filter(
+ item => item.cme_role === 'ADMIN'
+ );
+ let moderators = res.data?.admins_moderators.filter(
+ item => item.cme_role === 'MODERATOR'
+ );
+
+ if (admins?.length === 1) {
+ setAdminsInfo(
+ admins[0]?.user?.name?.split(' ', 1) + ' is administrator. '
+ );
+ } else if (admins?.length > 1) {
+ let aux = admins
+ .map((obj, i) => {
+ return (
+ (i + 1 === admins.length
+ ? 'and ' + obj.user.name.split(' ', 1)
+ : obj.user.name.split(' ', 1)) +
+ (i + 2 === admins.length ? '' : ',')
+ );
+ })
+ .join(' ');
+
+ setAdminsInfo(aux.slice(0, -1) + ' are administrators. ');
+ }
+
+ if (moderators?.length === 1) {
+ setModeratorsInfo(
+ moderators[0]?.user?.name?.split(' ', 1) + ' is moderator. '
+ );
+ } else if (moderators?.length > 1) {
+ let aux = moderators
+ .map((obj, i) => {
+ return (
+ (i + 1 === moderators.length
+ ? 'and ' + obj.user.name.split(' ', 1)
+ : obj.user.name.split(' ', 1)) +
+ (i + 2 === moderators.length ? '' : ',')
+ );
+ })
+ .join(' ');
+
+ setModeratorsInfo(aux.slice(0, -1) + ' are moderators.');
+ }
+ }
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ setRefreshing(false);
+ });
+ };
+
+ useEffect(() => {
+ if (option_bar === 1) {
+ setFetchingHabits(true);
+ fetchHabits();
+ // return () => {
+ // setListPosts([]);
+ // }
+ }
+ }, [option_bar]);
+
+ const fetchHabits = () => {
+ listHabits(community.id)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setListHabits(res.data);
+ }
+ }
+ })
+ .finally(() => {
+ setFetchingHabits(false);
+ });
+ };
+
+ const handleJoinCommunity = () => {
+ let request = {
+ cme_id_community: community.id,
+ };
+
+ joinCommunity(request)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ community.com_private
+ ? setModalSucessPrivate(true)
+ : setModalSucess(true);
+ fetchCommunity(false, false);
+ }
+ }
+ })
+ .finally(() => {});
+ };
+
+ const shareCommunity = async () => {
+ const urlAndroid =
+ 'https://play.google.com/store/apps/details?id=com.alex.live.timeless';
+ const urlApple =
+ 'https://apps.apple.com/br/app/live-timeless/id1556115926?l=en';
+ const message =
+ 'Check my community ' +
+ community?.com_name +
+ ' on Live Timeless App.\nAndroid Link: ' +
+ urlAndroid +
+ '\nApple Link: ' +
+ urlApple;
+
+ try {
+ const result = await Share.share({
+ title: 'Join to my community in Live Timeless App',
+ message,
+ url: urlApple,
+ });
+ if (result.action === Share.sharedAction) {
+ if (result.activityType) {
+ // shared with activity type of result.activityType
+ } else {
+ }
+ } else if (result.action === Share.dismissedAction) {
+ // dismissed
+ }
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const inputSearchTheme = {
+ colors: {
+ primary: Colors.primary4,
+ text: '#FFFFFF',
+ placeholder: Colors.primary4,
+ },
+ fonts: {
+ regular: {
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: 'normal',
+ },
+ },
+ roundness: 8,
+ };
+
+ const handleNextModal = () => {
+ setRefreshing(true);
+ setModalSucess(false);
+ if (!community.com_private) {
+ props.navigation.push('FeedCommunity', {
+ community: { id: community.id, option: 3 },
+ });
+ }
+ setRefreshing(false);
+ };
+
+ const setAutomaticPost = value => {
+ setRefreshing(true);
+ setPublishAutomatic(value);
+
+ updateAutomaticPublish(community.id, { cme_automatic_posting: value })
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ }
+ }
+ })
+ .finally(() => {
+ setRefreshing(false);
+ });
+ setRefreshing(false);
+ };
+
+ return (
+
+ fetchCommunity(false, true)}
+ refreshing={refreshing}
+ />
+ }>
+
+
+
+
+
+
+
+ props.route.params?.backPop
+ ? props.navigation.pop()
+ : props.navigation.navigate('CommunityIndex')
+ }>
+
+
+
+ {community.com_private ? (
+
+
+
+ Private community
+
+
+ ) : null}
+
+
+
+ {community.com_name}
+
+
+ {community.members?.length > 0 && !community.com_private ? (
+
+ {community.members?.length > 0 ? (
+
+ {community.members.map((obj, i) => {
+ return obj.user?.image ? (
+
+ ) : (
+
+ );
+ })}
+
+
+ {community.members_count} members
+
+
+ ) : null}
+
+ {!community.com_private ? (
+
+ ) : null}
+
+ ) : null}
+
+
+ setOptionBar(1)}>
+ Habits
+
+
+ setOptionBar(2)}>
+ About
+
+
+
+
+ {community.user_member?.cme_approved === 0 &&
+ community.user_member?.cme_active ? (
+
+ ) : (
+
+ )}
+
+
+
+ {option_bar === 2 ? (
+ <>
+
+
+
+ {community.com_description}
+
+
+
+
+
+
+ {community.com_private ? 'Private' : 'Public'}
+
+
+
+ {community.com_private
+ ? 'Only members can see who is in the Community and what is published in it. '
+ : 'Anyone can see who is in the group and what is posted in it. '}
+
+
+
+
+
+ Visible
+
+
+ {community.com_private
+ ? 'Anyone can find the Community '
+ : 'Anyone can find the Community '}
+
+
+
+
+
+
+
+ Administrators' Rules
+
+ {community.rules?.length > 0
+ ? community.rules.map((rul, i) => {
+ return (
+
+
+ {i + 1}. {rul.cor_title}
+
+
+ {rul.cor_description}
+
+
+ );
+ })
+ : null}
+
+
+
+
+
+
+ Administrators and moderators
+
+
+ {community.admins_moderators?.length > 0
+ ? community.admins_moderators.map((obj, i) => {
+ return (
+
+ {obj.user.image ? (
+
+ ) : (
+
+ )}
+
+
+ {obj.user.name.split(' ', 1)}
+
+
+ {obj.cme_role}
+
+
+
+ );
+ })
+ : null}
+
+
+
+ {admins_info + moderators_info}
+
+
+
+
+
+
+
+
+ Community Activities
+
+
+
+
+ {community.post_count} new publications today
+
+
+
+
+ {community.last_month_post_count} publications in the
+ last month
+
+
+
+
+
+ Created{' '}
+ {moment().diff(
+ moment(community?.created_at).format(
+ 'YYYY-MM-DD'
+ ),
+ 'months'
+ )}{' '}
+ months ago
+
+
+
+
+
+
+ {community.user_member?.cme_approved === 0 &&
+ community.user_member?.cme_active ? (
+
+ ) : (
+
+ )}
+
+ >
+ ) : (
+
+
+
+ Daily Habits
+ {list_habits.length > 0 ? (
+ setSeeAll(!see_all)}>
+
+
+ {see_all ? 'Dismiss' : 'See All'}
+
+
+
+ ) : null}
+
+
+ {!see_all ? (
+ <>
+ {list_habits.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ scrollEnabled={!see_all}
+ snapToAlignment={'start'}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ numColumns={see_all ? 2 : 0}
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+
+ We don't have any Habits yet
+
+
+ )}
+ >
+ ) : (
+
+ {list_habits.length > 0
+ ? list_habits.map((obj, i) => {
+ return (
+
+
+
+ );
+ })
+ : null}
+
+ )}
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {community.com_private
+ ? 'Private community'
+ : 'Public community'}
+
+
+
+
+
+ Your application has
+
+
+ been approved!
+
+
+
+ setAutomaticPost(!publish_automatic)}
+ style={styles.containerAutomaticPublish}>
+ {publish_automatic ? (
+
+ ) : (
+
+ )}
+
+
+ Do you want to automatically publish your actions on
+ the community timeline?
+
+
+
+
+
+
+ Community Rules
+
+
+
+
+
+
+
+ setModalSucessPrivate(!modalSucessPrivate)
+ }>
+
+
+
+
+ setModalSucessPrivate(!modalSucessPrivate)
+ }>
+
+
+
+
+
+
+ {community.com_private
+ ? 'Private community'
+ : 'Public community'}
+
+
+
+
+
+ {community.com_name}
+
+
+
+
+
+
+ Your membership approval is pending. The group
+ administrators will review your request to join.{' '}
+
+
+
+
+
+
+
+
+
+
+
+ {modalSucess || modalSucessPrivate ? (
+ <>
+
+
+ >
+ ) : null}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get('window').width,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerBody: {
+ flex: 1,
+ alignItems: 'flex-start',
+ marginHorizontal: 4,
+ zIndex: 4,
+ elevation: 4,
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: 'flex-end',
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerTitle: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignSelf: 'center',
+ paddingVertical: 16,
+ width: Dimensions.get('window').width - 32,
+ },
+ containerList: {
+ marginBottom: 8,
+ paddingHorizontal: 16,
+ paddingTop: 16,
+ zIndex: 1,
+ elevation: 1,
+ width: Dimensions.get('window').width,
+ },
+ containerButton: {
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginTop: 16,
+ marginBottom: 16,
+ },
+ containerMembers: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginHorizontal: 16,
+ marginBottom: 16,
+ height: 40,
+ },
+ containerAutomaticPublish: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginTop: 16,
+ width: Dimensions.get('window').width - 76,
+ },
+ circleIcon: {
+ width: 32,
+ height: 32,
+ },
+ containerShadow: {
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ containerBlur: {
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ marginTop: -3,
+ },
+ textSucessModal: {
+ textAlign: 'center',
+ fontSize: 24,
+ fontWeight: '700',
+ lineHeight: 33,
+ color: Colors.text,
+ },
+ adminsPhoto: {
+ width: 38,
+ height: 38,
+ borderRadius: 32,
+ marginRight: 12,
+ //marginLeft: 16,
+ },
+ containerHeaderModal: {
+ flexDirection: 'row',
+ alignSelf: 'flex-end',
+ marginBottom: 12,
+ },
+ centeredView: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalView: {
+ borderRadius: 4,
+ marginHorizontal: 32,
+ alignItems: 'flex-start',
+ shadowColor: '#000',
+ paddingBottom: 50,
+ paddingTop: 16,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ width: Dimensions.get('window').width - 44,
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: '#082139',
+ },
+ containerTextModal: {
+ alignContent: 'center',
+ },
+ containerSectionSucessModal: {
+ flexDirection: 'column',
+ alignSelf: 'center',
+ alignItems: 'center',
+ width: Dimensions.get('window').width - 76,
+ },
+ containerSectionModal: {
+ alignSelf: 'center',
+ width: Dimensions.get('window').width - 80,
+ zIndex: 5,
+ elevation: 5,
+ },
+ typeCommunityModal: {
+ marginBottom: 16,
+ flexDirection: 'row',
+ alignItems: 'center',
+ alignSelf: 'center',
+ borderRadius: 4,
+ padding: 9,
+ },
+ inviteText: {
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 24,
+ color: Colors.primary8,
+ },
+ infoMembers: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ width: Dimensions.get('window').width - 211,
+ },
+ containerAdmins: {
+ flex: 1,
+ flexDirection: 'row',
+ //justifyContent: 'flex-start',
+ alignItems: 'flex-start',
+ flexWrap: 'wrap',
+ marginTop: 16,
+ },
+ joinButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ width: Dimensions.get('window').width - 32,
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 22,
+ color: Colors.text,
+ },
+ textCountPosts: {
+ flex: 1,
+ fontSize: 14,
+ fontWeight: '400',
+ lineHeight: 21,
+ color: Colors.text,
+ marginTop: 9,
+ },
+ textSubtitle: {
+ fontWeight: '400',
+ fontSize: 13,
+ lineHeight: 18,
+ color: '#FCFCFC',
+ },
+ headerIcon: {
+ width: 18,
+ height: 18,
+ marginRight: 3,
+ },
+ infoIcon: {
+ width: 24,
+ height: 24,
+ marginRight: 8,
+ },
+ ellipseIcon: {
+ width: 4,
+ height: 4,
+ marginLeft: 14,
+ marginRight: 8,
+ alignSelf: 'center',
+ left: 0,
+ },
+ backButton: {
+ marginLeft: 25,
+ marginTop: 32,
+ width: 60,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ top: 0,
+ zIndex: 3,
+ elevation: 3,
+ position: 'absolute',
+ },
+ imageModalHeader: {
+ resizeMode: 'cover',
+ width: 24,
+ height: 24,
+ borderRadius: 62,
+ //marginRight: 12,
+ //marginLeft: 16,
+ },
+ typeCommunity: {
+ marginLeft: 32,
+ marginBottom: 16,
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(48, 46, 80, 0.5)',
+ width: Dimensions.get('window').width - 215,
+ borderRadius: 4,
+ padding: 9,
+ },
+ containerInfo: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ inputSearch: {
+ flex: 1,
+ fontSize: 16,
+ lineHeight: 16,
+ backgroundColor: '#002544',
+ borderRadius: 8,
+ marginHorizontal: 16,
+ marginBottom: 16,
+ },
+ shareButton: {
+ height: 40,
+ borderRadius: 4,
+ padding: 8,
+ backgroundColor: 'rgba(153, 37, 56, 1)',
+ width: 163,
+ },
+ containerHeader: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 18,
+ marginTop: 16,
+ },
+ infoCommunity: {
+ marginBottom: 17,
+ },
+ textTitleSection: {
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 16,
+ color: '#FCFCFC',
+ },
+ textInfo: {
+ fontWeight: '400',
+ fontSize: 14,
+ lineHeight: 19,
+ color: '#FFFFFF',
+ marginTop: 8,
+ },
+ textInfoModal: {
+ fontWeight: '400',
+ fontSize: 14,
+ lineHeight: 21,
+ color: '#FFFFFF',
+ },
+ textTitle: {
+ fontWeight: '700',
+ fontSize: 20,
+ lineHeight: 30,
+ color: '#FCFCFC',
+ width: Dimensions.get('window').width - 78,
+ },
+ typeCommunityText: {
+ fontWeight: '600',
+ fontSize: 13,
+ lineHeight: 16,
+ color: '#FCFCFC',
+ },
+ communityImage: {
+ width: '100%',
+ height: '100%',
+ position: 'absolute',
+ },
+ containerNavBar: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: Dimensions.get('window').width - 32,
+ alignSelf: 'center',
+ alignItems: 'center',
+ },
+ containerItemNavBar: {
+ alignItems: 'center',
+ width: (Dimensions.get('window').width - 32) / 2,
+ paddingBottom: 8,
+ },
+ textPost: {
+ fontWeight: '400',
+ fontSize: 14,
+ lineHeight: 21,
+ color: '#FFFFFF',
+ marginHorizontal: 16,
+ },
+ containerHabits: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ //alignItems: 'center',
+ },
+ sectionHeader: {
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 16,
+ color: '#FCFCFC',
+ marginLeft: 16,
+ zIndex: 2,
+ elevation: 2,
+ },
+ containerSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ alignSelf: 'flex-end',
+ marginLeft: 16,
+ zIndex: 4,
+ elevation: 4,
+ position: 'absolute',
+ },
+ containerListHabits: {
+ //height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerViewSection: {
+ flex: 1,
+ paddingTop: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ textSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignSelf: 'flex-end',
+ marginRight: 16,
+ },
+ textEmpty: {
+ alignSelf: 'flex-start',
+ marginRight: 16,
+ marginTop: 16,
+ },
+ textNoHabits: {
+ fontSize: 12,
+ lineHeight: 16,
+ height: 16,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginLeft: 16,
+ },
+});
+
+export default ViewCommunity;
diff --git a/src/screens/habits/AddHabit.js b/src/screens/habits/AddHabit.js
new file mode 100644
index 0000000..cdbcf94
--- /dev/null
+++ b/src/screens/habits/AddHabit.js
@@ -0,0 +1,590 @@
+import React, { useState, useEffect, useRef, act } from 'react';
+import PropTypes from 'prop-types';
+import 'react-native-get-random-values';
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ Dimensions,
+ Text,
+ TouchableOpacity,
+ TextInput,
+ Alert,
+ Image,
+ Platform,
+} from 'react-native';
+import { v4 as uuidv4 } from 'uuid';
+import { decode } from 'base64-arraybuffer';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Header from '../../components/Header';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Button, Input } from 'react-native-elements';
+import { Picker } from '@react-native-picker/picker';
+import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+import { LinearGradient } from 'expo-linear-gradient';
+import ActionSheet from 'react-native-actionsheet';
+import * as ImagePicker from 'expo-image-picker';
+import { supabase } from '../../config/supabaseClient';
+import DateTimePicker from '@react-native-community/datetimepicker';
+
+const AddHabit = props => {
+ const [sending, setSending] = useState(false);
+ const [name, setName] = useState('');
+ const [habit_description, setHabitDecription] = useState('');
+ const [habitPhoto, setHabitPhoto] = useState(null);
+ const [activeDays, setActiveDays] = useState(0);
+ const [end_date, setEndDate] = useState(new Date());
+ const [showDatePicker, setShowDatePicker] = useState(false);
+
+ const ASPhotoOptions = useRef();
+
+ useEffect(() => {
+ if (props.route?.params?.habit) {
+ setName(props.route.params.habit.hab_name);
+ setHabitDecription(props.route.params.habit.hab_description);
+ }
+ }, []);
+
+ const blobToBase64 = (blob) => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = () => {
+ const base64data = reader.result.split(',')[1];
+ resolve(base64data);
+ };
+ reader.onerror = () => {
+ reject(new Error('Failed to convert blob to base64'));
+ };
+ });
+ };
+
+ const addHabit = async () => {
+ const { data: user, error: userError } = await supabase.auth.getUser();
+ if (userError || !user) {
+ Alert.alert('Error', 'User not authenticated');
+ setSending(false);
+ return;
+ }
+
+ const userId = user.user.id;
+ console.log('User ID:', userId);
+
+ if (name.trim() === '' || habit_description === '' || activeDays === 0) {
+ Alert.alert('Oops!', 'All fields are required.');
+ return;
+ }
+
+ setSending(true);
+
+ let habitPhotoUrl = null;
+ if (habitPhoto && habitPhoto.uri) {
+ const fileName = `${Date.now()}_${habitPhoto.uri.split('/').pop()}`;
+ console.log('Uploading file:', fileName);
+
+ try {
+ const response = await fetch(habitPhoto.uri);
+ const blob = await response.blob();
+
+ const reader = new FileReader();
+ reader.onloadend = async () => {
+ const base64data = reader.result.split(',')[1];
+ const arrayBuffer = decode(base64data);
+
+ const { data: uploadData, error: uploadError } = await supabase.storage
+ .from('habit')
+ .upload(fileName, arrayBuffer, {
+ cacheControl: '3600',
+ upsert: false,
+ contentType: 'image/jpeg',
+ });
+
+ if (uploadError) {
+ console.error('Upload error:', uploadError);
+ Alert.alert('Error', uploadError.message);
+ setSending(false);
+ return;
+ }
+ console.log('Upload data:', uploadData);
+
+ const publicUrlResponse = supabase.storage.from('habit').getPublicUrl(fileName);
+ console.log('getPublicUrl:', publicUrlResponse);
+ habitPhotoUrl = publicUrlResponse.data.publicUrl;
+ console.log('publicUrl:', habitPhotoUrl);
+
+ if (!habitPhotoUrl) {
+ Alert.alert('Error', 'Failed to generate public URL for the image');
+ setSending(false);
+ return;
+ }
+
+ const habitId = uuidv4();
+
+ // insert
+ const { data: insertData, error: insertError } = await supabase
+ .from('Habit')
+ .insert([{ habit_id: habitId, habit_title: name, habit_description, habit_photo: habitPhotoUrl }])
+ .select();
+
+ if (insertError) {
+ console.error('Insert error:', insertError);
+ Alert.alert('Error', insertError.message);
+ setSending(false);
+ return;
+ }
+ console.log('Inserted Habit:', insertData);
+
+ if (!insertData || insertData.length === 0) {
+ Alert.alert('Error', 'Failed to insert habit');
+ setSending(false);
+ return;
+ }
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .insert([{
+ habit_id: habitId,
+ user_id: userId,
+ created_at: new Date().toISOString(),
+ schedule_state: 'Open',
+ schedule_active_days: activeDays,
+ schedule_quantity: '10',
+ schedule_end_date: end_date.toISOString(),
+ }])
+ .select();
+
+ if (scheduleError) {
+ console.error('Schedule insert error:', scheduleError);
+ Alert.alert('Error', scheduleError.message);
+ } else {
+ Alert.alert('Success', 'You have successfully created a habit!', [
+ { text: 'OK', onPress: () => props.navigation.navigate('HabitsIndex') }
+ ]);
+ }
+
+ setSending(false);
+ };
+ reader.readAsDataURL(blob);
+ } catch (fetchError) {
+ console.error('Fetch error:', fetchError);
+ Alert.alert('Error', 'Failed to create Blob from the image URI');
+ setSending(false);
+ return;
+ }
+ } else {
+ const habitId = uuidv4(); // generate unique identifier for the new habit
+
+ // insert
+ const { data: insertData, error: insertError } = await supabase
+ .from('Habit')
+ .insert([{ habit_id: habitId, habit_title: name, habit_description, habit_photo: habitPhotoUrl }])
+ .select();
+
+ if (insertError) {
+ console.error('Insert error:', insertError);
+ Alert.alert('Error', insertError.message);
+ setSending(false);
+ return;
+ }
+ console.log('Inserted Habit:', insertData);
+
+ if (!insertData || insertData.length === 0) {
+ Alert.alert('Error', 'Failed to insert habit');
+ setSending(false);
+ return;
+ }
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .insert([{
+ habit_id: habitId,
+ user_id: userId,
+ created_at: new Date().toISOString(),
+ schedule_state: 'Open',
+ schedule_active_days: activeDays,
+ schedule_quantity: '10',
+ schedule_end_date: end_date.toISOString(),
+ }])
+ .select();
+
+ if (scheduleError) {
+ console.error('Schedule insert error:', scheduleError);
+ Alert.alert('Error', scheduleError.message);
+ } else {
+ console.log('Schedule Data:', scheduleData);
+ props.navigation.navigate('Habits');
+ }
+
+ setSending(false);
+ }
+ };
+
+ const handleActionSheet = async index => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+ if (status === 'granted') {
+ pickGallery();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the library first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ console.log('Camera result:', result);
+ setHabitPhoto(result.assets[0]);
+ }
+ };
+
+ const pickGallery = async () => {
+ let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ console.log('Gallery result:', result);
+ setHabitPhoto(result.assets[0]);
+ }
+ };
+
+ const onToggleActiveDay = index => {
+ setActiveDays(activeDays ^ (1 << index));
+ }
+
+ const getActiveDay = index => {
+ return (activeDays & (1 << index)) !== 0;
+ }
+
+ return (
+
+
+
+
+
+
+ {habitPhoto ? (
+ ASPhotoOptions.current.show()} style={styles.habitImage}>
+
+
+
+ Edit Photo
+
+
+ ) : (
+ ASPhotoOptions.current.show()} style={styles.containerPhoto}>
+
+
+ Add Photo
+
+
+ )}
+
+
+ handleActionSheet(index)}
+ styles={{
+ buttonBox: Default.actionSheetButtonBox,
+ body: Default.actionSheetBody,
+ cancelButtonBox: Default.actionSheetCancelButtonBox,
+ }}
+ />
+
+
+
+
+ Habit Description
+
+
+
+ Scheduled Days
+
+ {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, index) => (
+ onToggleActiveDay(index)}>
+
+ {day}
+
+
+ ))}
+
+
+
+ {/*Create a date picker to select the end date*/}
+ End Date
+
+ {Platform.OS === 'ios' && (
+
+ {
+ setEndDate(selectedDate);
+ }}
+ textColor={Colors.text}
+ minimumDate={new Date()}
+ onPressCancel={() => setEndDate(new Date())}
+ />
+
+ )}
+
+ {Platform.OS === 'android' && (
+
+ setShowDatePicker(true)}>
+ {end_date.toDateString()}
+
+ {showDatePicker &&
+ {
+ setShowDatePicker(false);
+ setEndDate(selectedDate);
+ }}
+ textColor={Colors.text}
+ minimumDate={new Date()}
+ />
+ }
+
+ )}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ paddingVertical: 32,
+ paddingLeft: 22,
+ },
+ containerHeaderImage: {
+ height: 189,
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ marginTop: 16,
+ },
+ habitImage: {
+ width: '100%',
+ height: '100%',
+ position: 'absolute',
+ },
+ addPhoto: {
+ width: 50,
+ height: 50,
+ marginBottom: 8,
+ },
+ textAddPhoto: {
+ fontWeight: '400',
+ fontSize: 16,
+ lineHeight: 19,
+ color: '#FCFCFC',
+ },
+ pickerStyle: {
+ width: Dimensions.get('window').width - 44,
+ backgroundColor: Colors.primary,
+ borderRadius: 2,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#455c8a',
+ marginHorizontal: 10,
+ paddingHorizontal: 16,
+ paddingVertical: 15,
+ marginBottom: 32,
+ fontSize: 16,
+ color: Colors.text,
+ },
+ containerBottomSheetFillDays: {
+ alignItems: 'center',
+ backgroundColor: Colors.primary,
+ paddingHorizontal: 22,
+ },
+ containerTextBottomSheet: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ warningIconStyle: {
+ width: 80,
+ height: 80,
+ },
+ textDelete: {
+ marginTop: 26,
+ fontSize: 14,
+ color: Colors.text,
+ textAlign: 'center',
+ paddingHorizontal: 22,
+ },
+ buttonContainer: {
+ marginBottom: 32,
+ },
+ pickerStyleAndroid: {
+ marginHorizontal: 0,
+ paddingVertical: 15,
+ marginBottom: 0,
+ color: Colors.primary4,
+ },
+ pickerStyleIOS: {
+ backgroundColor: '#1c1c1e',
+ borderWidth: 0,
+ color: Colors.text,
+ height: 200,
+ paddingVertical: 0,
+ marginVertical: 0,
+ },
+ labelStyle: {
+ color: '#FCFCFC',
+ fontSize: 16,
+ marginBottom: 12,
+ },
+ textInputStyle: {
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ paddingVertical: 15,
+ paddingHorizontal: 12,
+ paddingTop: 15,
+ fontSize: 16,
+ color: Colors.primary4,
+ width: Dimensions.get('window').width - 44,
+ textAlignVertical: 'top',
+ marginBottom: 32,
+ },
+ containerSelectIOS: {
+ paddingVertical: 15,
+ paddingHorizontal: 16,
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ marginBottom: 32,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: Dimensions.get('window').width - 44,
+ },
+ textSelectIOS: {
+ fontSize: 16,
+ color: '#455c8a',
+ },
+ containerBottomSheet: {
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ backgroundColor: '#1c1c1e',
+ },
+ containerHeaderBottomSheet: {
+ backgroundColor: '#282828',
+ paddingHorizontal: 16,
+ paddingVertical: 15,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: Dimensions.get('window').width,
+ },
+ textHeaderBottomSheet: {
+ fontSize: 16,
+ color: '#d7892b',
+ },
+ viewPicker: {
+ marginBottom: 32,
+ borderRadius: 2,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#455c8a',
+ alignItems: 'center',
+ width: Dimensions.get('window').width - 40,
+ },
+ title: {
+ color: Colors.text,
+ fontSize: 16,
+ fontWeight: '400',
+ marginBottom: 12,
+ },
+ textContent: {
+ fontSize: 16,
+ color: Colors.primary4,
+ fontWeight: '400',
+ marginBottom: 32,
+ },
+ // Days
+ frequencyDay: {
+ width: 36,
+ height: 36,
+ borderRadius: 18,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: Colors.text,
+ },
+ frequencyDaySelected: {
+ borderColor: Colors.primary4,
+ backgroundColor: Colors.primary4,
+ },
+ textFrequencyDay: {
+ fontSize: 12,
+ color: Colors.text,
+ fontWeight: '400',
+ },
+ datePicker: {
+ marginBottom: 16,
+ // alignSelf: 'center',
+ },
+});
+
+AddHabit.propTypes = {
+ navigation: PropTypes.object,
+ route: PropTypes.object,
+};
+
+export default AddHabit;
+
diff --git a/src/screens/habits/HabitPlan.js b/src/screens/habits/HabitPlan.js
new file mode 100644
index 0000000..3f3ee2a
--- /dev/null
+++ b/src/screens/habits/HabitPlan.js
@@ -0,0 +1,197 @@
+import React, { useState, useEffect } from "react";
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ TouchableOpacity,
+} from "react-native";
+import Accordion from "react-native-collapsible/Accordion";
+import Colors from "../../../assets/styles/Colors";
+import { AntDesign } from '@expo/vector-icons';
+
+const HabitPlan = ({ habitPlan }) => {
+ const [activeSections, setActiveSections] = useState([]);
+ const [activeStages, setActiveStages] = useState([]);
+ const [parsedHabitPlan, setParsedHabitPlan] = useState([]);
+
+ useEffect(() => {
+ if (habitPlan) {
+ try {
+ const parsed = JSON.parse(habitPlan);
+ const formattedPlan = Object.keys(parsed).map((habitName) => ({
+ hac_name: habitName,
+ stages: parsed[habitName][0].stages,
+ }));
+ // by this point setparsedhabitplan should be getting a properly formatted JSON object
+ setParsedHabitPlan(formattedPlan);
+ } catch (error) {
+ console.error("Error parsing habit plan:", error);
+ }
+ }
+ }, [habitPlan]);
+
+ const updateActiveSections = (indexNumber, stageIndex) => {
+ let activeSectionsAux = [...activeSections];
+
+ if (stageIndex !== undefined) {
+ const stageIndexInActive = activeSectionsAux.indexOf(stageIndex);
+ if (stageIndexInActive !== -1) {
+ activeSectionsAux.splice(stageIndexInActive, 1);
+ } else {
+ activeSectionsAux.push(stageIndex);
+ }
+ } else {
+ const index = activeSectionsAux.indexOf(indexNumber);
+ if (index !== -1) {
+ activeSectionsAux.splice(index, 1);
+ } else {
+ activeSectionsAux.push(indexNumber);
+ }
+ }
+
+ setActiveSections(activeSectionsAux);
+ };
+
+ const updateActiveStages = (indexNumber, stageIndex) => {
+ let activeStagesAux = [...activeStages];
+
+ if (stageIndex !== undefined) {
+ const stageIndexInActive = activeStagesAux.indexOf(stageIndex);
+ if (stageIndexInActive !== -1) {
+ activeStagesAux.splice(stageIndexInActive, 1);
+ } else {
+ activeStagesAux.push(stageIndex);
+ }
+ } else {
+ const index = activeStagesAux.indexOf(indexNumber);
+ if (index !== -1) {
+ activeStagesAux.splice(index, 1);
+ } else {
+ activeStagesAux.push(indexNumber);
+ }
+ }
+
+ setActiveStages(activeStagesAux);
+ };
+
+
+
+ const _renderHeader = (section, index, isActive) => {
+ return (
+ updateActiveSections(index)}>
+
+ {section.hac_name}
+
+
+
+ );
+ };
+
+ const _renderContent = (section) => {
+ return (
+
+ {section.stages.map((stage, i) => (
+
+ updateActiveStages(i)}>
+
+ {stage.name}
+
+
+
+ {activeStages.includes(i) && (
+
+
+ Duration: {stage.duration_weeks} weeks
+
+ Goals: {stage.goals}
+ {stage.steps.map((step, j) => (
+
+ Step {j + 1}: {step.description}
+
+ ))}
+
+ )}
+
+ ))}
+
+ );
+ };
+
+ return (
+ null}
+ />
+ );
+};
+
+const styles = StyleSheet.create({
+ containerAccordionHeader: {
+ width: Dimensions.get("window").width - 44,
+ backgroundColor: "rgba(156, 198, 255, 0.084)",
+ paddingVertical: 17,
+ paddingHorizontal: 17,
+ borderRadius: 4,
+ marginTop: 8,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ textAccordionHeader: {
+ fontSize: 16,
+ color: Colors.text,
+ },
+ containerAccordionContent: {
+ width: Dimensions.get("window").width - 44,
+ backgroundColor: "rgba(156, 198, 255, 0.084)",
+ borderBottomLeftRadius: 4,
+ borderBottomRightRadius: 4,
+ paddingHorizontal: 14,
+ paddingVertical: 15,
+ },
+ textAccordionContent: {
+ flex: 1,
+ fontSize: 14,
+ color: Colors.white,
+ },
+ stageItem: {
+ marginBottom: 10,
+ },
+ stageTitle: {
+ fontSize: 16,
+ fontWeight: "bold",
+ marginBottom: 5,
+ color: Colors.white,
+ },
+ stageDuration: {
+ fontSize: 14,
+ marginBottom: 5,
+ color: Colors.white,
+ },
+ stageGoals: {
+ fontSize: 14,
+ marginBottom: 10,
+ color: Colors.white,
+ },
+ stepDescription: {
+ fontSize: 14,
+ marginLeft: 10,
+ marginBottom: 5,
+ color: Colors.white,
+ },
+ headerOpened: {
+ borderBottomLeftRadius: 0,
+ borderBottomRightRadius: 0,
+ },
+});
+
+export default HabitPlan;
diff --git a/src/screens/habits/Habits.js b/src/screens/habits/Habits.js
new file mode 100644
index 0000000..e484d90
--- /dev/null
+++ b/src/screens/habits/Habits.js
@@ -0,0 +1,162 @@
+import React, { useState, useCallback } from 'react';
+import {
+ View,
+ StyleSheet,
+ Text,
+ Alert,
+ FlatList,
+ TouchableOpacity,
+ Dimensions,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+import { Button } from 'react-native-elements';
+import { useNavigation, useFocusEffect } from '@react-navigation/native';
+import store from '../../store/storeConfig';
+import { supabase } from '../../config/supabaseClient';
+
+const Habits = () => {
+ const session = store.getState().user.session;
+ const navigation = useNavigation();
+ const [schedules, setSchedules] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useFocusEffect(
+ useCallback(() => {
+ fetchSchedules();
+ }, [])
+ );
+
+ const fetchSchedules = async () => {
+ try {
+ setLoading(true);
+ // if (!session?.user) throw new Error('No user on the session!');
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .select('*')
+ .eq('user_id', session?.user.id);
+
+ console.log('Fetched schedule data:', scheduleData);
+
+ if (scheduleError) {
+ throw scheduleError;
+ }
+
+ if (scheduleData) {
+ const habitIds = scheduleData.map(schedule => schedule.habit_id);
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .select('*')
+ .in('habit_id', habitIds);
+
+ console.log('Fetched habit data:', habitData);
+
+ if (habitError) {
+ throw habitError;
+ }
+
+ if (habitData) {
+ const combinedData = scheduleData.map(schedule => {
+ const habit = habitData.find(h => h.habit_id === schedule.habit_id);
+ return {
+ ...schedule,
+ habit_title: habit?.habit_title,
+ habit_description: habit?.habit_description,
+ habit_id: schedule.habit_id,
+ };
+ });
+ setSchedules(combinedData);
+ console.log('Combined data:', combinedData);
+ }
+ }
+ } catch (error) {
+ console.error('Error fetching schedules or habits:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const addHabit = () => {
+ navigation.navigate('AddHabit');
+ };
+
+ const renderSchedule = ({ item }) => (
+ navigation.navigate('ViewHabit', { habit: item })}
+ style={styles.scheduleItem}>
+ {item.habit_title || 'N/A'}
+
+ {item.schedule_state === 'Open' ? 'ACTIVE' : 'INACTIVE'}
+
+
+ );
+
+ return (
+
+
+ {loading ? (
+
+ ) : (
+ index.toString()}
+ renderItem={renderSchedule}
+ contentContainerStyle={styles.list}
+ />
+ )}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: Colors.background,
+ padding: 20,
+ },
+ list: {
+ padding: 10,
+ },
+ scheduleItem: {
+ padding: 15,
+ marginVertical: 10,
+ backgroundColor: Colors.white,
+ borderRadius: 8,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 3,
+ },
+ shadowOpacity: 0.27,
+ shadowRadius: 4.65,
+ elevation: 6,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ habitTitle: {
+ fontSize: 18,
+ color: Colors.primary5,
+ },
+ habitStatus: {
+ fontSize: 14,
+ },
+});
+
+export default Habits;
diff --git a/src/screens/habits/ViewHabit.js b/src/screens/habits/ViewHabit.js
new file mode 100644
index 0000000..9f12214
--- /dev/null
+++ b/src/screens/habits/ViewHabit.js
@@ -0,0 +1,1317 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ Dimensions,
+ FlatList,
+ Text,
+ TouchableOpacity,
+ ActivityIndicator,
+ Alert,
+ Image,
+ TextInput,
+ Modal,
+} from 'react-native';
+import { useNavigation, useRoute } from '@react-navigation/native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Header from '../../components/Header';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Button } from 'react-native-elements';
+import { supabase } from '../../config/supabaseClient';
+import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+import { KeyboardAwareFlatList } from 'react-native-keyboard-aware-scroll-view';
+import RBSheet from 'react-native-raw-bottom-sheet';
+import moment from 'moment';
+import store from '../../store/storeConfig';
+import StepIndicator from 'react-native-step-indicator';
+import * as ImagePicker from 'expo-image-picker';
+import { v4 as uuidv4 } from 'uuid';
+import { decode } from 'base64-arraybuffer';
+import ActionSheet from 'react-native-actionsheet';
+import { updateHabitDataInSchedules } from './Habits';
+import DateTimePickerModal from "react-native-modal-datetime-picker";
+
+const { GoogleGenerativeAI } = require('@google/generative-ai');
+const apikey = process.env.EXPO_PUBLIC_REACT_APP_GEMINI_KEY;
+const genAI = new GoogleGenerativeAI(apikey);
+const { width } = Dimensions.get('window');
+const imageSize = width / 3;
+
+const ViewHabit = () => {
+ const session = store.getState().user.session;
+ const [isLoading, setIsLoading] = useState(false);
+ const [habitPhoto, setHabitPhoto] = useState(null);
+ const [generatedSchedule, setGeneratedSchedule] = useState(null);
+ const [currentPosition, setCurrentPosition] = useState(0);
+ const [habitImages, setHabitImages] = useState([]);
+ const [newPhoto, setNewPhoto] = useState(null);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [selectedImage, setSelectedImage] = useState(null);
+ const RBSDelete = useRef();
+ const ASPhotoOptions = useRef();
+ const navigation = useNavigation();
+ const route = useRoute();
+ const { habit } = route.params;
+ const [editable, setEditable] = useState(false);
+ const [newTitle, setNewTitle] = useState(habit.habit_title);
+ const [newEndDate, setNewEndDate] = useState(new Date());
+ const [showEndDatePicker, setShowEndDatePicker] = useState(false);
+ const [newDescription, setNewDescription] = useState(habit.habit_description);
+ const [loadingDisable, setLoadingDisable] = useState(false);
+ const [loadingDelete, setLoadingDelete] = useState(false);
+ const [editedHabit, setEditedHabit] = useState(habit);
+ const [deleteModalVisible, setDeleteModalVisible] = useState(false);
+ const [activeDays, setActiveDays] = useState(habit.schedule_active_days);
+
+ useEffect(() => {
+ const updatedEndDate = new Date(habit.schedule_end_date);
+ if (habit?.schedule_end_date) {
+ setNewEndDate(updatedEndDate);
+ }
+ }, [editedHabit]);
+
+ const unpackActiveDays = (packedNumber) => {
+ const days = new Array(7).fill(false);
+ for (let i = 0; i < 7; i++) {
+ days[i] = (packedNumber & (1 << i)) !== 0;
+ }
+ return days;
+ };
+
+ const packActiveDays = (days) => {
+ return days.reduce((packedNumber, isActive, index) => {
+ return packedNumber | (isActive ? (1 << index) : 0);
+ }, 0);
+ };
+
+
+ const toggleDay = (index) => {
+ setActiveDays((prevActiveDays) => {
+ return prevActiveDays ^ (1 << index);
+ });
+ };
+
+ const isDayActive = (index) => {
+ return (activeDays & (1 << index)) !== 0;
+ };
+
+ const updateHabit = (key, value) => {
+ setEditedHabit((prevHabit) => ({
+ ...prevHabit,
+ [key]: value,
+ }));
+ };
+
+ useEffect(() => {
+ const fetchHabit = async () => {
+ // console.log("Fetching habit");
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .select('*')
+ .eq('habit_id', habit.habit_id)
+ .single();
+
+ if (habitError) {
+ Alert.alert('Error fetching habit', habitError.message);
+ return;
+ }
+
+ // console.log("This is the habit data: " + JSON.stringify(habitData));
+ // console.log("This is the route: " + JSON.stringify(route));
+
+ // console.log("Setting habit photo");
+ setHabitPhoto(habitData?.habit_photo);
+ // console.log("Done setting habit photo");
+ if (habitData?.habit_plan) {
+ // TODO: verify that generated plan is valid JSON
+
+ // console.log("Setting generated schedule");
+ setGeneratedSchedule(JSON.parse(habitData.habit_plan));
+ // console.log("Done setting generated schedule")
+ }
+
+ console.log("Fetching habit images");
+ const { data: imagesData, error: imagesError } = await supabase
+ .from('HabitImages')
+ .select('*')
+ .eq('habit_id', habit.habit_id);
+
+ if (imagesError) {
+ Alert.alert('Error fetching images', imagesError.message);
+ return;
+ }
+
+ // console.log("This is the images data: " + JSON.stringify(imagesData));
+
+
+
+ const { data: schedulesData, error: schedulesError } = await supabase
+ .from('Schedule')
+ .select('schedule_id')
+ .eq('habit_id', habit.habit_id);
+
+ if (schedulesError) {
+ throw schedulesError;
+ }
+
+ const scheduleIds = schedulesData.map(schedule => schedule.schedule_id);
+
+ const { data: postsData, error: postsError } = await supabase
+ .from('Post')
+ .select('post_id, post_description')
+ .in('schedule_id', scheduleIds);
+
+ if (postsError) {
+ throw postsError;
+ }
+
+ const postIds = postsData.map(post => post.post_id);
+
+ const { data: postImagesData, error: postImagesError } = await supabase
+ .from('Image')
+ .select('image_photo, post_id')
+ .in('post_id', postIds);
+
+ if (postImagesError) {
+ throw postImagesError;
+ }
+
+
+
+
+
+
+
+
+ const combinedImages = [
+ ...(habitData?.habit_photo ? [{ image_photo: habitData.habit_photo, id: 'habitPhoto' }] : []),
+ ...imagesData,
+ ...postImagesData.map(image => ({
+ ...image,
+ post_description: postsData.find(post => post.post_id === image.post_id)?.post_description
+ }))
+ ].filter(image => image.image_photo !== null);
+ // console.log("Setting habit images with: " + JSON.stringify(combinedImages));
+ setHabitImages(combinedImages);
+ // console.log("Done setting habit images");
+ };
+
+ fetchHabit();
+ }, [habit.habit_id]);
+
+ const updateHabitPlan = async (habitPlan) => {
+ try {
+ const { data, error } = await supabase
+ .from('Habit')
+ .update({ habit_plan: habitPlan })
+ .eq('habit_id', habit.habit_id)
+ .single();
+
+ return data;
+ } catch (error) {
+ console.error('Error updating habit plan in Supabase:', error);
+ throw error;
+ }
+ };
+
+ const onDeleteHabit = () => {
+ RBSDelete.current.open();
+ };
+
+ const deletePhoto = async (image) => {
+ try {
+ const { error: deleteError } = await supabase
+ .from('HabitImages')
+ .delete()
+ .eq('habit_image_id', image.habit_image_id);
+
+ if (deleteError) {
+ throw deleteError;
+ }
+
+ const { error: bucketError } = await supabase.storage
+ .from('habit')
+ .remove([image.image_photo.split('/').pop()]);
+
+ if (bucketError) {
+ throw bucketError;
+ }
+
+ setHabitImages(habitImages.filter(img => img.habit_image_id !== image.habit_image_id));
+ Alert.alert('Success', 'Photo has been successfully deleted');
+ closeModal();
+ } catch (error) {
+ Alert.alert('Error', error.message);
+ }
+ };
+
+
+
+ const confirmDeleteHabit = () => {
+ Alert.alert(
+ "Delete Habit",
+ "Are you sure you want to delete this habit?",
+ [
+ {
+ text: "Cancel",
+ onPress: () => setDeleteModalVisible(false),
+ style: "cancel"
+ },
+ {
+ text: "Delete",
+ onPress: () => deleteHabit(),
+ style: "destructive"
+ }
+ ],
+ { cancelable: false }
+ );
+ };
+
+
+
+ const onToggleHabit = async () => {
+ setLoadingDisable(true);
+
+ const updatedData = {
+ ...habit,
+ enabled: !habit.enabled,
+ };
+
+ const { error } = await supabase
+ .from('Habit')
+ .update(updatedData)
+ .eq('habit_id', habit.habit_id)
+ .select();
+
+ if (error) {
+ Alert.alert('Error updating habit', error.message);
+ setLoadingDisable(false);
+ return;
+ }
+
+ habit.enabled = !habit.enabled;
+ setLoadingDisable(false);
+ };
+
+ const deleteHabit = async () => {
+ setLoadingDelete(true);
+
+ const { error } = await supabase
+ .from('Habit')
+ .delete()
+ .eq('habit_id', habit.habit_id);
+
+ if (error) {
+ Alert.alert('Error deleting habit', error.message);
+ setLoadingDelete(false);
+ return;
+ }
+
+ RBSDelete.current.close();
+ navigation.pop();
+ };
+
+ const generateHabitSchedule = async () => {
+ const MAX_RETRIES = 10;
+ let attempt = 0;
+ let success = false;
+
+ try {
+ setIsLoading(true);
+ const model = genAI.getGenerativeModel({ model: 'gemini-pro' });
+
+ const chat = model.startChat({
+ history: [
+ {
+ role: 'user',
+ parts: [
+ {
+ text: `Hello, I would like you to generate a habit plan in strictly correct JSON format for me to follow that will help me reach my goals for my habit called ${habit.habit_title} with this description: \"${habit.habit_description}\". The JSON should contain exactly 3 stages.`,
+ },
+ ],
+ },
+ {
+ role: 'model',
+ parts: [
+ {
+ text: 'Great to meet you. I would love to design a plan for you to follow. Can you give an example of the JSON format you would like it in?',
+ },
+ ],
+ },
+ ],
+ generationConfig: {
+ maxOutputTokens: 6000,
+ },
+ });
+
+ const prompt = `{"stages": [
+ {
+ "name": "",
+ "duration_weeks": "",
+ "goals": "",
+ "steps": [
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "",
+ "duration_weeks": "",
+ "goals": "",
+ "steps": [
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "",
+ "duration_weeks": "",
+ "goals": "",
+ "steps": [
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ },
+ {
+ "description": ""
+ }
+ ]
+ }
+ ]
+ }`;
+
+ while (attempt < MAX_RETRIES && !success) {
+ try {
+ const result = await chat.sendMessage(`Generate the plan with with this format: ${prompt}`);
+ const response = await result.response;
+ const text = await response.text();
+
+ const cleanedText = text
+ .replace(/^```(?:json)?\n/, '')
+ .replace(/\n```$/, '')
+ .replace(/\n/g, '');
+ const jsonStartIndex = cleanedText.indexOf('{');
+ const jsonEndIndex = cleanedText.lastIndexOf('}');
+ const validJsonString = cleanedText.substring(jsonStartIndex, jsonEndIndex + 1);
+
+ const parsedSchedule = JSON.parse(validJsonString);
+ setGeneratedSchedule(parsedSchedule);
+ await updateHabitPlan(validJsonString);
+ success = true;
+ } catch (parseError) {
+ attempt++;
+ console.error(`Error parsing JSON on attempt ${attempt}:`, parseError);
+ if (attempt >= MAX_RETRIES) {
+ throw new Error('Maximum retries reached. Unable to generate a valid habit schedule.');
+ }
+ }
+ }
+ } catch (error) {
+ console.error('Error generating habit schedule:', error);
+ Alert.alert('Error', 'Failed to generate habit schedule. Please try again.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+
+ const handleActionSheet = async (index) => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Oops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+ if (status === 'granted') {
+ pickGallery();
+ } else {
+ Alert.alert('Oops', 'You need to allow access to the library first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ setNewPhoto(result.assets[0]);
+ }
+ };
+
+ const pickGallery = async () => {
+ let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ setNewPhoto(result.assets[0]);
+ }
+ };
+
+ const cancelImageSelection = () => {
+ setNewPhoto(null);
+ setNewDescription('');
+ };
+
+ const blobToBase64 = (blob) => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = () => {
+ const base64data = reader.result.split(',')[1];
+ resolve(base64data);
+ };
+ reader.onerror = () => {
+ reject(new Error('Failed to convert blob to base64'));
+ };
+ });
+ };
+
+ const addImage = async () => {
+ if (!newPhoto || !newDescription) {
+ Alert.alert('Error', 'Please select a photo and add a description');
+ return;
+ }
+
+ const fileName = `${Date.now()}_${newPhoto.uri.split('/').pop()}`;
+ console.log('Uploading file:', fileName);
+
+ try {
+ const response = await fetch(newPhoto.uri);
+ const blob = await response.blob();
+
+ const reader = new FileReader();
+ reader.onloadend = async () => {
+ const base64data = reader.result.split(',')[1];
+ const arrayBuffer = decode(base64data);
+
+ const { data: uploadData, error: uploadError } = await supabase.storage
+ .from('habit')
+ .upload(fileName, arrayBuffer, {
+ cacheControl: '3600',
+ upsert: false,
+ contentType: 'image/jpeg',
+ });
+
+ if (uploadError) {
+ console.error('Upload error:', uploadError);
+ Alert.alert('Error', uploadError.message);
+ return;
+ }
+
+ const publicUrlResponse = supabase.storage.from('habit').getPublicUrl(fileName);
+ const imagePhotoUrl = publicUrlResponse.data.publicUrl;
+
+ const { data: insertData, error: insertError } = await supabase
+ .from('HabitImages')
+ .insert([{ habit_id: habit.habit_id, image_photo: imagePhotoUrl, description: newDescription }]);
+
+ if (insertError) {
+ console.error('Insert error:', insertError);
+ Alert.alert('Error', insertError.message);
+ return;
+ }
+
+ setHabitImages([...habitImages, { habit_id: habit.habit_id, image_photo: imagePhotoUrl, description: newDescription }]);
+ setNewPhoto(null);
+ setNewDescription('');
+ };
+ reader.readAsDataURL(blob);
+ } catch (fetchError) {
+ console.error('Fetch error:', fetchError);
+ Alert.alert('Error', 'Failed to create Blob from the image URI');
+ }
+ };
+
+ const customStyles = {
+ stepIndicatorSize: 25,
+ currentStepIndicatorSize: 30,
+ separatorStrokeWidth: 2,
+ currentStepStrokeWidth: 3,
+ stepStrokeCurrentColor: Colors.primary,
+ stepStrokeWidth: 3,
+ stepStrokeFinishedColor: Colors.primary,
+ stepStrokeUnFinishedColor: '#aaaaaa',
+ separatorFinishedColor: Colors.primary,
+ separatorUnFinishedColor: '#aaaaaa',
+ stepIndicatorFinishedColor: Colors.primary,
+ stepIndicatorUnFinishedColor: '#ffffff',
+ stepIndicatorCurrentColor: '#ffffff',
+ stepIndicatorLabelFontSize: 0,
+ currentStepIndicatorLabelFontSize: 0,
+ labelColor: '#999999',
+ labelSize: 13,
+ currentStepLabelColor: Colors.primary,
+ };
+
+ const renderStepContent = (step) => (
+
+ {step.name}
+ Duration: {step.duration_weeks} weeks
+ Goals: {step.goals}
+ {step.steps.map((item, index) => (
+
+ Step {index + 1}: {item.description}
+
+ ))}
+
+ );
+
+ const openModal = (image) => {
+ setSelectedImage(image);
+ setModalVisible(true);
+ };
+
+ const closeModal = () => {
+ setModalVisible(false);
+ setSelectedImage(null);
+ };
+ const renderImageItem = ({ item }) => {
+ return (
+ openModal(item)}>
+
+
+ );
+ };
+
+
+
+
+ const toggleEdit = () => {
+ setEditable(!editable);
+ };
+
+ const getActiveDay = (index) => {
+ return (habit.schedule_active_days & (1 << index)) !== 0;
+ };
+
+ const saveChanges = async () => {
+ try {
+ const packedActiveDays = calculatePackedActiveDays();
+ const { data: habitData, error: habitError } = await supabase
+ .from('Habit')
+ .update({
+ habit_title: newTitle,
+ habit_description: newDescription,
+ })
+ .eq('habit_id', habit.habit_id);
+
+ const formattedEndDate = newEndDate.toISOString();
+
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .update({
+ schedule_end_date: formattedEndDate,
+ schedule_active_days: packedActiveDays,
+ })
+ .eq('habit_id', habit.habit_id);
+
+ if (scheduleError) {
+ throw scheduleError;
+ }
+ const updatedHabitData = {
+ ...habit,
+ habit_title: newTitle,
+ habit_description: newDescription,
+ schedule_end_date: formattedEndDate,
+ packed_active_days: packedActiveDays,
+ };
+ setEditedHabit(updatedHabitData);
+
+ Alert.alert('Habit updated successfully');
+ setEditable(false);
+ } catch (error) {
+ Alert.alert('Error updating habit', error.message);
+ }
+ };
+
+ const cancelEdit = () => {
+ setNewTitle(habit.habit_title);
+ setNewDescription(habit.habit_description);
+ setNewEndDate(habit.schedule_end_date);
+ setActiveDays(habit.schedule_active_days);
+ setEditable(false);
+ }
+
+ const calculatePackedActiveDays = () => {
+ let packedActiveDays = 0;
+ ['S', 'M', 'T', 'W', 'T', 'F', 'S'].forEach((day, index) => {
+ if (isDayActive(index)) {
+ packedActiveDays |= (1 << index);
+ }
+ });
+
+ return packedActiveDays;
+ };
+
+ return (
+
+ {
+ if (item.key === 'header') {
+ return (
+
+
+ Cancel
+
+
+ Save
+
+
+ ) : (
+
+ )
+ }
+ />
+ );
+ } else if (item.key === 'content') {
+ return (
+ <>
+
+ item.description}
+ numColumns={3}
+ contentContainerStyle={styles.scrollContainer}
+ />
+
+
+
+
+ {selectedImage && (
+ <>
+
+ {selectedImage.description}
+ {selectedImage.post_description && (
+ {selectedImage.post_description}
+ )}
+ deletePhoto(selectedImage)} />
+
+ >
+ )}
+
+
+
+
+
+
+
+
+ Habit Title
+ {editable ? (
+
+ ) : (
+
+ {editedHabit?.habit_title || 'N/A'}
+
+ )}
+
+ Habit Description
+ {editable ? (
+
+ ) : (
+
+ {editedHabit?.habit_description || 'N/A'}
+
+ )}
+
+ Start Date
+
+ {habit?.created_at
+ ? moment(habit.created_at).format('MMMM Do YYYY')
+ : 'N/A'}
+
+
+
+ End Date
+ {editable ? (
+ setShowEndDatePicker(true)}>
+
+ {moment(newEndDate).format('MMMM Do YYYY')}
+
+
+ ) : (
+
+ {editedHabit?.schedule_end_date
+ ? moment(new Date(editedHabit?.schedule_end_date)).format('MMMM Do YYYY')
+ : 'N/A'}
+
+ )}
+ {showEndDatePicker && (
+
+ {
+ setShowEndDatePicker(false);
+ setNewEndDate(date);
+ }}
+ onCancel={() => setShowEndDatePicker(false)}
+ />
+
+ )}
+
+
+ {editable ? (
+
+ Active Days
+
+ {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, index) => (
+ toggleDay(index)}
+ style={[styles.frequencyDay, isDayActive(index) ? styles.frequencyDaySelected : null]}
+ >
+ {day}
+
+ ))}
+
+
+ ) : (
+
+ Active Days
+
+ {['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((day, index) => (
+
+ {day}
+
+ ))}
+
+
+ )}
+
+ State
+
+ {habit?.schedule_state || 'N/A'}
+
+
+ Created At
+
+ {habit?.created_at
+ ? moment(habit.created_at).format('MMMM Do YYYY, h:mm:ss a')
+ : 'N/A'}
+
+
+
+ ASPhotoOptions.current.show()} style={styles.pickImageButton}>
+ Pick an image
+
+
+ handleActionSheet(index)}
+ />
+
+ {newPhoto && (
+
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ {isLoading ? (
+
+ ) : generatedSchedule ? (
+ <>
+ Your Habit Plan by Your AI Coach:
+ stage.name)}
+ />
+ {
+ const contentOffsetX = e.nativeEvent.contentOffset.x;
+ const screenWidth = Dimensions.get('window').width;
+ const currentStep = Math.round(contentOffsetX / screenWidth);
+ setCurrentPosition(currentStep);
+ }}
+ scrollEventThrottle={16}
+ >
+ {generatedSchedule.stages.map((stage, index) =>
+ renderStepContent(stage)
+ )}
+
+ >
+ ) : (
+ No generated plan yet!
+ )}
+
+
+
+ setDeleteModalVisible(true)}
+ title="DELETE HABIT"
+ />
+
+ setDeleteModalVisible(false)}
+ >
+
+
+ Are you sure you want to delete this habit?
+
+ setDeleteModalVisible(false)}
+ >
+ Cancel
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+ >
+ );
+ }
+ }}
+ keyExtractor={(item) => item.key}
+ />
+
+
+
+
+
+
+ Are you sure you want to delete this habit?
+
+
+
+ RBSDelete.current.close()}
+ buttonStyle={styles.sheetCancelButton}
+ />
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ saveCancelButtonsContainer: {
+ paddingLeft: 30,
+ flexDirection: 'row',
+ },
+ saveCancelButtons: {
+ padding: 10,
+ backgroundColor: '#5c6bc0',
+ borderRadius: 5,
+ marginHorizontal: 10,
+ },
+ saveCancelButtonsText: {
+ color: '#fff',
+ },
+ container: {
+ width: Dimensions.get("window").width,
+ backgroundColor: Colors.primary,
+ flex: 1,
+ padding: 16,
+ },
+ gridContainer: {
+ marginTop: 20,
+ marginBottom: 16,
+ flex: 3,
+ },
+ gridImage: {
+ width: imageSize - 10,
+ height: imageSize - 10,
+ margin: 5,
+ },
+ modalContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0,0,0,0.95)',
+ },
+ modalContent: {
+ width: '90%',
+ backgroundColor: 'white',
+ padding: 20,
+ borderRadius: 10,
+ alignItems: 'center',
+ },
+ fullImage: {
+ width: '100%',
+ height: Dimensions.get('window').width,
+ marginBottom: 5,
+ resizeMode: 'contain',
+
+ },
+ imageDescription: {
+ marginTop: 10,
+ textAlign: 'center',
+ color: Colors.text,
+ fontSize: 16,
+ marginBottom: 40,
+ },
+ imageCaption: {
+ textAlign: 'center',
+ marginTop: 5,
+ color: Colors.text,
+ fontSize: 12,
+ },
+
+
+ // imageDescription: {
+ // color: Colors.white,
+ // marginBottom: 16,
+ // },
+ title: {
+ fontSize: 16,
+ fontWeight: '400',
+ color: Colors.white,
+ marginBottom: 8,
+ },
+ textContent: {
+ fontSize: 14,
+ color: Colors.white,
+ marginBottom: 16,
+ },
+ textInput: {
+ fontSize: 16,
+ marginBottom: 10,
+ borderWidth: 1,
+ borderColor: '#ccc',
+ padding: 10,
+ borderRadius: 5,
+ color: Colors.black,
+ backgroundColor: Colors.white,
+ },
+ photoContainer: {
+ width: Dimensions.get('window').width,
+ height: Dimensions.get('window').width,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 16,
+ marginTop: 20,
+ },
+ habitPhoto: {
+ width: '100%',
+ height: '100%',
+ borderRadius: 45,
+ },
+ pickImageButton: {
+ backgroundColor: '#5c6bc0',
+ padding: 12,
+ borderRadius: 30,
+ alignItems: 'center',
+ marginVertical: 16,
+ },
+ pickImageButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: '400',
+ letterSpacing: 1,
+ paddingVertical: 10,
+ paddingHorizontal: 20,
+ borderRadius: 25,
+ overflow: 'hidden',
+ },
+ newImageContainer: {
+ alignItems: 'center',
+ },
+ newImage: {
+ width: '100%',
+ height: 200,
+ },
+ newDescriptionInput: {
+ borderColor: Colors.border,
+ borderWidth: 1,
+ borderRadius: 10,
+ padding: 10,
+ marginVertical: 10,
+ width: '100%',
+ color: Colors.text,
+ backgroundColor: Colors.background,
+ },
+ containerButton: {
+ marginBottom: 16,
+ width: '100%',
+ justifyContent: 'center',
+ alignContent: 'center',
+ alignItems: 'center',
+ },
+ generateButton: {
+ backgroundColor: 'green',
+ borderRadius: 45,
+ paddingHorizontal: 32,
+ paddingVertical: 12,
+ },
+ scheduleDetails: {
+ backgroundColor: Colors.scheduleBackground,
+ borderRadius: 45,
+ padding: 0,
+ marginBottom: 35,
+ marginTop: 25,
+ },
+ stepContentContainer: {
+ marginTop: 20,
+ width: Dimensions.get('window').width - 50,
+ },
+ stepContent: {
+ width: Dimensions.get('window').width - 50,
+ padding: 16,
+ backgroundColor: 'rgba(220, 260, 255, 0.26)',
+ borderRadius: 10,
+ marginHorizontal: 8,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.3)',
+ width: Dimensions.get('window').width - 90,
+ },
+ stepTitle: {
+ fontSize: 18,
+ fontWeight: '400',
+ color: Colors.white,
+ marginBottom: 8,
+ },
+ stepText: {
+ fontSize: 16,
+ padding: 10,
+ color: Colors.white,
+ },
+ buttonContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginTop: 20,
+ },
+ toggleButton: {
+ flex: 1,
+ backgroundColor: Colors.primary,
+ padding: 12,
+ borderRadius: 30,
+ marginRight: 8,
+ alignItems: 'center',
+ },
+ toggleButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: '400',
+ },
+ deleteButton: {
+ flex: 20,
+ backgroundColor: '#d9534f',
+ padding: 12,
+ borderRadius: 45,
+ marginLeft: 125,
+ },
+ deleteButtonTitle: {
+ color: Colors.white,
+ fontSize: 12,
+
+ },
+ modalOverlay: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.95)',
+ },
+ modalContent: {
+ width: '80%',
+ backgroundColor: Colors.cardBackground,
+ borderRadius: 10,
+ padding: 20,
+ alignItems: 'center',
+ },
+ modalText: {
+ fontSize: 18,
+ color: Colors.text,
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ modalButtonContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: '100%',
+ },
+ modalCancelButton: {
+ flex: 1,
+ backgroundColor: Colors.secondary,
+ padding: 12,
+ borderRadius: 5,
+ marginRight: 10,
+ alignItems: 'center',
+ },
+ modalDeleteButton: {
+ flex: 1,
+ backgroundColor: '#d9534f',
+ padding: 12,
+ borderRadius: 5,
+ alignItems: 'center',
+ },
+ modalButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ },
+ sheetContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ sheetTitle: {
+ fontSize: 18,
+ fontWeight: '400',
+ color: Colors.white,
+ marginBottom: 16,
+ },
+ sheetButtonContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: '100%',
+ paddingHorizontal: 16,
+ },
+ sheetCancelButton: {
+ flex: 1,
+ backgroundColor: Colors.secondary,
+ padding: 12,
+ borderRadius: 30,
+ marginRight: 8,
+ alignItems: 'center',
+ },
+ sheetDeleteButton: {
+ flex: 1,
+ backgroundColor: '#d9534f',
+ padding: 12,
+ borderRadius: 30,
+ alignItems: 'center',
+ },
+ absolute: {
+ ...StyleSheet.absoluteFillObject,
+ borderRadius: 45,
+ },
+ overlay: {
+ ...StyleSheet.absoluteFillObject,
+ backgroundColor: 'rgba(255, 255, 255, 0.5)',
+ borderRadius: 40,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.5)',
+ },
+ frequencyDay: {
+ width: 30,
+ height: 30,
+ borderRadius: 15,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: Colors.primary,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: Colors.text,
+ },
+ frequencyDaySelected: {
+ backgroundColor: Colors.primary4,
+ borderColor: Colors.primary4,
+ },
+ textFrequencyDay: {
+ fontSize: 12,
+ color: Colors.text,
+ fontWeight: '400',
+ },
+});
+
+export default ViewHabit;
diff --git a/src/screens/profile/HealthHabitReport.js b/src/screens/profile/HealthHabitReport.js
new file mode 100644
index 0000000..ad8f8ee
--- /dev/null
+++ b/src/screens/profile/HealthHabitReport.js
@@ -0,0 +1,570 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ Dimensions,
+ Text,
+ Image,
+ Alert,
+ TouchableOpacity,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+import { Button } from 'react-native-elements';
+import { getAllCategoryWithCheckedHabbits } from '../../store/ducks/habit';
+import CustomPicker from '../../components/CustomPicker';
+import { LineChart } from 'react-native-chart-kit';
+import { LinearGradient, Svg, Defs, Stop, Rect } from 'react-native-svg';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import {
+ formatCategoryAndUserHabits,
+ getHabitsByCategoryBarColor,
+ getInitialAndFinalDateBasedOnPeriod,
+ getOverallSummaryContent,
+ getPeriodLabelList,
+ openPdf,
+} from './HealthHabitReportUtils';
+import { percentageOfValueFromTotal } from '../../utils/Utils';
+
+const HealthHabitReport = props => {
+ const [fetching, setFetching] = useState(true);
+
+ const [inputsValue, setInputsValue] = useState({
+ period: 'Month',
+ habit: '',
+ });
+ const [filtersForBackend, setFiltersForBackend] = useState(
+ getInitialAndFinalDateBasedOnPeriod(inputsValue.period)
+ );
+ const [overallSummaryLabels, setOverallSummaryLabels] = useState(
+ getPeriodLabelList(inputsValue.period)
+ );
+
+ const [formattedData, setFormattedData] = useState({ categoryList: [] });
+ const [periodList, _setPeriodList] = useState(['Month', 'Quarter', 'Year']);
+
+ useEffect(() => {
+ fetchFormatAndSetData();
+ }, []);
+
+ useEffect(() => {
+ if (!fetching) {
+ setFetching(true);
+
+ fetchFormatAndSetData();
+ }
+ }, [filtersForBackend]);
+
+ const fetchFormatAndSetData = () => {
+ getAllCategoryWithCheckedHabbits(filtersForBackend)
+ .catch(res => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res.status === 200) {
+ if (res.data.errors) {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ } else {
+ const formattedData = formatCategoryAndUserHabits(
+ res.data,
+ filtersForBackend
+ );
+
+ formattedData.categoryList = formattedData.categoryList.sort(
+ (a, b) =>
+ percentageOfValueFromTotal(
+ b.categoryHabitsChecked,
+ b.categoryHabitsNeeded
+ ) -
+ percentageOfValueFromTotal(
+ a.categoryHabitsChecked,
+ a.categoryHabitsNeeded
+ )
+ );
+
+ setFormattedData(formattedData);
+ }
+ }
+ })
+ .finally(e => {
+ setFetching(false);
+ });
+ };
+
+ const SectionInputs = () => (
+
+ customSetInputsValue('period', itemValue)}
+ customKey="period-key"
+ label="Period"
+ customContainerStyle={{ marginBottom: 17 }}
+ options={periodList.map(item => ({ label: item, value: item }))}
+ />
+ customSetInputsValue('habit', itemValue)}
+ customKey="habit-key"
+ label="Habit"
+ placeholder="Select a Habit"
+ options={getHabitsLabelAndValue()}
+ />
+
+ );
+
+ const getHabitsLabelAndValue = () => {
+ const habits = [];
+
+ formattedData?.categoryList?.forEach(item =>
+ item?.habits?.forEach(habitItem => {
+ habits.push({ label: habitItem.habit.hab_name, value: habitItem.id });
+ return { label: habitItem.habit.hab_name, value: habitItem.id };
+ })
+ );
+
+ return habits;
+ };
+
+ const customSetInputsValue = (key, value) => {
+ setInputsValue({
+ ...inputsValue,
+ [key]: value,
+ });
+
+ if (key === 'period') {
+ setOverallSummaryLabels(getPeriodLabelList(value));
+ setFiltersForBackend(getInitialAndFinalDateBasedOnPeriod(value));
+ return;
+ }
+ };
+
+ const SectionOverallSummary = () => {
+ const overallSummaryContent =
+ formattedData.categoryList.length > 0
+ ? getOverallSummaryContent(formattedData, inputsValue.habit)
+ : { average: 0, data: [0, 0, 0, 0, 0] };
+
+ return (
+
+
+ Overall Summary
+
+ See details
+
+
+ {`${overallSummaryContent.average}%`}
+ `${y}%`}
+ xLabelsOffset={0}
+ yLabelsOffset={8}
+ chartConfig={{
+ backgroundGradientFrom: Colors.background,
+ backgroundGradientTo: Colors.background,
+ fillShadowGradientFrom: '#9CC6FF',
+ fillShadowGradientFromOpacity: 1,
+ fillShadowGradientTo: Colors.background,
+ decimalPlaces: 0,
+ color: () => '#9CC6FF',
+ labelColor: () => Colors.text,
+ }}
+ style={{
+ marginLeft: -30,
+ marginBottom: 0,
+ paddingBottom: 0,
+ }}
+ />
+
+ {overallSummaryLabels.map((item, index) => (
+
+ {item}
+
+ ))}
+
+
+ );
+ };
+
+ const navigateToHealthHabitReportDetails = () =>
+ props.navigation.navigate('HealthHabitReportDetails', {
+ category: inputsValue.habit,
+ period: inputsValue.period,
+ formattedData,
+ });
+
+ const SectionHabitsByCategory = () => (
+
+
+ Habits by Category
+
+
+ {formattedData?.categoryList ? (
+
+ {formattedData?.categoryList.map((item, idx) => {
+ const categoryCheckPercentage = percentageOfValueFromTotal(
+ item.categoryHabitsChecked,
+ item.categoryHabitsNeeded
+ );
+ const barColors = getHabitsByCategoryBarColor(
+ categoryCheckPercentage
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+ {item.hac_name}
+
+
+ = 95
+ ? { position: 'absolute', right: 16 }
+ : null,
+ ]}>
+ {categoryCheckPercentage}
+
+
+
+ );
+ })}
+
+
+ 0
+ 25
+ 50
+ 75
+ 100
+
+
+ ) : null}
+
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ More details
+
+
+
+
+ openPdf(filtersForBackend, inputsValue.period)}
+ title="EXPORT TO PDF"
+ />
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'column',
+ flex: 1,
+ paddingBottom: 24,
+ width: Dimensions.get('window').width,
+ paddingHorizontal: 22,
+ },
+ customHeader: {
+ paddingTop: 15,
+ marginBottom: 16,
+ textAlign: 'center',
+ },
+ headerSeparator: {
+ marginBottom: 17,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: Colors.primary4,
+ opacity: 0.2,
+ width: Dimensions.get('window').width - 44,
+ alignSelf: 'center',
+ },
+ sectionContainer: {
+ flexDirection: 'column',
+ },
+ sectionTitleContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 16,
+ width: Dimensions.get('window').width - 44,
+ },
+ sectionTitle: {
+ fontSize: 16,
+ color: Colors.text,
+ fontWeight: '700',
+ marginRight: 32,
+ },
+ sectionTitleOption: {
+ fontSize: 14,
+ color: Colors.text,
+ fontWeight: '700',
+ },
+ viewPicker: {
+ marginBottom: 32,
+ borderBottomRadius: 2,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: 'rgba(156, 198, 255, 0.4)',
+ alignItems: 'center',
+ width: Dimensions.get('window').width - 40,
+ },
+ pickerStyle: {
+ width: Dimensions.get('window').width - 44,
+ backgroundColor: Colors.primary,
+ borderRadius: 2,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#455c8a',
+ marginHorizontal: 10,
+ paddingHorizontal: 16,
+ paddingVertical: 15,
+ marginBottom: 32,
+ fontSize: 16,
+ color: Colors.text,
+ },
+ pickerStyleAndroid: {
+ marginHorizontal: 0,
+ paddingVertical: 15,
+ marginBottom: 0,
+ color: Colors.primary4,
+ },
+ pickerStyleIOS: {
+ backgroundColor: '#1c1c1e',
+ borderWidth: 0,
+ color: Colors.text,
+ height: 200,
+ paddingVertical: 0,
+ marginVertical: 0,
+ },
+ containerSelectIOS: {
+ paddingVertical: 15,
+ paddingHorizontal: 16,
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ marginBottom: 32,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: Dimensions.get('window').width - 44,
+ },
+ textSelectIOS: {
+ fontSize: 16,
+ color: '#455c8a',
+ },
+ moreDetailsContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 40,
+ },
+ moreDetailsTouchableContainer: {
+ flexDirection: 'row',
+ },
+ moreDetailsText: {
+ fontSize: 16,
+ fontWeight: '700',
+ color: Colors.primary8,
+ marginRight: 13,
+ },
+ moreDetailsArrow: {
+ width: 24,
+ height: 24,
+ transform: [{ rotate: '45deg' }],
+ },
+ overallSummaryEstimatedTextContainer: {
+ color: Colors.text,
+ fontWeight: '600',
+ lineHeight: 46,
+ fontSize: 34,
+ marginBottom: 16,
+ },
+ overallSummaryBottomGraphContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ paddingLeft: 34,
+ marginTop: -20,
+ },
+ overallSummaryBottomGraphLabel: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 12,
+ lineHeight: 16,
+ },
+ habitsByCategoryBarContainer: {
+ flexDirection: 'column',
+ },
+ habitByCategoryItem: {
+ flexDirection: 'row',
+ height: 32,
+ marginBottom: 8,
+ alignItems: 'center',
+ },
+ habitBarContainer: {
+ height: '100%',
+ borderRadius: 4,
+ },
+ habitByCategoryBarContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ },
+ habitByCategoryBarLabel: {
+ fontWeight: '700',
+ color: Colors.text,
+ marginLeft: 8,
+ fontSize: 14,
+ lineHeight: 16,
+ },
+ habitByCategoryItemLabel: {
+ fontWeight: '700',
+ color: Colors.text,
+ marginLeft: 8,
+ fontSize: 12,
+ lineHeight: 16,
+ },
+ habitByCategoyBottomLabelContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ habitByCategoyBottomLabel: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 12,
+ lineHeight: 16,
+ },
+ exportButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#992538',
+ width: Dimensions.get('window').width - 44,
+ },
+ exportButtonText: {
+ color: '#FCFCFC',
+ fontSize: 16,
+ fontWeight: '700',
+ },
+});
+
+export default HealthHabitReport;
diff --git a/src/screens/profile/HealthHabitReportDetails.js b/src/screens/profile/HealthHabitReportDetails.js
new file mode 100644
index 0000000..eeaccd0
--- /dev/null
+++ b/src/screens/profile/HealthHabitReportDetails.js
@@ -0,0 +1,298 @@
+import React, { useState, useEffect } from 'react';
+import { View, ScrollView, StyleSheet, Dimensions, Text } from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Header from '../../components/Header';
+import { Button } from 'react-native-elements';
+import { percentageOfValueFromTotal } from '../../utils/Utils';
+import {
+ getInitialAndFinalDateBasedOnPeriod,
+ openPdf,
+} from './HealthHabitReportUtils';
+
+const HealthHabitReportDetails = props => {
+ const [period, _setPeriod] = useState(props.route?.params?.period ?? 'Month');
+ const [filtersForBackend] = useState(
+ getInitialAndFinalDateBasedOnPeriod(period)
+ );
+ const [formattedData, _setFormattedData] = useState(
+ props.route?.params?.formattedData ?? { categoryList: [] }
+ );
+
+ useEffect(() => {
+ if (!props.route?.params?.formattedData) {
+ props.navigation.navigate('HealthHabitReport');
+ }
+ }, []);
+
+ const SectionInputs = () => (
+
+
+ Period
+ {period}
+
+
+
+ Habits
+
+
+ {formattedData?.categoryList &&
+ formattedData.categoryList.map(item => (
+
+
+ {item.hac_name ?? 'N/A'}
+
+
+
+
+ {item.habits ? item.habits.length : 0}
+
+
+
+ ))}
+
+
+
+ );
+
+ const SectionCategoryAndHabits = ({ categoryAndHabit }) => {
+ const categoryCheckPercentage = percentageOfValueFromTotal(
+ categoryAndHabit.categoryHabitsChecked,
+ categoryAndHabit.categoryHabitsNeeded
+ );
+
+ return (
+
+
+
+ {categoryAndHabit.hac_name ?? 'N/A'}
+
+ {`${categoryCheckPercentage}%`}
+
+
+
+ {categoryAndHabit.habits.map((habitItem, index) => {
+ const habitCheckPercentage = percentageOfValueFromTotal(
+ habitItem.habitsChecked,
+ habitItem.habitsNeeded
+ );
+
+ return (
+
+
+ {habitItem.habit?.hab_name ?? 'N/A'}
+
+
+
+
+
+
+ );
+ })}
+
+
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ {formattedData?.categoryList ? (
+
+ {formattedData?.categoryList.map(item => (
+
+ ))}
+
+ ) : null}
+
+ openPdf(filtersForBackend, period)}
+ title="EXPORT TO PDF"
+ />
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'column',
+ flex: 1,
+ paddingBottom: 24,
+ width: Dimensions.get('window').width,
+ paddingHorizontal: 22,
+ },
+ customHeader: {
+ paddingTop: 15,
+ marginBottom: 16,
+ textAlign: 'center',
+ },
+ headerSeparator: {
+ marginBottom: 17,
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: Colors.primary4,
+ opacity: 0.2,
+ width: Dimensions.get('window').width - 44,
+ alignSelf: 'center',
+ },
+ sectionContainer: {
+ flexDirection: 'column',
+ marginBottom: 40,
+ },
+ sectionTitleContainer: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ paddingBottom: 8,
+ borderBottomColor: 'rgba(156, 198, 255, 0.4)',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ marginBottom: 32,
+ },
+ sectionTitle: {
+ fontSize: 18,
+ color: Colors.text,
+ fontWeight: '700',
+ marginRight: 32,
+ lineHeight: 20.6,
+ },
+ sectionTitleOption: {
+ fontSize: 18,
+ color: Colors.text,
+ fontWeight: '400',
+ },
+ inputContainer: {
+ flexDirection: 'column',
+ paddingBottom: 10,
+ borderBottomColor: 'rgba(156, 198, 255, 0.4)',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ },
+ inputLabel: {
+ fontSize: 16,
+ color: Colors.text,
+ lineHeight: 16,
+ fontWeight: '400',
+ },
+ inputText: {
+ fontSize: 16,
+ marginTop: 8,
+ color: Colors.primary4,
+ opacity: 0.4,
+ },
+ categoriesContainer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ },
+ categoryTagContainer: {
+ flexDirection: 'row',
+ marginTop: 8,
+ marginRight: 8,
+ paddingVertical: 2.5,
+ paddingHorizontal: 4,
+ height: 20,
+ alignSelf: 'flex-start',
+ borderColor: Colors.primary10,
+ borderWidth: StyleSheet.hairlineWidth,
+ alignItems: 'center',
+ },
+ categoryText: {
+ fontSize: 11,
+ lineHeight: 14,
+ color: Colors.primary4,
+ opacity: 0.4,
+ textTransform: 'uppercase',
+ },
+ categoryLengthContainer: {
+ backgroundColor: 'rgba(156, 198, 255, 0.4)',
+ height: 12,
+ width: 12,
+ borderRadius: 50,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginLeft: 8,
+ },
+ categoryLengthText: {
+ color: Colors.text,
+ fontSize: 8,
+ lineHeight: 8,
+ paddingTop: 1,
+ fontWeight: '700',
+ },
+ sectionHabitsContainer: {
+ flexDirection: 'column',
+ paddingHorizontal: 8,
+ },
+ sectionHabits: {
+ flexDirection: 'column',
+ },
+ habitContainer: {
+ flexDirection: 'row',
+ marginBottom: 24,
+ justifyContent: 'space-between',
+ flexGrow: 1,
+ },
+ habitName: {
+ color: Colors.primary4,
+ fontSize: 12,
+ lineHeight: 13.4,
+ width: '55.6%',
+ },
+ habitBarContainer: {
+ borderRadius: 500,
+ backgroundColor: '#992538',
+ flex: 1,
+ marginLeft: 4,
+ height: 6,
+ },
+ habitDoneBarContainer: {
+ borderRadius: 500,
+ backgroundColor: Colors.primary4,
+ height: '100%',
+ },
+ exportButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#992538',
+ width: Dimensions.get('window').width - 44,
+ },
+ exportButtonText: {
+ color: '#FCFCFC',
+ fontSize: 16,
+ fontWeight: '700',
+ },
+});
+
+export default HealthHabitReportDetails;
diff --git a/src/screens/profile/HealthHabitReportUtils.js b/src/screens/profile/HealthHabitReportUtils.js
new file mode 100644
index 0000000..7f0dfe8
--- /dev/null
+++ b/src/screens/profile/HealthHabitReportUtils.js
@@ -0,0 +1,373 @@
+import moment from 'moment';
+import { percentageOfValueFromTotal } from '../../utils/Utils';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { Constants } from '../../constants/Constants';
+import { Linking } from 'react-native';
+
+const dividedPeriods = 5;
+
+export const getInitialAndFinalDateBasedOnPeriod = period => {
+ if (period === 'Month') {
+ return {
+ initialDate: moment().subtract(30, 'd').format('YYYY-MM-DD'),
+ finalDate: moment().subtract(1, 'd').format('YYYY-MM-DD'),
+ };
+ }
+
+ if (period === 'Quarter') {
+ return {
+ initialDate: moment().subtract(90, 'd').format('YYYY-MM-DD'),
+ finalDate: moment().subtract(1, 'd').format('YYYY-MM-DD'),
+ };
+ }
+
+ return {
+ initialDate: moment().subtract(365, 'd').format('YYYY-MM-DD'),
+ finalDate: moment().subtract(1, 'd').format('YYYY-MM-DD'),
+ };
+};
+
+export const formatCategoryAndUserHabits = (
+ data,
+ { initialDate, finalDate }
+) => {
+ if (!data || (Array.isArray(data) && data.length === 0)) {
+ return { categoryList: [] };
+ }
+
+ const totalDays = getDifferenceInDaysAndAddOneToIncludeLastDate(
+ initialDate,
+ finalDate
+ );
+ const totalNumberOfEachDayInWeek = generateAndReturnTotalDaysOnWeekBaseList(
+ totalDays,
+ initialDate
+ );
+
+ // totalNeededAndDoneInPeriods é apenas um array base para ser clonado.
+ const totalNeededAndDoneInPeriods =
+ generateAndReturnTotalCheckAndDoneInFourPeriodsBaseList(
+ totalDays,
+ initialDate
+ );
+
+ let totalOfAllCategoriesChecked = 0;
+ let totalOfAllCategoriesNeeded = 0;
+
+ const formattedHabitsAndCategories = data.map(categoryItem => {
+ let categoryHabitsNeeded = 0,
+ categoryHabitsChecked = 0;
+ let categoryTotalNeededAndDoneInPeriods = [...totalNeededAndDoneInPeriods];
+
+ const habits = categoryItem.habits.map(habitItem => {
+ const { habitsChecked, habitsNeeded, habitTotalNeededAndDoneInPeriods } =
+ getHabitsCheckAndNeeded(
+ habitItem,
+ totalDays,
+ totalNumberOfEachDayInWeek,
+ totalNeededAndDoneInPeriods
+ );
+
+ categoryHabitsChecked += habitsChecked;
+ categoryHabitsNeeded += habitsNeeded;
+
+ habitTotalNeededAndDoneInPeriods.forEach((item, index) => {
+ categoryTotalNeededAndDoneInPeriods[index] = {
+ ...categoryTotalNeededAndDoneInPeriods[index],
+ checked:
+ categoryTotalNeededAndDoneInPeriods[index].checked + item.checked,
+ needed:
+ categoryTotalNeededAndDoneInPeriods[index].needed + item.needed,
+ };
+ });
+
+ return {
+ ...habitItem,
+ habitsChecked,
+ habitsNeeded,
+ habitTotalNeededAndDoneInPeriods,
+ };
+ });
+
+ totalOfAllCategoriesChecked += categoryHabitsChecked;
+ totalOfAllCategoriesNeeded += categoryHabitsNeeded;
+
+ return {
+ ...categoryItem,
+ categoryHabitsChecked,
+ categoryHabitsNeeded,
+ categoryTotalNeededAndDoneInPeriods,
+ habits,
+ };
+ });
+
+ return {
+ totalOfAllCategoriesChecked,
+ totalOfAllCategoriesNeeded,
+ categoryList: formattedHabitsAndCategories,
+ };
+};
+
+const getDifferenceInDaysAndAddOneToIncludeLastDate = (
+ initialDate,
+ finalDate
+) => {
+ const momentInitialDate = moment(initialDate);
+ const momentFinalDate = moment(finalDate);
+
+ return momentFinalDate.diff(momentInitialDate, 'd') + 1;
+};
+
+const generateAndReturnTotalDaysOnWeekBaseList = (totalDays, initialDate) => {
+ // Formato utilizado [Domingo, S, T, Q, Q, S, Sábado], o mesmo do day() do Moment.JS.
+ let totalNumberOfEachDayInWeek = [0, 0, 0, 0, 0, 0, 0];
+
+ Array(totalDays)
+ .fill()
+ .forEach((_item, index) => {
+ const initialDateClone = moment(initialDate);
+ const dayOfWeek = initialDateClone.add(index, 'd').day();
+
+ totalNumberOfEachDayInWeek[dayOfWeek]++;
+ });
+
+ return totalNumberOfEachDayInWeek;
+};
+
+const generateAndReturnTotalCheckAndDoneInFourPeriodsBaseList = (
+ totalDays,
+ initialDate
+) =>
+ Array(dividedPeriods)
+ .fill({ checked: 0, needed: 0 })
+ .map((item, index) => {
+ const startDate = moment(initialDate).add(
+ index * (totalDays / dividedPeriods),
+ 'd'
+ );
+
+ // O -1 faz com que a finalDate termine exatamente na data esperada,
+ // isso é necessário pois a initialDate está inclusa no perÃodo.
+ const finalDate = moment(initialDate).add(
+ (index + 1) * (totalDays / dividedPeriods) - 1,
+ 'd'
+ );
+
+ return { ...item, startDate, finalDate };
+ });
+
+const getHabitsCheckAndNeeded = (
+ habitItem,
+ totalDays,
+ totalNumberOfEachDayInWeek,
+ totalNeededAndDoneInPeriods
+) => {
+ const habitItemFrequency = JSON.parse(habitItem.ush_frequency);
+
+ if (habitItemFrequency.type === 'EVERYDAY') {
+ // Usa totalDays e habitItem.user_habit_check.length pois é todo dia.
+
+ return {
+ habitsNeeded: totalDays,
+ habitsChecked: habitItem.user_habit_check.length,
+
+ habitTotalNeededAndDoneInPeriods: getHabitTotalNeededAndDoneInPeriods(
+ totalNeededAndDoneInPeriods,
+ totalDays,
+ habitItem.user_habit_check
+ ),
+ };
+ }
+
+ if (habitItemFrequency.type === 'WEEKDAY') {
+ // Retira o Domingo (index 0) e Sábado (index 6).
+ const habitsNeeded = totalNumberOfEachDayInWeek
+ .filter((_item, index) => index !== 0 && index !== 6)
+ .reduce(
+ (totalNumberInWeekDayA, totalNumberInWeekDayB) =>
+ totalNumberInWeekDayA + totalNumberInWeekDayB,
+ 0
+ );
+ const habitCheckedList = habitItem.user_habit_check.filter(item => {
+ const dayOfWeekChecked = moment(item.uhc_date).day();
+
+ return dayOfWeekChecked !== 0 && dayOfWeekChecked !== 6;
+ });
+
+ return {
+ habitsNeeded: habitsNeeded,
+ habitsChecked: habitCheckedList.length,
+
+ habitTotalNeededAndDoneInPeriods: getHabitTotalNeededAndDoneInPeriods(
+ totalNeededAndDoneInPeriods,
+ habitsNeeded,
+ habitCheckedList
+ ),
+ };
+ }
+
+ // Como o habitItemFrequency.days é retornado em um array de boolean da para utilizá-lo
+ // como variável TRUE / FALSE para filtrar o array,
+ // se tiver preenchido ele será TRUE e se não for utilizado será FALSE.
+
+ const customHabitsNeeded = totalNumberOfEachDayInWeek
+ .filter(
+ (_item, index) =>
+ habitItemFrequency.days[transformDayNumberOnWeekToStoredFormat(index)]
+ )
+ .reduce(
+ (totalNumberInWeekDayA, totalNumberInWeekDayB) =>
+ totalNumberInWeekDayA + totalNumberInWeekDayB,
+ 0
+ );
+
+ const customHabitsCheckedList = habitItem.user_habit_check.filter(item => {
+ const formattedDayOfWeekChecked = transformDayNumberOnWeekToStoredFormat(
+ moment(item.uhc_date).day()
+ );
+
+ return habitItemFrequency.days[formattedDayOfWeekChecked];
+ });
+
+ return {
+ habitsNeeded: customHabitsNeeded,
+ habitsChecked: customHabitsCheckedList.length,
+
+ habitTotalNeededAndDoneInPeriods: getHabitTotalNeededAndDoneInPeriods(
+ totalNeededAndDoneInPeriods,
+ customHabitsNeeded,
+ customHabitsCheckedList
+ ),
+ };
+};
+
+const transformDayNumberOnWeekToStoredFormat = numberInMoment => {
+ // O formato que o Backend salva que dia o CUSTOM HABIT acontece é [Sábado, D, S, T, Q, Q, Sexta].
+ // O utilizado no totalNumberOfEachDayInWeek foi [Domingo, S, T, Q, Q, S, Sábado], o mesmo do day() do Moment.JS.
+
+ if (numberInMoment === 6) {
+ return 0;
+ }
+
+ return numberInMoment + 1;
+};
+
+const getHabitTotalNeededAndDoneInPeriods = (
+ totalNeededAndDoneInPeriods,
+ totalNeeded,
+ listToFilter
+) => {
+ const formattedListPeriod = totalNeededAndDoneInPeriods.map(item => {
+ const filteredHabitChecks = listToFilter.filter(
+ (userHabitCheckItem, index) => {
+ const momentUhcDate = moment(userHabitCheckItem.uhc_date);
+
+ // Inclui tanto a primeira data quanto a última, ou seja, [].
+ return momentUhcDate.isBetween(
+ item.startDate,
+ item.finalDate,
+ undefined,
+ '[]'
+ );
+ }
+ );
+
+ return {
+ ...item,
+ needed: totalNeeded / dividedPeriods,
+ checked: filteredHabitChecks.length,
+ };
+ });
+
+ return formattedListPeriod;
+};
+
+export const getPeriodLabelList = period => {
+ if (period === 'Month') {
+ return ['1ST WEEK', '2ND WEEK', '3RD WEEK', '4TH WEEK'];
+ }
+
+ if (period === 'Quarter') {
+ return ['1ST MONTH', '2ND MONTH', '3RD MONTH'];
+ }
+
+ return ['1ST QUART.', '2ND QUART.', '3RD QUART.', '4TH QUART.'];
+};
+
+export const getOverallSummaryContent = (formattedData, habitInputValue) => {
+ if (habitInputValue) {
+ const categoryIndex = formattedData.categoryList.findIndex(item =>
+ item.habits.some(userHabitItem => userHabitItem.id === habitInputValue)
+ );
+ const userHabitIndex = formattedData.categoryList[
+ categoryIndex
+ ].habits.findIndex(userHabitItem => userHabitItem.id === habitInputValue);
+ const selectedUserHabit =
+ formattedData.categoryList[categoryIndex].habits[userHabitIndex];
+
+ return {
+ average: percentageOfValueFromTotal(
+ selectedUserHabit.habitsChecked,
+ selectedUserHabit.habitsNeeded
+ ),
+ data: selectedUserHabit.habitTotalNeededAndDoneInPeriods.map(item =>
+ percentageOfValueFromTotal(item.checked, item.needed)
+ ),
+ };
+ }
+
+ let parcialData = Array(dividedPeriods)
+ .fill(null)
+ .map(() => ({ checked: 0, needed: 0 }));
+
+ formattedData.categoryList.forEach(item => {
+ item.categoryTotalNeededAndDoneInPeriods.forEach(
+ (categoryTotalPeriodItem, index) => {
+ parcialData[index].checked =
+ parcialData[index].checked + categoryTotalPeriodItem.checked;
+ parcialData[index].needed += categoryTotalPeriodItem.needed;
+ }
+ );
+ });
+
+ return {
+ average: percentageOfValueFromTotal(
+ formattedData.totalOfAllCategoriesChecked,
+ formattedData.totalOfAllCategoriesNeeded
+ ),
+ data: parcialData.map(item =>
+ percentageOfValueFromTotal(item.checked, item.needed)
+ ),
+ };
+};
+
+export const getHabitsByCategoryBarColor = donePercentage => {
+ if (donePercentage >= 60) {
+ return { from: '#33BD8A', to: '#21835F' };
+ }
+
+ if (donePercentage >= 40) {
+ return { from: '#DACE67', to: '#9A9C26' };
+ }
+
+ return { from: '#DA6767', to: '#C53030' };
+};
+
+export const openPdf = async (filtersForBackend, period) => {
+ let value = await AsyncStorage.getItem('token');
+ let initialDateClone = filtersForBackend.initialDate;
+
+ // Foram utilizados valores de 28 para Month e 364 para Year pois se pode dividir isto em 4 perÃodos que é o que o PDF pede.
+ if (period === 'Month')
+ initialDateClone = moment(initialDateClone)
+ .add(2, 'd')
+ .format('YYYY-MM-DD');
+ if (period === 'Year')
+ initialDateClone = moment(initialDateClone)
+ .add(1, 'd')
+ .format('YYYY-MM-DD');
+
+ Linking.openURL(
+ `${Constants.baseUrl}/getPdfHistoricoFicha?token=${value}&initialDate=${initialDateClone}&finalDate=${filtersForBackend.finalDate}`
+ );
+};
diff --git a/src/screens/profile/Profile.js b/src/screens/profile/Profile.js
new file mode 100644
index 0000000..c7377e7
--- /dev/null
+++ b/src/screens/profile/Profile.js
@@ -0,0 +1,1240 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ Dimensions,
+ Image,
+ FlatList,
+ Text,
+ RefreshControl,
+ TouchableOpacity,
+ Alert,
+ Modal,
+ TextInput,
+ Share,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+// import { useDispatch, useSelector } from "react-redux";
+import { Button } from 'react-native-elements';
+import { systemWeights } from 'react-native-typography';
+import moment from 'moment';
+// import {
+// logout,
+// updateInfos,
+// getUserInfos,
+// userLogged,
+// } from "../../store/ducks/user";
+import { LinearGradient } from 'expo-linear-gradient';
+import CardHabits from '../../components/community/CardHabits';
+import CardCommunity from '../../components/community/CardCommunity';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+
+const mockUser = {
+ name: 'Kylie Jenner',
+ image: { url: 'https://via.placeholder.com/70' },
+ usr_quote_to_live_by: "It's not just a bag! It's Prada.",
+ usr_biggest_hack: 'Botox of course.',
+ usr_biggest_challenge: 'Being nice to my fans.',
+ usr_favorite_book: 'To Kill a Mockingbird',
+ image_book: { url: 'https://via.placeholder.com/150' },
+ usr_favorite_food: 'Caviar',
+ image_food: { url: 'https://via.placeholder.com/150' },
+};
+
+const Profile = props => {
+ const [fetching, setFetching] = useState(true);
+ const [refreshing, setRefreshing] = useState(false);
+ const [text_quote, setTextQuote] = useState('');
+ const [text_hack, setTextHack] = useState('');
+ const [text_challenge, setTextChallenge] = useState('');
+ const [last_user_score, setLastUserScore] = useState(null);
+ const [can_review_score, setCanReviewScore] = useState(false);
+ const [connections, setConnections] = useState([]);
+ const [list_habits, setListHabits] = useState('');
+ const [communities, setCommunities] = useState([]);
+ const [modal, setModal] = useState(false);
+ const [modal_type, setModalType] = useState('');
+
+ // const user = useSelector(({ user }) => user);
+
+ // const dispatch = useDispatch();
+
+ const RBSExit = useRef();
+
+ useEffect(() => {
+ setInfos();
+ fetchAll(true, false, false);
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = props.navigation.addListener('focus', () => {
+ fetchAll(false, false, true);
+ });
+
+ return unsubscribe;
+ }, [props.navigation, fetchAll]);
+
+ const setInfos = () => {
+ mockUser?.usr_quote_to_live_by
+ ? setTextQuote(mockUser?.usr_quote_to_live_by)
+ : null;
+ mockUser?.usr_biggest_hack ? setTextHack(mockUser?.usr_biggest_hack) : null;
+ mockUser?.usr_biggest_challenge
+ ? setTextChallenge(mockUser?.usr_biggest_challenge)
+ : null;
+ };
+
+ const fetchAll = async (isFetching, isRefreshing, force) => {
+ if (force || isFetching) {
+ isRefreshing
+ ? setRefreshing(true)
+ : isFetching
+ ? setFetching(true)
+ : null;
+
+ // Simulating API call
+ setTimeout(() => {
+ let habits_aux = [];
+
+ setConnections([]);
+ setCommunities([]);
+ setLastUserScore(null);
+ setListHabits(habits_aux);
+
+ setFetching(false);
+ setRefreshing(false);
+ }, 1000);
+ }
+ };
+
+ const onConnections = () => {
+ props.navigation.navigate('Connections');
+ };
+
+ const onRanking = () => {
+ props.navigation.navigate('Ranking');
+ };
+
+ const sendInfo = () => {
+ let request = {
+ type_info: modal_type,
+ user_info:
+ modal_type === 'quote'
+ ? text_quote
+ : modal_type === 'hack'
+ ? text_hack
+ : modal_type === 'challenge'
+ ? text_challenge
+ : null,
+ };
+
+ // Simulating API call
+ Alert.alert('Info Saved', JSON.stringify(request));
+ };
+
+ const shareMyProfile = async () => {
+ const urlAndroid =
+ 'https://play.google.com/store/apps/details?id=com.alex.live.timeless';
+ const urlApple =
+ 'https://apps.apple.com/br/app/live-timeless/id1556115926?l=en';
+ const message =
+ 'Check my profile on Live Timeless App.\nAndroid Link: ' +
+ urlAndroid +
+ '\nApple Link: ' +
+ urlApple;
+
+ try {
+ const result = await Share.share({
+ title: 'Join to my community in Live Timeless App',
+ message,
+ url: urlApple,
+ });
+ if (result.action === Share.sharedAction) {
+ if (result.activityType) {
+ // shared with activity type of result.activityType
+ } else {
+ }
+ } else if (result.action === Share.dismissedAction) {
+ // dismissed
+ }
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const handleLogout = () => {
+ AsyncStorage.getAllKeys()
+ .then(keys => AsyncStorage.multiRemove(keys))
+ .then(() => navigation.reset({ index: 0, routes: [{ name: 'Login' }] }));
+ };
+
+ return (
+
+ fetchAll(false, true, true)}
+ refreshing={refreshing}
+ />
+ }>
+
+
+
+
+
+
+ {mockUser.image ? (
+
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+
+ {`Hi, ${mockUser.name}`}
+
+
+
+
+
+
+
+ Share My Profile
+
+
+
+ props.navigation.navigate('UpdateProfile')}>
+
+
+ Edit Profile
+
+
+
+
+ onRanking()}
+ style={[styles.containerSection, { marginBottom: 8 }]}>
+
+ {last_user_score ? (
+ <>
+
+
+ Ranking
+
+
+
+ {can_review_score ? (
+ props.navigation.push('ScoreForm')}>
+
+ Review Score
+
+
+ ) : null}
+
+
+ {last_user_score.uss_score?.toFixed(0)}
+
+
+ >
+ ) : (
+ <>
+
+
+ Ranking
+
+
+ props.navigation.push('ScoreForm')}
+ title="Get your score"
+ />
+ >
+ )}
+
+
+
+ onConnections()}
+ style={[styles.containerSection, { marginBottom: 8 }]}>
+
+
+
+ {connections}
+ Connections
+
+
+
+
+ props.navigation.navigate('SavedPost')}
+ style={[styles.containerSection, { marginBottom: 8 }]}>
+
+
+ Saved Items
+
+
+
+ props.navigation.navigate('HealthHabitReport')}
+ title="Health Habits Report"
+ />
+
+
+
+ Quote to live by
+ {mockUser?.usr_quote_to_live_by !== '' &&
+ mockUser?.usr_quote_to_live_by !== null ? (
+ <>
+
+ {mockUser?.usr_quote_to_live_by}
+
+ {
+ setModal(!modal), setModalType('quote');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Edit
+
+
+ >
+ ) : (
+ {
+ setModal(!modal), setModalType('quote');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Phrases
+
+
+ )}
+
+
+
+
+
+ Biggest
+ Hack in Life
+
+ {mockUser?.usr_biggest_hack !== '' &&
+ mockUser?.usr_biggest_hack !== null ? (
+ <>
+
+ {mockUser?.usr_biggest_hack}
+
+ {
+ setModal(!modal), setModalType('hack');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Edit
+
+
+ >
+ ) : (
+ <>
+
+ {
+ setModal(!modal), setModalType('hack');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Hacks
+
+
+ >
+ )}
+
+
+
+ Biggest Challenge
+
+ {mockUser?.usr_biggest_challenge !== '' &&
+ mockUser?.usr_biggest_challenge !== null ? (
+ <>
+
+ {mockUser?.usr_biggest_challenge}
+
+
+ {
+ setModal(!modal), setModalType('challenge');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Edit
+
+
+ >
+ ) : (
+ <>
+
+ {
+ setModal(!modal), setModalType('challenge');
+ }}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Challenges
+
+
+ >
+ )}
+
+
+
+
+
+
+
+ Communities Public
+
+ {communities?.length > 0 ? (
+ props.navigation.push('UserCommunity')}>
+
+ See All
+
+
+ ) : null}
+
+ {communities?.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ snapToAlignment={'start'}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+
+ We don't have any public communities yet.
+
+
+ )}
+
+
+
+
+
+ Daily Habits
+
+ {list_habits.length > 0 ? (
+ props.navigation.push('UserHabit')}>
+
+ See All
+
+
+ ) : null}
+
+ {list_habits?.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ snapToAlignment={'start'}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+
+ We don't have habits yet.
+
+
+ )}
+
+
+
+
+ Favorite Book
+
+ {mockUser.image_book ? (
+ <>
+
+ {mockUser?.usr_favorite_book}
+
+
+
+
+ >
+ ) : null}
+ props.navigation.push('UpdateFavoriteBook')}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Edit
+
+
+
+
+
+ Favorite Food
+
+ {mockUser.image_food ? (
+
+
+ {mockUser?.usr_favorite_food}
+
+
+
+
+
+ ) : null}
+ props.navigation.push('UpdateFavoriteFood')}
+ style={{
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginTop: 16,
+ }}>
+ Edit
+
+
+
+
+
+
+
+
+ {
+ handleLogout();
+ }}>
+
+
+ Logout
+
+
+
+
+
+
+ setModal(!modal)}>
+
+
+ setModal(!modal)}>
+
+
+ {modal_type === 'quote' ? (
+
+ Quote to live by
+
+
+ ) : modal_type === 'hack' ? (
+
+
+ Biggest Hack in life
+
+
+
+ ) : modal_type === 'challenge' ? (
+
+ Biggest Challenge
+
+
+ ) : null}
+
+
+
+
+
+
+ {modal ? (
+ setModal(!modal)}
+ style={styles.containerShadow}>
+ ) : null}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingHorizontal: 15,
+ paddingTop: 28,
+ marginTop: -38,
+ width: Dimensions.get('window').width,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerHeader: {
+ flexDirection: 'column',
+ alignSelf: 'center',
+ alignItems: 'center',
+ marginBottom: 32,
+ },
+ containerPhoto: {
+ flexDirection: 'row',
+ },
+ userPhoto: {
+ alignSelf: 'center',
+ width: 70,
+ height: 70,
+ borderRadius: 32,
+ marginTop: 5,
+ position: 'relative',
+ },
+ borderPhoto: {
+ alignSelf: 'center',
+ width: 80,
+ height: 80,
+ borderRadius: 32,
+ position: 'absolute',
+ },
+ containerFavorite: {
+ //flex: 1,
+ marginTop: 16,
+ height: 191,
+ width: (Dimensions.get('window').width - 110) / 2,
+ borderRadius: 4,
+ alignSelf: 'flex-end',
+ },
+ imageFavorite: {
+ height: '100%',
+ width: '100%',
+ borderRadius: 4,
+ },
+ favoriteImage: {
+ width: '100%',
+ height: '100%',
+ },
+ iconsHeader: {
+ marginRight: 4,
+ alignSelf: 'center',
+ width: 16,
+ height: 16,
+ },
+ iconMedal: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 32,
+ height: 32,
+ },
+ iconRay: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 24,
+ height: 24,
+ },
+ textName: {
+ fontSize: 24,
+ color: Colors.text,
+ marginTop: 14,
+ alignSelf: 'center',
+ },
+ containerList: {
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ marginHorizontal: -16,
+ },
+ textSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignSelf: 'flex-end',
+ marginRight: 16,
+ },
+ containerSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ alignSelf: 'flex-end',
+ marginLeft: 16,
+ zIndex: 4,
+ elevation: 4,
+ position: 'absolute',
+ },
+ containerShadow: {
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ marginTop: -58,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ containerActionsHeader: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 34,
+ },
+ containerTitle: {
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: '#264261',
+ paddingBottom: 8,
+ marginBottom: 19,
+ },
+ textTitle: {
+ fontSize: 16,
+ color: Colors.text,
+ fontWeight: 'bold',
+ },
+ containerSection: {
+ marginBottom: 40,
+ borderRadius: 8,
+ },
+ textEmpty: {
+ fontSize: 12,
+ lineHeight: 16,
+ height: 16,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginLeft: 16,
+ },
+ containerBlur: {
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ marginTop: -3,
+ },
+ warningIconStyle: {
+ width: 80,
+ height: 80,
+ },
+ containerScore: {
+ borderRadius: 8,
+ padding: 24,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerConnection: {
+ borderRadius: 8,
+ padding: 28,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ containerSaved: {
+ borderRadius: 8,
+ padding: 28,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ },
+ containerProfile: {
+ borderRadius: 8,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerCard: {
+ borderRadius: 4,
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ width: Dimensions.get('window').width - 32,
+ },
+ containerBiggest: {
+ flexGrow: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignSelf: 'center',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ },
+ containerCardBiggest: {
+ display: 'flex',
+ height: '100%',
+ flexDirection: 'column',
+ width: (Dimensions.get('window').width - 48) / 2,
+ borderRadius: 4,
+ padding: 16,
+ alignItems: 'flex-start',
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ },
+ containerGroup: {
+ flexDirection: 'row',
+ marginBottom: 24,
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerInfo: {
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ alignSelf: 'center',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ },
+ textConnections: {
+ color: Colors.text,
+ fontWeight: '600',
+ letterSpacing: -0.02,
+ fontSize: 10,
+ lineHeight: 10,
+ marginTop: 3,
+ },
+ textTotalConnections: {
+ color: Colors.text,
+ fontSize: 13,
+ lineHeight: 10,
+ fontWeight: '600',
+ textAlign: 'center',
+ paddingTop: 3,
+ marginRight: 4,
+ marginTop: 4,
+ },
+ textRanking: {
+ color: Colors.text,
+ fontWeight: '400',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ },
+ textPositionRanking: {
+ color: Colors.primary4,
+ fontWeight: '700',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ alignContent: 'center',
+ },
+ textInfo: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 16,
+ },
+ textBiggest: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 22.4,
+ },
+ textNameFavorite: {
+ color: Colors.primary8,
+ fontWeight: '700',
+ fontSize: 14,
+ lineHeight: 16,
+ },
+ textTouchable: {
+ color: Colors.primary8,
+ fontWeight: '700',
+ fontSize: 14,
+ lineHeight: 21,
+ },
+ textReviewScore: {
+ color: Colors.primary4,
+ fontSize: 14,
+ lineHeight: 19,
+ textDecorationLine: 'underline',
+ //marginTop: 6
+ },
+ userScore: {
+ fontSize: 16,
+ fontWeight: '700',
+ color: Colors.text,
+ lineHeight: 16,
+ },
+ ratingUserScore: {
+ fontSize: 16,
+ lineHeight: 19,
+ color: Colors.text,
+ },
+ btnGetScore: {
+ backgroundColor: Colors.primary3,
+ borderRadius: 4,
+ paddingHorizontal: 35,
+ paddingVertical: 12,
+ },
+ titleBtnGetScore: {
+ color: Colors.text,
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: 'bold',
+ },
+ containerButton: {
+ marginTop: 12,
+ marginBottom: 22,
+ alignItems: 'center',
+ },
+ buttonProducts: {
+ height: 56,
+ borderRadius: 30,
+ backgroundColor: 'transparent',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#9CC6FF',
+ width: Dimensions.get('window').width - 44,
+ marginHorizontal: 24,
+ },
+ titleProducts: {
+ fontSize: 16,
+ lineHeight: 21,
+ color: Colors.primary4,
+ alignContent: 'center',
+ },
+ containerEmpty: {
+ alignSelf: 'flex-start',
+ marginRight: 16,
+ marginTop: 16,
+ },
+ logoutText: {
+ fontSize: 16,
+ lineHeight: 22,
+ fontWeight: '700',
+ color: 'white',
+ },
+ editProfile: {
+ alignSelf: 'flex-end',
+ },
+ editProfileText: {
+ fontSize: 14,
+ lineHeight: 19,
+ color: Colors.primary8,
+ fontWeight: '700',
+ },
+ centeredView: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalView: {
+ borderRadius: 4,
+ marginHorizontal: 32,
+ alignItems: 'flex-start',
+ shadowColor: '#000',
+ paddingBottom: 50,
+ paddingTop: 16,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ width: Dimensions.get('window').width - 44,
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: '#082139',
+ },
+ containerTextModal: {
+ alignContent: 'center',
+ },
+ textInputStyle: {
+ color: Colors.primary4,
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ fontSize: 16,
+ paddingHorizontal: 12,
+ paddingTop: 13,
+ height: 96,
+ backgroundColor: Colors.primary,
+ textAlignVertical: 'top',
+ marginBottom: 26,
+ marginTop: 12,
+ },
+ textDescriptionStyle: {
+ color: Colors.text,
+ textAlignVertical: 'top',
+ },
+ nextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ width: Dimensions.get('window').width - 48,
+ },
+ modalButton: {
+ height: 56,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ },
+ modalInput: {
+ borderColor: '#455c8a',
+ color: Colors.primary4,
+ fontSize: 16,
+ width: Dimensions.get('window').width - 80,
+ },
+ containerHeaderModal: {
+ flexDirection: 'row',
+ alignSelf: 'flex-end',
+ marginBottom: 12,
+ },
+ imageModalHeader: {
+ width: 24,
+ resizeMode: 'contain',
+ height: 24,
+ },
+ containerSectionModal: {
+ alignSelf: 'center',
+ width: Dimensions.get('window').width - 80,
+ zIndex: 5,
+ elevation: 5,
+ },
+ textLabelModal: {
+ fontSize: 16,
+ fontWeight: '400',
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ textDescriptionInfo: {
+ fontSize: 18,
+ fontWeight: '400',
+ lineHeight: 27,
+ color: Colors.text,
+ marginTop: 16,
+ },
+ healthHabitReportButton: {
+ height: 64,
+ borderRadius: 8,
+ marginTop: 16,
+ marginBottom: 16,
+ backgroundColor: 'transparent',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#9CC6FF',
+ width: Dimensions.get('window').width - 32,
+ },
+});
+export default Profile;
diff --git a/src/screens/profile/SavedPost.js b/src/screens/profile/SavedPost.js
new file mode 100644
index 0000000..d284cd0
--- /dev/null
+++ b/src/screens/profile/SavedPost.js
@@ -0,0 +1,873 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ ScrollView,
+ StyleSheet,
+ RefreshControl,
+ Dimensions,
+ ActivityIndicator,
+ Image,
+ Text,
+ TouchableOpacity,
+ Alert,
+ Share,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+import PostCommunity from '../../components/community/PostCommunity';
+import PostHabits from '../../components/community/PostHabits';
+import { useSelector } from 'react-redux';
+import { LinearGradient } from 'expo-linear-gradient';
+import { getSavedPosts } from '../../store/ducks/user';
+import { BlurView } from 'expo-blur';
+import CardPost from '../../components/CardPost';
+
+const SavedPost = props => {
+ const [fetching, setFetching] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [page, setPage] = useState(0);
+ const [load_more, setLoadMore] = useState(true);
+ const [refreshing, setRefreshing] = useState(false);
+ const [list_posts, setListPosts] = useState([]);
+ const [blurActive, setBlurActive] = useState(false);
+ const [length_post, setLengthPost] = useState(0);
+ const user = useSelector(({ user }) => user);
+
+ useEffect(() => {
+ fetchAll(true, false);
+ }, []);
+
+ const fetchAll = (isFetching, isRefreshing) => {
+ isFetching ? setFetching(true) : isRefreshing ? setRefreshing(true) : null;
+
+ getSavedPosts({ page: 0 })
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setListPosts(res.data.data);
+ setLengthPost(res.data.data.length);
+ setPage(0);
+ }
+
+ if (res.data?.current_page === res.data?.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ } else {
+ setLoadMore(true);
+ }
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ setRefreshing(false);
+ });
+ };
+
+ const loadMore = () => {
+ let post_aux = [];
+ let number_page = page + 1;
+
+ setPage(number_page);
+ setLoading(true);
+
+ let request = {
+ page: number_page,
+ };
+
+ getSavedPosts(request)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ post_aux = [...list_posts];
+ post_aux = post_aux.concat(res.data?.data);
+
+ setListPosts(post_aux);
+ setLengthPost(res.data.data.length);
+
+ if (res.data?.current_page === res.data?.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ }
+ }
+
+ setLoading(false);
+ }
+ });
+ };
+
+ const shareMyProfile = async () => {
+ const urlAndroid =
+ 'https://play.google.com/store/apps/details?id=com.alex.live.timeless';
+ const urlApple =
+ 'https://apps.apple.com/br/app/live-timeless/id1556115926?l=en';
+ const message =
+ 'Check my profile on Live Timeless App.\nAndroid Link: ' +
+ urlAndroid +
+ '\nApple Link: ' +
+ urlApple;
+
+ try {
+ const result = await Share.share({
+ title: 'Join to my community in Live Timeless App',
+ message,
+ url: urlApple,
+ });
+ if (result.action === Share.sharedAction) {
+ if (result.activityType) {
+ // shared with activity type of result.activityType
+ } else {
+ }
+ } else if (result.action === Share.dismissedAction) {
+ // dismissed
+ }
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const isCloseToBottom = ({
+ layoutMeasurement,
+ contentOffset,
+ contentSize,
+ }) => {
+ const paddingToBottom = 16;
+ return (
+ layoutMeasurement.height + contentOffset.y >=
+ contentSize.height - paddingToBottom
+ );
+ };
+
+ const onLikePostSuccess = postIndex => {
+ const listPostsClone = [...list_posts];
+
+ listPostsClone[postIndex].likeFromUser =
+ !listPostsClone[postIndex].likeFromUser;
+
+ if (listPostsClone[postIndex].likeFromUser) {
+ listPostsClone[postIndex].count_likes++;
+ } else {
+ listPostsClone[postIndex].count_likes--;
+ }
+
+ setListPosts(listPostsClone);
+ };
+
+ const onSavePostSuccess = postIndex => {
+ const listPostsClone = [...list_posts];
+
+ listPostsClone[postIndex].saveFromUser =
+ !listPostsClone[postIndex].saveFromUser;
+
+ setListPosts(listPostsClone);
+ };
+
+ const onDeletePostSuccess = postIndex => {
+ const listPostsClone = [...list_posts].filter(
+ (_item, postIndexFromItem) => postIndexFromItem !== postIndex
+ );
+
+ setListPosts(listPostsClone);
+ setBlurActive(false);
+ };
+
+ return (
+
+ {
+ if (
+ list_posts.length > 0 &&
+ load_more &&
+ isCloseToBottom(nativeEvent)
+ ) {
+ loadMore();
+ }
+ }}
+ refreshControl={
+ fetchAll(false, true)}
+ refreshing={refreshing}
+ />
+ }>
+
+
+
+
+
+ {user.image ? (
+
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+ {`Hi, ${user.name}!`}
+
+
+
+
+
+
+ Share My Profile
+
+
+
+ props.navigation.push('UpdateProfile')}>
+
+
+ Edit Profile
+
+
+
+
+
+
+
+ Saved Items
+
+
+
+
+ {list_posts.length > 0 ? (
+ list_posts.map((timelinePost, index) => {
+ return (
+
+ onLikePostSuccess(index),
+
+ count_comments: timelinePost.count_comments,
+
+ saveFromUser: timelinePost.saveFromUser,
+ onSavePostSuccess: postId => onSavePostSuccess(index),
+
+ onDeletePostSuccess: postId =>
+ onDeletePostSuccess(index),
+ }}
+ setBlurActive={setBlurActive}
+ />
+
+ );
+ })
+ ) : (
+
+
+ No posts saved.
+
+
+ )}
+
+ {loading ? (
+
+
+
+ ) : null}
+
+
+
+
+
+ {blurActive ? (
+ <>
+
+
+ >
+ ) : null}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingTop: 10,
+ //marginTop: -38,
+ width: Dimensions.get('window').width,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerBody: {
+ display: 'flex',
+ justifyContent: 'center',
+ marginTop: 16,
+ paddingHorizontal: 16,
+ paddingBottom: 32,
+ },
+ containerNoPost: {
+ flex: 1,
+ marginTop: 16,
+ alignSelf: 'center',
+ width: Dimensions.get('window').width - 32,
+ },
+ containerHeader: {
+ alignItems: 'center',
+ marginBottom: 32,
+ },
+ containerPhoto: {
+ flexDirection: 'row',
+ },
+ userPhoto: {
+ alignSelf: 'center',
+ width: 70,
+ height: 70,
+ borderRadius: 32,
+ marginTop: 5,
+ position: 'absolute',
+ },
+ borderPhoto: {
+ alignSelf: 'center',
+ width: 80,
+ height: 80,
+ borderRadius: 32,
+ position: 'relative',
+ },
+ containerFavorite: {
+ marginTop: 16,
+ width: '100%',
+ height: 191,
+ borderRadius: 4,
+ alignSelf: 'flex-start',
+ },
+ imageFavorite: {
+ flex: 1,
+ justifyContent: 'flex-end',
+ borderRadius: 4,
+ },
+ favoriteImage: {
+ width: '100%',
+ height: '100%',
+ },
+ iconsHeader: {
+ marginRight: 4,
+ alignSelf: 'center',
+ width: 16,
+ height: 16,
+ },
+ iconMedal: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 32,
+ height: 32,
+ },
+ iconRay: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 24,
+ height: 24,
+ },
+ textName: {
+ fontSize: 24,
+ color: Colors.text,
+ marginTop: 14,
+ },
+ containerList: {
+ height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ marginHorizontal: -16,
+ },
+ textSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignSelf: 'flex-end',
+ marginRight: 16,
+ },
+ containerSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ alignSelf: 'flex-end',
+ marginLeft: 16,
+ zIndex: 4,
+ elevation: 4,
+ position: 'absolute',
+ },
+ containerShadow: {
+ backgroundColor: 'rgba(0,0,0,0.6)',
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ marginTop: -58,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ containerActionsHeader: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 34,
+ marginHorizontal: 16,
+ },
+ containerTitle: {
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: '#264261',
+ paddingBottom: 8,
+ marginBottom: 19,
+ },
+ textTitle: {
+ fontSize: 16,
+ color: Colors.text,
+ fontWeight: 'bold',
+ },
+ containerSection: {
+ borderRadius: 8,
+ },
+ textEmpty: {
+ fontSize: 12,
+ lineHeight: 16,
+ height: 16,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginLeft: 16,
+ },
+ containerBlur: {
+ zIndex: 1,
+ elevation: 1,
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ marginTop: -3,
+ },
+ containerBottomSheet: {
+ alignItems: 'center',
+ backgroundColor: Colors.primary,
+ },
+ containerTextBottomSheet: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonContainer: {
+ marginBottom: 30,
+ alignItems: 'center',
+ },
+ createAccountText: {
+ fontSize: 14,
+ color: 'white',
+ },
+ textExit: {
+ marginTop: 26,
+ fontSize: 14,
+ color: Colors.text,
+ },
+ containerAchievement: {
+ backgroundColor: Colors.primary2,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ borderRadius: 4,
+ paddingHorizontal: 16,
+ marginBottom: 16,
+ },
+ containerAchievementName: {
+ flexDirection: 'row',
+ },
+ achievementHabitNme: {
+ color: Colors.text,
+ paddingVertical: 20,
+ marginLeft: 15,
+ fontSize: 12,
+ },
+ containerAchievementCategory: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ achievementCategory: {
+ marginLeft: 8,
+ fontSize: 12,
+ color: Colors.text,
+ },
+ warningIconStyle: {
+ width: 80,
+ height: 80,
+ },
+ containerScore: {
+ borderRadius: 8,
+ padding: 24,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerConnection: {
+ borderRadius: 8,
+ padding: 28,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ containerSaved: {
+ borderRadius: 8,
+ padding: 28,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ },
+ containerProfile: {
+ borderRadius: 8,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerCard: {
+ borderRadius: 4,
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ width: Dimensions.get('window').width - 32,
+ },
+ containerBiggest: {
+ flexGrow: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignSelf: 'center',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ },
+ containerCardBiggest: {
+ display: 'flex',
+ height: '100%',
+ flexDirection: 'column',
+ width: (Dimensions.get('window').width - 48) / 2,
+ backgroundColor: 'rgba(156, 198, 255, 0.042)',
+ borderRadius: 4,
+ padding: 16,
+ alignItems: 'flex-start',
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ },
+ containerGroup: {
+ flexDirection: 'row',
+ marginBottom: 24,
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ containerInfo: {
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ alignSelf: 'center',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ },
+ textConnections: {
+ color: Colors.text,
+ fontWeight: '600',
+ letterSpacing: -0.02,
+ fontSize: 10,
+ lineHeight: 10,
+ marginTop: 3,
+ },
+ textTotalConnections: {
+ color: Colors.text,
+ fontSize: 13,
+ lineHeight: 10,
+ fontWeight: '600',
+ textAlign: 'center',
+ paddingTop: 3,
+ marginRight: 4,
+ marginTop: 4,
+ },
+ textRanking: {
+ color: Colors.text,
+ fontWeight: '400',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ },
+ textPositionRanking: {
+ color: Colors.primary4,
+ fontWeight: '700',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ alignContent: 'center',
+ },
+ textInfo: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 16,
+ },
+ textBiggest: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 22.4,
+ },
+ textNameFavorite: {
+ color: Colors.primary8,
+ fontWeight: '700',
+ fontSize: 14,
+ lineHeight: 16,
+ },
+ textTouchable: {
+ color: Colors.primary8,
+ fontWeight: '700',
+ fontSize: 14,
+ lineHeight: 21,
+ },
+ textReviewScore: {
+ color: Colors.primary4,
+ fontSize: 14,
+ lineHeight: 19,
+ textDecorationLine: 'underline',
+ marginTop: 6,
+ },
+ userScore: {
+ fontSize: 16,
+ fontWeight: '700',
+ color: Colors.text,
+ lineHeight: 16,
+ },
+ ratingUserScore: {
+ fontSize: 16,
+ lineHeight: 19,
+ color: Colors.text,
+ },
+ btnGetScore: {
+ backgroundColor: Colors.primary3,
+ borderRadius: 4,
+ paddingHorizontal: 35,
+ paddingVertical: 12,
+ },
+ titleBtnGetScore: {
+ color: Colors.text,
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: 'bold',
+ },
+ containerButton: {
+ marginTop: 12,
+ marginBottom: 22,
+ alignItems: 'center',
+ },
+ buttonProducts: {
+ height: 56,
+ borderRadius: 30,
+ backgroundColor: 'transparent',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: '#9CC6FF',
+ width: Dimensions.get('window').width - 44,
+ marginHorizontal: 24,
+ },
+ titleProducts: {
+ fontSize: 16,
+ lineHeight: 21,
+ color: Colors.primary4,
+ alignContent: 'center',
+ },
+ containerEmpty: {
+ alignSelf: 'flex-start',
+ marginRight: 16,
+ marginTop: 16,
+ },
+ logoutText: {
+ fontSize: 16,
+ lineHeight: 22,
+ fontWeight: '700',
+ color: 'white',
+ },
+ editProfile: {
+ alignSelf: 'flex-end',
+ },
+ editProfileText: {
+ fontSize: 14,
+ lineHeight: 19,
+ color: Colors.primary8,
+ },
+ centeredView: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ modalView: {
+ borderRadius: 4,
+ marginHorizontal: 32,
+ alignItems: 'flex-start',
+ shadowColor: '#000',
+ paddingBottom: 50,
+ paddingTop: 16,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ width: Dimensions.get('window').width - 44,
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ backgroundColor: '#082139',
+ },
+ containerTextModal: {
+ alignContent: 'center',
+ },
+ textInputStyle: {
+ color: Colors.primary4,
+ borderColor: '#455c8a',
+ borderWidth: StyleSheet.hairlineWidth,
+ borderRadius: 2,
+ fontSize: 16,
+ paddingHorizontal: 12,
+ paddingTop: 13,
+ height: 96,
+ backgroundColor: Colors.primary,
+ textAlignVertical: 'top',
+ marginBottom: 26,
+ marginTop: 12,
+ },
+ textDescriptionStyle: {
+ color: Colors.text,
+ textAlignVertical: 'top',
+ },
+ nextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ width: Dimensions.get('window').width - 48,
+ },
+ modalButton: {
+ height: 56,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ },
+ modalInput: {
+ borderColor: '#455c8a',
+ color: Colors.primary4,
+ fontSize: 16,
+ width: Dimensions.get('window').width - 80,
+ },
+ containerHeaderModal: {
+ flexDirection: 'row',
+ alignSelf: 'flex-end',
+ marginBottom: 12,
+ },
+ imageModalHeader: {
+ width: 24,
+ resizeMode: 'contain',
+ height: 24,
+ },
+ containerSectionModal: {
+ alignSelf: 'center',
+ width: Dimensions.get('window').width - 80,
+ zIndex: 5,
+ elevation: 5,
+ },
+ textLabelModal: {
+ fontSize: 16,
+ fontWeight: '400',
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ textDescriptionInfo: {
+ fontSize: 18,
+ fontWeight: '400',
+ lineHeight: 27,
+ color: Colors.text,
+ marginTop: 16,
+ },
+});
+export default SavedPost;
diff --git a/src/screens/profile/UpdateFavoriteBook.js b/src/screens/profile/UpdateFavoriteBook.js
new file mode 100644
index 0000000..c95d74f
--- /dev/null
+++ b/src/screens/profile/UpdateFavoriteBook.js
@@ -0,0 +1,337 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ ScrollView,
+ Image,
+ SafeAreaView,
+ TouchableOpacity,
+ Alert,
+ Platform,
+} from 'react-native';
+import Fetching from '../../components/Fetching';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import { useDispatch, useSelector } from 'react-redux';
+import { Button, Input } from 'react-native-elements';
+import * as ImagePicker from 'expo-image-picker';
+import * as mime from 'react-native-mime-types';
+import { updateFavoriteBook } from '../../store/ducks/user';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import ActionSheet from 'react-native-actionsheet';
+import { LinearGradient } from 'expo-linear-gradient';
+import { takeCamera, takeGaleria } from '../../utils/Utils';
+
+const UpdateFavoriteBook = props => {
+ const [fetching, setFetching] = useState(false);
+ const [sending, setSending] = useState(false);
+ const [image_changed, setImageChanged] = useState(false);
+ const [name, setName] = useState('');
+ const [book_photo, setBookPhoto] = useState(null);
+
+ const ASPhotoOptions = useRef();
+
+ const dispatch = useDispatch();
+ const user = useSelector(({ user }) => user);
+ const { isFetching } = useSelector(({ fetching }) => fetching);
+
+ useEffect(() => {
+ setFetching(true);
+ user.image_book ? setBookPhoto(user.image_book) : null;
+ user.usr_favorite_book ? setName(user.usr_favorite_book) : null;
+ setFetching(false);
+ }, []);
+
+ useEffect(() => {
+ user.image_book ? setBookPhoto(user.image_book) : null;
+ user.usr_favorite_book ? setName(user.usr_favorite_book) : null;
+ setSending(false);
+ }, [user]);
+
+ const createFavoriteBook = () => {
+ if (name === '' || !book_photo) {
+ Alert.alert('Ops!', 'Please fill in all fields.');
+ return;
+ }
+
+ setSending(true);
+
+ let request = {
+ book_name: name,
+ book_picture: book_photo,
+ image_changed: image_changed,
+ };
+
+ dispatch(updateFavoriteBook(request, props.navigation));
+ setSending(false);
+ };
+
+ const handleActionSheet = async index => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+
+ if (status === 'granted') {
+ pickGaleria();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await takeCamera();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error ocurred when trying to open the camera.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const pickGaleria = async () => {
+ let result = await takeGaleria();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error ocurred when trying to open the library.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const setAnexos = foto => {
+ let auxFoto = { ...foto };
+ auxFoto.url = foto.uri;
+
+ setImageChanged(true);
+ setBookPhoto(auxFoto);
+ };
+
+ return (
+
+
+
+
+
+ props.navigation.pop()}>
+
+
+
+
+ Favorite Book
+
+
+
+ handleActionSheet(index)}
+ styles={{
+ buttonBox: Default.actionSheetButtonBox,
+ body: Default.actionSheetBody,
+ cancelButtonBox: Default.actionSheetCancelButtonBox,
+ }}
+ />
+
+
+
+ {book_photo ? (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.bookImage}>
+
+
+ >
+ ) : (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.containerPhoto}>
+
+ Add Photo
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get('window').width,
+ },
+ containerActions: {
+ marginTop: 32,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'flex-start',
+ marginBottom: 16,
+ },
+ textHeaderTitle: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 24,
+ lineHeight: 32,
+ alignSelf: 'flex-start',
+ },
+ nextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ width: Dimensions.get('window').width - 48,
+ },
+ containerTitle: {
+ paddingHorizontal: 24,
+ paddingTop: 24,
+ },
+ containerButton: {
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginBottom: 16,
+ },
+ textAddPhoto: {
+ fontWeight: '400',
+ fontSize: 16,
+ lineHeight: 19,
+ color: '#FCFCFC',
+ },
+ bookImage: {
+ width: '100%',
+ height: '100%',
+ position: 'absolute',
+ },
+ containerPhoto: {
+ flex: 1,
+ flexDirection: 'column',
+ alignSelf: 'center',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ zIndex: 3,
+ elevation: 3,
+ marginTop: 56,
+ },
+ textTitle: {
+ fontWeight: '400',
+ fontSize: 16,
+ lineHeight: 16,
+ color: '#FCFCFC',
+ },
+ bookPhoto: {
+ width: 50,
+ height: 50,
+ marginBottom: 8,
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: 'flex-end',
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerList: {
+ height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerViewSection: {
+ flex: 1,
+ zIndex: 1,
+ elevation: 1,
+ },
+ backButtonStyle: {
+ marginLeft: 24,
+ marginRight: 16,
+ alignSelf: 'center',
+ },
+ containerHabits: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'flex-start',
+ marginTop: -8,
+ },
+});
+
+export default UpdateFavoriteBook;
diff --git a/src/screens/profile/UpdateFavoriteFood.js b/src/screens/profile/UpdateFavoriteFood.js
new file mode 100644
index 0000000..f444937
--- /dev/null
+++ b/src/screens/profile/UpdateFavoriteFood.js
@@ -0,0 +1,332 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ ScrollView,
+ Image,
+ SafeAreaView,
+ TouchableOpacity,
+ Alert,
+ Platform,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import { useDispatch, useSelector } from 'react-redux';
+import Fetching from '../../components/Fetching';
+import { Button, Input } from 'react-native-elements';
+import * as ImagePicker from 'expo-image-picker';
+import * as mime from 'react-native-mime-types';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { updateFavoriteFood } from '../../store/ducks/user';
+import ActionSheet from 'react-native-actionsheet';
+import { LinearGradient } from 'expo-linear-gradient';
+import { takeCamera, takeGaleria } from '../../utils/Utils';
+
+const UpdateFavoriteFood = props => {
+ const [fetching, setFetching] = useState(false);
+ const [name, setName] = useState('');
+ const [image_changed, setImageChanged] = useState(false);
+ const [food_photo, setFoodPhoto] = useState(null);
+
+ const ASPhotoOptions = useRef();
+
+ const dispatch = useDispatch();
+ const user = useSelector(({ user }) => user);
+ const { isFetching } = useSelector(({ fetching }) => fetching);
+
+ useEffect(() => {
+ setFetching(true);
+ user.image_food ? setFoodPhoto(user.image_food) : null;
+ user.usr_favorite_food ? setName(user.usr_favorite_food) : null;
+ setFetching(false);
+ }, []);
+
+ const createFavoriteFood = () => {
+ if (name === '' || !food_photo) {
+ Alert.alert('Ops!', 'Please fill in all fields.');
+ return;
+ }
+
+ let request = {
+ food_name: name,
+ food_picture: food_photo,
+ image_changed: image_changed,
+ };
+
+ dispatch(updateFavoriteFood(request, props.navigation));
+ };
+
+ const handleActionSheet = async index => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+
+ if (status === 'granted') {
+ pickGaleria();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await takeCamera();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error ocurred when trying to open the camera.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const pickGaleria = async () => {
+ let result = await takeGaleria();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error ocurred when trying to open the library.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const setAnexos = foto => {
+ let auxFoto = { ...foto };
+ auxFoto.url = foto.uri;
+
+ setImageChanged(true);
+ setFoodPhoto(auxFoto);
+ };
+
+ return (
+
+
+
+
+
+ props.navigation.pop()}>
+
+
+
+
+ Favorite food
+
+
+
+ handleActionSheet(index)}
+ styles={{
+ buttonBox: Default.actionSheetButtonBox,
+ body: Default.actionSheetBody,
+ cancelButtonBox: Default.actionSheetCancelButtonBox,
+ }}
+ />
+
+
+
+ {food_photo ? (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.foodImage}>
+
+
+ >
+ ) : (
+ <>
+ ASPhotoOptions.current.show()}
+ style={styles.containerPhoto}>
+
+ Add Photo
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get('window').width,
+ },
+ containerActions: {
+ marginTop: 32,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'flex-start',
+ marginBottom: 16,
+ },
+ textHeaderTitle: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 24,
+ lineHeight: 32,
+ alignSelf: 'flex-start',
+ },
+ nameInput: {
+ width: Dimensions.get('window').width - 44,
+ color: '#9CC6FF',
+ fontSize: 16,
+ },
+ nextButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#982538',
+ width: Dimensions.get('window').width - 48,
+ },
+ containerTitle: {
+ paddingHorizontal: 24,
+ paddingTop: 24,
+ },
+ containerButton: {
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginBottom: 16,
+ },
+ textAddPhoto: {
+ fontWeight: '400',
+ fontSize: 16,
+ lineHeight: 19,
+ color: '#FCFCFC',
+ },
+ foodImage: {
+ width: '100%',
+ height: '100%',
+ position: 'absolute',
+ },
+ containerPhoto: {
+ flex: 1,
+ flexDirection: 'column',
+ alignSelf: 'center',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ zIndex: 3,
+ elevation: 3,
+ marginTop: 56,
+ },
+ textTitle: {
+ fontWeight: '400',
+ fontSize: 16,
+ lineHeight: 16,
+ color: '#FCFCFC',
+ },
+ foodPhoto: {
+ width: 50,
+ height: 50,
+ marginBottom: 8,
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: 'flex-end',
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerList: {
+ height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerViewSection: {
+ flex: 1,
+ zIndex: 1,
+ elevation: 1,
+ },
+ backButtonStyle: {
+ marginLeft: 24,
+ marginRight: 16,
+ alignSelf: 'center',
+ },
+ containerHabits: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'flex-start',
+ marginTop: -8,
+ },
+});
+
+export default UpdateFavoriteFood;
diff --git a/src/screens/profile/UpdateProfile.js b/src/screens/profile/UpdateProfile.js
new file mode 100644
index 0000000..e56e5e1
--- /dev/null
+++ b/src/screens/profile/UpdateProfile.js
@@ -0,0 +1,526 @@
+import React, { useState, useEffect, useRef } from 'react';
+import {
+ View,
+ ScrollView,
+ Dimensions,
+ StyleSheet,
+ Image,
+ Text,
+ Alert,
+ TouchableOpacity,
+ Platform,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Header from '../../components/Header';
+import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+// import { useDispatch, useSelector } from "react-redux";
+import { Button, Input, Avatar } from 'react-native-elements';
+import { systemWeights } from 'react-native-typography';
+import ActionSheet from 'react-native-actionsheet';
+import * as ImagePicker from 'expo-image-picker';
+import * as mime from 'react-native-mime-types';
+// import { deleteAccount, logout, update } from "../../store/ducks/user";
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { LinearGradient } from 'expo-linear-gradient';
+import Fetching from '../../components/Fetching';
+import { takeCamera, takeGaleria } from '../../utils/Utils';
+
+const UpdateProfile = props => {
+ const [userPhoto, setUserPhoto] = useState(null);
+ const [userName, setUserName] = useState('');
+ const [gender, setGender] = useState(null);
+ const [userEmail, setUserEmail] = useState('');
+ const [userPassword, setUserPassword] = useState('');
+ const [userPasswordConfirmation, setUserPasswordConfirmation] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ // const user = useSelector(({ user }) => user);
+ // const { isFetching } = useSelector(({ fetching }) => fetching);
+
+ // const dispatch = useDispatch();
+
+ const ASPhotoOptions = useRef();
+
+ // useEffect(() => {
+ // setUserPhoto(user.image);
+ // setUserName(user.name);
+ // setUserEmail(user.email);
+ // setGender(user.usr_gender);
+ // }, []);
+
+ // Mock values
+ useEffect(() => {
+ setUserPhoto(null);
+ setUserName('Default Name');
+ setUserEmail('email@example.com');
+ setGender(null);
+ }, []);
+
+ const handleActionSheet = async index => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+
+ if (status === 'granted') {
+ pickGaleria();
+ } else {
+ Alert.alert('Ops', 'You need to allow access to the library first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await takeCamera();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error occurred when trying to open the camera.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const pickGaleria = async () => {
+ let result = await takeGaleria();
+
+ if (result === 'failed') {
+ Alert.alert('Ops', 'An error occurred when trying to open the library.');
+ return;
+ }
+
+ if (result?.uri) {
+ setAnexos(result);
+ }
+ };
+
+ const setAnexos = foto => {
+ let auxFoto = { ...foto };
+ auxFoto.url = foto.uri;
+
+ setUserPhoto(auxFoto);
+ };
+
+ const saveUserData = () => {
+ if (userPassword.trim() !== '' || userPasswordConfirmation.trim() !== '') {
+ if (userPassword !== userPasswordConfirmation) {
+ Alert.alert('Ops', 'The passwords do not match.');
+ return;
+ }
+ }
+
+ if (!gender) {
+ Alert.alert('Ops', 'Please select a gender.');
+ return;
+ }
+
+ let userData = {
+ name: userName,
+ email: userEmail,
+ gender: gender,
+ };
+
+ if (userPassword.trim() !== '') userData.password = userPassword.trim();
+ if (userPhoto !== null) userData.profile_picture = userPhoto;
+
+ // dispatch(update(userData, props.navigation));
+ };
+
+ const deleteProfile = () => {
+ Alert.alert(
+ 'DELETE ACCOUNT',
+ 'Are you sure? This action cannot be undone.',
+ [
+ {
+ text: 'No',
+ onPress: () => {},
+ style: 'cancel',
+ },
+ {
+ text: 'Yes',
+ onPress: () => {
+ doDeleteProfile();
+ },
+ },
+ ],
+ { cancelable: false }
+ );
+ };
+
+ const doDeleteProfile = () => {
+ setLoading(true);
+
+ // deleteAccount()
+ // .catch((err) => {
+ // Alert.alert("Ops!", err?.response?.data?.errors?.[0] ?? "An error occurred.");
+ // })
+ // .then((res) => {
+ // if (res?.status === 200) {
+ // setLoading(false);
+ // dispatch(logout());
+ // props.navigation.navigate("Auth");
+ // }
+ // });
+ };
+
+ return (
+
+
+
+
+
+
+ Profile
+
+ {userPhoto ? (
+ ASPhotoOptions.current.show()}>
+ ASPhotoOptions.current.show()}
+ />
+
+ ) : (
+ ASPhotoOptions.current.show()}>
+ ASPhotoOptions.current.show()}
+ />
+
+ )}
+
+
+ handleActionSheet(index)}
+ />
+
+
+
+
+
+
+
+
+
+
+ Gender
+
+ setGender('man')}>
+
+
+ Man
+ {gender === 'man' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ setGender('woman')}>
+
+
+ Woman
+ {gender === 'woman' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ setGender('non-binary')}>
+
+
+ Non-binary
+ {gender === 'non-binary' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ setGender('not-say')}>
+
+
+ Prefer not to say
+ {gender === 'not-say' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+ props.navigation.pop()}
+ title="CANCEL"
+ />
+
+
+
+
+ Delete my account
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ paddingVertical: 22,
+ paddingHorizontal: 22,
+ },
+ textTitle: {
+ fontSize: 24,
+ color: Colors.text,
+ display: 'flex',
+ },
+ textInfo: {
+ fontSize: 14,
+ color: Colors.text,
+ lineHeight: 16,
+ fontWeight: '700',
+ },
+ containerTitleGender: {
+ width: Dimensions.get('window').width - 48,
+ borderBottomColor: '#264261',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ paddingBottom: 16,
+ marginBottom: 16,
+ },
+ textProfile: {
+ position: 'absolute',
+ alignSelf: 'center',
+ marginTop: -32,
+ fontWeight: '400',
+ fontSize: 20,
+ lineHeight: 27,
+ color: Colors.text,
+ },
+ containerGroup: {
+ marginBottom: 16,
+ width: Dimensions.get('window').width - 48,
+ },
+ containerGradient: {
+ flex: 1,
+ width: Dimensions.get('window').width - 48,
+ borderRadius: 8,
+ paddingVertical: 18,
+ paddingHorizontal: 16,
+ },
+ containerItem: {
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ flexWrap: 'wrap',
+ },
+ circleIcon: {
+ width: 20,
+ height: 20,
+ marginRight: 8,
+ },
+ containerAvatar: {
+ alignItems: 'center',
+ marginVertical: 32,
+ },
+ userPhoto: {
+ width: 88,
+ height: 88,
+ borderRadius: 44,
+ },
+ createAccountText: {
+ fontSize: 14,
+ color: 'white',
+ },
+ iconChevron: {
+ marginLeft: 8,
+ marginRight: 8,
+ },
+ cancelButton: {
+ height: 64,
+ borderRadius: 4,
+ backgroundColor: '#004B7D',
+ width: Dimensions.get('window').width - 32,
+ marginVertical: 16,
+ },
+});
+
+export default UpdateProfile;
diff --git a/src/screens/profile/UserCommunity.js b/src/screens/profile/UserCommunity.js
new file mode 100644
index 0000000..86ddabf
--- /dev/null
+++ b/src/screens/profile/UserCommunity.js
@@ -0,0 +1,280 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ RefreshControl,
+ ScrollView,
+ Image,
+ SafeAreaView,
+ TouchableOpacity,
+ Alert,
+ ActivityIndicator,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Header from '../../components/Header';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import CardCommunity from '../../components/community/CardCommunity';
+import { listPublicCommunities } from '../../store/ducks/community';
+
+const UserCommunity = props => {
+ const [fetching, setFetching] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [user_id, setUserId] = useState(null);
+ const [refreshing, setRefreshing] = useState(false);
+ const [load_more, setLoadMore] = useState(true);
+
+ const [list_communities, setListCommunities] = useState([]);
+
+ const [page, setPage] = useState(0);
+
+ useEffect(() => {
+ fetchCommunities(true, false);
+ }, []);
+
+ useEffect(() => {
+ const unsubscribe = props.navigation.addListener('focus', () => {
+ fetchCommunities(false, false);
+ });
+
+ return unsubscribe;
+ }, [props.navigation, fetchCommunities]);
+
+ const fetchCommunities = (isFetching, isRefreshing) => {
+ isFetching ? setFetching(true) : isRefreshing ? setRefreshing(true) : null;
+
+ let request = {
+ page: 0,
+ };
+
+ props.route?.params?.user
+ ? [
+ (request.cme_id_user = props.route.params.user),
+ setUserId(props.route?.params?.user),
+ ]
+ : null;
+
+ listPublicCommunities(request)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setListCommunities(res.data.data);
+
+ if (res.data?.current_page === res.data?.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ } else {
+ setLoadMore(true);
+ }
+ }
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ setRefreshing(false);
+ });
+ };
+
+ const loadMore = () => {
+ let communities_aux = [];
+ let number_page = page + 1;
+
+ setPage(number_page);
+ setLoading(true);
+
+ let request = {
+ page: number_page,
+ };
+
+ user_id ? (request.cme_id_user = user_id) : null;
+
+ listPublicCommunities(request)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ communities_aux = [...list_communities];
+ communities_aux = communities_aux.concat(res.data.data);
+
+ setListCommunities(communities_aux);
+
+ if (res.data?.current_page === res.data?.last_page) {
+ setLoadMore(false);
+ setPage(0);
+ }
+ }
+
+ setLoading(false);
+ }
+ });
+ };
+
+ const isCloseToBottom = ({
+ layoutMeasurement,
+ contentOffset,
+ contentSize,
+ }) => {
+ const paddingToBottom = 15;
+ return (
+ layoutMeasurement.height + contentOffset.y >=
+ contentSize.height - paddingToBottom
+ );
+ };
+
+ return (
+
+ {
+ if (
+ list_communities.length > 0 &&
+ load_more &&
+ isCloseToBottom(nativeEvent)
+ ) {
+ loadMore();
+ }
+ }}
+ scrollEnabled
+ refreshControl={
+ fetchCommunities(false, true)}
+ refreshing={refreshing}
+ />
+ }>
+
+
+
+
+
+ props.navigation.pop()}>
+
+
+
+
+ Communities
+
+
+
+
+ {list_communities.length > 0
+ ? list_communities.map((obj, i) => {
+ return (
+
+
+
+ );
+ })
+ : null}
+
+ {loading ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get('window').width,
+ },
+ containerActions: {
+ flexDirection: 'column',
+ marginBottom: 16,
+ paddingBottom: 16,
+ borderBottomColor: '#264261',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ //marginTop: 32,
+ //height: 26
+ },
+ textUserHeaderName: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 20,
+ lineHeight: 27,
+ alignSelf: 'center',
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: 'flex-end',
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerList: {
+ height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerViewSection: {
+ flex: 1,
+ zIndex: 1,
+ elevation: 1,
+ },
+ backButtonStyle: {
+ marginLeft: 24,
+ alignSelf: 'flex-start',
+ marginBottom: -23,
+ },
+ containerHabits: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'flex-start',
+ marginTop: -8,
+ },
+});
+
+export default UserCommunity;
diff --git a/src/screens/profile/UserHabit.js b/src/screens/profile/UserHabit.js
new file mode 100644
index 0000000..9a130e3
--- /dev/null
+++ b/src/screens/profile/UserHabit.js
@@ -0,0 +1,247 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ StyleSheet,
+ Dimensions,
+ Text,
+ RefreshControl,
+ ScrollView,
+ Image,
+ SafeAreaView,
+ TouchableOpacity,
+ Alert,
+ ActivityIndicator,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Header from '../../components/Header';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import CardHabits from '../../components/community/CardHabits';
+import { getAllUserHabits, getMomentum } from '../../store/ducks/habit';
+import { getBasicInformationUser } from '../../store/ducks/user';
+
+const UserHabit = props => {
+ const [fetching, setFetching] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [refreshing, setRefreshing] = useState(false);
+ const [load_more, setLoadMore] = useState(true);
+ const [list_habits, setListHabits] = useState([]);
+
+ const [page, setPage] = useState(0);
+ const [option_bar, setOptionBar] = useState(1);
+
+ useEffect(() => {
+ fetchHabits(true, false);
+ }, []);
+
+ const fetchHabits = (isFetching, isRefreshing) => {
+ isFetching ? setFetching(true) : isRefreshing ? setRefreshing(true) : null;
+
+ props.route?.params?.user ? habitsUserProfile() : habitsUserLogged();
+ };
+
+ const habitsUserLogged = () => {
+ getMomentum()
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ let habits_aux = [];
+
+ res.data.forEach(obj => {
+ let aux_category = obj.hac_name;
+
+ obj.habits.forEach(hab => {
+ let data = {};
+ hab.habit.category = aux_category;
+ data.hab = hab;
+ habits_aux.push(data);
+ });
+ });
+
+ setListHabits(habits_aux);
+ }
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ setRefreshing(false);
+ });
+ };
+
+ const habitsUserProfile = () => {
+ getBasicInformationUser(props.route.params.user)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ let habits_aux = [];
+
+ res.data.momentum.forEach(obj => {
+ let data = {};
+ let aux_category = obj.hac_name;
+
+ obj.habits.forEach(hab => {
+ hab.habit.category = aux_category;
+ data.hab = hab;
+ habits_aux.push(data);
+ });
+ });
+
+ setListHabits(habits_aux);
+ }
+ })
+ .finally(() => {
+ setFetching(false);
+ setRefreshing(false);
+ });
+ };
+
+ return (
+
+ fetchHabits(false, true)}
+ refreshing={refreshing}
+ />
+ }>
+
+
+
+
+
+ props.navigation.pop()}>
+
+
+
+
+ Habits
+
+
+
+
+ {list_habits.length > 0
+ ? list_habits.map((obj, i) => {
+ return (
+
+
+
+ );
+ })
+ : null}
+
+ {loading ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ width: Dimensions.get('window').width,
+ },
+ containerActions: {
+ flexDirection: 'column',
+ marginBottom: 16,
+ paddingBottom: 16,
+ borderBottomColor: '#264261',
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ //marginTop: 32,
+ //height: 26
+ },
+ textUserHeaderName: {
+ color: Colors.text,
+ fontWeight: '400',
+ fontSize: 20,
+ lineHeight: 27,
+ alignSelf: 'center',
+ },
+ containerHeaderImage: {
+ height: 189,
+ flex: 1,
+ justifyContent: 'flex-end',
+ width: Dimensions.get('window').width,
+ zIndex: 0,
+ elevation: 0,
+ },
+ containerList: {
+ height: 268,
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ },
+ containerViewSection: {
+ flex: 1,
+ zIndex: 1,
+ elevation: 1,
+ },
+ backButtonStyle: {
+ marginLeft: 24,
+ alignSelf: 'flex-start',
+ marginBottom: -23,
+ },
+ containerHabits: {
+ flex: 1,
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'flex-start',
+ marginTop: -8,
+ },
+});
+
+export default UserHabit;
diff --git a/src/screens/profile/UserProfile.js b/src/screens/profile/UserProfile.js
new file mode 100644
index 0000000..76b22a3
--- /dev/null
+++ b/src/screens/profile/UserProfile.js
@@ -0,0 +1,984 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ ScrollView,
+ BackHandler,
+ StyleSheet,
+ Dimensions,
+ Image,
+ Text,
+ TouchableOpacity,
+ Alert,
+ FlatList,
+} from 'react-native';
+import Default from '../../../assets/styles/Default';
+import Colors from '../../../assets/styles/Colors';
+import Fetching from '../../components/Fetching';
+import Header from '../../components/Header';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import { Button } from 'react-native-elements';
+// import { useSelector } from "react-redux";
+import {
+ storeConnection,
+ answerConnection,
+ deleteConnection,
+ cancelConnection,
+} from '../../store/ducks/connection';
+import { listPublicCommunities } from '../../store/ducks/community';
+// import { getBasicInformationUser } from "../../store/ducks/user";
+import { LinearGradient } from 'expo-linear-gradient';
+import { getAchievements } from '../../utils/Utils';
+import CardHabits from '../../components/community/CardHabits';
+import CardCommunity from '../../components/community/CardCommunity';
+
+const Profile = props => {
+ const [fetching, setFetching] = useState(false);
+ const [achievements, setAchievements] = useState([]);
+ const [user_profile, setUserProfile] = useState(null);
+ const [communities, setCommunities] = useState([]);
+ const [list_habits, setListHabits] = useState([]);
+ const user = useSelector(({ user }) => user);
+
+ // useEffect(() => {
+ // fetchAll();
+ // }, []);
+
+ // useEffect(() => {
+ // BackHandler.addEventListener("hardwareBackPress", backAction);
+
+ // return () =>
+ // BackHandler.removeEventListener("hardwareBackPress", backAction);
+ // }, []);
+
+ // Mock values:
+ useEffect(() => {
+ // Initialize with default values
+ setUserProfile({
+ image: null,
+ name: 'Default Name',
+ usr_quote_to_live_by: 'Default Quote',
+ usr_biggest_hack: 'Default Hack',
+ usr_biggest_challenge: 'Default Challenge',
+ usr_favorite_book: 'Default Book',
+ usr_favorite_food: 'Default Food',
+ type_connection: 'not_connection',
+ connections: 0,
+ ranking: 'N/A',
+ momentum: [],
+ });
+ setCommunities({ data: [] });
+ setListHabits([]);
+ }, []);
+
+ useEffect(() => {
+ BackHandler.addEventListener('hardwareBackPress', backAction);
+
+ return () =>
+ BackHandler.removeEventListener('hardwareBackPress', backAction);
+ }, []);
+
+ const backAction = () => {
+ props.route.params?.prevPage
+ ? props.navigation.navigate(props.route.params?.prevPage)
+ : props.navigation.goBack();
+ return true;
+ };
+
+ const fetchAll = async () => {
+ setFetching(true);
+
+ await getBasicInformationUser(props.route.params.user.id_user)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setUserProfile(res.data);
+
+ let achievements_aux = [];
+ let habits_aux = [];
+
+ res.data.momentum.forEach(obj => {
+ let data = {};
+ let aux_category = obj.hac_name;
+
+ obj.habits.forEach(hab => {
+ hab.habit.category = aux_category;
+ data.hab = hab;
+ habits_aux.push(data);
+ });
+ });
+
+ setListHabits(habits_aux);
+
+ if (res.data.momentum.length > 0) {
+ achievements_aux = getAchievements(res.data.momentum);
+
+ setAchievements(achievements_aux);
+ }
+ }
+
+ setFetching(false);
+ });
+
+ let request = {
+ page: 0,
+ cme_id_user: props.route.params.user.id_user,
+ };
+
+ await listPublicCommunities(request)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ setCommunities(res.data);
+ }
+
+ setFetching(false);
+ }
+ });
+ };
+
+ const sendInvite = () => {
+ let data = { usc_id_user_received_request: user_profile.id };
+
+ storeConnection(data)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ fetchAll();
+ }
+ }
+ });
+ };
+
+ const answerInvite = accept => {
+ let data = { accepted: accept };
+
+ let id_connection =
+ props.route.params.user.id_connection ?? user_profile.connection_id;
+
+ answerConnection(id_connection, data)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ fetchAll();
+ }
+ }
+ });
+ };
+
+ const doCancelConnection = () => {
+ Alert.alert(
+ user.name,
+ 'Are you sure you want to cancel this connection request?',
+ [
+ {
+ text: 'No',
+ style: 'cancel',
+ },
+ {
+ text: 'Yes',
+ onPress: () =>
+ cancelConnection(user_profile?.connection_id)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ fetchAll();
+ }
+ }
+ }),
+ },
+ ],
+ { cancelable: false }
+ );
+ };
+
+ const breakConnection = () => {
+ let id_connection =
+ props.route.params.user.id_connection ?? user_profile.connection_id;
+
+ Alert.alert(
+ user.name,
+ 'Are you sure you want to break this connection?',
+ [
+ {
+ text: 'No',
+ style: 'cancel',
+ },
+ {
+ text: 'Yes',
+ onPress: () =>
+ deleteConnection(id_connection)
+ .catch(err => {
+ Alert.alert(
+ 'Ops!',
+ 'Something went wrong with our servers. Please contact us.'
+ );
+ })
+ .then(res => {
+ if (res?.status === 200) {
+ if (res.data.errors) {
+ Alert.alert('Ops!', res.data.errors[0]);
+ } else {
+ fetchAll();
+ }
+ }
+ }),
+ },
+ ],
+ { cancelable: false }
+ );
+ };
+
+ const onRanking = () => {
+ props.navigation.navigate('Ranking');
+ };
+
+ const onConnections = () => {
+ props.navigation.navigate('UserConnections', {
+ user: { id: user_profile?.id },
+ });
+ };
+
+ return (
+
+
+
+
+
+ props.route.params.prevPage
+ ? [
+ props.navigation.navigate(props.route.params.prevPage),
+ (props.route.params.prevPage = null),
+ ]
+ : props.navigation.pop()
+ }>
+
+
+
+
+
+
+
+ {user_profile?.image ? (
+
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+ {`${user_profile?.name}`}
+
+
+ {user_profile?.type_connection === 'connection' ? (
+ onRanking()}
+ style={[styles.containerSection, { marginBottom: 8 }]}>
+
+
+
+ Ranking
+
+
+
+
+
+ {user_profile?.ranking}
+
+
+
+
+ ) : null}
+
+ onConnections()}
+ style={[styles.containerSection, { marginBottom: 8 }]}>
+
+
+
+
+ {user_profile?.connections} Connections
+
+
+
+
+
+ {user_profile?.type_connection === 'not_connection' ? (
+ sendInvite()}>
+
+
+ ADD CONNECTION
+
+
+ ) : null}
+
+ {user_profile?.type_connection === 'connection' ? (
+
+ breakConnection()}
+ title="BREAK CONNECTION"
+ />
+
+ ) : null}
+
+ {user_profile?.type_connection === 'invited' ? (
+
+ doCancelConnection()}
+ title="CANCEL CONNECTION REQUEST"
+ />
+
+ ) : null}
+
+ {user_profile?.type_connection === 'pending' ? (
+
+
+
+ answerInvite(true)}
+ title="Accept"
+ />
+
+
+ answerInvite(false)}
+ title="Decline"
+ />
+
+
+
+
+
+
+ Connections
+
+
+ {user_profile?.connections}
+
+
+
+
+ ) : null}
+
+
+
+ Quote to live by
+ {user_profile?.usr_quote_to_live_by !== '' &&
+ user_profile?.usr_quote_to_live_by !== null ? (
+
+ {user_profile?.usr_quote_to_live_by}
+
+ ) : (
+
+ No quotes for this user.
+
+ )}
+
+
+
+
+
+ Biggest
+ Hack in Life
+ {user_profile?.usr_biggest_hack !== '' &&
+ user_profile?.usr_biggest_hack !== null ? (
+
+ {user_profile?.usr_biggest_hack}
+
+ ) : (
+
+ No biggest hack in life for this user.
+
+ )}
+
+
+ Biggest Challenge
+ {user_profile?.usr_biggest_challenge !== '' &&
+ user_profile?.usr_biggest_hack !== null ? (
+
+ {user_profile?.usr_biggest_challenge}
+
+ ) : (
+
+ No biggest challenge for this user.
+
+ )}
+
+
+
+
+
+
+ Communities Public
+
+ {communities.data?.length > 0 ? (
+
+ props.navigation.push('UserCommunity', {
+ user: user_profile?.id,
+ })
+ }>
+
+ See All
+
+
+ ) : null}
+
+ {communities.data?.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ snapToAlignment={'start'}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+
+ No public communities yet.
+
+
+ )}
+
+
+
+
+
+ Daily Habits
+
+ {list_habits.length > 0 ? (
+
+ props.navigation.push('UserHabit', {
+ user: user_profile?.id,
+ })
+ }>
+
+ See All
+
+
+ ) : null}
+
+ {list_habits.length > 0 ? (
+ String(index)}
+ showsHorizontalScrollIndicator={false}
+ snapToAlignment={'start'}
+ scrollEventThrottle={16}
+ decelerationRate="fast"
+ renderItem={({ item }) => (
+
+ )}
+ />
+ ) : (
+
+ No habits yet.
+
+ )}
+
+
+
+
+ Favorite Book
+ {user_profile?.image_book ? (
+ <>
+
+ {user_profile?.usr_favorite_book}
+
+
+
+
+ >
+ ) : (
+ No favorite book yet.
+ )}
+
+
+ Favorite Food
+ {user_profile?.image_food ? (
+ <>
+
+ {user_profile?.usr_favorite_food}
+
+
+
+
+ >
+ ) : (
+ No favorite food yet.
+ )}
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ paddingHorizontal: 16,
+ width: Dimensions.get('window').width,
+ },
+ containerHeader: {
+ alignItems: 'center',
+ marginBottom: 16,
+ },
+ containerPhoto: {
+ flexDirection: 'row',
+ },
+ userPhoto: {
+ alignSelf: 'center',
+ width: 70,
+ height: 70,
+ borderRadius: 32,
+ marginTop: 5,
+ position: 'absolute',
+ },
+ borderPhoto: {
+ alignSelf: 'center',
+ width: 80,
+ height: 80,
+ borderRadius: 32,
+ position: 'relative',
+ },
+ containerScore: {
+ borderRadius: 8,
+ padding: 24,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ iconMedal: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 32,
+ height: 32,
+ },
+ iconRay: {
+ marginRight: 8,
+ alignSelf: 'center',
+ width: 24,
+ height: 24,
+ },
+ containerConnection: {
+ borderRadius: 8,
+ padding: 28,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ containerInfo: {
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ alignSelf: 'center',
+ alignItems: 'center',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ },
+ containerCard: {
+ borderRadius: 4,
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ width: Dimensions.get('window').width - 32,
+ },
+ textInfo: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 16,
+ },
+ textDescriptionInfo: {
+ fontSize: 18,
+ fontWeight: '400',
+ lineHeight: 27,
+ color: Colors.text,
+ marginTop: 16,
+ },
+ textNoInfos: {
+ marginTop: 16,
+ fontSize: 12,
+ lineHeight: 16,
+ fontWeight: 'bold',
+ color: Colors.text,
+ textAlign: 'justify',
+ alignSelf: 'flex-start',
+ },
+ containerBiggest: {
+ flexGrow: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignSelf: 'center',
+ alignItems: 'flex-start',
+ padding: 16,
+ backgroundColor: 'rgba(13, 40, 65, 1)',
+ width: Dimensions.get('window').width,
+ marginBottom: 8,
+ },
+ containerCardBiggest: {
+ display: 'flex',
+ height: '100%',
+ flexDirection: 'column',
+ width: (Dimensions.get('window').width - 48) / 2,
+ backgroundColor: 'rgba(156, 198, 255, 0.042)',
+ borderRadius: 4,
+ padding: 16,
+ alignItems: 'flex-start',
+ backgroundColor: 'rgba(14, 49, 80, 1)',
+ },
+ containerList: {
+ marginBottom: 8,
+ paddingVertical: 16,
+ zIndex: 1,
+ elevation: 1,
+ marginHorizontal: -16,
+ },
+ textSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignSelf: 'flex-end',
+ marginRight: 16,
+ },
+ containerSeeAll: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ alignSelf: 'flex-end',
+ marginLeft: 16,
+ zIndex: 4,
+ elevation: 4,
+ position: 'absolute',
+ },
+ textEmpty: {
+ fontSize: 12,
+ lineHeight: 16,
+ height: 16,
+ fontWeight: 'bold',
+ color: Colors.text,
+ marginLeft: 16,
+ },
+ containerEmpty: {
+ alignSelf: 'flex-start',
+ marginRight: 16,
+ marginTop: 16,
+ },
+ textBiggest: {
+ color: Colors.text,
+ fontWeight: '700',
+ fontSize: 16,
+ lineHeight: 22.4,
+ },
+ textName: {
+ fontSize: 24,
+ color: Colors.text,
+ marginTop: 8,
+ },
+ containerTitle: {
+ borderBottomWidth: StyleSheet.hairlineWidth,
+ borderBottomColor: '#264261',
+ paddingBottom: 8,
+ marginBottom: 19,
+ },
+ textTitle: {
+ fontSize: 16,
+ color: Colors.text,
+ fontWeight: 'bold',
+ },
+ containerSection: {
+ marginBottom: 40,
+ },
+ containerAchievement: {
+ backgroundColor: Colors.primary2,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ borderRadius: 4,
+ paddingHorizontal: 16,
+ marginBottom: 16,
+ },
+ containerAchievementName: {
+ flexDirection: 'row',
+ },
+ achievementHabitNme: {
+ color: Colors.text,
+ paddingVertical: 20,
+ marginLeft: 15,
+ fontSize: 12,
+ },
+ containerAchievementCategory: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ achievementCategory: {
+ marginLeft: 8,
+ fontSize: 12,
+ color: Colors.text,
+ },
+ containerProfile: {
+ flex: 1,
+ width: (Dimensions.get('window').width - 64) / 2,
+ backgroundColor: 'rgba(156, 198, 255, 0.042)',
+ borderRadius: 8,
+ paddingHorizontal: 20,
+ paddingVertical: 16,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ containerGroup: {
+ flexDirection: 'row',
+ marginBottom: 24,
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ textConnections: {
+ color: Colors.text,
+ flexShrink: 1,
+ fontWeight: '400',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ },
+ textTotalConnections: {
+ color: Colors.text,
+ fontWeight: '600',
+ letterSpacing: -0.02,
+ fontSize: 13,
+ lineHeight: 16,
+ alignContent: 'center',
+ },
+ textRanking: {
+ color: Colors.text,
+ fontWeight: '400',
+ letterSpacing: -0.02,
+ fontSize: 20,
+ lineHeight: 27,
+ },
+ textPositionRanking: {
+ color: Colors.text,
+ fontWeight: '700',
+ letterSpacing: -0.02,
+ fontSize: 16,
+ lineHeight: 16,
+ alignSelf: 'center',
+ },
+ containerButton: {
+ marginBottom: 8,
+ alignItems: 'center',
+ },
+ containerButtonAddConnection: {
+ marginBottom: 8,
+ borderRadius: 4,
+ padding: 16,
+ alignItems: 'center',
+ backgroundColor: '#992538',
+ },
+ buttonConnection: {
+ height: 56,
+ borderRadius: 4,
+ backgroundColor: '#992538',
+ width: Dimensions.get('window').width - 32,
+ marginHorizontal: 16,
+ },
+ buttonInvite: {
+ height: 56,
+ borderRadius: 30,
+ borderWidth: StyleSheet.hairlineWidth,
+ borderColor: 'transparent',
+ width: (Dimensions.get('window').width - 64) / 2,
+ alignItems: 'center',
+ },
+ titleDefaultButton: {
+ fontSize: 16,
+ lineHeight: 21,
+ color: Colors.primary4,
+ alignContent: 'center',
+ },
+ titleInvite: {
+ fontSize: 16,
+ lineHeight: 21,
+ color: 'white',
+ alignContent: 'center',
+ },
+ backButtonStyle: {
+ width: 50,
+ height: 50,
+ marginTop: -33,
+ marginLeft: 12,
+ marginBottom: -33,
+ },
+});
+export default Profile;
diff --git a/src/screens/timeline/AddPost.js b/src/screens/timeline/AddPost.js
new file mode 100644
index 0000000..ad02389
--- /dev/null
+++ b/src/screens/timeline/AddPost.js
@@ -0,0 +1,336 @@
+import React, { useEffect, useState, useRef } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TouchableOpacity,
+ Image,
+ Alert,
+ ScrollView,
+ FlatList,
+ Dimensions,
+} from 'react-native';
+import PropTypes from 'prop-types';
+import { supabase } from '../../config/supabaseClient';
+import Header from '../../components/Header';
+import store from '../../store/storeConfig';
+import { Picker } from '@react-native-picker/picker';
+import * as ImagePicker from 'expo-image-picker';
+import Colors from '../../../assets/styles/Colors';
+import { Input, Button } from 'react-native-elements';
+import ActionSheet from 'react-native-actionsheet';
+import { decode } from 'base64-arraybuffer';
+import moment from 'moment';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import Fetching from '../../components/Fetching';
+
+export default function AddPost({ navigation }) {
+ const [postText, setPostText] = useState('');
+ const [image, setImage] = useState(null);
+ const [schedules, setSchedules] = useState([]);
+ const [selectedSchedule, setSelectedSchedule] = useState(null);
+ const [uploading, setUploading] = useState(false);
+ const [loading, setLoading] = useState(true);
+ const ASPhotoOptions = useRef();
+ const session = store.getState().user.session;
+
+ useEffect(() => {
+ const fetchSchedules = async () => {
+ try {
+ setLoading(true);
+ if (session?.user) {
+ const { data: scheduleData, error: scheduleError } = await supabase
+ .from('Schedule')
+ .select('*, habit_id(habit_title)')
+ .eq('user_id', session.user.id);
+
+ if (scheduleError) {
+ throw scheduleError;
+ }
+
+ if (scheduleData) {
+ setSchedules(scheduleData);
+ if (scheduleData.length > 0) {
+ setSelectedSchedule(scheduleData[0].schedule_id);
+ }
+ }
+ }
+ } catch (error) {
+ console.log('error:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSchedules();
+ }, []);
+
+ const addPost = async () => {
+ try {
+ const { data: postInsertData, error: postInsertError } = await supabase
+ .from('Post')
+ .insert([{ user_id: session.user.id, schedule_id: selectedSchedule, post_description: postText }])
+ .select();
+
+ if (postInsertError) {
+ console.log('error in post:', postInsertError);
+ return;
+ }
+
+ setPostText('');
+
+ let imageUrl = null;
+ if (image) {
+ setUploading(true);
+ const file = image;
+ const fileExt = file.uri.split('/').pop();
+ const fileName = `${Date.now()}_${fileExt}`;
+ const filePath = `posts/${fileName}`;
+
+ const response = await fetch(file.uri);
+ const blob = await response.blob();
+
+ const reader = new FileReader();
+ reader.onloadend = async () => {
+ const base64data = reader.result.split(',')[1];
+ const arrayBuffer = decode(base64data);
+
+ const { data: uploadData, error: uploadError } = await supabase.storage
+ .from('habit')
+ .upload(fileName, arrayBuffer, {
+ cacheControl: '3600',
+ upsert: false,
+ contentType: 'image/jpeg'
+ });
+
+ if (uploadError) {
+ throw uploadError;
+ }
+
+ const publicUrlResponse = supabase.storage.from('habit').getPublicUrl(fileName);
+ const imageUrl = publicUrlResponse.data.publicUrl;
+
+ const { data: postImageInsertData, error: postImageInsertError } = await supabase
+ .from('Image')
+ .insert([{
+ post_id: postInsertData[0].post_id,
+ image_photo: imageUrl
+ }])
+ .select();
+
+ if (postImageInsertError) {
+ console.log('error in post image:', postImageInsertError);
+ }
+
+ setImage(null);
+ setUploading(false);
+ };
+ reader.readAsDataURL(blob);
+ }
+
+ Alert.alert('Success', 'Your post was successfully posted to the timeline!', [
+ { text: 'OK', onPress: () => navigation.navigate('TimelineScreen') },
+ ]);
+ } catch (error) {
+ console.log('error:', error);
+ }
+ };
+
+
+ const handleActionSheet = async (index) => {
+ if (index === 0) {
+ let { status } = await ImagePicker.requestCameraPermissionsAsync();
+ if (status === 'granted') {
+ pickCamera();
+ } else {
+ Alert.alert('Oops', 'You need to allow access to the camera first.');
+ }
+ } else if (index === 1) {
+ let { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
+ if (status === 'granted') {
+ pickGallery();
+ } else {
+ Alert.alert('Oops', 'You need to allow access to the library first.');
+ }
+ }
+ };
+
+ const pickCamera = async () => {
+ let result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ setImage(result.assets[0]);
+ }
+ };
+
+ const pickGallery = async () => {
+ let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images });
+ if (!result.cancelled) {
+ setImage(result.assets[0]);
+ }
+ };
+
+ const addHabit = () => {
+ navigation.navigate('AddHabit');
+ };
+
+ const renderAddPost = () => {
+ if (loading) {
+ return ;
+ }
+
+ if (schedules.length === 0) {
+ return (
+
+ No habits found. Add your first habit to get started!
+
+
+ );
+ }
+
+ return (
+
+
+
+ Select a Habit:
+ setSelectedSchedule(itemValue)}
+ style={{ color: Colors.text }}
+ >
+ {schedules.map(schedule => (
+
+ ))}
+
+
+ {image && }
+
+ ASPhotoOptions.current.show()} style={styles.pickImageButton}>
+ Pick an image
+
+
+ Post
+
+
+ handleActionSheet(index)}
+ />
+
+ );
+ };
+
+ return (
+
+
+ {renderAddPost()}
+
+ );
+}
+
+AddPost.proptypes = {
+ navigation: PropTypes.object,
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: Colors.background,
+ },
+ addPostContainer: {
+ padding: 10,
+ borderBottomWidth: 1,
+ borderBottomColor: '#ccc',
+ },
+ input: {
+ height: 100,
+ fontSize: 16,
+ marginBottom: 10,
+ backgroundColor: 'rgba(156, 198, 255, 0.025)',
+ borderRadius: 8,
+ color: Colors.text,
+ },
+ pickImageButton: {
+ backgroundColor: '#5c6bc0',
+ padding: 12,
+ borderRadius: 30,
+ alignItems: 'center',
+ marginVertical: 4,
+ },
+ pickImageButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: 'bold',
+ letterSpacing: 1,
+ paddingVertical: 10,
+ paddingHorizontal: 20,
+ borderRadius: 25,
+ overflow: 'hidden',
+ },
+ postButton: {
+ backgroundColor: '#5c6bc0',
+ padding: 12,
+ borderRadius: 30,
+ alignItems: 'center',
+ marginVertical: 4,
+ },
+ postButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: 'bold',
+ letterSpacing: 1,
+ paddingVertical: 10,
+ paddingHorizontal: 20,
+ borderRadius: 25,
+ overflow: 'hidden',
+ },
+ image: {
+ width: '100%',
+ height: 200,
+ marginBottom: 10,
+ },
+ label: {
+ fontSize: 16,
+ fontWeight: '700',
+ marginBottom: 10,
+ color: Colors.text,
+ },
+ text: {
+ color: Colors.text,
+ },
+ emptyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ emptyText: {
+ fontSize: 18,
+ color: Colors.text,
+ marginBottom: 20,
+ textAlign: 'center',
+ },
+ addHabitButton: {
+ backgroundColor: Colors.primary,
+ paddingVertical: 15,
+ paddingHorizontal: 30,
+ borderRadius: 30,
+ },
+ addHabitButtonText: {
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: 'bold',
+ },
+});
diff --git a/src/screens/timeline/CommentsScreen.js b/src/screens/timeline/CommentsScreen.js
new file mode 100644
index 0000000..76512f1
--- /dev/null
+++ b/src/screens/timeline/CommentsScreen.js
@@ -0,0 +1,219 @@
+import React, { useEffect, useState } from 'react';
+import { View, Text, StyleSheet, Image } from 'react-native';
+import PropTypes from 'prop-types';
+import { supabase } from '../../config/supabaseClient';
+import Header from '../../components/Header';
+import CommentCard from '../../components/CommentCard';
+import { FlatList, ScrollView } from 'react-native-gesture-handler';
+import Icon from 'react-native-vector-icons/FontAwesome';
+import Colors from '../../../assets/styles/Colors';
+import { Input } from 'react-native-elements';
+import Default from '../../../assets/styles/Default';
+import { TouchableOpacity } from 'react-native-gesture-handler';
+import store from '../../store/storeConfig';
+
+export default function CommentsScreen({ route }) {
+ const [comments, setComments] = useState(null);
+ const [comment, setComment] = useState('');
+ const postId = route.params.postId;
+ const [isAddingComment, setIsAddingComment] = useState(false);
+
+ useEffect(() => {
+ async function fetchData() {
+ try {
+ const { data: commentData, error } = await supabase
+ .from('Comments')
+ .select('*, user_id(*)')
+ .eq('post_id', postId)
+ .order('created_at', { ascending: true });
+
+ if (error) {
+ throw error;
+ }
+ setComments(commentData);
+ } catch (error) {
+ console.log('error', error);
+ }
+
+
+ }
+
+ fetchData();
+ }, []);
+
+ const addComment = async () => {
+ if (isAddingComment) {
+ try {
+ const { data: commentInsertData, error: commentInsertError} = await supabase
+ .from('Comments')
+ .insert([{ user_id: store.getState().user.session.user.id, post_id: postId, content: comment }])
+ .select();
+
+ setComment('');
+ setIsAddingComment(false);
+ } catch (error) {
+ console.log('error:', error);
+ }
+ }
+ };
+
+ const toggleCommenting = () => {
+ setComment('');
+ setIsAddingComment(!isAddingComment);
+ };
+
+
+
+ const commentListFooter = () => {
+ return (
+
+
+
+ );
+ }
+
+
+
+ return (
+
+
+
+
+
+ {route.params.userData.name}
+
+ {route.params.postData.description}
+
+ {isAddingComment ?
+
+
+
+
+ Add Comment
+
+
+ Cancel
+
+
+
+ :
+ item.id}
+ renderItem={({ item }) => (
+
+ )}
+ contentContainerStyle={styles.list}
+ ListFooterComponent={commentListFooter}
+ />}
+
+ );
+}
+
+CommentsScreen.propTypes = {
+ postId: PropTypes.string,
+ route: PropTypes.object,
+};
+
+const styles = StyleSheet.create({
+ postContainer: {
+ padding: 16,
+ backgroundColor: 'rgba(156, 198, 255, 0.25)',
+ marginTop: 10,
+ marginLeft: 10,
+ marginRight: 10,
+ borderRadius: 8,
+ },
+ postHeader: {
+ display: 'flex',
+ flexDirection: 'row',
+ },
+ container: {
+ flex: 1,
+ },
+ list: {
+ padding: 16,
+ },
+ addButton: {
+ flex: 1,
+ width: '100%',
+ backgroundColor: 'blue',
+ padding: 16,
+ borderRadius: 50,
+ marginTop: 16,
+ },
+ cancelButton: {
+ flex: 1,
+ width: '100%',
+ backgroundColor: 'red',
+ padding: 16,
+ borderRadius: 50,
+ marginTop: 16,
+ },
+ buttonText: {
+ color: Colors.text,
+ textAlign: 'center',
+ fontSize: 18,
+ fontWeight: '700',
+ },
+ buttonIcon: {
+ color: 'white',
+ textAlign: 'center',
+ },
+ userImage: {
+ width: 38,
+ height: 38,
+ borderRadius: 32,
+ marginRight: 8,
+ },
+ postText: {
+ justifyContent: 'flex-start',
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 16,
+ color: Colors.text,
+ width: '100%',
+ flexShrink: 1,
+ paddingLeft: 15,
+ paddingTop: 15,
+ },
+ textUserName: {
+ flex: 1,
+ fontSize: 16,
+ fontWeight: '700',
+ lineHeight: 16,
+ color: Colors.text,
+ },
+ commentInput: {
+ padding: 8,
+ paddingTop: 16,
+ width: '100%',
+ backgroundColor: 'rgba(156, 198, 255, 0.05)',
+ borderRadius: 8,
+ marginLeft: 10,
+ marginRight: 10,
+ },
+ addCommentButtonContainer: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-evenly',
+ padding: 10,
+ },
+});
+
diff --git a/src/screens/timeline/Timeline.js b/src/screens/timeline/Timeline.js
new file mode 100644
index 0000000..7a075c2
--- /dev/null
+++ b/src/screens/timeline/Timeline.js
@@ -0,0 +1,206 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ StyleSheet,
+ FlatList,
+ ActivityIndicator,
+ Alert,
+ RefreshControl,
+ Text,
+} from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import { supabase } from '../../config/supabaseClient';
+import Colors from '../../../assets/styles/Colors';
+import Header from '../../components/Header';
+import CardPost from '../../components/CardPost';
+import store from '../../store/storeConfig';
+import { Button } from 'react-native-paper';
+
+const Timeline = () => {
+ const navigation = useNavigation();
+ const session = store.getState().user.session;
+ const [timelinePosts, setTimelinePosts] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [refreshing, setRefreshing] = useState(false);
+ const [page, setPage] = useState(0);
+ const [loadMore, setLoadMore] = useState(true);
+
+ useEffect(() => {
+ fetchPosts(true);
+ }, []);
+
+ const fetchPosts = async (isInitialFetch = false) => {
+ try {
+ setLoading(true);
+
+ const { data: followingData, error: followingError } = await supabase
+ .from('Following')
+ .select('following')
+ .eq('follower', session?.user.id);
+
+ if (followingError) throw followingError;
+
+ const followedUserIds = followingData.map(following => following.following);
+ const userIds = [...followedUserIds, session?.user.id]; // includes the user's own ID so u can now see ur own posts
+
+ const { data: postData, error: postError } = await supabase
+ .from('Post')
+ .select('*')
+ .in('user_id', userIds) // queried the posts by followed users and the user themselves
+ .order('created_at', { ascending: false });
+
+ if (postError) throw postError;
+
+ if (postData.length === 0) {
+ setLoadMore(false);
+ return;
+ }
+
+ const userIdsInPosts = postData.map(post => post.user_id);
+ const { data: userData, error: userError } = await supabase
+ .from('User')
+ .select('user_id, username, profile_image')
+ .in('user_id', userIdsInPosts);
+
+ if (userError) throw userError;
+
+ const postIds = postData.map(post => post.post_id);
+ const { data: likeData, error: likeError } = await supabase
+ .from('Like')
+ .select('post_id, user_id')
+ .in('post_id', postIds);
+
+ if (likeError) throw likeError;
+
+ const { data: imagePostData, error: imagePostError } = await supabase
+ .from('Image')
+ .select('*')
+ .in('post_id', postIds);
+
+ if (imagePostError) throw imagePostError;
+
+ const likesMap = postIds.reduce((acc, postId) => {
+ acc[postId] = likeData.filter(like => like.post_id === postId);
+ return acc;
+ }, {});
+
+ const combinedData = postData.map(post => {
+ const user = userData.find(u => u.user_id === post.user_id);
+ const likes = likesMap[post.post_id] || [];
+ const image = imagePostData.find(i => i.post_id === post.post_id);
+
+ return {
+ ...post,
+ username: user ? user.username : 'Unknown User',
+ profile_image: user ? user.profile_image : null,
+ likeFromUser: likes.some(like => like.user_id === session.user.id),
+ countLikes: likes.length,
+ habit_photo: image ? image.image_photo : null,
+ };
+ });
+
+ setTimelinePosts(combinedData);
+ setPage(prevPage => prevPage + 1);
+ setLoadMore(combinedData.length === 10);
+ } catch (error) {
+ Alert.alert('Error fetching posts', error.message);
+ } finally {
+ setLoading(false);
+ setRefreshing(false);
+ }
+ };
+
+ const onRefresh = () => {
+ setRefreshing(true);
+ fetchPosts(true);
+ };
+
+ const renderPost = ({ item }) => {
+ return (
+ {},
+ countComments: 0,
+ onDeletePostSuccess: () => {},
+ }}
+ navigation={navigation}
+ />
+ );
+ };
+
+ const renderFooter = () => {
+ if (!loading) return null;
+ return ;
+ };
+
+ const renderEmpty = () => (
+
+ No posts to show
+
+ );
+
+ return (
+
+
+ {/* need to add a button to navigate to the add post page */}
+ navigation.navigate('AddPost')}>
+ Add Post
+
+ index.toString()}
+ renderItem={renderPost}
+ ListEmptyComponent={renderEmpty}
+ contentContainerStyle={styles.list}
+ refreshControl={
+
+ }
+ ListFooterComponent={renderFooter}
+ onEndReached={() => {
+ if (loadMore && !loading) {
+ fetchPosts();
+ }
+ }}
+ onEndReachedThreshold={0.5}
+ />
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: Colors.background,
+ },
+ list: {
+ padding: 10,
+ },
+ emptyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ emptyText: {
+ fontSize: 16,
+ color: Colors.text,
+ },
+});
+
+export default Timeline;
diff --git a/src/services/PaymentService.js b/src/services/PaymentService.js
new file mode 100644
index 0000000..ca17ce3
--- /dev/null
+++ b/src/services/PaymentService.js
@@ -0,0 +1,23 @@
+import { initStripe } from '@stripe/stripe-react-native';
+import { useStripe } from '@stripe/stripe-react-native';
+
+const publishableKey =
+ 'pk_test_51PCOhVKV9eTtyARpJXFmNodQEPjtVYgDm6VyeFXmMUgWx1047bvdaDkfZeKoMHEF3hoqKBjLATDue6dF5a5JqHTs00sanjaOtO';
+
+export const initializeStripe = async () => {
+ await initStripe({
+ publishableKey,
+ //merchantIdentifier: 'merchant.identifier', // optional, used for Apple Pay
+ });
+};
+
+export const handlePayment = async (paymentMethodId, paymentIntentId) => {
+ const { confirmPayment } = useStripe(); // uses stripe made method called confirmPayment
+
+ const paymentResult = await confirmPayment(paymentIntentId, {
+ type: 'Card',
+ paymentMethodId: paymentMethodId,
+ });
+
+ return paymentResult;
+};
diff --git a/src/store/ducks/community.js b/src/store/ducks/community.js
new file mode 100644
index 0000000..7d126ea
--- /dev/null
+++ b/src/store/ducks/community.js
@@ -0,0 +1,124 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+export const searchCommunity = (search) => {
+ return axios.post(`${Constants.baseUrl}/community/search`, search);
+};
+
+export const getByCategory = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/getByCategory`, data);
+};
+
+export const listPublicCommunities = (data) => {
+ return axios.post(
+ `${Constants.baseUrl}/community/listPublicCommunities`,
+ data,
+ );
+};
+
+export const get = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/${id}`);
+};
+
+export const sendRequest = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/joinCommunity`, data);
+};
+
+export const store = (data) => {
+ return axios.post(`${Constants.baseUrl}/community`, data);
+};
+
+export const updateAutomaticPublish = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/community/settings/${id}`, data);
+};
+
+export const getSettings = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/settings/${id}`);
+};
+
+export const getMembers = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/member/getAll`, data);
+};
+
+export const updateMember = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/member/update`, data);
+};
+
+export const answerRequest = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/member/answer`, data);
+};
+
+export const removeMember = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/member/remove`, data);
+};
+
+export const leaveCommunity = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/member/leave`, data);
+};
+
+export const getOldestMember = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/member/oldMember/${id}`);
+};
+
+export const listPosts = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/post/list`, data);
+};
+
+export const storePost = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/post`, data);
+};
+
+export const likePost = (id) => {
+ return axios.post(`${Constants.baseUrl}/community/post/like/${id}`);
+};
+
+export const savePost = (id) => {
+ return axios.post(`${Constants.baseUrl}/community/post/save/${id}`);
+};
+
+export const getPost = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/post/${id}`);
+};
+
+export const updatePost = (id, data) => {
+ return axios.post(
+ `${Constants.baseUrl}/community/post/${id}?_method=PUT`,
+ data,
+ );
+};
+
+export const storeComment = (id, data) => {
+ return axios.post(`${Constants.baseUrl}/community/post/comment/${id}`, data);
+};
+
+export const listHabits = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/habit/list/${id}`);
+};
+
+export const getCommunityHabit = (id) => {
+ return axios.get(`${Constants.baseUrl}/community/habit/${id}`);
+};
+
+export const storeCommunityHabit = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/habit`, data);
+};
+
+export const storeCustomCommunityHabit = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/habit/custom`, data);
+};
+
+export const updateCustomCommunityHabit = (data) => {
+ return axios.post(`${Constants.baseUrl}/community/habit/updateCustom`, data);
+};
+
+export const updateCommunityHabit = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/community/habit/${id}`, data);
+};
+
+export const deleteCommunityHabit = (id) => {
+ return axios.delete(`${Constants.baseUrl}/community/habit/${id}`);
+};
+
+export const deleteCommunityPost = (id) => {
+ return axios.delete(`${Constants.baseUrl}/community/post/${id}`);
+};
diff --git a/src/store/ducks/connection.js b/src/store/ducks/connection.js
new file mode 100644
index 0000000..2cfbd97
--- /dev/null
+++ b/src/store/ducks/connection.js
@@ -0,0 +1,73 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const listConnection = () => {
+ return axios.get(`${Constants.baseUrl}/userconnection/list`);
+};
+
+export const listConnectionByUser = (id) => {
+ return axios.get(`${Constants.baseUrl}/userconnection/listByUser/${id}`);
+};
+
+export const getPending = () => {
+ return axios.get(`${Constants.baseUrl}/userconnection/pending`);
+};
+
+export const storeConnection = (data) => {
+ return axios.post(`${Constants.baseUrl}/userconnection`, data);
+};
+
+export const searchConnection = (search) => {
+ return axios.post(`${Constants.baseUrl}/userconnection/search`, search);
+};
+
+export const getConnection = (id) => {
+ return axios.get(`${Constants.baseUrl}/userconnection/${id}`);
+};
+
+export const getUsersByEmails = (data) => {
+ return axios.post(`${Constants.baseUrl}/userconnection/getByEmails`, data);
+};
+
+export const answerConnection = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/userconnection/answer/${id}`, data);
+};
+
+export const cancelConnection = (id) => {
+ return axios.put(`${Constants.baseUrl}/userconnection/cancel/${id}`);
+};
+
+export const cancelFirstInvite = (id) => {
+ return axios.delete(
+ `${Constants.baseUrl}/userconnection/cancelFirstInvite/${id}`,
+ );
+};
+
+export const inviteAll = (data) => {
+ return axios.post(`${Constants.baseUrl}/userconnection/inviteAll`, data);
+};
+
+export const cancelAll = (data) => {
+ return axios.post(`${Constants.baseUrl}/userconnection/cancelAll`, data);
+};
+
+export const deleteConnection = (id) => {
+ return axios.delete(`${Constants.baseUrl}/userconnection/${id}`);
+};
diff --git a/src/store/ducks/extraTip.js b/src/store/ducks/extraTip.js
new file mode 100644
index 0000000..a20b489
--- /dev/null
+++ b/src/store/ducks/extraTip.js
@@ -0,0 +1,27 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const get = (id) => {
+ return axios.get(`${Constants.baseUrl}/extratip/${id}`);
+};
+
+export const getAll = () => {
+ return axios.get(`${Constants.baseUrl}/extratip/all`);
+};
diff --git a/src/store/ducks/fetching.js b/src/store/ducks/fetching.js
new file mode 100644
index 0000000..fd0bcfd
--- /dev/null
+++ b/src/store/ducks/fetching.js
@@ -0,0 +1,43 @@
+// Action Types
+
+export const Types = {
+ IS_FETCHING: "IS_FETCHING",
+ DONE_FETCHING: "DONE_FETCHING",
+};
+
+// Reducer
+
+const initialState = {
+ isFetching: false,
+};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ case Types.IS_FETCHING:
+ return {
+ ...state,
+ isFetching: true,
+ };
+ case Types.DONE_FETCHING:
+ return {
+ ...state,
+ isFetching: false,
+ };
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const isFetching = () => {
+ return {
+ type: Types.IS_FETCHING,
+ };
+};
+
+export const doneFetching = () => {
+ return {
+ type: Types.DONE_FETCHING,
+ };
+};
diff --git a/src/store/ducks/habit.js b/src/store/ducks/habit.js
new file mode 100644
index 0000000..6cab525
--- /dev/null
+++ b/src/store/ducks/habit.js
@@ -0,0 +1,93 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const get = (id) => {
+ return axios.get(`${Constants.baseUrl}/habit/${id}`);
+};
+
+export const storeUserHabit = (user_habit) => {
+ return axios.post(`${Constants.baseUrl}/userhabit`, user_habit);
+};
+
+export const deleteUserHabit = (id) => {
+ return axios.delete(`${Constants.baseUrl}/userhabit/${id}`);
+};
+
+export const checkUserHabit = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/userhabit/check/${id}`, data);
+};
+
+export const storeCustom = (user_habit) => {
+ return axios.post(`${Constants.baseUrl}/userhabit/custom`, user_habit);
+};
+
+export const getMomentum = () => {
+ return axios.get(`${Constants.baseUrl}/userhabit/momentum`);
+};
+
+export const getAllCategory = () => {
+ return axios.get(`${Constants.baseUrl}/category/all`);
+};
+
+export const getAllCategoryUserHabits = () => {
+ return axios.get(`${Constants.baseUrl}/userhabit/category/all`);
+};
+
+export const getUserHabit = (id) => {
+ return axios.get(`${Constants.baseUrl}/userhabit/${id}`);
+};
+
+export const updateCustomUserHabit = (data) => {
+ return axios.post(`${Constants.baseUrl}/userhabit/updateCustom`, data);
+};
+
+export const updateUserHabit = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/userhabit/${id}`, data);
+};
+
+export const getAllUserHabits = () => {
+ return axios.get(`${Constants.baseUrl}/userhabit/all`);
+};
+
+export const listAllHabitsByUser = (id) => {
+ return axios.get(`${Constants.baseUrl}/userhabit/listByUser/${id}`);
+};
+
+export const getUserChecklist = (data) => {
+ return axios.post(`${Constants.baseUrl}/userhabit/checklist`, data);
+};
+
+export const toggleUserHabit = (id, data) => {
+ return axios.put(`${Constants.baseUrl}/userhabit/toggle/${id}`, data);
+};
+
+export const getAllCategoryWithCheckedHabbits = (data) => {
+ return axios.post(
+ `${Constants.baseUrl}/userhabit/category/getAllCategoryWithCheckedHabbits`,
+ data,
+ );
+};
+
+export const storeWithCommunityHabits = (communityHabitsId) => {
+ return axios.post(
+ `${Constants.baseUrl}/userhabit/withCommunityHabits`,
+ communityHabitsId,
+ );
+};
diff --git a/src/store/ducks/post.js b/src/store/ducks/post.js
new file mode 100644
index 0000000..7e75e3d
--- /dev/null
+++ b/src/store/ducks/post.js
@@ -0,0 +1,70 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const getMyConnectionPost = (data) => {
+ return axios.post(`${Constants.baseUrl}/post/connection`, data);
+};
+
+export const getTimelineMyPosts = (data) => {
+ return axios.post(`${Constants.baseUrl}/post/timeline/myPosts`, data);
+};
+
+export const getTimelineAllPosts = (data) => {
+ return axios.post(`${Constants.baseUrl}/post/timeline/all`, data);
+};
+
+export const getMyPost = (data) => {
+ return axios.post(`${Constants.baseUrl}/post/user`, data);
+};
+
+export const getPost = (id) => {
+ return axios.get(`${Constants.baseUrl}/post/${id}`);
+};
+
+export const updatePost = (id, data) => {
+ return axios.get(`${Constants.baseUrl}/post/${id}?_method=PUT`, data);
+};
+
+export const savePost = (id) => {
+ return axios.post(`${Constants.baseUrl}/post/save/${id}`);
+};
+
+export const storeComment = (id, data) => {
+ return axios.post(`${Constants.baseUrl}/post/comment/${id}`, data);
+};
+
+export const likePost = (id) => {
+ return axios.post(`${Constants.baseUrl}/post/like/${id}`);
+};
+
+export const deletePost = (id) => {
+ return axios.delete(`${Constants.baseUrl}/post/${id}`);
+};
+export const deleteComment = (id) => {
+ return axios.delete(`${Constants.baseUrl}/post/${id}`);
+};
+
+export const randomHabits = () => {
+ return axios.post(`${Constants.baseUrl}/habit/random`);
+};
+
+export const listRandomHabits = (data) => {
+ return axios.post(`${Constants.baseUrl}/habit/list`, data);
+};
diff --git a/src/store/ducks/product.js b/src/store/ducks/product.js
new file mode 100644
index 0000000..1add442
--- /dev/null
+++ b/src/store/ducks/product.js
@@ -0,0 +1,35 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const deleteUserProduct = (id) => {
+ return axios.delete(`${Constants.baseUrl}/userproduct/${id}`);
+};
+
+export const getAllUserProduct = () => {
+ return axios.get(`${Constants.baseUrl}/userproduct/all`);
+};
+
+export const getAll = () => {
+ return axios.get(`${Constants.baseUrl}/product/all`);
+};
+
+export const storeUserProduct = (ids) => {
+ return axios.post(`${Constants.baseUrl}/userproduct`, ids);
+};
diff --git a/src/store/ducks/ranking.js b/src/store/ducks/ranking.js
new file mode 100644
index 0000000..5848b8d
--- /dev/null
+++ b/src/store/ducks/ranking.js
@@ -0,0 +1,27 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const getRanking = (data) => {
+ return axios.post(`${Constants.baseUrl}/ranking/list`, data);
+};
+
+export const getUserRankingPosition = (data) => {
+ return axios.get(`${Constants.baseUrl}/ranking/position`);
+};
diff --git a/src/store/ducks/report.js b/src/store/ducks/report.js
new file mode 100644
index 0000000..88f3bc8
--- /dev/null
+++ b/src/store/ducks/report.js
@@ -0,0 +1,23 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const storeReport = (data) => {
+ return axios.post(`${Constants.baseUrl}/report`, data);
+};
diff --git a/src/store/ducks/score.js b/src/store/ducks/score.js
new file mode 100644
index 0000000..9baba27
--- /dev/null
+++ b/src/store/ducks/score.js
@@ -0,0 +1,31 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const getLastUserScore = (id) => {
+ return axios.get(`${Constants.baseUrl}/userscore/last/${id}`);
+};
+
+export const storeUserScore = (data) => {
+ return axios.post(`${Constants.baseUrl}/userscore`, data);
+};
+
+export const getScoreForm = (id) => {
+ return axios.get(`${Constants.baseUrl}/scoreform/${id}`);
+};
diff --git a/src/store/ducks/team.js b/src/store/ducks/team.js
new file mode 100644
index 0000000..a3eddcb
--- /dev/null
+++ b/src/store/ducks/team.js
@@ -0,0 +1,67 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {
+ TEAM_SET_CREATE_DATA: "TEAM_SET_CREATE_DATA",
+};
+
+// Reducer
+
+const initialState = {
+ createTeamData: {},
+};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ case Types.TEAM_SET_CREATE_DATA:
+ return {
+ ...initialState,
+ createTeamData: action.payload,
+ };
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const setCreateTeamData = (payload) => {
+ return {
+ type: Types.TEAM_SET_CREATE_DATA,
+ payload,
+ };
+};
+
+export const exitTeam = (id) => {
+ return axios.put(`${Constants.baseUrl}/team/exit/${id}`);
+};
+
+export const getAll = () => {
+ return axios.get(`${Constants.baseUrl}/team/all`);
+};
+
+export const get = (id) => {
+ return axios.get(`${Constants.baseUrl}/team/${id}`);
+};
+
+export const store = (team) => {
+ return axios.post(`${Constants.baseUrl}/team`, team);
+};
+
+export const addHabitsToUserTeam = (data) => {
+ return axios.post(`${Constants.baseUrl}/userteam/habits`, data);
+};
+
+export const removeHabitFromUserTeam = (id) => {
+ return axios.delete(`${Constants.baseUrl}/userteam/habits/${id}`);
+};
+
+export const inviteUsersToUserTeam = (data) => {
+ return axios.post(`${Constants.baseUrl}/userteam/invite`, data);
+};
+
+export const removeUserFromUserTeam = (id) => {
+ return axios.delete(`${Constants.baseUrl}/userteam/user/${id}`);
+};
diff --git a/src/store/ducks/titan.js b/src/store/ducks/titan.js
new file mode 100644
index 0000000..4b46426
--- /dev/null
+++ b/src/store/ducks/titan.js
@@ -0,0 +1,31 @@
+import { Constants } from "../../constants/Constants";
+import axios from "axios";
+
+// Action Types
+
+export const Types = {};
+
+// Reducer
+
+const initialState = {};
+
+export default function reducer(state = initialState, action) {
+ switch (action.type) {
+ default:
+ return state;
+ }
+}
+
+// Action Creators
+
+export const getAllByHabit = (id) => {
+ return axios.get(`${Constants.baseUrl}/titan/habit/${id}`);
+};
+
+export const getAll = () => {
+ return axios.get(`${Constants.baseUrl}/titan/all`);
+};
+
+export const get = (id) => {
+ return axios.get(`${Constants.baseUrl}/titan/${id}`);
+};
diff --git a/src/store/ducks/user.js b/src/store/ducks/user.js
new file mode 100644
index 0000000..6fde7bf
--- /dev/null
+++ b/src/store/ducks/user.js
@@ -0,0 +1,417 @@
+// import { Alert } from "react-native";
+// import { isFetching, doneFetching } from "./fetching";
+// import { Constants } from "../../constants/Constants";
+// import axios from "axios";
+// import AsyncStorage from "@react-native-async-storage/async-storage";
+// import * as Notifications from "expo-notifications";
+// import { supabase } from "../../config/supabaseClient";
+// import { useSelector } from "react-redux";
+
+// // Action Types
+
+export const Types = {
+ USER_LOGGED_IN: 'USER_LOGGED_IN',
+ USER_LOGGED_OUT: 'USER_LOGGED_OUT',
+ USER_SET_REGISTER_DATA: 'USER_SET_REGISTER_DATA',
+};
+
+// // Reducer
+
+const initialState = {};
+
+export default function users(state = initialState, action) {
+ switch (action.type) {
+ case Types.USER_LOGGED_IN:
+ var { session } = action.payload;
+ return { ...state, session: session };
+ case Types.USER_LOGGED_OUT:
+ return {
+ ...initialState,
+ };
+ // case Types.USER_SET_REGISTER_DATA:
+ // return action.payload;
+ default:
+ return state;
+ }
+}
+
+// // Action Creators
+
+export function userLogin(session) {
+ return {
+ type: Types.USER_LOGGED_IN,
+ payload: { session },
+ };
+}
+
+// export const getBasicInformationUser = (id) => {
+// return axios.get(`${Constants.baseUrl}/user/${id}`);
+// };
+
+// export const getUserInfos = (id) => {
+// return axios.post(`${Constants.baseUrl}/user/getInfos`);
+// };
+
+// export const setRegisterData = (payload) => {
+// return {
+// type: Types.USER_SET_REGISTER_DATA,
+// payload,
+// };
+// };
+
+// export const userLogged = (user) => {
+// return {
+// type: Types.USER_LOGGED_IN,
+// payload: user,
+// };
+// };
+
+// export const setLogout = () => {
+// return {
+// type: Types.USER_LOGGED_OUT,
+// };
+// };
+
+// export const login = (email, password) => {
+// return (dispatch) => {
+// dispatch(isFetching());
+// axios
+// .post(`${Constants.baseUrl}/auth`, {
+// email: user.email,
+// password: user.password,
+// facebookToken: user.facebookToken,
+// googleToken: user.googleToken,
+// appleCredential: user.appleCredential,
+// firstLogin: user.firstLogin === undefined ? false : user.firstLogin,
+// })
+// .catch((err) => {
+// if (err?.response?.status === 500) {
+// AsyncStorage.multiRemove([
+// "password",
+// "email",
+// "token",
+// "facebookUser",
+// "googleToken",
+// ]);
+
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// } else {
+// if (err.response.data?.errors?.email) {
+// Alert.alert("Ops!", err.response.data.errors.email[0]);
+// } else {
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// }
+// }
+
+// dispatch(doneFetching());
+// })
+// .then((res) => {
+// if (res?.status === 200) {
+// if (res.data.errors) {
+// AsyncStorage.multiRemove([
+// "password",
+// "email",
+// "token",
+// "facebookUser",
+// "googleToken",
+// ]);
+
+// Alert.alert("Ops!", res.data.errors[0]);
+// } else {
+// AsyncStorage.setItem("email", res.data.user.email);
+// AsyncStorage.setItem("token", res.data.token);
+
+// user.password
+// ? AsyncStorage.setItem("password", user.password)
+// : null;
+// user.googleToken
+// ? AsyncStorage.setItem("googleToken", user.googleToken)
+// : null;
+// user.facebookToken
+// ? AsyncStorage.setItem("facebookToken", user.facebookToken)
+// : null;
+
+// var payload = res.data.user;
+// payload.isLogged = true;
+// payload.firstLogin = res.data.firstLogin;
+
+// axios.defaults.headers.common["Authorization"] =
+// `Bearer ${res.data.token}`;
+
+// axios.interceptors.response.use(
+// function (response) {
+// return response;
+// },
+// function (error) {
+// if (error.response.status === 401) {
+// dispatch(logout());
+// navigation.navigate("Auth");
+// }
+// },
+// );
+
+// dispatch(userLogged(payload));
+// }
+// }
+
+// dispatch(doneFetching());
+// });
+// };
+// };
+
+// export const update = (user, navigation) => {
+// let userForm = new FormData();
+
+// userForm.append("name", user.name);
+// userForm.append("email", user.email);
+// userForm.append("gender", user.gender);
+
+// user.password ? userForm.append("password", user.password) : null;
+// user.profile_picture
+// ? userForm.append("profile_picture", user.profile_picture)
+// : null;
+
+// return (dispatch, getState) => {
+// dispatch(isFetching());
+
+// axios
+// .post(`${Constants.baseUrl}/user/update`, userForm)
+// .catch((err) => {
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// })
+// .then((res) => {
+// if (res?.status === 200) {
+// if (res.data.errors) {
+// Alert.alert("Ops!", res.data.errors[0]);
+// } else {
+// AsyncStorage.setItem("email", user.email);
+// user.password
+// ? AsyncStorage.setItem("password", user.password)
+// : null;
+
+// let userAux = getState().user;
+
+// userAux.name = user.name;
+// userAux.email = user.email;
+// userAux.image = res.data.image;
+// userAux.usr_gender = user.gender;
+
+// dispatch(userLogged(userAux));
+
+// Alert.alert(
+// "Success",
+// "Your profile was successfully updated!",
+// [
+// {
+// text: "Ok",
+// onPress: () => [navigation.pop()],
+// },
+// ],
+// { cancelable: false },
+// );
+// }
+// }
+
+// dispatch(doneFetching());
+// });
+// };
+// };
+
+// export const deleteAccount = () => {
+// return axios.delete(`${Constants.baseUrl}/user`);
+// };
+
+// export const register = (user) => {
+// return axios.post(`${Constants.baseUrl}/register`, user);
+// };
+
+// export const updateInfos = (data) => {
+// return (dispatch, getState) => {
+// dispatch(isFetching());
+
+// axios
+// .post(`${Constants.baseUrl}/user/updateInfos`, data)
+// .catch((err) => {
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// })
+// .then((res) => {
+// if (res?.status === 200) {
+// if (res.data.errors) {
+// Alert.alert("Ops!", res.data.errors[0]);
+// } else {
+// let userAux = getState().user;
+
+// userAux.usr_quote_to_live_by = res.data.usr_quote_to_live_by;
+// userAux.usr_biggest_hack = res.data.usr_biggest_hack;
+// userAux.usr_biggest_challenge = res.data.usr_biggest_challenge;
+
+// dispatch(userLogged(userAux));
+
+// Alert.alert("Success", "Your profile was successfully updated!");
+// }
+// }
+
+// dispatch(doneFetching());
+// });
+// };
+// };
+
+// export const updateFavoriteBook = (data, navigation) => {
+// let userForm = new FormData();
+
+// userForm.append("book_name", data.book_name);
+// data.book_picture && data.image_changed
+// ? userForm.append("book_picture", data.book_picture)
+// : null;
+
+// return (dispatch, getState) => {
+// dispatch(isFetching());
+
+// axios
+// .post(`${Constants.baseUrl}/user/updateBook`, userForm)
+// .catch((err) => {
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// })
+// .then((res) => {
+// if (res?.status === 200) {
+// if (res.data.errors) {
+// Alert.alert("Ops!", res.data.errors[0]);
+// } else {
+// let userAux = getState().user;
+
+// userAux.usr_favorite_book = data.book_name;
+// userAux.image_book = res.data.image_book;
+
+// dispatch(userLogged(userAux));
+
+// Alert.alert(
+// "Success",
+// "Your profile was successfully updated!",
+// [
+// {
+// text: "Ok",
+// onPress: () => [navigation.pop()],
+// },
+// ],
+// { cancelable: false },
+// );
+// }
+// }
+
+// dispatch(doneFetching());
+// });
+// };
+// };
+
+// export const updateFavoriteFood = (data, navigation) => {
+// let userForm = new FormData();
+
+// userForm.append("food_name", data.food_name);
+// data.food_picture && data.image_changed
+// ? userForm.append("food_picture", data.food_picture)
+// : null;
+
+// return (dispatch, getState) => {
+// dispatch(isFetching());
+
+// axios
+// .post(`${Constants.baseUrl}/user/updateFood`, userForm)
+// .catch((err) => {
+// Alert.alert(
+// "Ops!",
+// "Something went wrong with our servers. Please contact us.",
+// );
+// })
+// .then((res) => {
+// if (res?.status === 200) {
+// if (res.data.errors) {
+// Alert.alert("Ops!", res.data.errors[0]);
+// } else {
+// let userAux = getState().user;
+
+// userAux.usr_favorite_food = data.food_name;
+// userAux.image_food = res.data.image_food;
+
+// dispatch(userLogged(userAux));
+
+// Alert.alert(
+// "Success",
+// "Your profile was successfully updated!",
+// [
+// {
+// text: "Ok",
+// onPress: () => [navigation.pop()],
+// },
+// ],
+// { cancelable: false },
+// );
+// }
+// }
+
+// dispatch(doneFetching());
+// });
+// };
+// };
+
+// export const logout = () => {
+// return (dispatch) => {
+// AsyncStorage.multiRemove([
+// "password",
+// "email",
+// "token",
+// "googleToken",
+// "facebookToken",
+// ]);
+
+// dispatch(removePush());
+// dispatch(setLogout());
+// };
+// };
+
+// export const sendPush = (dados) => {
+// if (Platform.OS === "android") {
+// Notifications.createChannelAndroidAsync("pushChannel", {
+// name: "pushChannel",
+// priority: "max",
+// vibrate: [0, 250, 250, 250],
+// });
+// }
+
+// return axios.post(`${Constants.baseUrl}/push`, dados);
+// };
+
+// export const removePush = () => {
+// return (dispatch) => {
+// axios.delete(`${Constants.baseUrl}/push`).catch((err) => {
+// dispatch(
+// setMessage({
+// title: "Erro",
+// text: "Ocorreu um erro inesperado!",
+// }),
+// );
+// });
+// };
+// };
+
+// export const getSavedPosts = (data) => {
+// return axios.post(`${Constants.baseUrl}/user/savedPosts`, data);
+// };
+
+// export const checkEmail = (data) => {
+// return axios.post(`${Constants.baseUrl}/checkEmail`, data);
+// };
diff --git a/src/store/storeConfig.js b/src/store/storeConfig.js
new file mode 100644
index 0000000..511d5a3
--- /dev/null
+++ b/src/store/storeConfig.js
@@ -0,0 +1,28 @@
+import { combineReducers } from 'redux';
+import { configureStore } from '@reduxjs/toolkit';
+import user from './ducks/user';
+// import product from "./ducks/product";
+// import fetching from "./ducks/fetching";
+import habit from './ducks/habit';
+// import titan from "./ducks/titan";
+// import extraTip from "./ducks/extraTip";
+
+// const reducers = combineReducers({
+// fetching,
+// user,
+// product,
+// habit,
+// titan,
+// extraTip,
+// });
+
+const reducers = combineReducers({
+ user,
+ habit,
+});
+
+const store = configureStore({
+ reducer: reducers,
+});
+
+export default store;
diff --git a/src/utils/Utils.js b/src/utils/Utils.js
new file mode 100644
index 0000000..d928dae
--- /dev/null
+++ b/src/utils/Utils.js
@@ -0,0 +1,167 @@
+import React from "react";
+import { Image, Platform } from "react-native";
+import Default from "../../assets/styles/Default";
+import * as ImagePicker from "expo-image-picker";
+import * as mime from "react-native-mime-types";
+import { manipulateAsync } from "expo-image-manipulator";
+
+export const getIcon = (hac_name, icon) => {
+ if (icon !== null) {
+ return ;
+ } else {
+ switch (hac_name) {
+ case "Sleep":
+ return (
+
+ );
+ case "Stress":
+ return (
+
+ );
+ case "Fuel":
+ return (
+
+ );
+ case "Movement":
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+ }
+};
+
+export const getFrequencyTypes = () => {
+ return ["EVERYDAY", "WEEKDAY", "CUSTOM"];
+};
+
+export function getAchievements(data) {
+ let achievements = [];
+ data.map((cat, cat_i) => {
+ return cat.habits.map((hab, hab_i) => {
+ if (hab.ush_current_streak > 29) {
+ hab.hac_name = cat.hac_name;
+
+ achievements.push(hab);
+ }
+ });
+ });
+ return achievements;
+}
+
+export function getIconPost(type_post) {
+ switch (type_post) {
+ case "connection":
+ return (
+
+ );
+ case "community":
+ return (
+
+ );
+ case "score":
+ return (
+
+ );
+ case "check_habit":
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+}
+
+export const takeCamera = async () => {
+ try {
+ let result = await ImagePicker.launchCameraAsync({
+ allowsEditing: true,
+ base64: true,
+ });
+
+ if (!result.canceled) {
+ const manipulateResult = await manipulateAsync(
+ result.assets[0].uri,
+ [{ resize: { width: 756 } }],
+ { compress: 0.5 }, // from 0 to 1 "1 for best quality"
+ );
+
+ return {
+ name: manipulateResult.uri.split("\\").pop().split("/").pop(),
+ type: mime.lookup(manipulateResult.uri),
+ uri:
+ Platform.OS === "android"
+ ? manipulateResult.uri
+ : manipulateResult.uri.replace("file://", ""),
+ };
+ }
+ } catch (err) {
+ return "failed";
+ }
+};
+
+export const takeGaleria = async () => {
+ try {
+ let result = await ImagePicker.launchImageLibraryAsync({
+ allowsEditing: true,
+ base64: true,
+ });
+
+ if (!result.canceled) {
+ const manipulateResult = await manipulateAsync(
+ result.assets[0].uri,
+ [{ resize: { width: 756 } }],
+ { compress: 0.5 }, // from 0 to 1 "1 for best quality"
+ );
+
+ return {
+ name: manipulateResult.uri.split("\\").pop().split("/").pop(),
+ type: mime.lookup(manipulateResult.uri),
+ uri:
+ Platform.OS === "android"
+ ? manipulateResult.uri
+ : manipulateResult.uri.replace("file://", ""),
+ };
+ }
+ } catch (err) {
+ return "failed";
+ }
+};
+
+export const percentageOfValueFromTotal = (value, total) =>
+ Math.floor((value * 100) / total);