diff --git a/.expo-shared/README.md b/.expo-shared/README.md new file mode 100644 index 0000000..e9e5318 --- /dev/null +++ b/.expo-shared/README.md @@ -0,0 +1,11 @@ +> Why do I have a folder named ".expo-shared" in my project? + +The ".expo-shared" folder is created when running commands that produce state that is intended to be shared with all developers on the project. For example, "npx expo-optimize". + +> What does the "assets.json" file contain? + +The "assets.json" file describes the assets that have been optimized through "expo-optimize" and do not need to be processed again. + +> Should I commit the ".expo-shared" folder? + +Yes, you should share the ".expo-shared" folder with your collaborators. diff --git a/.expo-shared/assets.json b/.expo-shared/assets.json index 1e6decf..0967ef4 100644 --- a/.expo-shared/assets.json +++ b/.expo-shared/assets.json @@ -1,4 +1 @@ -{ - "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, - "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true -} +{} diff --git a/App.js b/App.js index 09f879b..f376742 100644 --- a/App.js +++ b/App.js @@ -1,20 +1,69 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; +import { StyleSheet, View } from "react-native"; +import { useFonts } from "expo-font"; +import Navigation from "./navigation/index"; +import AlertModal from "./src/components/AlertModal"; +import { Provider } from "react-redux"; +import { store, persistor } from "./src/redux/store"; +import * as SplashScreen from 'expo-splash-screen'; +import React from "react"; +import { PersistGate } from "redux-persist/integration/react"; + +const App = () => { + const [appIsReady, setAppIsReady] = React.useState(false); + let [fontsLoaded] = useFonts({ + light: require("./assets/fonts/Farsi/Light.ttf"), + regular: require("./assets/fonts/Farsi/Regular.ttf"), + bold: require("./assets/fonts/Farsi/Medium.ttf"), + demi: require("./assets/fonts/Farsi/DemiBold.ttf"), + }); + + React.useEffect(() => { + async function prepare() { + try { + // Keep the splash screen visible while we fetch resources + await SplashScreen.preventAutoHideAsync(); + // Pre-load fonts, make any API calls you need to do here + // Artificially delay for two seconds to simulate a slow loading + // experience. Please remove this if you copy and paste the code! + await new Promise((resolve) => setTimeout(resolve, 2000)); + } catch (e) { + console.warn(e); + } finally { + // Tell the application to render + setAppIsReady(true); + } + } + + prepare(); + }, []); + + const onLayoutRootView = React.useCallback(async () => { + if (fontsLoaded) { + // This tells the splash screen to hide immediately! If we call this after + // `setAppIsReady`, then we may see a blank screen while the app is + // loading its initial state and rendering its first pixels. So instead, + // we hide the splash screen once we know the root view has already + // performed layout. + await SplashScreen.hideAsync(); + } + }, [fontsLoaded]); + + if (!fontsLoaded) { + return null; + } -export default function App() { return ( - - Open up App.js to start working on your app! - + + + + + + + ); -} +}; + +const styles = StyleSheet.create({}); -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); +export default App; diff --git a/app.json b/app.json index 2fc77c0..ee445ce 100644 --- a/app.json +++ b/app.json @@ -4,10 +4,10 @@ "slug": "final_danovin_expo", "version": "1.0.0", "orientation": "portrait", - "icon": "./assets/icon.png", + "icon": "./assets/appLogo.png", "userInterfaceStyle": "light", "splash": { - "image": "./assets/splash.png", + "image": "", "resizeMode": "contain", "backgroundColor": "#ffffff" }, @@ -22,12 +22,14 @@ }, "android": { "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", + "foregroundImage": "", "backgroundColor": "#FFFFFF" - } + }, + "package": "com.appsazz.final_danovin_expo" }, "web": { - "favicon": "./assets/favicon.png" - } + "favicon": "" + }, + "description": "" } } diff --git a/assets/adaptive-icon.png b/assets/adaptive-icon.png deleted file mode 100644 index 03d6f6b..0000000 Binary files a/assets/adaptive-icon.png and /dev/null differ diff --git a/assets/appLogo.png b/assets/appLogo.png new file mode 100644 index 0000000..c247f50 Binary files /dev/null and b/assets/appLogo.png differ diff --git a/assets/favicon.png b/assets/favicon.png deleted file mode 100644 index e75f697..0000000 Binary files a/assets/favicon.png and /dev/null differ diff --git a/assets/fonts/Farsi/Black.ttf b/assets/fonts/Farsi/Black.ttf new file mode 100644 index 0000000..a073217 Binary files /dev/null and b/assets/fonts/Farsi/Black.ttf differ diff --git a/assets/fonts/Farsi/Bold.ttf b/assets/fonts/Farsi/Bold.ttf new file mode 100644 index 0000000..84d2767 Binary files /dev/null and b/assets/fonts/Farsi/Bold.ttf differ diff --git a/assets/fonts/Farsi/DemiBold.ttf b/assets/fonts/Farsi/DemiBold.ttf new file mode 100644 index 0000000..4fd2c73 Binary files /dev/null and b/assets/fonts/Farsi/DemiBold.ttf differ diff --git a/assets/fonts/Farsi/IRANSansXFaNum-ExtraBold.ttf b/assets/fonts/Farsi/IRANSansXFaNum-ExtraBold.ttf new file mode 100644 index 0000000..21d4fbf Binary files /dev/null and b/assets/fonts/Farsi/IRANSansXFaNum-ExtraBold.ttf differ diff --git a/assets/fonts/Farsi/IRANSansXFaNum-Thin.ttf b/assets/fonts/Farsi/IRANSansXFaNum-Thin.ttf new file mode 100644 index 0000000..da257b5 Binary files /dev/null and b/assets/fonts/Farsi/IRANSansXFaNum-Thin.ttf differ diff --git a/assets/fonts/Farsi/IRANSansXFaNum-UltraLight.ttf b/assets/fonts/Farsi/IRANSansXFaNum-UltraLight.ttf new file mode 100644 index 0000000..d8a5468 Binary files /dev/null and b/assets/fonts/Farsi/IRANSansXFaNum-UltraLight.ttf differ diff --git a/assets/fonts/Farsi/Light.ttf b/assets/fonts/Farsi/Light.ttf new file mode 100644 index 0000000..4807f62 Binary files /dev/null and b/assets/fonts/Farsi/Light.ttf differ diff --git a/assets/fonts/Farsi/Medium.ttf b/assets/fonts/Farsi/Medium.ttf new file mode 100644 index 0000000..4d88319 Binary files /dev/null and b/assets/fonts/Farsi/Medium.ttf differ diff --git a/assets/fonts/Farsi/Regular.ttf b/assets/fonts/Farsi/Regular.ttf new file mode 100644 index 0000000..385e013 Binary files /dev/null and b/assets/fonts/Farsi/Regular.ttf differ diff --git a/assets/icon.png b/assets/icon.png deleted file mode 100644 index a0b1526..0000000 Binary files a/assets/icon.png and /dev/null differ diff --git a/assets/splash.png b/assets/splash.png deleted file mode 100644 index 0e89705..0000000 Binary files a/assets/splash.png and /dev/null differ diff --git a/bottomNavigation.json b/bottomNavigation.json new file mode 100644 index 0000000..2ea77e8 --- /dev/null +++ b/bottomNavigation.json @@ -0,0 +1,24 @@ +{ + "bottomNavigationList": { + "home": { + "label": "خانه", + "isActive": true + }, + "products": { + "label": "کتاب‌ها", + "isActive": true + }, + "vod": { + "label": "ویدئوها", + "isActive": true + }, + "cart": { + "label": "سبد خرید", + "isActive": true + }, + "profile": { + "label": "پروفایل", + "isActive": true + } + } +} diff --git a/eas.json b/eas.json new file mode 100644 index 0000000..50821b5 --- /dev/null +++ b/eas.json @@ -0,0 +1,18 @@ +{ + "cli": { + "version": ">= 0.52.0" + }, + "build": { + "development": { + "developmentClient": true, + "distribution": "internal" + }, + "preview": { + "distribution": "internal" + }, + "production": {} + }, + "submit": { + "production": {} + } +} diff --git a/filters.json b/filters.json new file mode 100644 index 0000000..8ce4df9 --- /dev/null +++ b/filters.json @@ -0,0 +1,10 @@ +{ + "sort": ["مرتب سازی", "مرتب سازی", "محبوب ترین", "جدیدترین"], + "productTypes" : [ + "نوع محصول", + "کتاب فیزیکی", + "کتاب دیجیتال", + "دوره ویدئویی", + "بسته های آموزشی" + ] +} diff --git a/navigation/bottomNavigation.js b/navigation/bottomNavigation.js new file mode 100644 index 0000000..400a694 --- /dev/null +++ b/navigation/bottomNavigation.js @@ -0,0 +1,24 @@ +const bottomNavigationList = { + home: { + label: "خانه", + isActive: true, + }, + products: { + label: "کتاب‌ها", + isActive: true, + }, + vod: { + label: "ویدئوها", + isActive: false, + }, + cart: { + label: "سبد خرید", + isActive: true, + }, + profile: { + label: "پروفایل", + isActive: true, + }, +}; + +export default bottomNavigationList; \ No newline at end of file diff --git a/navigation/index.js b/navigation/index.js new file mode 100644 index 0000000..aaa5d7f --- /dev/null +++ b/navigation/index.js @@ -0,0 +1,668 @@ +import React, { useEffect } from "react"; +import { Restart } from "fiction-expo-restart"; +import { I18nManager, StatusBar } from "react-native"; +import * as Linking from "expo-linking"; +import Svg, { G, Path } from "react-native-svg"; +import { NavigationContainer } from "@react-navigation/native"; +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; +import { connect } from "react-redux"; +import { user, publicApi } from "../src/redux/actions"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import bottomNavigationList from "./bottomNavigation"; + +//components +import OrderDetails from "../src/screens/OrderDetails"; +import OrdersFollowUp from "../src/screens/OrdersFollowUp"; +import Splash from "../src/screens/Splash"; +import Home from "../src/screens/Home"; +import Products from "../src/screens/Products"; +import Login from "../src/screens/Login"; +import Otp from "../src/screens/Otp"; +import Product from "../src/screens/Product"; +import AR from "../src/screens/AR"; +import QR from "../src/screens/QR"; +import QRContent from "../src/screens/QRContent"; +import Faq from "../src/screens/Faq"; +import Activation from "../src/screens/Activation"; +import Videos from "../src/screens/Videos"; +import Video from "../src/screens/Video"; +import VideoDisplay from "../src/screens/VideoDisplay"; +import Contact from "../src/screens/Contact"; +import Tests from "../src/screens/Tests"; +import ShoppingCart from "../src/screens/ShoppingCart"; +import DeliveryForm from "../src/screens/DeliveryForm"; +import Address from "../src/screens/Address"; +import Profile from "../src/screens/Profile"; +import ProfileBookmark from "../src/screens/Profile/components/ProfileBookmark"; +import ProfileAddress from "../src/screens/Profile/components/ProfileAddress"; +import Blogs from "../src/screens/Blogs"; +import Blog from "../src/screens/Blog"; +import Sample from "../src/screens/Product/components/Sample"; +import Info from "../src/screens/Product/components/Info"; +import Rules from "../src/screens/Rules/index"; +import MyCourses from "../src/screens/Profile/components/MyCourses"; +import Ticket from "../src/screens/Contact/layouts/Ticket"; +import Tickets from "../src/screens/Contact/layouts/Tickets"; +import TicketStatus from "../src/screens/Contact/layouts/TicketStatus"; +import MoreBlog from "../src/screens/Blogs/layouts/More"; + +//style +import Colors from "../src/constants/Colors"; +import fontSize from "../src/constants/fontSize"; + +const Tab = createBottomTabNavigator(); +const BottomTabNavigator = ({ theme, userProducts }) => { + const insets = useSafeAreaInsets(); + const [headerShown, setHeaderShown] = React.useState(true); + React.useEffect(() => { + setTimeout(() => { + setHeaderShown(false); + }, 600); + }, []); + + return ( + + {bottomNavigationList.home.isActive ? ( + } + options={{ + title: bottomNavigationList.home.label, + tabBarIcon: ({ focused, color }) => + focused ? ( + + + + + ) : ( + + + + + ), + }} + /> + ) : null} + + {bottomNavigationList.products.isActive ? ( + } + options={{ + title: bottomNavigationList.products.label, + tabBarIcon: ({ focused, color }) => + focused ? ( + + + + + ) : ( + + + + + + + ), + }} + /> + ) : null} + + {bottomNavigationList.vod.isActive ? ( + } + options={{ + unmountOnExit: true, + unmountOnBlur: true, + title: bottomNavigationList.vod.label, + tabBarIcon: ({ focused, color }) => + focused ? ( + + + + + + + ) : ( + + + + + + + ), + }} + /> + ) : null} + + {bottomNavigationList.cart.isActive ? ( + } + options={{ + tabBarBadge: userProducts.filter((product) => product.condition < 2) + .length + ? userProducts.filter((product) => product.condition < 2).length + : null, + tabBarBadgeStyle: { + color: "white", + backgroundColor: + theme === "light" + ? Colors.theme1.greenCF + : Colors.theme1.greenCF, + fontSize: fontSize.sm, + paddingTop: 2, + color: + theme === "light" ? Colors.theme1.white : Colors.theme1.dark, + borderWidth: 1, + borderColor: + theme === "light" ? Colors.theme1.greenCF : Colors.theme1.dark, + fontFamily: "bold", + }, + title: bottomNavigationList.cart.label, + tabBarIcon: ({ focused, color }) => + focused ? ( + + + + + + + ) : ( + + + + + + + ), + }} + /> + ) : null} + + {bottomNavigationList.profile.isActive ? ( + } + options={{ + title: bottomNavigationList.profile.label, + tabBarIcon: ({ focused, color }) => + focused ? ( + + + + + + + ) : ( + + + + + + + ), + contentStyle: { + backgroundColor: "white", + }, + }} + /> + ) : null} + + ); +}; + +const HomeStack = createNativeStackNavigator(); +const HomeStackScreen = ({ theme }) => { + return ( + + + + + + + + + + + + + + ); +}; + +const ProductsStack = createNativeStackNavigator(); +const ProductsStackScreen = ({ theme }) => { + return ( + + + + + + + ); +}; + +const VODStack = createNativeStackNavigator(); +const VODStackScreen = ({ theme }) => { + return ( + + + + + + ); +}; + +const CARTStack = createNativeStackNavigator(); +const CARTStackScreen = ({ theme }) => { + return ( + + + + + + ); +}; + +const PROFILEStack = createNativeStackNavigator(); +const PROFILEStackScreen = ({ theme }) => { + return ( + + + + + + + + + + + ); +}; + +const prefix = Linking.createURL("/"); + +const Stack = createNativeStackNavigator(); +const Navigation = ({ + getStatus, + isLogin, + userProducts, + ltr, + setLTR, + theme, +}) => { + const linking = { + prefixes: [prefix], + }; + + const setDirection = async () => { + setLTR(); + await I18nManager.allowRTL(false); + }; + + useEffect(() => { + if (I18nManager.isRTL && !ltr) { + setDirection(); + Restart(); + } + getStatus(); + StatusBar.setBarStyle( + `${theme === "light" ? "dark" : "light"}-content`, + true + ); + return () => { + console.log("This will be logged on unmount"); + }; + }, []); + + return ( + + + + + + + ( + + )} + /> + + + + + + + + + + + + + + + + + ); +}; + +const mapStateToProps = (state) => ({ + isLogin: state.user.status, + userProducts: state.userProduct.list, + theme: state.publicApi.theme, + ltr: state.publicApi.ltr, +}); +const mapDispatchToProps = { + getStatus: user.getStatus, + setLTR: publicApi.setLTR, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(Navigation); diff --git a/package.json b/package.json index 6a22450..54320c9 100644 --- a/package.json +++ b/package.json @@ -7,15 +7,48 @@ "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", - "eject": "expo eject" + "eject": "expo eject", + "build:tailwind": "tailwindcss --input input.css --output tailwind.css --no-autoprefixer && tailwind-rn", + "dev:tailwind": "concurrently \"tailwindcss --input input.css --output tailwind.css --no-autoprefixer --watch\" \"tailwind-rn --watch\"" }, "dependencies": { - "expo": "~45.0.0", + "@react-native-async-storage/async-storage": "~1.17.3", + "@react-native-community/masked-view": "^0.1.11", + "@react-navigation/bottom-tabs": "^6.3.1", + "@react-navigation/native": "^6.0.10", + "@react-navigation/native-stack": "^6.6.2", + "axios": "^0.27.2", + "expo": "^45.0.0", + "expo-av": "~11.2.3", + "expo-barcode-scanner": "~11.3.0", + "expo-blur": "~11.1.0", + "expo-font": "~10.1.0", + "expo-linear-gradient": "~11.3.0", + "expo-linking": "~3.1.0", + "expo-screen-orientation": "~4.2.0", + "expo-splash-screen": "~0.15.1", "expo-status-bar": "~1.3.0", + "fiction-expo-restart": "^1.1.2", + "jalali-moment": "^3.3.11", "react": "17.0.2", "react-dom": "17.0.2", "react-native": "0.68.1", - "react-native-web": "0.17.7" + "react-native-awesome-alerts": "^1.5.2", + "react-native-htmlview": "^0.16.0", + "react-native-openanything": "^0.0.6", + "react-native-ratings": "^8.1.0", + "react-native-safe-area-context": "4.2.4", + "react-native-screens": "~3.11.1", + "react-native-select-dropdown": "^1.13.0", + "react-native-svg": "12.3.0", + "react-native-web": "0.17.7", + "react-native-webview": "11.18.1", + "react-navigation-stack": "^2.10.4", + "react-redux": "^8.0.1", + "redux": "^4.2.0", + "redux-persist": "^6.0.0", + "redux-thunk": "^2.4.1", + "tailwind-rn": "3.0.1" }, "devDependencies": { "@babel/core": "^7.12.9" diff --git a/src/assets/icons/activation.png b/src/assets/icons/activation.png new file mode 100644 index 0000000..f368c64 Binary files /dev/null and b/src/assets/icons/activation.png differ diff --git a/src/assets/icons/bookmark.png b/src/assets/icons/bookmark.png new file mode 100644 index 0000000..cc45130 Binary files /dev/null and b/src/assets/icons/bookmark.png differ diff --git a/src/assets/icons/leftWhite.png b/src/assets/icons/leftWhite.png new file mode 100644 index 0000000..913c78b Binary files /dev/null and b/src/assets/icons/leftWhite.png differ diff --git a/src/assets/icons/link.png b/src/assets/icons/link.png new file mode 100644 index 0000000..b328dc1 Binary files /dev/null and b/src/assets/icons/link.png differ diff --git a/src/assets/icons/pdf.png b/src/assets/icons/pdf.png new file mode 100644 index 0000000..7f3bd1e Binary files /dev/null and b/src/assets/icons/pdf.png differ diff --git a/src/assets/icons/ppt.png b/src/assets/icons/ppt.png new file mode 100644 index 0000000..80965ba Binary files /dev/null and b/src/assets/icons/ppt.png differ diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000..42ea219 Binary files /dev/null and b/src/assets/images/logo.png differ diff --git a/src/components/AlertModal.js b/src/components/AlertModal.js new file mode 100644 index 0000000..03eee00 --- /dev/null +++ b/src/components/AlertModal.js @@ -0,0 +1,172 @@ +import React, { useState } from "react"; +import { Dimensions } from "react-native"; +import { connect } from "react-redux"; +import AwesomeAlert from "react-native-awesome-alerts"; + +//style +import Colors from "../constants/Colors"; + +//variables +const { width } = Dimensions.get("window"); + +const AlertModal = (props) => { + const [alert, setAlert] = useState({ + show: false, + showProgress: false, + title: "خطا", + message: "خطای ناشناخته", + closeOnTouchOutside: false, + closeOnHardwareBackPress: false, + showCancelButton: true, + showConfirmButton: true, + cancelText: "بیخیال", + confirmText: "باشه", + confirmButtonColor: "#2980b9", + onCancel: () => {}, + onConfirm: () => {}, + }); + const [apiAlert, setApiAlert] = useState({ + show: false, + showProgress: false, + title: "خطا", + message: "خطای ناشناخته", + closeOnTouchOutside: false, + closeOnHardwareBackPress: false, + showCancelButton: true, + showConfirmButton: true, + cancelText: "بیخیال", + confirmText: "باشه", + confirmButtonColor: "#2980b9", + onCancel: () => {}, + onConfirm: () => {}, + }); + + global.globalAlert = (params) => { + setAlert({ + ...alert, + ...params, + }); + }; + global.globalApiAlert = (params) => { + setApiAlert({ + ...apiAlert, + ...params, + }); + }; + + function hideAlert() { + setAlert({ ...alert, show: false }); + } + function onApiCancelPressed() { + globalApiAlert({ show: false }); + + apiAlert.onCancel(); + } + function onApiConfirmPressed() { + globalApiAlert({ show: false }); + apiAlert.onConfirm(); + } + function onCancelPressed() { + hideAlert(); + alert.onCancel(); + } + function onConfirmPressed() { + hideAlert(); + alert.onConfirm(); + } + return ( + <> + + + + ); +}; + +const mapStateToProps = (state) => ({ + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(AlertModal); diff --git a/src/components/Avatar.js b/src/components/Avatar.js new file mode 100644 index 0000000..8abf3a3 --- /dev/null +++ b/src/components/Avatar.js @@ -0,0 +1,62 @@ +import React from "react"; +import { Image, View } from "react-native"; +import Svg, { G, Path } from "react-native-svg"; +import { connect } from "react-redux"; + +//style +import tw from "tailwind-rn"; +import Colors from "../constants/Colors"; + +function Avatar({ source, size, theme }) { + return ( + + {source ? ( + + ) : ( + + + + + + + + + + + )} + + ); +} + +const mapStateToProps = (state) => ({ + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(Avatar); diff --git a/src/components/Blur.js b/src/components/Blur.js new file mode 100644 index 0000000..9a86fd8 --- /dev/null +++ b/src/components/Blur.js @@ -0,0 +1,26 @@ +import React from "react"; +import { BlurView } from "expo-blur"; +import { ActivityIndicator } from "react-native"; +import { connect } from "react-redux"; +import tw from "tailwind-rn"; + +function Blur({ theme }) { + return ( + + + + ); +} + +const mapStateToProps = (state) => ({ + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(Blur); diff --git a/src/components/CustomTextInput.js b/src/components/CustomTextInput.js new file mode 100644 index 0000000..e411b0f --- /dev/null +++ b/src/components/CustomTextInput.js @@ -0,0 +1,107 @@ +import React from "react"; +import { TextInput, View, Text, StyleSheet } from "react-native"; +import { connect } from "react-redux"; + +import tw from "tailwind-rn"; +import Colors from "../constants/Colors"; +import fontSize from "../constants/fontSize"; + +const FloatingLabelInput = ({ + label, + textAlign, + direction, + labelBackgroundColor, + onChange, + name, + value, + textColor, + placeholder, + maxLength, + autoFocus, + regex, + mobile, + keyboardType, + multiline, + theme, + editable = true, + ...props +}) => { + const [isFocused, setIsFocused] = React.useState(false); + + const styles = StyleSheet.create({ + labelStyle: { + fontFamily: "regular", + top: !isFocused ? "50%" : 1, + fontSize: !isFocused + ? Math.floor(fontSize.base) + : Math.floor(fontSize.sm), + transform: [ + { + translateY: !isFocused + ? -Math.floor(fontSize.base - 2) + : -Math.floor(fontSize.sm), + }, + ], + backgroundColor : theme === 'light' ? Colors.theme1.white : Colors.theme1.dark, + color: !isFocused ? theme === 'light' ? Colors.theme1.gray2077 : Colors.theme1.white + 'aa' : Colors.theme1.green79, + }, + inputStyle: { + fontSize: fontSize.lg, + color: theme === 'light' ? Colors.theme1.black : Colors.theme1.white, + borderWidth: 1, + borderColor: isFocused + ? Colors.theme1.greenC8 + : theme === "light" ? Colors.theme1.gray2077 + "66" : Colors.theme1.white + 'aa', + textAlign: textAlign, + fontFamily: "regular", + direction: direction ? direction : "rtl", + }, + }); + + React.useEffect(() => { + if (value) { + setIsFocused(true); + } else { + setIsFocused(false); + } + }, [value]); + + return ( + + setIsFocused(true)} + > + {label} + + onChange(name, (text = regex(text)))} + placeholder={placeholder} + onFocus={() => setIsFocused(true)} + autoFocus={autoFocus} + underlineColorAndroid={"transparent"} + maxLength={maxLength} + value={value} + onBlur={() => (value ? {} : setIsFocused(() => false))} + keyboardType={keyboardType} + multiline={multiline} + editable={editable} + placeholderTextColor={theme === 'light' ? Colors.theme1.gray20 + 'aa' : Colors.theme1.white + '55'} + /> + + ); +}; + +const mapStateToProps = (state) => ({ + mobile: state.publicApi.mobile, + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(FloatingLabelInput); diff --git a/src/components/Divider.js b/src/components/Divider.js new file mode 100644 index 0000000..7663cdd --- /dev/null +++ b/src/components/Divider.js @@ -0,0 +1,24 @@ +import React from "react"; +import { View, Text } from "react-native"; +import { connect } from "react-redux"; +import tw from "tailwind-rn"; + +const Divider = ({ type, theme }) => { + return ( + + ); +}; + +const mapStateToProps = (state) => ({ + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(Divider); diff --git a/src/components/Drawer.js b/src/components/Drawer.js new file mode 100644 index 0000000..9aae2c7 --- /dev/null +++ b/src/components/Drawer.js @@ -0,0 +1,539 @@ +import React, { useState, useEffect } from "react"; +import { + View, + Platform, + UIManager, + Dimensions, + Pressable, + Text, + TouchableOpacity, + Image, + LayoutAnimation, + Animated, +} from "react-native"; +import { useNavigation } from "@react-navigation/native"; +import { asyncAwesomeAlert } from "../utils/AsyncWrappers"; +import { connect } from "react-redux"; +import { user, publicApi } from "../redux/actions"; +import Svg, { Path, G } from "react-native-svg"; +import { AntDesign } from "@expo/vector-icons"; + +//components +import Avatar from "../components/Avatar"; + +//style +import tw from "tailwind-rn"; +import fontSize from "../constants/fontSize"; +import Colors from "../constants/Colors"; + +//variables +const ios = Platform.OS === "ios"; + +if ( + Platform.OS === "android" && + UIManager.setLayoutAnimationEnabledExperimental +) { + UIManager.setLayoutAnimationEnabledExperimental(true); +} + +const Drawer = ({ + route, + position, + setBoxPosition, + user, + mobile, + theme, + setIsDark, +}) => { + const [height, setHeight] = useState(Dimensions.get("window").height); + const [width, setWidth] = useState(Dimensions.get("window").width); + const fadeAnim = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0 + const [dropdown, setDropdown] = useState(false); + const [dropdownHeight, setDropdownHeight] = useState(0); + const navigation = useNavigation(); + + const routes = React.useMemo(() => { + return [ + { + name: "صفحه اصلی", + route: "Home", + icon: ( + + + + + + + ), + + message: "", + }, + { + name: "فعال سازی", + route: "Activation", + icon: ( + + + + + + + ), + + message: "", + }, + { + name: "واقعیت افزوده", + route: "AR", + icon: ( + + + + + + + ), + + message: "", + }, + { + name: "وبلاگ", + route: "Blogs", + icon: ( + + + + + + + ), + + message: "", + }, + { + name: "سفارشات", + route: "Orders", + icon: ( + + + + + ), + + message: "", + }, + { + name: "سوالات متداول", + route: "Faq", + icon: ( + + + + + + + ), + + message: "", + }, + { + name: "تماس با ما", + route: "Contact", + icon: ( + + + + + + + ), + + message: "", + }, + ]; + }, [user, theme]); + + const Route = React.useCallback( + ({ route }) => { + return ( + { + setBoxPosition(); + setTimeout(() => { + navigation.navigate(route.route); + }, 300); + }} + > + {route.icon} + + {route.name} + + + ); + }, + [mobile, position, theme] + ); + + React.useEffect(() => { + if (position === "0%") { + setTimeout(() => { + Animated.timing(fadeAnim, { + toValue: 0.7, + duration: 300, + useNativeDriver: true, // Add This line + }).start(); + }, 300); + } + if (position === "100%") { + Animated.timing(fadeAnim, { + toValue: 0, + duration: 10, + useNativeDriver: true, // Add This line + }).start(); + } + }, [position]); + + const onLayout = React.useCallback(() => { + setHeight(Dimensions.get("window").height); + setWidth(Dimensions.get("window").width); + }, [mobile]); + + const animateDropdown = React.useCallback(() => { + LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); + setDropdown(() => !dropdown); + setDropdownHeight(dropdownHeight === 0 ? fontSize.base + 30 : 0); + }, [position]); + + return ( + onLayout()} + > + + setBoxPosition()} + > + setBoxPosition()} + /> + + + + + + + {theme === "light" ? ( + setIsDark("dark")} + width={24} + height={24} + style={{ padding: 10 }} + > + + + ) : ( + setIsDark("light")} + width={24} + height={24} + style={{ padding: 10 }} + > + + + + + + )} + + + + + + {user?.firstName + " " + user?.lastName} + + + {user?.cellphone} + + + {/* animateDropdown()} + > + + */} + + + + {/* + asyncAwesomeAlert("نتیجه", "این قابلیت در حال توسعه است.", { + showCancelButton: false, + confirmText: "باشه", + }) + } + > + + افزودن حساب کاربری جدید + + + + + + */} + + {routes.map((route, index) => ( + + ))} + + + + + ); +}; + +const mapStateToProps = (state) => ({ + user: state.user.status, + mobile: state.publicApi.mobile, + theme: state.publicApi.theme, +}); + +const mapDispatchToProps = { + logout: user.logout, + setIsDark: publicApi.setIsDark, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(Drawer); diff --git a/src/components/Empty.js b/src/components/Empty.js new file mode 100644 index 0000000..880ae7c --- /dev/null +++ b/src/components/Empty.js @@ -0,0 +1,63 @@ +import { View, Text, Pressable } from "react-native"; +import React from "react"; +import { connect } from "react-redux"; + +//style +import Colors from "../constants/Colors"; +import tw from "tailwind-rn"; +import fontSize from "../constants/fontSize"; + +const Empty = ({ text, buttonText, buttonAction, theme }) => { + return ( + + + {text + " " + ":("} + + {buttonText ? ( + buttonAction()} + > + + {buttonText} + + + ) : null} + + ); +}; + +const mapStateToProps = (state) => ({ + theme: state.publicApi.theme, +}); + +export default connect(mapStateToProps)(Empty); diff --git a/src/components/FormButton.js b/src/components/FormButton.js new file mode 100644 index 0000000..033799c --- /dev/null +++ b/src/components/FormButton.js @@ -0,0 +1,30 @@ +import React from 'react'; +import {StyleSheet, Text, Pressable, Dimensions } from 'react-native'; + +export default function FormButton({text, color, onPress}) { + const styles = StyleSheet.create({ + button: { + width : '100%', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 12, + paddingHorizontal: 0, + borderRadius: 6, + elevation: 3, + backgroundColor: color, + }, + text: { + fontSize: Dimensions.get('window').width / 25, + color: 'white', + }, + }); + return ( + onPress} + > + {text} + + ); +} diff --git a/src/components/Header.js b/src/components/Header.js new file mode 100644 index 0000000..5434186 --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,58 @@ +import { View, Text, Dimensions, ImageBackground } from "react-native"; +import { connect } from "react-redux"; + +//style +import tw from "tailwind-rn"; +import fontSize from "../constants/fontSize"; +import Colors from "../constants/Colors"; + +const height = Dimensions.get("window").height; + +function Header({ title, description, background }) { + return ( + + + + {title} + + + {description} + + + ); +} + +const mapStateToProps = (state) => ({ + mobile: state.publicApi.mobile, +}); + +export default connect(mapStateToProps)(Header); diff --git a/src/components/ImageSlider.js b/src/components/ImageSlider.js new file mode 100644 index 0000000..40fa297 --- /dev/null +++ b/src/components/ImageSlider.js @@ -0,0 +1,18 @@ +import { View, Dimensions } from "react-native"; +import React from "react"; +import tw from "tailwind-rn"; + +const { width, height } = Dimensions.get("window"); + +const ImageSlider = ({}) => { + return ( + + ); +}; + +export default ImageSlider; diff --git a/src/components/MapSelector.js b/src/components/MapSelector.js new file mode 100644 index 0000000..7e57fad --- /dev/null +++ b/src/components/MapSelector.js @@ -0,0 +1,35 @@ +import React from "react"; +import { StyleSheet } from "react-native"; +// import MapboxGL from '@rnmapbox/maps'; + +// MapboxGL.setAccessToken('pk.eyJ1IjoicmV6YS1hc2hyYWZpNzciLCJhIjoiY2tsdXRxaGxjMjl3azJ2cW1wNDl4MWdpNCJ9.0EIgtsWEuemb7IpGtGhraw'); + + +export default function MapSelector() { + onRegionDidChange = (e) => {}; + return ( + // + // + // + // + // +<> + ); +} + +const styles = StyleSheet.create({ + page: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF' + }, + container: { + height: 300, + width: 300, + backgroundColor: 'tomato' + }, + map: { + flex: 1 + } +}); diff --git a/src/components/MusicPlayer.js b/src/components/MusicPlayer.js new file mode 100644 index 0000000..71528c4 --- /dev/null +++ b/src/components/MusicPlayer.js @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { View, Button } from 'react-native'; +import { Audio } from 'expo-av'; + +export default function VoicePlayer({fileIds}) { + const [sound, setSound] = React.useState(); + + async function playSound() { + const { sound } = await Audio.Sound.createAsync( + {uri : `https://dnvn.ir/api/v1/file/${fileIds}`} + ); + setSound(sound); + + await sound.playAsync(); } + + React.useEffect(() => { + return sound + ? () => { + sound.unloadAsync(); } + : undefined; + }, [sound]); + + return ( + +