/src/App.tsx',
16 | ],
17 | coverageReporters: ['html', 'text', 'text-summary', 'cobertura'],
18 | testMatch: ['**/*.test.ts?(x)', '**/*.test.js?(x)'],
19 | };
20 |
--------------------------------------------------------------------------------
/src/assets/icons/login/ic_apple.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/screens/Category/mockData.js:
--------------------------------------------------------------------------------
1 | export const categories = [
2 | {
3 | id: 1,
4 | label: 'Study',
5 | value: 'study',
6 | name: 'Study',
7 | color: '#ff555d',
8 | },
9 | {
10 | id: 2,
11 | label: 'Work',
12 | value: 'work',
13 | name: 'Work',
14 | color: '#22c065',
15 | },
16 | {
17 | id: 3,
18 | label: 'Workout',
19 | value: 'workout',
20 | name: 'Workout',
21 | color: '#714dff',
22 | },
23 | ];
24 |
25 | export const LIST_COLOR = [
26 | '#F9ED69',
27 | '#F08A5D',
28 | '#B83B5E',
29 | '#6A2C70',
30 | '#08D9D6',
31 | '#252A34',
32 | '#FF2E63',
33 | '#EAEAEA',
34 | '#FFF5E4',
35 | '#FFE3E1',
36 | '#FFD1D1',
37 | '#A6B1E1',
38 | '#424874',
39 | '#AD8B73',
40 | '#CEAB93',
41 | '#E3CAA5',
42 | ];
43 |
--------------------------------------------------------------------------------
/src/screens/EditProfile/EditProfile.styles.js:
--------------------------------------------------------------------------------
1 | const { StyleSheet } = require("react-native");
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | paddingTop: 60,
7 | paddingHorizontal: 15,
8 | backgroundColor: '#fcfcfc',
9 | },
10 | formWrap: {
11 | flex: 1,
12 | position: 'relative',
13 | },
14 | input: {
15 | marginVertical: 10,
16 | },
17 | buttonTextStyle: {
18 | color: 'white',
19 | },
20 | buttonNext: {
21 | marginTop: 50,
22 | width: '100%',
23 | height: 50,
24 | backgroundColor: '#ff585d',
25 | borderRadius: 50,
26 | display: 'flex',
27 | alignItems: 'center',
28 | justifyContent: 'center',
29 | position: 'absolute',
30 | bottom: 50,
31 | },
32 | })
33 |
34 | export default styles;
--------------------------------------------------------------------------------
/src/screens/Task/CustomCalendar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import TextView from 'src/components/TextView';
4 | import styles from './Task.styles';
5 | import { HOURS } from './constant';
6 | import CalendarEvent from './CalendarEvent';
7 |
8 | const CustomCalendar = ({ events }) => {
9 | return (
10 |
11 | {HOURS.map(hour => {
12 | const event = events?.find(e => e.time === hour);
13 |
14 | return (
15 |
16 | {hour}
17 |
18 |
19 | );
20 | })}
21 |
22 | );
23 | };
24 |
25 | export default CustomCalendar;
26 |
--------------------------------------------------------------------------------
/android/app/src/release/java/com/myapp/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.myapp;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/screens/AppSetting/AppSetting.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const styles = StyleSheet.create({
4 | container: {
5 | flex: 1,
6 | paddingTop: 60,
7 | paddingHorizontal: 15,
8 | backgroundColor: '#fcfcfc',
9 | },
10 | itemWrap: {
11 | width: '100%',
12 | display: 'flex',
13 | flexDirection: 'row',
14 | justifyContent: 'flex-start',
15 | alignItems: 'center',
16 | gap: 25,
17 | padding: 10,
18 | marginVertical: 5,
19 | position: 'relative',
20 | },
21 | switchWrap: {
22 | position: 'absolute',
23 | right: 0,
24 | },
25 | iconStyle: {
26 | width: 25,
27 | height: 25,
28 | },
29 | textStyle: {
30 | fontSize: 18,
31 | fontWeight: 'semibold',
32 | },
33 | });
34 |
35 | export default styles;
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
2 |
3 | /**
4 | * Metro configuration
5 | * https://facebook.github.io/metro/docs/configuration
6 | *
7 | * @type {import('metro-config').MetroConfig}
8 | */
9 | // const {
10 | // resolver: { sourceExts, assetExts },
11 | // } = getDefaultConfig();
12 |
13 | const defaultConfig = getDefaultConfig(__dirname);
14 | const { assetExts, sourceExts } = defaultConfig.resolver;
15 |
16 | const config = {
17 | transformer: {
18 | babelTransformerPath: require.resolve('react-native-svg-transformer'),
19 | },
20 | resolver: {
21 | assetExts: assetExts.filter(ext => ext !== 'svg'),
22 | sourceExts: [...sourceExts, 'svg'],
23 | },
24 | };
25 |
26 | module.exports = mergeConfig(defaultConfig, config);
27 |
--------------------------------------------------------------------------------
/src/screens/AllCompletedTask/AllCompletedTask.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | const styles = StyleSheet.create({
3 | container: {
4 | flex: 1,
5 | paddingTop: 60,
6 | paddingHorizontal: 15,
7 | backgroundColor: '#fcfcfc',
8 | },
9 | deteleIconContainer: {
10 | display: 'flex',
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | marginLeft: 20,
14 | width: 50,
15 | height: 100,
16 | },
17 | deleteIconWrap: {
18 | alignItems: 'center',
19 | alignContent: 'center',
20 | justifyContent: 'center',
21 | width: '100%',
22 | height: '80%',
23 | backgroundColor: '#fee8eb',
24 | borderRadius: 10,
25 | },
26 |
27 | deleteIcon: {
28 | width: 25,
29 | height: 25,
30 | },
31 | });
32 |
33 | export default styles;
34 |
--------------------------------------------------------------------------------
/src/screens/FillProfile/FillProfile.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Dimensions } from 'react-native';
2 | import { AppTheme, Dimens, scaleSize } from '../../utils/appConstant';
3 | const { width, height } = Dimensions.get('window');
4 |
5 | export default StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | alignItems: 'center',
9 | textAlign: 'center',
10 | paddingTop: 60,
11 | paddingHorizontal: 20,
12 | backgroundColor: AppTheme.colors.white,
13 | },
14 | titleHeader: {
15 | fontSize: AppTheme.fontSize.s30,
16 | color: AppTheme.colors.black,
17 | fontWeight: 700,
18 | textAlign: 'center',
19 | marginBottom: 10,
20 | },
21 | subTitle: {
22 | fontSize: AppTheme.fontSize.s16,
23 | color: AppTheme.colors.black,
24 | fontWeight: 400,
25 | textAlign: 'center',
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/ios/MyAppTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/theme/Images.js:
--------------------------------------------------------------------------------
1 | export default function ({}) {
2 | return {
3 | logo: require('./assets/images/tom_light.png'),
4 | sparkles: {
5 | topLeft: require('./assets/images/sparkles-top-left.png'),
6 | top: require('./assets/images/sparkles-top.png'),
7 | topRight: require('./assets/images/sparkles-top-right.png'),
8 | right: require('./assets/images/sparkles-right.png'),
9 | bottomRight: require('./assets/images/sparkles-bottom-right.png'),
10 | bottom: require('./assets/images/sparkles-bottom.png'),
11 | bottomLeft: require('./assets/images/sparkles-bottom-left.png'),
12 | },
13 | icons: {
14 | colors: require('./assets/images/colorswatch.png'),
15 | send: require('./assets/images/send.png'),
16 | translate: require('./assets/images/translate.png'),
17 | },
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/src/screens/NotificationSetting/NotificationSetting.styles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | },
11 | itemWrap: {
12 | width: '100%',
13 | display: 'flex',
14 | flexDirection: 'row',
15 | justifyContent: 'flex-start',
16 | alignItems: 'center',
17 | gap: 25,
18 | padding: 10,
19 | marginVertical: 5,
20 | position: 'relative',
21 | },
22 | switchWrap: {
23 | position: 'absolute',
24 | right: 0,
25 | },
26 | iconStyle: {
27 | width: 25,
28 | height: 25,
29 | },
30 | textStyle: {
31 | fontSize: 18,
32 | fontWeight: 'semibold',
33 | },
34 | })
35 |
36 | export default styles;
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler';
2 | import React from 'react';
3 | import { Provider } from 'react-redux';
4 | import { PersistGate } from 'redux-persist/lib/integration/react';
5 | import { store, persistor } from './store';
6 | import './translations';
7 | import ApplicationStack from './navigators/ApplicationStack';
8 | import { ToastProvider } from 'react-native-toast-notifications';
9 | import CustomToast from './components/Toast';
10 |
11 | const App = () => (
12 |
13 |
14 | }
18 | >
19 |
20 |
21 |
22 |
23 | );
24 | export default App;
25 |
--------------------------------------------------------------------------------
/src/navigators/constants.js:
--------------------------------------------------------------------------------
1 | import { CardStyleInterpolators } from '@react-navigation/stack';
2 | import { Platform } from 'react-native';
3 | import RouteName from './RouteName';
4 |
5 | const screenOptions = {
6 | headerShown: false,
7 | cardStyleInterpolator: Platform.select({
8 | android: CardStyleInterpolators.forFadeFromBottomAndroid,
9 | ios: CardStyleInterpolators.forScaleFromCenterAndroid,
10 | }),
11 | tabBarHideOnKeyboard: true,
12 | adaptive: true,
13 | keyboardHidesTabBar: true,
14 | };
15 |
16 | const BOTTOM_TAB_TITLE = {
17 | Home: 'Home',
18 | Task: 'Task',
19 | Statistics: 'Statistics',
20 | Goal: 'Goal',
21 | };
22 |
23 | const routesBottomBar = [
24 | RouteName.Home,
25 | RouteName.Task,
26 | RouteName.Statistics,
27 | RouteName.Goal,
28 | ];
29 |
30 | export { screenOptions, BOTTOM_TAB_TITLE, routesBottomBar };
31 |
--------------------------------------------------------------------------------
/ios/MyApp/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"MyApp";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | #if DEBUG
20 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
21 | #else
22 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
23 | #endif
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/src/theme/Common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file defines the base application styles.
3 | *
4 | * Use it to define generic component styles (e.g. the default text styles, default button styles...).
5 | */
6 | import { StyleSheet } from 'react-native';
7 | import buttonStyles from './components/Buttons';
8 | export default function ({ Colors, ...args }) {
9 | return {
10 | button: buttonStyles({ Colors, ...args }),
11 | ...StyleSheet.create({
12 | backgroundPrimary: {
13 | backgroundColor: Colors.primary,
14 | },
15 | backgroundReset: {
16 | backgroundColor: Colors.transparent,
17 | },
18 | textInput: {
19 | backgroundColor: Colors.inputBackground,
20 | color: Colors.textGray400,
21 | height: 45,
22 | borderRadius: 10,
23 | paddingStart: 20,
24 | },
25 | }),
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/src/screens/FillProfile/FillProfile.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import TextView from 'src/components/TextView';
4 | import styles from './FillProfile.styles';
5 | import ProfileAvatar from './ProfileAvatar';
6 | import ProfileForm from './ProfileForm';
7 |
8 | const FillProfile = () => {
9 | const handleEditAvatar = () => {
10 | console.log('hello');
11 | };
12 |
13 | return (
14 |
15 | Fill you profile
16 |
17 | Don't worry, you can always change it later, or {'\n'} you can skip it
18 | for now
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default FillProfile;
27 |
--------------------------------------------------------------------------------
/src/components/RadioButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const RadioButton = props => {
6 | return (
7 |
21 | {props.selected ? (
22 |
30 | ) : null}
31 |
32 | );
33 | };
34 |
35 | export default RadioButton;
36 |
--------------------------------------------------------------------------------
/src/store/theme/index.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 | const slice = createSlice({
3 | name: 'theme',
4 | initialState: { theme: 'default', darkMode: null },
5 | reducers: {
6 | changeTheme: (state, { payload: { theme, darkMode } }) => {
7 | if (typeof theme !== 'undefined') {
8 | state.theme = theme;
9 | }
10 | if (typeof darkMode !== 'undefined') {
11 | state.darkMode = darkMode;
12 | }
13 | },
14 | setDefaultTheme: (state, { payload: { theme, darkMode } }) => {
15 | if (!state.theme) {
16 | if (typeof theme !== 'undefined') {
17 | state.theme = theme;
18 | }
19 | if (typeof darkMode !== 'undefined') {
20 | state.darkMode = darkMode;
21 | }
22 | }
23 | },
24 | },
25 | });
26 | export const { changeTheme, setDefaultTheme } = slice.actions;
27 | export default slice.reducer;
28 |
--------------------------------------------------------------------------------
/src/screens/Notification/mockData.js:
--------------------------------------------------------------------------------
1 | export const notifications = [
2 | {
3 | title: 'Congratulation',
4 | // eslint-disable-next-line quotes
5 | subTitle: "You've been focus for 2 hours",
6 | type: 'success',
7 | date: new Date(),
8 | },
9 | {
10 | title: 'New Service is Available!',
11 | subTitle: 'Now you can do multi-task tracking',
12 | type: 'service',
13 | date: new Date(),
14 | },
15 | {
16 | title: 'Subscription Cancelled',
17 | subTitle: 'You have cancelled you subscription',
18 | type: 'cancel',
19 | date: new Date(),
20 | },
21 | {
22 | title: 'Vertification Successfull',
23 | subTitle: 'Account vertification complete',
24 | type: 'vertification',
25 | date: new Date(),
26 | },
27 | {
28 | title: 'New Category is Available!',
29 | subTitle: 'Focus now buddy!',
30 | type: 'success',
31 | date: new Date(),
32 | },
33 | ];
34 |
--------------------------------------------------------------------------------
/src/screens/Goal/Goal.styles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | paddingTop: 60,
9 | paddingHorizontal: 15,
10 | backgroundColor: '#fcfcfc',
11 | },
12 |
13 | goalItemWrap: {
14 | width: '100%',
15 | padding: 20,
16 | marginBottom: 10,
17 | borderWidth: 1,
18 | borderColor: AppTheme.colors.primary_1,
19 | color: AppTheme.colors.white,
20 | display: 'flex',
21 | flexDirection: 'row',
22 | alignItems: 'center',
23 | justifyContent: 'space-between',
24 | },
25 |
26 | modalDetailGoal: {
27 | display: 'flex',
28 | alignItems: 'center',
29 | justifyContent: 'center',
30 | backgroundColor: '#fff',
31 | borderRadius: 5,
32 | padding: 15,
33 | },
34 | });
35 |
36 | export default styles;
37 |
--------------------------------------------------------------------------------
/src/components/Brand/Brand.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Image } from 'react-native';
3 | import { useTheme } from '../../hooks';
4 | import logo from '../../assets/images/logo.png';
5 | import { StyleSheet } from 'react-native';
6 | import FastImage from 'react-native-fast-image';
7 | const Brand = ({ height, width, mode }) => {
8 | return (
9 |
13 |
14 | {/* */}
15 |
16 | );
17 | };
18 | Brand.defaultProps = {
19 | height: 200,
20 | width: 200,
21 | mode: 'contain',
22 | };
23 |
24 | const styles = StyleSheet.create({
25 | brandWrapper: {
26 | backgroundColor: 'white',
27 | width: 300,
28 | height: 300,
29 | borderRadius: 50,
30 | },
31 | });
32 | export default Brand;
33 |
--------------------------------------------------------------------------------
/src/assets/icons/login/ic_google.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/store/app/appReducer.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 | import persistReducer from 'redux-persist/es/persistReducer';
3 | import persistConfig from '../persistConfig';
4 |
5 | const initialState = {
6 | showBottomTabStatus: true,
7 | theme: 'light',
8 | };
9 | const appSlice = createSlice({
10 | name: 'appReducer',
11 | initialState: { ...initialState },
12 | reducers: {
13 | setTheme: (state, action) => {
14 | return {
15 | ...state,
16 | theme: action.payload,
17 | };
18 | },
19 | setStatusBottomTab: (state, action) => {
20 | return {
21 | ...state,
22 | showBottomTabStatus: action.payload,
23 | };
24 | },
25 | },
26 | });
27 |
28 | export const { setStatusBottomTab, setTheme } = appSlice.actions;
29 |
30 | const appReducer = appSlice.reducer;
31 | export default persistReducer(
32 | persistConfig({
33 | key: 'appReducer',
34 | whitelist: [''],
35 | }),
36 | appReducer,
37 | );
38 |
--------------------------------------------------------------------------------
/src/navigators/RouteName.js:
--------------------------------------------------------------------------------
1 | export default {
2 | StartScreen: 'StartScreen',
3 | AppBottomTab: 'AppBottomTab',
4 | Statistics: 'Statistics',
5 | Home: 'Home',
6 | Profile: 'Profile',
7 | TodayTask: 'TodayTask',
8 | Task: 'Task',
9 | StartUp: 'StartUp',
10 | SignIn: 'SignIn',
11 | SignUp: 'SignUp',
12 | Login: 'Login',
13 | LoginWithPass: 'LoginWithPass',
14 | FillProfile: 'FillProfile',
15 | ForgotPass: 'ForgotPass',
16 | Notification: 'Notification',
17 | NewTask: 'NewTask',
18 | Timer: 'Timer',
19 | CreateNewTask: 'CreateNewTask',
20 | Category: 'Category',
21 | AllCompletedTask: 'AllCompletedTask',
22 | // Profile
23 | EditProfile: 'EditProfile',
24 | AppSetting: 'AppSetting',
25 | ReminderRingTone: 'ReminderRingTone',
26 | NotificationSetting: 'NotificationSetting',
27 | Security: 'Security',
28 | UpgradeApp: 'UpgradeApp',
29 | // Timer
30 | CompletedTimer: 'CompletedTimer',
31 | // BottomTab
32 | BottomNewTask: 'BottomNewTask',
33 | Goal: 'Goal',
34 | };
35 |
--------------------------------------------------------------------------------
/src/screens/Startup/Startup.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { ActivityIndicator, StyleSheet, View } from 'react-native';
3 | import { Brand } from '../../components';
4 |
5 | const Startup = ({ navigation }) => {
6 | const init = async () => {
7 | await new Promise(resolve =>
8 | setTimeout(() => {
9 | resolve(true);
10 | }, 2000),
11 | );
12 | navigation.reset({
13 | index: 0,
14 | routes: [{ name: 'StartScreen' }],
15 | });
16 | };
17 | useEffect(() => {
18 | init();
19 | }, []);
20 |
21 | return (
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | const styles = StyleSheet.create({
30 | startupWrapper: {
31 | flex: 1,
32 | width: '100%',
33 | height: '100%',
34 | backgroundColor: 'white',
35 | justifyContent: 'center',
36 | alignItems: 'center',
37 | gap: 100,
38 | },
39 | });
40 | export default Startup;
41 |
--------------------------------------------------------------------------------
/src/theme/components/Buttons.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | export default function ({ Colors, Gutters, Layout }) {
3 | const base = {
4 | ...Layout.center,
5 | ...Gutters.regularHPadding,
6 | height: 40,
7 | backgroundColor: Colors.primary,
8 | };
9 | const rounded = {
10 | ...base,
11 | borderRadius: 10,
12 | };
13 | const circle = {
14 | ...Layout.center,
15 | height: 70,
16 | width: 70,
17 | borderRadius: 35,
18 | backgroundColor: Colors.circleButtonBackground,
19 | color: Colors.circleButtonColor,
20 | fill: Colors.circleButtonColor,
21 | };
22 | return StyleSheet.create({
23 | base,
24 | rounded,
25 | circle,
26 | outline: {
27 | ...base,
28 | backgroundColor: Colors.transparent,
29 | borderWidth: 2,
30 | borderColor: Colors.primary,
31 | },
32 | outlineRounded: {
33 | ...rounded,
34 | backgroundColor: Colors.transparent,
35 | borderWidth: 2,
36 | borderColor: Colors.primary,
37 | },
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/src/screens/NewTask/NewTask.styles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | paddingTop: 60,
9 | paddingHorizontal: 15,
10 | backgroundColor: '#fcfcfc',
11 | position: 'relative',
12 | },
13 | formCreateNewTask: {
14 | marginTop: 10,
15 | },
16 | titleTextInput: {
17 | fontSize: AppTheme.fontSize.s14,
18 | fontWeight: 600,
19 | marginBottom: 5,
20 | color: AppTheme.colors.neutral_80,
21 | },
22 | buttonNextWrap: {
23 | width: '100%',
24 | height: 50,
25 | position: 'absolute',
26 | bottom: 50,
27 | left: 20,
28 | },
29 | buttonNext: {
30 | width: '100%',
31 | height: 50,
32 | backgroundColor: '#ff585d',
33 | borderRadius: 50,
34 | display: 'flex',
35 | alignItems: 'center',
36 | justifyContent: 'center',
37 | },
38 | buttonTextStyle: {
39 | color: 'white',
40 | },
41 | });
42 | export default styles;
43 |
--------------------------------------------------------------------------------
/src/screens/Timer/Timer.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | position: 'relative',
11 | },
12 | rightIconStyles: {
13 | width: 30,
14 | height: 30,
15 | color: 'red',
16 | },
17 | timerCategory: {
18 | marginTop: 20,
19 | },
20 | timerWrapper: {
21 | flex: 1,
22 | alignItems: 'center',
23 | backgroundColor: 'transparent',
24 | },
25 | circularWrap: {
26 | marginTop: 50,
27 | },
28 | buttonWrapper: {
29 | position: 'absolute',
30 | bottom: 50,
31 | display: 'flex',
32 | },
33 | buttonStartStyle: {},
34 | buttonCancelStyle: {
35 | backgroundColor: 'white',
36 | borderWidth: 1,
37 | borderColor: AppTheme.colors.primary_1,
38 | },
39 | buttonCancelTextStyle: {
40 | color: AppTheme.colors.primary_1,
41 | },
42 | });
43 | export default styles;
44 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import 'whatwg-fetch';
2 | import 'react-native-gesture-handler/jestSetup';
3 | import '@testing-library/jest-native/extend-expect';
4 |
5 | jest.mock('react-native-reanimated', () =>
6 | require('react-native-reanimated/mock'),
7 | );
8 |
9 | jest.mock('redux-persist', () => {
10 | const real = jest.requireActual('redux-persist');
11 | return {
12 | ...real,
13 | persistReducer: jest
14 | .fn()
15 | .mockImplementation((config, reducers) => reducers),
16 | };
17 | });
18 |
19 | // Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
20 | jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
21 |
22 | jest.mock('react-i18next', () => ({
23 | // this mock makes sure any components using the translation hook can use it without a warning being shown
24 | useTranslation: () => {
25 | return {
26 | t: str => str,
27 | i18n: {
28 | changeLanguage: () => new Promise(() => {}),
29 | },
30 | };
31 | },
32 | initReactI18next: {
33 | type: '3rdParty',
34 | init: jest.fn(),
35 | },
36 | }));
37 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/screens/Task/constant.js:
--------------------------------------------------------------------------------
1 | export const HOURS = [
2 | '07:00 AM',
3 | '08:00 AM',
4 | '09:00 AM',
5 | '10:00 AM',
6 | '11:00 AM',
7 | '12:00 AM',
8 | '13:00 PM',
9 | '14:00 PM',
10 | '15:00 PM',
11 | '16:00 PM',
12 | '17:00 PM',
13 | '18:00 PM',
14 | '19:00 PM',
15 | '20:00 PM',
16 | '21:00 PM',
17 | ];
18 |
19 | export const mockCalendarData = [
20 | {
21 | time: '07:00 AM',
22 | event: 'Learn UI Design',
23 | category: 'study',
24 | duration: 1,
25 | },
26 | {
27 | time: '08:00 AM',
28 | event: 'Learn Programming',
29 | category: 'study',
30 | duration: 1,
31 | },
32 | {
33 | time: '13:00 PM',
34 | event: 'Edit Photos & Videos',
35 | category: 'work',
36 | duration: 1,
37 | },
38 | {
39 | time: '14:00 PM',
40 | event: 'Reading Marketing Books',
41 | category: 'study',
42 | duration: 1,
43 | },
44 | {
45 | time: '16:00 PM',
46 | event: 'Running exercise',
47 | category: 'workout',
48 | duration: 1,
49 | },
50 | ];
51 |
52 | export const CALENDAR_STYLE_BG = {
53 | study: '#ff585d',
54 | workout: '#22c065',
55 | work: '#714dff',
56 | };
57 |
--------------------------------------------------------------------------------
/src/screens/Security/Security.styles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | alignItems: 'center',
9 | paddingTop: 60,
10 | paddingHorizontal: 15,
11 | backgroundColor: '#fcfcfc',
12 | },
13 | itemWrap: {
14 | width: '100%',
15 | display: 'flex',
16 | flexDirection: 'row',
17 | justifyContent: 'flex-start',
18 | alignItems: 'center',
19 | gap: 25,
20 | padding: 10,
21 | marginVertical: 5,
22 | position: 'relative',
23 | },
24 | switchWrap: {
25 | position: 'absolute',
26 | right: 0,
27 | },
28 | iconStyle: {
29 | width: 25,
30 | height: 25,
31 | },
32 | textStyle: {
33 | fontSize: 18,
34 | fontWeight: 'semibold',
35 | },
36 | buttonNext: {
37 | marginTop: 50,
38 | width: '100%',
39 | height: 50,
40 | backgroundColor: '#feeeee',
41 | borderRadius: 50,
42 | display: 'flex',
43 | alignItems: 'center',
44 | justifyContent: 'center',
45 | position: 'absolute',
46 | bottom: 50,
47 | },
48 | buttonTextStyle: {
49 | color: AppTheme.colors.primary_1,
50 | },
51 | })
52 |
53 | export default styles;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | template/ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | ../.idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # Enviroment
44 | .env
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/
52 |
53 | **/fastlane/report.xml
54 | **/fastlane/Preview.html
55 | **/fastlane/screenshots
56 | **/fastlane/test_output
57 |
58 | # Bundle artifact
59 | *.jsbundle
60 |
61 | # Ruby / CocoaPods
62 | /ios/Pods/
63 | /vendor/bundle/
64 |
65 | # Temporary files created by Metro to check the health of the file watcher
66 | .metro-health-check*
67 |
--------------------------------------------------------------------------------
/src/screens/Goal/GoalItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text } from 'react-native';
3 | import styles from './Goal.styles';
4 | import TouchableDebounce from 'src/components/TouchableDebounce';
5 | import FastImage from 'react-native-fast-image';
6 | import ArrowRight from 'src/assets/images/goal/arrow-right.png';
7 | import ModalDetailGoal from './ModalDetailGoal';
8 |
9 | const GoalItem = ({ item }) => {
10 | const [openDetailModal, setOpenDetailModal] = React.useState(false);
11 | const handleOpenModalDetail = () => {
12 | setOpenDetailModal(true);
13 | };
14 |
15 | const handleCloseModal = () => {
16 | setOpenDetailModal(false);
17 | };
18 |
19 | return (
20 | <>
21 |
25 |
26 | {item?.title}
27 | {item?.content}
28 |
29 |
30 |
31 |
36 | >
37 | );
38 | };
39 |
40 | export default GoalItem;
41 |
--------------------------------------------------------------------------------
/src/screens/Notification/Notification.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { FlatList, View } from 'react-native';
3 | import HeaderWrap from 'src/components/HeaderWrap';
4 | import styles from './Notification.styles';
5 | import { notifications } from './mockData';
6 | import NotificationComp from 'src/components/Notification/Notification';
7 |
8 | const Notification = () => {
9 | const [refreshing, setRefreshing] = React.useState(false);
10 | const [data, setData] = React.useState(notifications);
11 |
12 | useEffect(() => {
13 | if (refreshing) {
14 | setTimeout(() => {
15 | setData(prev => [...prev, ...notifications]);
16 | setRefreshing(false);
17 | }, 1000);
18 | }
19 | }, [refreshing]);
20 |
21 | const onRefresh = React.useCallback(() => {
22 | setRefreshing(true);
23 | }, []);
24 |
25 | return (
26 |
27 |
28 | }
34 | />
35 |
36 | );
37 | };
38 |
39 | export default Notification;
40 |
--------------------------------------------------------------------------------
/src/theme/Variables.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains the application's variables.
3 | *
4 | * Define color, sizes, etc. here instead of duplicating them throughout the components.
5 | * That allows to change them more easily later on.
6 | */
7 | /**
8 | * Colors
9 | */
10 | export const Colors = {
11 | transparent: 'rgba(0,0,0,0)',
12 | inputBackground: '#FFFFFF',
13 | white: '#ffffff',
14 | //Typography
15 | textGray800: '#000000',
16 | textGray400: '#4D4D4D',
17 | textGray200: '#A1A1A1',
18 | primary: '#E14032',
19 | success: '#28a745',
20 | error: '#dc3545',
21 | //ComponentColors
22 | circleButtonBackground: '#E1E1EF',
23 | circleButtonColor: '#44427D',
24 | };
25 | export const NavigationColors = {
26 | primary: Colors.primary,
27 | background: '#EFEFEF',
28 | card: '#EFEFEF',
29 | };
30 | /**
31 | * FontSize
32 | */
33 | export const FontSize = {
34 | tiny: 14,
35 | small: 16,
36 | regular: 20,
37 | large: 40,
38 | };
39 | /**
40 | * Metrics Sizes
41 | */
42 | const tiny = 10;
43 | const small = tiny * 2; // 20
44 | const regular = tiny * 3; // 30
45 | const large = regular * 2; // 60
46 | export const MetricsSizes = {
47 | tiny,
48 | small,
49 | regular,
50 | large,
51 | };
52 | export default {
53 | Colors,
54 | NavigationColors,
55 | FontSize,
56 | MetricsSizes,
57 | };
58 |
--------------------------------------------------------------------------------
/src/screens/Profile/Profile.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | },
11 | contentWrap: {
12 | display: 'flex',
13 | alignItems: 'center',
14 | paddingBottom: 30
15 | },
16 | upgradeToPremiumWrap: {
17 | marginTop: 20 ,
18 | width: '100%',
19 | backgroundColor: AppTheme.colors.primary_1,
20 | borderRadius: 15,
21 | padding: 20,
22 | },
23 | premiumFirstLine: {
24 | display: 'flex',
25 | flexDirection: 'row',
26 | justifyContent: 'space-between',
27 | alignItems: 'center',
28 | marginBottom: 10,
29 | },
30 | proIcon: {
31 | backgroundColor: '#fed301',
32 | borderRadius: 15,
33 | paddingHorizontal: 10,
34 | paddingVertical: 5,
35 | },
36 | nextIconWrap: {
37 | width: 25,
38 | height: 25,
39 | backgroundColor: 'white',
40 | borderRadius: 50,
41 | display: 'flex',
42 | justifyContent: 'center',
43 | alignItems: 'center',
44 | },
45 | nextIcon: {
46 | width: 30,
47 | height: 30,
48 | borderRadius: 50,
49 | },
50 | screenWrap: {
51 | width: '100%',
52 | marginTop: 20,
53 | }
54 | });
55 |
56 | export default styles;
57 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/myapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.myapp;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate;
7 | import android.os.Bundle;
8 |
9 | public class MainActivity extends ReactActivity {
10 |
11 | /**
12 | * Returns the name of the main component registered from JavaScript. This is used to schedule
13 | * rendering of the component.
14 | */
15 | @Override
16 | protected String getMainComponentName() {
17 | return "MyApp";
18 | }
19 |
20 | /**
21 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
22 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
23 | * (aka React 18) with two boolean flags.
24 | */
25 | @Override
26 | protected ReactActivityDelegate createReactActivityDelegate() {
27 | return new DefaultReactActivityDelegate(
28 | this,
29 | getMainComponentName(),
30 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
31 | DefaultNewArchitectureEntryPoint.getFabricEnabled());
32 | }
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstance) {
36 | super.onCreate(savedInstance);
37 | setTheme(R.style.AppTheme);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.styles.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | paddingTop: 60,
9 | paddingHorizontal: 15,
10 | backgroundColor: '#fcfcfc',
11 | },
12 | headerIconLeft: {
13 | width: 60,
14 | height: 60,
15 | },
16 | headerIconRight: {
17 | width: 30,
18 | height: 30,
19 | },
20 | titleWrapper: {
21 | display: 'flex',
22 | flexDirection: 'row',
23 | alignItems: 'center',
24 | gap: 10,
25 | },
26 | homeTitle: {
27 | marginTop: 15,
28 | marginBottom: 15,
29 | fontSize: AppTheme.fontSize.s24,
30 | fontWeight: 600,
31 | },
32 | progressWrapper: {
33 | backgroundColor: 'white',
34 | padding: 10,
35 | paddingVertical: 15,
36 | borderRadius: 10,
37 | width: '100%',
38 | display: 'flex',
39 | flexDirection: 'row',
40 | justifyContent: 'space-evenly',
41 | alignItems: 'center',
42 | gap: 20,
43 | },
44 | processTitle: {
45 | fontWeight: 600,
46 | fontSize: AppTheme.fontSize.s16,
47 | marginBottom: 10,
48 | },
49 |
50 | processText: {
51 | fontSize: AppTheme.fontSize.s12,
52 | },
53 |
54 | taskTextWrapper: {
55 | display: 'flex',
56 | flexDirection: 'row',
57 | justifyContent: 'space-between',
58 | marginTop: 20,
59 | },
60 | });
61 |
62 | export default styles;
63 |
--------------------------------------------------------------------------------
/src/components/IconComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TouchableDebounce from './TouchableDebounce';
3 | import { AppTheme } from '../utils/appConstant';
4 | import { Image, View } from 'react-native';
5 | import FastImage from 'react-native-fast-image';
6 | import TextView from './TextView';
7 |
8 | const IconComponent = ({
9 | width,
10 | height,
11 | color = AppTheme.colors.black, // update 'fill' field in svg file to 'currentColor' to get effect
12 | style,
13 | source = '',
14 | size = AppTheme.iconSize.s24,
15 | onPress = () => {},
16 | disabled = false,
17 | hitSlop,
18 | activeOpacity,
19 | children,
20 | noTouchDebounce = false,
21 | onPressIn = null,
22 | onPressOut = null,
23 | text,
24 | }) => {
25 | if (!source) {
26 | return null;
27 | }
28 | const IconView = source;
29 | return (
30 |
31 |
41 |
46 | {children}
47 |
48 |
49 | );
50 | };
51 |
52 | export default IconComponent;
53 |
--------------------------------------------------------------------------------
/src/screens/CompletedTimer/CompletedTimer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import TextView from 'src/components/TextView';
4 | import styles from './CompletedTimer.styles';
5 | import TrophyImage from 'src/assets/images/upgradeApp/trophy.png';
6 | import FastImage from 'react-native-fast-image';
7 | import Button from 'src/components/Button';
8 | import { navigate } from 'src/navigators/NavigationServices';
9 | import RouteName from 'src/navigators/RouteName';
10 |
11 | const CompletedTimer = () => {
12 | const handeBackToHome = () => {
13 | navigate(RouteName.Home);
14 | };
15 |
16 | return (
17 |
18 |
19 |
20 | Congratulation
21 |
22 | You have completed the task. Keep it up!
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default CompletedTimer;
43 |
--------------------------------------------------------------------------------
/src/screens/Task/CalendarEvent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './Task.styles';
3 | import { Text, View } from 'react-native';
4 | import moment from 'moment';
5 | import TextView from 'src/components/TextView';
6 | import { CALENDAR_STYLE_BG } from './constant';
7 | const CalendarEvent = ({ item }) => {
8 | const handleDurationTime = () => {
9 | const startTime = moment(item?.time, 'hh:mm AM').format('HH:mm');
10 | const endTime = moment(startTime, 'HH:mm')
11 | .add(item?.duration, 'hour')
12 | .format('HH:mm');
13 |
14 | return [startTime, endTime];
15 | };
16 | if (!item) {
17 | return (
18 |
19 | {/* No event this time */}
20 |
21 | );
22 | }
23 | return (
24 |
30 | {item?.event}
31 |
34 | {handleDurationTime()?.map((time, index) => (
35 |
36 | {`${
37 | index === 0 ? `${time}: ` : time
38 | }`}
39 |
40 | ))}
41 |
42 |
43 | );
44 | };
45 |
46 | export default CalendarEvent;
47 |
--------------------------------------------------------------------------------
/src/components/TextView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text } from 'react-native';
3 | import { AppFont, AppTheme } from '../utils/appConstant';
4 |
5 | const CustomFont = {
6 | normal: 'Regular',
7 | bold: 'Bold',
8 | 100: 'Light',
9 | 200: 'Light',
10 | 300: 'Light',
11 | 400: 'Regular',
12 | 500: 'Medium',
13 | 600: 'SemiBold',
14 | 700: 'Bold',
15 | 800: 'ExtraBold',
16 | 900: 'ExtraBold',
17 | };
18 |
19 | const TextView = ({
20 | children,
21 | style,
22 | color = AppTheme.colors.black,
23 | fontSize = AppTheme.fontSize.defaultFontSize,
24 | numberOfLines,
25 | adjustsFontSizeToFit = false,
26 | fontWeight,
27 | font = '',
28 | ...rest
29 | }) => {
30 | const {
31 | fontWeight: fontWeightStyle,
32 | fontStyle,
33 | fontSize: fontSizeStyle,
34 | } = StyleSheet.flatten(style || {});
35 | const fontFamily = `${font}-${
36 | CustomFont[fontWeightStyle] || CustomFont[fontWeight] || CustomFont[400]
37 | }${fontStyle === 'italic' ? 'Italic' : ''}`;
38 | return (
39 |
55 | {children}
56 |
57 | );
58 | };
59 |
60 | export default TextView;
61 |
--------------------------------------------------------------------------------
/src/store/auth/authReducer.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 | import persistReducer from 'redux-persist/es/persistReducer';
3 | import { RequestStatus } from 'src/utils/appConstant';
4 | import persistConfig from '../persistConfig';
5 |
6 | const initialState = {
7 | accessToken: '',
8 | password: '',
9 | email: '',
10 | loginStatus: '',
11 | };
12 | const authSlice = createSlice({
13 | name: 'authReducer',
14 | initialState: { ...initialState },
15 | reducers: {
16 | loginRequestAction: state => {
17 | return {
18 | ...state,
19 | loginStatus: RequestStatus.Request,
20 | };
21 | },
22 | loginRequestSuccessAction: (state, action) => {
23 | return {
24 | ...state,
25 | loginStatus: RequestStatus.Success,
26 | accessToken: action?.payload?.accessToken,
27 | };
28 | },
29 | loginRequestFailAction: (state, action) => {
30 | return {
31 | ...state,
32 | loginStatus: RequestStatus.Failure,
33 | };
34 | },
35 | setAccessToken: (state, action) => {
36 | return {
37 | ...state,
38 | accessToken: action.payload,
39 | };
40 | },
41 | },
42 | });
43 |
44 | export const {
45 | loginRequestAction,
46 | loginRequestSuccessAction,
47 | loginRequestFailAction,
48 | setAccessToken,
49 | } = authSlice.actions;
50 |
51 | const authReducer = authSlice.reducer;
52 | export default persistReducer(
53 | persistConfig({
54 | key: 'authReducer',
55 | whitelist: [''],
56 | }),
57 | authReducer,
58 | );
59 |
--------------------------------------------------------------------------------
/src/screens/CompletedTimer/CompletedTimer.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | position: 'relative',
11 | },
12 | screenWrap: {
13 | flex: 1,
14 | height: '9%',
15 | display: 'flex',
16 | justifyContent: 'center',
17 | alignItems: 'center',
18 | },
19 | titleWrap: {
20 | display: 'flex',
21 | alignItems: 'center',
22 | marginTop: 10,
23 | },
24 | mainTitle: {
25 | fontSize: 50,
26 | fontWeight: 'bold',
27 | marginBottom: 10,
28 | color: AppTheme.colors.primary_1,
29 | },
30 | subTitle: {
31 | fontSize: 18,
32 | },
33 | imageWrapper: {
34 | width: '100%',
35 | height: '60%',
36 | display: 'flex',
37 | alignItems: 'center',
38 | justifyContent: 'center',
39 | },
40 | trophyImage: {
41 | width: '70%',
42 | height: '50%',
43 | position: 'absolute',
44 | },
45 | upgradeOptionsWrap: {
46 | display: 'flex',
47 | justifyContent: 'space-between',
48 | marginTop: 30,
49 | width: '100%',
50 | },
51 | buttonNext: {
52 | marginTop: 20,
53 | width: '100%',
54 | height: 50,
55 | backgroundColor: '#ff585d',
56 | borderRadius: 50,
57 | display: 'flex',
58 | alignItems: 'center',
59 | justifyContent: 'center',
60 | position: 'absolute',
61 | bottom: 0,
62 | },
63 | buttonTextStyle: {
64 | color: 'white',
65 | },
66 | });
67 |
68 | export default styles;
69 |
--------------------------------------------------------------------------------
/src/navigators/NavigationServices.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Used to navigating without the navigation prop
3 | * @see https://reactnavigation.org/docs/navigating-without-navigation-prop/
4 | *
5 | * You can add other navigation functions that you need and export them
6 | */
7 | import {
8 | createNavigationContainerRef,
9 | StackActions,
10 | DrawerActions,
11 | } from '@react-navigation/native';
12 | import * as React from 'react';
13 |
14 | const navigationRef = createNavigationContainerRef();
15 | const routeNameRef = React.createRef();
16 |
17 | function goBack(number) {
18 | if (!navigationRef.isReady()) return;
19 | if (!number) {
20 | return navigationRef.goBack();
21 | }
22 | const popAction = StackActions.pop(number);
23 | navigationRef.dispatch(popAction);
24 | }
25 |
26 | function navigate(name, params) {
27 | if (navigationRef.isReady()) navigationRef.navigate(name, params);
28 | }
29 |
30 | function replace(name, params) {
31 | if (navigationRef.isReady())
32 | navigationRef.dispatch(StackActions.replace(name, params));
33 | }
34 |
35 | function resetTo(name) {
36 | if (navigationRef.isReady())
37 | navigationRef.resetRoot({
38 | index: 0,
39 | routes: [{ name }],
40 | });
41 | }
42 |
43 | function getCurrentRoute() {
44 | return navigationRef?.getCurrentRoute?.()?.name;
45 | }
46 |
47 | function toggleDrawer() {
48 | if (!navigationRef.isReady()) return;
49 | const actions = DrawerActions.toggleDrawer();
50 | navigationRef.dispatch(actions);
51 | }
52 |
53 | export {
54 | navigationRef,
55 | routeNameRef,
56 | goBack,
57 | navigate,
58 | replace,
59 | resetTo,
60 | getCurrentRoute,
61 | toggleDrawer,
62 | };
63 |
--------------------------------------------------------------------------------
/src/screens/index.js:
--------------------------------------------------------------------------------
1 | export { default as Startup } from './Startup/Startup';
2 | export { default as Example } from './Example/Example';
3 | export { default as StartScreen } from './StartScreen/StartScreen';
4 | export { default as Login } from './Login/Login';
5 | export { default as LoginWithPass } from './Login/LoginWithPass';
6 | export { default as SignUp } from './SignUp/SignUp';
7 | export { default as FillProfile } from './FillProfile/FillProfile';
8 | export { default as ForgotPass } from './ForgotPass/ForgotPass';
9 | export { default as TodayTask } from './TodayTask/TodayTask';
10 | export { default as Notification } from './Notification/Notification';
11 | export { default as Profile } from './Profile/Profile';
12 | export { default as Timer } from './Timer/Timer';
13 | export { default as Category } from './Category/Category';
14 | export { default as AllCompletedTask } from './AllCompletedTask/AllCompletedTask';
15 | // BottomTab
16 | export { default as Home } from './Home/Home';
17 | export { default as NewTask } from './NewTask/NewTask';
18 | export { default as Statistics } from './Statistics/Statistics';
19 | export { default as Task } from './Task/Task';
20 | export { default as Goal } from './Goal/Goal';
21 |
22 | // Profile
23 | export { default as EditProfile } from './EditProfile/EditProfile';
24 | export { default as AppSetting } from './AppSetting/AppSetting';
25 | export { default as ReminderRingTone } from './ReminderRingTone/ReminderRingTone';
26 | export { default as NotificationSetting } from './NotificationSetting/NotificationSetting';
27 | export { default as Security } from './Security/Security';
28 | export { default as UpgradeApp } from './UpgradeApp/UpgradeApp';
29 | // Timer
30 | export { default as CompletedTimer } from './CompletedTimer/CompletedTimer';
31 |
--------------------------------------------------------------------------------
/ios/MyApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Goal
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/screens/ForgotPass/ForgotPass.style.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | headerWrapper: {
7 | position: 'absolute',
8 | top: 0,
9 | },
10 | container: {
11 | flex: 1,
12 | alignItems: 'center',
13 | justifyContent: 'center',
14 | width: '100%',
15 | position: 'relative',
16 | backgroundColor: AppTheme.colors.white,
17 | },
18 | containerScrollView: {
19 | width: '90%',
20 | height: '100%',
21 | position: 'relative',
22 | alignItems: 'center',
23 | justifyContent: 'flex-start',
24 | },
25 | screenContent: {
26 | marginTop: 70,
27 | },
28 | optionResetPassWrapper: {
29 | marginTop: 20,
30 | gap: 20,
31 | },
32 | optionResetWrapper: {
33 | display: 'flex',
34 | flexDirection: 'row',
35 | gap: 20,
36 | alignItems: 'center',
37 | borderWidth: 1,
38 | borderColor: AppTheme.colors.neutral_40,
39 | padding: 20,
40 | borderRadius: 20,
41 | },
42 | optionResetImage: {
43 | backgroundColor: AppTheme.colors.neutral_10,
44 | borderRadius: 50,
45 | padding: 10,
46 | },
47 | buttonNext: {
48 | marginTop: 50,
49 | width: '100%',
50 | height: 50,
51 | backgroundColor: '#ff585d',
52 | borderRadius: 50,
53 | display: 'flex',
54 | alignItems: 'center',
55 | justifyContent: 'center',
56 | },
57 | buttonTextStyle: {
58 | color: 'white',
59 | },
60 | enterCodeWrapper: {
61 | flex: 1,
62 | height: '100%',
63 | justifyContent: 'center',
64 | alignItems: 'center',
65 | gap: 50,
66 | padding: 20,
67 | },
68 | enterCodeKeyboardScrollView: {
69 | height: '100%',
70 | },
71 | });
72 |
73 | export default styles;
74 |
--------------------------------------------------------------------------------
/src/screens/Security/Security.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { View, Text } from 'react-native';
3 | import HeaderWrap from 'src/components/HeaderWrap';
4 | import styles from './Security.styles';
5 | import TextView from 'src/components/TextView';
6 | import { Switch } from 'react-native-gesture-handler';
7 | import { AppTheme } from 'src/utils/appConstant';
8 | import Button from 'src/components/Button';
9 | const SECURITY_SETTING_CONFIG = [
10 | {
11 | label: 'Face ID',
12 | },
13 | {
14 | label: 'Remember me',
15 | },
16 | {
17 | label: 'Touch Id',
18 | },
19 | ];
20 |
21 | const Security = () => {
22 | return (
23 |
24 |
25 | {SECURITY_SETTING_CONFIG.map((item, index) => (
26 |
27 | ))}
28 |
33 |
34 | );
35 | };
36 |
37 | const SecuritySettingItem = ({ item }) => {
38 | const [toggleValue, setToggleValue] = useState(false);
39 |
40 | const handleToggle = () => {
41 | setToggleValue(!toggleValue);
42 | };
43 |
44 | return (
45 |
46 | {item.label}
47 |
55 |
56 | );
57 | };
58 |
59 | export default Security;
60 |
--------------------------------------------------------------------------------
/src/screens/FillProfile/ProfileAvatar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import TextView from 'src/components/TextView';
4 | import IconComponent from 'src/components/IconComponent';
5 | import IconAvatar from 'src/assets/images/login/ic_avatar.png';
6 | import FastImage from 'react-native-fast-image';
7 | import IconEdit from 'src/assets/images/login/ic_edit.png';
8 | import TouchableDebounce from 'src/components/TouchableDebounce';
9 |
10 | const ProfileAvatar = ({ onShowImage, uri, onEditAvatar, info }) => {
11 | return (
12 |
13 | {!uri && (
14 |
21 | )}
22 |
23 |
24 |
25 | {info && (
26 |
27 |
28 | {info.name}
29 |
30 |
31 | {info.email}
32 |
33 |
34 | )}
35 |
36 | );
37 | };
38 |
39 | export default ProfileAvatar;
40 |
41 | const styles = StyleSheet.create({
42 | container: {
43 | marginTop: 50,
44 | position: 'relative',
45 | },
46 | iconEdit: {
47 | width: 25,
48 | height: 25,
49 | position: 'absolute',
50 | bottom: 10,
51 | right: 20,
52 | borderRadius: 5,
53 | backgroundColor: 'red',
54 | },
55 | infoWrap: {
56 | marginTop: 10,
57 | display: 'flex',
58 | justifyContent: 'center',
59 | alignItems: 'center'
60 | }
61 | });
62 |
--------------------------------------------------------------------------------
/src/theme/Fonts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file contains all application's style relative to fonts
3 | */
4 | import { StyleSheet } from 'react-native';
5 | export default function ({ FontSize, Colors }) {
6 | return StyleSheet.create({
7 | textTiny: {
8 | fontSize: FontSize.tiny,
9 | color: Colors.textGray400,
10 | },
11 | textSmall: {
12 | fontSize: FontSize.small,
13 | color: Colors.textGray400,
14 | },
15 | textRegular: {
16 | fontSize: FontSize.regular,
17 | color: Colors.textGray400,
18 | },
19 | textLarge: {
20 | fontSize: FontSize.large,
21 | color: Colors.textGray400,
22 | },
23 | textBold: {
24 | fontWeight: 'bold',
25 | },
26 | textUppercase: {
27 | textTransform: 'uppercase',
28 | },
29 | titleSmall: {
30 | fontSize: FontSize.small * 1.5,
31 | fontWeight: 'bold',
32 | color: Colors.textGray800,
33 | },
34 | titleRegular: {
35 | fontSize: FontSize.regular * 2,
36 | fontWeight: 'bold',
37 | color: Colors.textGray800,
38 | },
39 | titleLarge: {
40 | fontSize: FontSize.large * 2,
41 | fontWeight: 'bold',
42 | color: Colors.textGray800,
43 | },
44 | textCenter: {
45 | textAlign: 'center',
46 | },
47 | textJustify: {
48 | textAlign: 'justify',
49 | },
50 | textLeft: {
51 | textAlign: 'left',
52 | },
53 | textRight: {
54 | textAlign: 'right',
55 | },
56 | textError: {
57 | color: Colors.error,
58 | },
59 | textSuccess: {
60 | color: Colors.success,
61 | },
62 | textPrimary: {
63 | color: Colors.primary,
64 | },
65 | textLight: {
66 | color: Colors.textGray200,
67 | },
68 | textLobster: {
69 | fontFamily: 'lobster',
70 | fontWeight: 'normal',
71 | },
72 | });
73 | }
74 |
--------------------------------------------------------------------------------
/src/screens/Statistics/Statistics.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | },
11 | headerIconLeft: {
12 | width: 60,
13 | height: 60,
14 | },
15 | headerIconRight: {
16 | width: 30,
17 | height: 30,
18 | },
19 | statisticsTitleWrap: {
20 | width: '100%',
21 | marginTop: 20,
22 | display: 'flex',
23 | flexDirection: 'row',
24 | alignContent: 'center',
25 | justifyContent: 'space-between',
26 | },
27 | dropdown: {
28 | width: '100%',
29 | minHeight: 40,
30 | borderRadius: 8,
31 | paddingHorizontal: 12,
32 | borderWidth: 2,
33 | borderColor: AppTheme.colors.primary_1,
34 | },
35 | dropDownContainerStyle: {
36 | width: '100%',
37 | maxHeight: 200,
38 | borderWidth: 1,
39 | borderColor: AppTheme.colors.primary_1,
40 | marginTop: 6,
41 | borderRadius: 8,
42 | zIndex: 9999,
43 | top: 0,
44 | paddingLeft: AppTheme.gapSize.s10,
45 | color: AppTheme.colors.primary_1,
46 | },
47 | dropContainerStyle: {
48 | width: '35%',
49 | color: AppTheme.colors.primary_1,
50 | borderColor: AppTheme.colors.primary_1,
51 | zIndex: 9999,
52 | },
53 | lineGraphWrap: {
54 | width: '100%',
55 | height: 220,
56 | display: 'flex',
57 | justifyContent: 'center',
58 | alignItems: 'center',
59 | marginTop: 25,
60 | position: 'relative',
61 | zIndex: -1,
62 | },
63 | lineGraphStyle: {
64 | width: '100%',
65 | height: '100%',
66 | },
67 | taskTextWrapper: {
68 | display: 'flex',
69 | flexDirection: 'row',
70 | justifyContent: 'space-between',
71 | marginTop: 20,
72 | },
73 | });
74 |
75 | export default styles;
76 |
--------------------------------------------------------------------------------
/src/components/TouchableDebounce.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useRef } from 'react';
2 | import { ActivityIndicator, TouchableOpacity } from 'react-native';
3 | import { AppTheme } from '../utils/appConstant';
4 | import { delay } from '../utils/fun';
5 |
6 | const TouchableDebounce = (
7 | props = {
8 | timeout: Number,
9 | children: null,
10 | onPress: null,
11 | noDebounce: Boolean,
12 | hitSlop: {
13 | top: Number,
14 | left: Number,
15 | bottom: Number,
16 | right: Number,
17 | },
18 | loading: Boolean,
19 | loadingColor: String,
20 | activeOpacity: Number,
21 | disabled: Boolean,
22 | style: {},
23 | onPressIn: Function,
24 | onPressOut: Function,
25 | },
26 | ) => {
27 | const debounceRef = useRef();
28 | const {
29 | timeout = 500,
30 | children,
31 | onPress,
32 | noDebounce = false,
33 | loading,
34 | loadingColor = AppTheme.colors.white,
35 | disabled,
36 | activeOpacity = 0.8,
37 | } = props;
38 | const onLocalPress = useCallback(async () => {
39 | try {
40 | if (!onPress) {
41 | return;
42 | }
43 | if (noDebounce) {
44 | onPress();
45 | } else {
46 | if (debounceRef.current) {
47 | return;
48 | }
49 | onPress();
50 | debounceRef.current = delay;
51 | await debounceRef.current(timeout);
52 | debounceRef.current = null;
53 | }
54 | } catch (error) {
55 | console.log(error);
56 | }
57 | }, [noDebounce, onPress, timeout]);
58 | return (
59 |
65 | {loading ? : children}
66 |
67 | );
68 | };
69 |
70 | export default TouchableDebounce;
71 |
--------------------------------------------------------------------------------
/src/components/Notification/Notification.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 | import TextView from '../TextView';
4 | import { AppTheme } from 'src/utils/appConstant';
5 | import FastImage from 'react-native-fast-image';
6 | import SuccessImg from 'src/assets/images/notification/success.png';
7 | import CancelImg from 'src/assets/images/notification/cancel.png';
8 | import ServiceImg from 'src/assets/images/notification/service.png';
9 | import VertificationImg from 'src/assets/images/notification/vertification.png';
10 |
11 | const NotificationComp = ({ title, subTitle, type }) => {
12 | const handleRenderImage = value => {
13 | const NOTI_IMG = {
14 | success: SuccessImg,
15 | cancel: CancelImg,
16 | service: ServiceImg,
17 | vertification: VertificationImg,
18 | };
19 |
20 | return NOTI_IMG[value];
21 | };
22 |
23 | return (
24 |
25 |
26 |
33 |
34 |
35 | {title}
36 | {subTitle}
37 |
38 |
39 | );
40 | };
41 |
42 | export default NotificationComp;
43 |
44 | const styles = StyleSheet.create({
45 | container: {
46 | backgroundColor: 'white',
47 | borderRadius: 10,
48 | paddingVertical: 15,
49 | paddingHorizontal: 15,
50 | marginVertical: 10,
51 | display: 'flex',
52 | flexDirection: 'row',
53 | alignItems: 'center',
54 | gap: 15,
55 | },
56 | title: {
57 | fontSize: AppTheme.fontSize.s16,
58 | },
59 | subTitle: {
60 | fontSize: AppTheme.fontSize.s14,
61 | color: AppTheme.colors.neutral_50,
62 | },
63 | });
64 |
--------------------------------------------------------------------------------
/src/screens/UpgradeApp/UpgradeApp.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | },
11 | screenWrap: {
12 | display: 'flex',
13 | justifyContent: 'center',
14 | alignItems: 'center',
15 | },
16 | titleWrap: {
17 | display: 'flex',
18 | alignItems: 'center',
19 | marginTop: 10,
20 | },
21 | mainTitle: {
22 | fontSize: 50,
23 | fontWeight: 'bold',
24 | marginBottom: 10,
25 | color: AppTheme.colors.primary_1,
26 | },
27 | subTitle: {
28 | fontSize: 18,
29 | },
30 | trophyImage: {
31 | width: '100%',
32 | height: '100%',
33 | position: 'absolute',
34 | },
35 | simpleButton: {
36 | borderRadius: 20,
37 | padding: 10,
38 | justifyContent: 'center',
39 | alignItems: 'center',
40 | backgroundColor: 'red',
41 | },
42 | fireworkStyle: {
43 | backgroundColor: 'red',
44 | },
45 | upgradeOptionsWrap: {
46 | display: 'flex',
47 | justifyContent: 'space-between',
48 | marginTop: 30,
49 | // backgroundColor: 'red',
50 | width: '100%',
51 | },
52 | upgradeOption: {
53 | display: 'flex',
54 | backgroundColor: 'white',
55 | borderWidth: 3,
56 | flexDirection: 'row',
57 | alignItems: 'center',
58 | justifyContent: 'space-between',
59 | paddingVertical: 20,
60 | paddingHorizontal: 20,
61 | marginVertical: 10,
62 | borderRadius: 15,
63 | },
64 | buttonNext: {
65 | marginTop: 20,
66 | width: '100%',
67 | height: 50,
68 | backgroundColor: '#ff585d',
69 | borderRadius: 50,
70 | display: 'flex',
71 | alignItems: 'center',
72 | justifyContent: 'center',
73 | },
74 | buttonTextStyle: {
75 | color: 'white',
76 | },
77 | });
78 |
79 | export default styles;
80 |
--------------------------------------------------------------------------------
/src/theme/Gutters.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | /**
3 | * Generate Styles depending on MetricsSizes vars availabled at ./theme/Variables
4 | * Styles are like :
5 | * : {
6 | * :
7 | * }
8 | * where:
9 | * : is the key of the variable included in MetricsSizes
10 | * : can be ['Bottom','Top','Right','Left','Horizontal','Vertical']
11 | * : can be ['Margin', 'Padding']
12 | * : is the value of the
13 | */
14 | export default function ({ MetricsSizes }) {
15 | return StyleSheet.create(
16 | Object.entries(MetricsSizes).reduce(
17 | (acc, [key, value]) => ({
18 | ...acc,
19 | /* Margins */
20 | [`${key}Margin`]: {
21 | margin: value,
22 | },
23 | [`${key}BMargin`]: {
24 | marginBottom: value,
25 | },
26 | [`${key}TMargin`]: {
27 | marginTop: value,
28 | },
29 | [`${key}RMargin`]: {
30 | marginRight: value,
31 | },
32 | [`${key}LMargin`]: {
33 | marginLeft: value,
34 | },
35 | [`${key}VMargin`]: {
36 | marginVertical: value,
37 | },
38 | [`${key}HMargin`]: {
39 | marginHorizontal: value,
40 | },
41 | /* Paddings */
42 | [`${key}Padding`]: {
43 | padding: value,
44 | },
45 | [`${key}BPadding`]: {
46 | paddingBottom: value,
47 | },
48 | [`${key}TPadding`]: {
49 | paddingTop: value,
50 | },
51 | [`${key}RPadding`]: {
52 | paddingRight: value,
53 | },
54 | [`${key}LPadding`]: {
55 | paddingLeft: value,
56 | },
57 | [`${key}VPadding`]: {
58 | paddingVertical: value,
59 | },
60 | [`${key}HPadding`]: {
61 | paddingHorizontal: value,
62 | },
63 | }),
64 | {},
65 | ),
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/src/navigators/ApplicationStack.js:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native';
2 | import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';
3 | import { Platform, StatusBar } from 'react-native';
4 | import { SafeAreaProvider } from 'react-native-safe-area-context';
5 | import { useTheme } from '../hooks';
6 | import { navigationRef } from './NavigationServices';
7 | import {
8 | CardStyleInterpolators,
9 | createStackNavigator,
10 | } from '@react-navigation/stack';
11 | import ApplicationNavigator from './stack/AppicationNavigator';
12 | import { useSelector } from 'react-redux';
13 | import AuthNavigator from './stack/AuthNavigator';
14 |
15 | const Stack = createStackNavigator();
16 |
17 | function ApplicationStack() {
18 | const { accessToken } = useSelector(state => ({
19 | ...state.authReducer,
20 | }));
21 | // const accessToken = 'hello';
22 |
23 | const StackScreen = () => {
24 | if (!accessToken) {
25 | return AuthNavigator();
26 | }
27 |
28 | return ApplicationNavigator();
29 | };
30 |
31 | const onNavigationStateChange = useCallback(async () => {}, []);
32 | return (
33 |
34 |
39 |
43 |
52 | {StackScreen()}
53 |
54 |
55 |
56 | );
57 | }
58 |
59 | export default memo(ApplicationStack);
60 |
--------------------------------------------------------------------------------
/src/screens/Task/Task.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import { AppTheme } from 'src/utils/appConstant';
3 |
4 | export default StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | paddingTop: 60,
8 | paddingHorizontal: 15,
9 | backgroundColor: '#fcfcfc',
10 | },
11 | headerIconLeft: {
12 | width: 60,
13 | height: 60,
14 | },
15 | iconCalendar: {
16 | width: 20,
17 | height: 20,
18 | },
19 | dayPickStyle: {
20 | display: 'flex',
21 | flexDirection: 'row',
22 | justifyContent: 'space-between',
23 | width: '100%',
24 | },
25 | calendarStyle: {
26 | marginTop: 20,
27 | display: 'flex',
28 | flexDirection: 'row',
29 | justifyContent: 'space-between',
30 | },
31 | itemDayStyle: {
32 | backgroundColor: AppTheme.colors.primary_1,
33 | borderRadius: 15,
34 | width: 40,
35 | height: 100,
36 | display: 'flex',
37 | alignItems: 'center',
38 | justifyContent: 'center',
39 | marginHorizontal: 5,
40 | color: 'white',
41 | },
42 | calendarWrapper: {
43 | flex: 1,
44 | width: '100%',
45 | marginTop: 25,
46 | },
47 | emptyTask: {
48 | display: 'flex',
49 | justifyContent: 'center',
50 | alignItems: 'center',
51 | },
52 | customeCalendarWrapper: {
53 | width: '100%',
54 | },
55 | itemEventWrapper: {
56 | display: 'flex',
57 | flexDirection: 'row',
58 | justifyContent: 'space-between',
59 | marginTop: 10,
60 | marginBottom: 10,
61 | paddingHorizontal: 20,
62 | },
63 | itemCalendarStyle: {
64 | width: '70%',
65 | paddingHorizontal: 20,
66 | paddingVertical: 20,
67 | borderRadius: 20,
68 | },
69 | itemCalendarTitle: {
70 | color: AppTheme.colors.white,
71 | fontSize: AppTheme.fontSize.s16,
72 | fontWeight: '600',
73 | marginBottom: 10,
74 | },
75 | itemCalendarTimeTextStyle: {
76 | color: AppTheme.colors.white,
77 | fontSize: AppTheme.fontSize.s14,
78 | marginLeft: 0,
79 | },
80 | });
81 |
--------------------------------------------------------------------------------
/src/screens/NotificationSetting/NotificationSetting.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Switch, View } from 'react-native';
3 | import styles from './NotificationSetting.styles';
4 | import HeaderWrap from 'src/components/HeaderWrap';
5 | import { AppTheme } from 'src/utils/appConstant';
6 | import TextView from 'src/components/TextView';
7 | import RouteName from 'src/navigators/RouteName';
8 | import { navigate } from 'src/navigators/NavigationServices';
9 |
10 | const NOTIFICATIONS_SETTING_CONFIG = [
11 | {
12 | label: 'General Notificaion',
13 | },
14 | {
15 | label: 'Sound',
16 | },
17 | {
18 | label: 'Vibrate',
19 | },
20 | {
21 | label: 'App Updates',
22 | },
23 | {
24 | label: 'New Service Available',
25 | },
26 | {
27 | label: 'New Tips Available',
28 | },
29 | ];
30 |
31 | const NotificationSetting = () => {
32 | return (
33 |
34 |
35 | {NOTIFICATIONS_SETTING_CONFIG.map((item, index) => (
36 |
37 | ))}
38 |
39 | );
40 | };
41 |
42 | const NotificationSettingItem = ({ item }) => {
43 | const [toggleValue, setToggleValue] = useState(false);
44 |
45 | const handleToggle = () => {
46 | setToggleValue(!toggleValue);
47 | };
48 |
49 | const handleRedirectToRingTone = () => {
50 | navigate(RouteName.ReminderRingTone);
51 | };
52 |
53 | return (
54 |
55 | {item.label}
56 |
64 |
65 | );
66 | };
67 |
68 | export default NotificationSetting;
69 |
--------------------------------------------------------------------------------
/src/navigators/stack/AppBottomTab.js:
--------------------------------------------------------------------------------
1 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
2 | import React from 'react';
3 | import RouteName from '../RouteName';
4 | import { Home, Statistics, Task, Goal } from 'src/screens';
5 | import { screenOptions } from '../constants';
6 | import CustomAppTabBar from './CustomAppTabBar';
7 |
8 | const Tab = createBottomTabNavigator();
9 |
10 | const AppBottomTab = () => {
11 | const tabBottomRoutes = [
12 | {
13 | name: RouteName.Home,
14 | component: Home,
15 | title: 'Home',
16 | },
17 | {
18 | name: RouteName.Task,
19 | component: Task,
20 | title: 'Task',
21 | },
22 | {
23 | name: RouteName.BottomNewTask,
24 | component: Task,
25 | title: '',
26 | },
27 | {
28 | name: RouteName.Statistics,
29 | component: Statistics,
30 | title: 'Statistics',
31 | },
32 | {
33 | name: RouteName.Goal,
34 | component: Goal,
35 | title: 'Goal',
36 | },
37 | ];
38 |
39 | const renderTab = item => {
40 | let option = {
41 | title: item.title,
42 | tabBarHideOnKeyboard: true,
43 | };
44 |
45 | return (
46 | ({
55 | blur: () => {},
56 | })}
57 | />
58 | );
59 | };
60 | return (
61 | }
71 | >
72 | {tabBottomRoutes.map(item => renderTab(item))}
73 |
74 | );
75 | };
76 |
77 | export default AppBottomTab;
78 |
--------------------------------------------------------------------------------
/src/navigators/stack/AuthNavigator.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | CardStyleInterpolators,
4 | createStackNavigator,
5 | } from '@react-navigation/stack';
6 | import RouteName from '../RouteName';
7 | import {
8 | Startup,
9 | StartScreen,
10 | Login,
11 | LoginWithPass,
12 | SignUp,
13 | FillProfile,
14 | Home,
15 | ForgotPass,
16 | } from '../../screens';
17 | import { Platform } from 'react-native';
18 |
19 | const screenOptions = {
20 | headerShow: false,
21 | cardStyleInterpolator: Platform.select({
22 | android: CardStyleInterpolators.forFadeFromBottomAndroid,
23 | ios: CardStyleInterpolators.forScaleFromCenterAndroid,
24 | }),
25 | };
26 |
27 | const Stack = createStackNavigator();
28 |
29 | const AuthNavigator = props => {
30 | return (
31 | <>
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 | {/* */}
72 | >
73 | );
74 | };
75 |
76 | export default AuthNavigator;
77 |
--------------------------------------------------------------------------------
/src/screens/TodayTask/TodayTask.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useContext, useEffect, useState } from 'react';
2 | import { View } from 'react-native';
3 | import styles from './TodayTask.styles';
4 | import HeaderWrap from 'src/components/HeaderWrap';
5 | import { FlatList } from 'react-native-reanimated/mock';
6 | import TaskComp from 'src/components/Task';
7 | import AppwriteContext from 'src/utils/appwrite/AppwriteContext';
8 | import { useToast } from 'react-native-toast-notifications';
9 | import Config from 'react-native-config';
10 |
11 | const TASK_COLLECTION_ID = Config.TASK_COLLECTION_ID;
12 | const DATABASE_ID = Config.DATABASE_ID;
13 |
14 | const TodayTask = () => {
15 | const [refreshing, setRefreshing] = useState(false);
16 | const { appwrite } = useContext(AppwriteContext);
17 | const toast = useToast();
18 | const [tasks, setTasks] = useState([]);
19 |
20 | const handleGetTask = useCallback(() => {
21 | appwrite
22 | .getListDocument(DATABASE_ID, TASK_COLLECTION_ID)
23 | .then(response => {
24 | setTasks(response?.documents);
25 | })
26 | .catch(error => {
27 | toast.show('Connection error', { type: 'error' });
28 | })
29 | .finally(() => {
30 | setRefreshing(false);
31 | });
32 | }, []);
33 |
34 | useEffect(() => {
35 | handleGetTask();
36 | }, [handleGetTask]);
37 |
38 | useEffect(() => {
39 | if (refreshing) {
40 | handleGetTask();
41 | }
42 | }, [refreshing, handleGetTask]);
43 |
44 | const onRefresh = () => {
45 | setRefreshing(true);
46 | };
47 |
48 | return (
49 |
50 |
55 | }
60 | />
61 |
62 | );
63 | };
64 |
65 | export default TodayTask;
66 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.182.0
29 |
30 | # Use this property to specify which architecture you want to build.
31 | # You can also override it from the CLI using
32 | # ./gradlew -PreactNativeArchitectures=x86_64
33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
34 |
35 | # Use this property to enable support to the new architecture.
36 | # This will allow you to use TurboModules and the Fabric render in
37 | # your application. You should enable this flag either if you want
38 | # to write custom TurboModules/Fabric components OR use libraries that
39 | # are providing them.
40 | newArchEnabled=true
41 |
42 | # Use this property to enable or disable the Hermes JS engine.
43 | # If set to false, you will be using JSC instead.
44 | hermesEnabled=true
45 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/screens/Home/mockData.js:
--------------------------------------------------------------------------------
1 | export const tasks = [
2 | {
3 | title: 'Reading Books',
4 | date: new Date(),
5 | startTime: '10:00:00',
6 | category: 'reading',
7 | workingSession: 2,
8 | longBreak: 15,
9 | shortBreak: 5,
10 | status: 'new',
11 | },
12 | {
13 | title: 'Editing Audio',
14 | date: new Date(),
15 | startTime: '10:00:00',
16 | category: 'listening',
17 | workingSession: 3,
18 | longBreak: 15,
19 | shortBreak: 5,
20 | status: 'new',
21 | },
22 | {
23 | title: 'Learning Programming',
24 | date: new Date(),
25 | startTime: '10:00:00',
26 | category: 'learning',
27 | workingSession: 2,
28 | longBreak: 15,
29 | shortBreak: 5,
30 | status: 'new',
31 | },
32 | {
33 | title: 'Dumbbell Exercise',
34 | date: new Date(),
35 | startTime: '10:00:00',
36 | category: 'exercice',
37 | workingSession: 2,
38 | longBreak: 15,
39 | shortBreak: 5,
40 | status: 'new',
41 | },
42 | {
43 | title: 'Tech Exploration',
44 | date: new Date(),
45 | startTime: '10:00:00',
46 | category: 'tech',
47 | workingSession: 2,
48 | longBreak: 15,
49 | shortBreak: 5,
50 | status: 'new',
51 | },
52 | {
53 | title: 'Meditation',
54 | date: new Date(),
55 | startTime: '10:00:00',
56 | category: 'meditation',
57 | workingSession: 2,
58 | longBreak: 15,
59 | shortBreak: 5,
60 | status: 'new',
61 | },
62 | {
63 | title: 'Running',
64 | date: new Date(),
65 | startTime: '10:00:00',
66 | category: 'running',
67 | workingSession: 2,
68 | longBreak: 15,
69 | shortBreak: 5,
70 | status: 'new',
71 | },
72 | {
73 | title: 'Work',
74 | date: new Date(),
75 | startTime: '10:00:00',
76 | category: 'working',
77 | workingSession: 2,
78 | longBreak: 15,
79 | shortBreak: 5,
80 | status: 'new',
81 | },
82 | {
83 | title: 'Write blog',
84 | date: new Date(),
85 | startTime: '10:00:00',
86 | category: 'writing',
87 | workingSession: 2,
88 | longBreak: 15,
89 | shortBreak: 5,
90 | status: 'new',
91 | },
92 | ];
93 |
--------------------------------------------------------------------------------
/src/components/TextInput.js:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import { TextInput as RNTextInput, View, StyleSheet } from 'react-native';
3 | import FastImage from 'react-native-fast-image';
4 | import { AppTheme } from 'src/utils/appConstant';
5 |
6 | const TextInput = (
7 | {
8 | multiline = false,
9 | placeholder,
10 | placeholderIcon,
11 | style,
12 | onChangeText,
13 | value,
14 | maxLength,
15 | keyboardType,
16 | editable = true,
17 | fontSize = AppTheme.fontSize.defaultFontSize,
18 | placeholderTextColor = AppTheme.colors.neutral_30,
19 | secureTextEntry = false,
20 | returnKeyType,
21 | inputMode = 'text',
22 | onFocus,
23 | ...rest
24 | },
25 | _ref,
26 | ) => {
27 | return (
28 |
29 | {placeholderIcon && (
30 |
34 | )}
35 |
56 |
57 | );
58 | };
59 |
60 | const styles = StyleSheet.create({
61 | searchSection: {
62 | flex: 1,
63 | flexDirection: 'row',
64 | justifyContent: 'center',
65 | alignItems: 'center',
66 | backgroundColor: 'transparent',
67 | },
68 | searchIcon: {
69 | padding: 10,
70 | },
71 | input: {
72 | flex: 1,
73 | paddingTop: 10,
74 | paddingRight: 10,
75 | paddingBottom: 10,
76 | color: '#424242',
77 | },
78 | });
79 | export default forwardRef(TextInput);
80 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { configureStore, combineReducers } from '@reduxjs/toolkit';
2 | import { setupListeners } from '@reduxjs/toolkit/query';
3 | import createSagaMiddleware from 'redux-saga';
4 |
5 | import {
6 | persistReducer,
7 | persistStore,
8 | FLUSH,
9 | REHYDRATE,
10 | PAUSE,
11 | PERSIST,
12 | PURGE,
13 | REGISTER,
14 | createMigrate,
15 | } from 'redux-persist';
16 | import { createLogger } from 'redux-logger';
17 | import { MMKV } from 'react-native-mmkv';
18 | import rootReducer from './rootReducer';
19 | import { api } from 'src/services/api';
20 | import persistConfig from './persistConfig';
21 |
22 | // const storage = new MMKV();
23 | const sagaMiddleware = createSagaMiddleware();
24 | const middleware = [sagaMiddleware];
25 | const logger = createLogger();
26 | if (__DEV__) {
27 | middleware.push(logger);
28 | }
29 | // export const reduxStorage = {
30 | // setItem: (key, value) => {
31 | // storage.set(key, value);
32 | // return Promise.resolve(true);
33 | // },
34 | // getItem: key => {
35 | // const value = storage.getString(key);
36 | // return Promise.resolve(value);
37 | // },
38 | // removeItem: key => {
39 | // storage.delete(key);
40 | // return Promise.resolve();
41 | // },
42 | // };
43 |
44 | // const persistConfig = {
45 | // key: 'root',
46 | // storage: reduxStorage,
47 | // whitelist: ['theme', 'auth'],
48 | // };
49 | //
50 |
51 | const storePersistConfig = persistConfig({
52 | key: 'root',
53 | migrate: createMigrate(
54 | {
55 | 0: state => ({ ...state }),
56 | },
57 | { debug: false },
58 | ),
59 | });
60 |
61 | const persistedReducer = persistReducer(storePersistConfig, rootReducer);
62 |
63 | const store = configureStore({
64 | reducer: persistedReducer,
65 | middleware: getDefaultMiddleware => {
66 | const middlewares = getDefaultMiddleware({
67 | serializableCheck: {
68 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
69 | },
70 | }).concat(middleware);
71 | return middlewares;
72 | },
73 | devTools: __DEV__,
74 | });
75 | const persistor = persistStore(store);
76 | setupListeners(store.dispatch);
77 | export { store, persistor };
78 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/myapp/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.myapp;
2 |
3 | import android.app.Application;
4 | import com.facebook.react.PackageList;
5 | import com.facebook.react.ReactApplication;
6 | import com.facebook.react.ReactNativeHost;
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
9 | import com.facebook.react.defaults.DefaultReactNativeHost;
10 | import com.facebook.soloader.SoLoader;
11 | import java.util.List;
12 |
13 | public class MainApplication extends Application implements ReactApplication {
14 |
15 | private final ReactNativeHost mReactNativeHost =
16 | new DefaultReactNativeHost(this) {
17 | @Override
18 | public boolean getUseDeveloperSupport() {
19 | return BuildConfig.DEBUG;
20 | }
21 |
22 | @Override
23 | protected List getPackages() {
24 | @SuppressWarnings("UnnecessaryLocalVariable")
25 | List packages = new PackageList(this).getPackages();
26 | // Packages that cannot be autolinked yet can be added manually here, for example:
27 | // packages.add(new MyReactNativePackage());
28 | return packages;
29 | }
30 |
31 | @Override
32 | protected String getJSMainModuleName() {
33 | return "index";
34 | }
35 |
36 | @Override
37 | protected boolean isNewArchEnabled() {
38 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
39 | }
40 |
41 | @Override
42 | protected Boolean isHermesEnabled() {
43 | return BuildConfig.IS_HERMES_ENABLED;
44 | }
45 | };
46 |
47 | @Override
48 | public ReactNativeHost getReactNativeHost() {
49 | return mReactNativeHost;
50 | }
51 |
52 | @Override
53 | public void onCreate() {
54 | super.onCreate();
55 | SoLoader.init(this, /* native exopackage */ false);
56 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
57 | // If you opted-in for the New Architecture, we load the native entry point for this app.
58 | DefaultNewArchitectureEntryPoint.load();
59 | }
60 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/ios/MyAppTests/MyAppTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface MyAppTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation MyAppTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/src/components/Toast.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useMemo } from 'react';
2 | import { Text, View, StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 | import SuccessIcon from 'src/assets/images/toast/success.png';
5 | import WrongIcon from 'src/assets/images/toast/wrong.png';
6 | import FastImage from 'react-native-fast-image';
7 |
8 | const CustomToast = ({ toast }) => {
9 | const { message, type } = toast;
10 |
11 | const borderStyle = useMemo(() => {
12 | if (type === 'success') {
13 | return {
14 | borderColor: '#C8E6C9',
15 | borderWidth: 1,
16 | };
17 | }
18 | if (type === 'error') {
19 | return {
20 | borderColor: AppTheme.colors.primary_1,
21 | borderWidth: 1,
22 | };
23 | }
24 | return {};
25 | }, [type]);
26 |
27 | const MesgColor = useMemo(() => {
28 | if (type === 'success') {
29 | return {
30 | color: '#4cae51',
31 | };
32 | }
33 | if (type === 'error') {
34 | return {
35 | color: AppTheme.colors.primary_1,
36 | };
37 | }
38 | return {};
39 | }, [type]);
40 |
41 | const ToastIcon = useCallback(() => {
42 | if (type === 'success') {
43 | return (
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | if (type === 'error') {
51 | return (
52 |
53 |
54 |
55 | );
56 | }
57 | return null;
58 | }, [type]);
59 |
60 | return (
61 |
62 |
63 | {message}
64 |
65 | );
66 | };
67 |
68 | export default CustomToast;
69 |
70 | const styles = StyleSheet.create({
71 | toastContainer: {
72 | backgroundColor: 'white',
73 | padding: 15,
74 | borderRadius: 10,
75 | marginTop: 10,
76 | display: 'flex',
77 | flexDirection: 'row',
78 | justifyContent: 'center',
79 | alignItems: 'center',
80 | gap: 10,
81 | },
82 | toastMesg: {
83 | color: 'white',
84 | },
85 | toastIcon: {
86 | width: 20,
87 | height: 20,
88 | },
89 | });
90 |
--------------------------------------------------------------------------------
/src/screens/AppSetting/AppSetting.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Switch, View } from 'react-native';
3 | import HeaderWrap from 'src/components/HeaderWrap';
4 | import TextView from 'src/components/TextView';
5 | import styles from './AppSetting.styles';
6 | import TouchableDebounce from 'src/components/TouchableDebounce';
7 | import RouteName from 'src/navigators/RouteName';
8 | import { AppTheme } from 'src/utils/appConstant';
9 | import { navigate } from 'src/navigators/NavigationServices';
10 |
11 | const APP_SETTING_CONFIG = [
12 | {
13 | label: 'Do Not Disturb',
14 | },
15 | {
16 | label: 'Reminder',
17 | },
18 | {
19 | label: 'Reminder Ringtone',
20 | screen: RouteName.ReminderTone,
21 | },
22 | ];
23 |
24 | const AppSetting = () => {
25 | return (
26 |
27 |
28 | {APP_SETTING_CONFIG.map((item, index) => (
29 |
30 | ))}
31 |
32 | );
33 | };
34 |
35 | export default AppSetting;
36 |
37 | const AppSettingItem = ({ item }) => {
38 | const [toggleValue, setToggleValue] = useState(false);
39 |
40 | const handleToggle = () => {
41 | setToggleValue(!toggleValue);
42 | };
43 |
44 | const handleRedirectToRingTone = () => {
45 | console.log('hello');
46 | navigate(RouteName.ReminderRingTone);
47 | };
48 |
49 | return (
50 |
51 | {item.label}
52 | {item.label !== 'Reminder Ringtone' ? (
53 |
61 | ) : (
62 |
66 |
67 | Checking
68 |
69 |
70 | )}
71 |
72 | );
73 | };
74 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
13 | #
14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
15 | # ```js
16 | # module.exports = {
17 | # dependencies: {
18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
19 | # ```
20 | # flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
21 |
22 | linkage = ENV['USE_FRAMEWORKS']
23 | if linkage != nil
24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
25 | use_frameworks! :linkage => linkage.to_sym
26 | end
27 |
28 | target 'MyApp' do
29 | config = use_native_modules!
30 |
31 | # Flags change depending on the env values.
32 | flags = get_default_flags()
33 |
34 | use_react_native!(
35 | :path => config[:reactNativePath],
36 | # Hermes is now enabled by default. Disable by setting this flag to false.
37 | :hermes_enabled => false,
38 | :fabric_enabled => flags[:fabric_enabled],
39 | # Enables Flipper.
40 | #
41 | # Note that if you have use_frameworks! enabled, Flipper will not work and
42 | # you should disable the next line.
43 | # :flipper_configuration => flipper_config,
44 | # An absolute path to your application root.
45 | :app_path => "#{Pod::Config.instance.installation_root}/.."
46 | )
47 |
48 | # target 'MyAppTests' do
49 | # inherit! :complete
50 | # # Pods for testing
51 | # end
52 |
53 | post_install do |installer|
54 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
55 | react_native_post_install(
56 | installer,
57 | config[:reactNativePath],
58 | :mac_catalyst_enabled => false
59 | )
60 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/src/components/SliderComp.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import Slider from '@react-native-community/slider';
3 | import { StyleSheet, View } from 'react-native';
4 | import TextView from './TextView';
5 | import { AppTheme } from 'src/utils/appConstant';
6 | import { useController, useForm } from 'react-hook-form';
7 |
8 | const SliderComp = ({
9 | title,
10 | minimumValue,
11 | maximumValue,
12 | value,
13 | control,
14 | errorMessage = '',
15 | fieldName = 'fieldName',
16 | disable = false,
17 | defaultValue = 0,
18 | rules,
19 | step = 1,
20 | onChange = () => {},
21 | }) => {
22 | const { control: localControl } = useForm();
23 | const { field } = useController({
24 | control: control || localControl,
25 | defaultValue: `${defaultValue || ''}`,
26 | name: fieldName,
27 | rules: rules ?? {},
28 | });
29 | const onChangeValue = value => {
30 | field?.onChange(value);
31 | onChange(value);
32 | };
33 |
34 | return (
35 |
36 |
37 | {title}
38 |
39 | {value}
40 |
41 |
42 |
54 | {errorMessage && (
55 |
56 | {errorMessage}
57 |
58 | )}
59 |
60 | );
61 | };
62 |
63 | export default SliderComp;
64 |
65 | const styles = StyleSheet.create({
66 | container: {
67 | flex: 1,
68 | justifyContent: 'flex-start',
69 | width: '100%',
70 | height: 50,
71 | },
72 | title: {
73 | display: 'flex',
74 | flexDirection: 'row',
75 | justifyContent: 'space-between',
76 | alignContent: 'center',
77 | height: 25,
78 | fontWeight: 600,
79 | fontSize: AppTheme.fontSize.s14,
80 | color: AppTheme.colors.neutral_80,
81 | },
82 | });
83 |
--------------------------------------------------------------------------------
/src/screens/Profile/ScreenItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Switch } from 'react-native';
3 | import FastImage from 'react-native-fast-image';
4 | import TextView from 'src/components/TextView';
5 | import TouchableDebounce from 'src/components/TouchableDebounce';
6 | import { navigate } from 'src/navigators/NavigationServices';
7 | import { AppTheme } from 'src/utils/appConstant';
8 | import { useDispatch, useSelector } from 'react-redux';
9 | import { setTheme } from 'src/store/app/appReducer';
10 |
11 | const ScreenItem = ({ item, onLogout }) => {
12 | const [isDarkTheme, setIsDarkTheme] = React.useState(false);
13 | const { theme } = useSelector(state => state.appReducer);
14 | const dispatch = useDispatch();
15 | const onPress = () => {
16 | if (item?.label === 'Logout') {
17 | onLogout();
18 | return;
19 | }
20 | navigate(item.screen);
21 | };
22 |
23 | const onToggleSwitch = () => {
24 | dispatch(setTheme(theme === 'light' ? 'dark' : 'light'));
25 | setIsDarkTheme(!isDarkTheme);
26 | };
27 |
28 | console.log('helloo checking --->', theme);
29 |
30 | return (
31 |
32 |
33 |
34 |
43 | {item.label}
44 |
45 | {item.label === 'Dark Theme' && (
46 |
54 | )}
55 |
56 |
57 | );
58 | };
59 |
60 | export default ScreenItem;
61 | const styles = StyleSheet.create({
62 | itemWrap: {
63 | width: '100%',
64 | display: 'flex',
65 | flexDirection: 'row',
66 | justifyContent: 'flex-start',
67 | alignItems: 'center',
68 | gap: 25,
69 | padding: 10,
70 | marginVertical: 5,
71 | position: 'relative',
72 | },
73 | switchWrap: {
74 | position: 'absolute',
75 | right: 0,
76 | },
77 | iconStyle: {
78 | width: 25,
79 | height: 25,
80 | },
81 | textStyle: {
82 | fontSize: 18,
83 | fontWeight: 'semibold',
84 | },
85 | });
86 |
--------------------------------------------------------------------------------
/src/screens/AllCompletedTask/ModalDeleteTask.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import Modal from 'react-native-modal';
4 | import TextView from 'src/components/TextView';
5 | import Button from 'src/components/Button';
6 | import { View } from 'react-native-reanimated/mock';
7 | import { AppTheme } from 'src/utils/appConstant';
8 | import TaskComp from 'src/components/Task';
9 |
10 | const ModalDeleteTask = ({ open, handleClose, onDelete, item }) => {
11 | return (
12 |
17 |
18 |
19 | Delete Task
20 |
21 |
22 |
23 |
24 |
25 |
31 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default ModalDeleteTask;
43 | const styles = StyleSheet.create({
44 | view: {
45 | justifyContent: 'flex-end',
46 | margin: 0,
47 | position: 'relative',
48 | },
49 | modalContentWrap: {
50 | width: '100%',
51 | height: '30%',
52 | backgroundColor: '#fefefe',
53 | // backgroundColor: '#ccc',
54 | borderTopLeftRadius: 30,
55 | borderTopRightRadius: 30,
56 | },
57 | modalContent: {
58 | height: '65%',
59 | display: 'flex',
60 | alignItems: 'center',
61 | },
62 | modalTitle: {
63 | color: AppTheme.colors.primary_1,
64 | fontWeight: 700,
65 | fontSize: AppTheme.fontSize.s20,
66 | marginTop: 30,
67 | },
68 | buttonWrap: {
69 | display: 'flex',
70 | flexDirection: 'row',
71 | justifyContent: 'center',
72 | gap: 20,
73 | },
74 | buttonCancel: {
75 | backgroundColor: '#feecee',
76 | paddingHorizontal: 20,
77 | height: 60,
78 | width: '40%',
79 | display: 'flex',
80 | justifyContent: 'center',
81 | alignItems: 'center',
82 | borderRadius: 50,
83 | },
84 | buttonDelete: {
85 | backgroundColor: AppTheme.colors.primary_1,
86 | paddingHorizontal: 20,
87 | height: 60,
88 | width: '40%',
89 | display: 'flex',
90 | justifyContent: 'center',
91 | alignItems: 'center',
92 | borderRadius: 50,
93 | },
94 | taskInfoWrap: {
95 | width: '90%',
96 | },
97 | });
98 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.6)
5 | rexml
6 | activesupport (6.1.7.6)
7 | concurrent-ruby (~> 1.0, >= 1.0.2)
8 | i18n (>= 1.6, < 2)
9 | minitest (>= 5.1)
10 | tzinfo (~> 2.0)
11 | zeitwerk (~> 2.3)
12 | addressable (2.8.5)
13 | public_suffix (>= 2.0.2, < 6.0)
14 | algoliasearch (1.27.5)
15 | httpclient (~> 2.8, >= 2.8.3)
16 | json (>= 1.5.1)
17 | atomos (0.1.3)
18 | claide (1.1.0)
19 | cocoapods (1.12.1)
20 | addressable (~> 2.8)
21 | claide (>= 1.0.2, < 2.0)
22 | cocoapods-core (= 1.12.1)
23 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
24 | cocoapods-downloader (>= 1.6.0, < 2.0)
25 | cocoapods-plugins (>= 1.0.0, < 2.0)
26 | cocoapods-search (>= 1.0.0, < 2.0)
27 | cocoapods-trunk (>= 1.6.0, < 2.0)
28 | cocoapods-try (>= 1.1.0, < 2.0)
29 | colored2 (~> 3.1)
30 | escape (~> 0.0.4)
31 | fourflusher (>= 2.3.0, < 3.0)
32 | gh_inspector (~> 1.0)
33 | molinillo (~> 0.8.0)
34 | nap (~> 1.0)
35 | ruby-macho (>= 2.3.0, < 3.0)
36 | xcodeproj (>= 1.21.0, < 2.0)
37 | cocoapods-core (1.12.1)
38 | activesupport (>= 5.0, < 8)
39 | addressable (~> 2.8)
40 | algoliasearch (~> 1.0)
41 | concurrent-ruby (~> 1.1)
42 | fuzzy_match (~> 2.0.4)
43 | nap (~> 1.0)
44 | netrc (~> 0.11)
45 | public_suffix (~> 4.0)
46 | typhoeus (~> 1.0)
47 | cocoapods-deintegrate (1.0.5)
48 | cocoapods-downloader (1.6.3)
49 | cocoapods-plugins (1.0.0)
50 | nap
51 | cocoapods-search (1.0.1)
52 | cocoapods-trunk (1.6.0)
53 | nap (>= 0.8, < 2.0)
54 | netrc (~> 0.11)
55 | cocoapods-try (1.2.0)
56 | colored2 (3.1.2)
57 | concurrent-ruby (1.2.2)
58 | escape (0.0.4)
59 | ethon (0.16.0)
60 | ffi (>= 1.15.0)
61 | ffi (1.15.5)
62 | fourflusher (2.3.1)
63 | fuzzy_match (2.0.4)
64 | gh_inspector (1.1.3)
65 | httpclient (2.8.3)
66 | i18n (1.14.1)
67 | concurrent-ruby (~> 1.0)
68 | json (2.6.3)
69 | minitest (5.19.0)
70 | molinillo (0.8.0)
71 | nanaimo (0.3.0)
72 | nap (1.1.0)
73 | netrc (0.11.0)
74 | public_suffix (4.0.7)
75 | rexml (3.2.6)
76 | ruby-macho (2.5.1)
77 | typhoeus (1.4.0)
78 | ethon (>= 0.9.0)
79 | tzinfo (2.0.6)
80 | concurrent-ruby (~> 1.0)
81 | xcodeproj (1.22.0)
82 | CFPropertyList (>= 2.3.3, < 4.0)
83 | atomos (~> 0.1.3)
84 | claide (>= 1.0.2, < 2.0)
85 | colored2 (~> 3.1)
86 | nanaimo (~> 0.3.0)
87 | rexml (~> 3.2.4)
88 | zeitwerk (2.6.12)
89 |
90 | PLATFORMS
91 | ruby
92 |
93 | DEPENDENCIES
94 | cocoapods (~> 1.12)
95 |
96 | RUBY VERSION
97 | ruby 2.7.6p219
98 |
99 | BUNDLED WITH
100 | 2.4.22
101 |
--------------------------------------------------------------------------------
/src/screens/Category/Category.styles.js:
--------------------------------------------------------------------------------
1 | import { includes } from 'lodash';
2 | import { StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | flex: 1,
8 | paddingTop: 60,
9 | paddingHorizontal: 15,
10 | backgroundColor: '#fcfcfc',
11 | },
12 | headerIconRight: {
13 | width: 30,
14 | height: 30,
15 | },
16 | categoryItemWrapper: {
17 | flexDirection: 'row',
18 | gap: 10,
19 | alignItems: 'center',
20 | paddingBottom: 15,
21 | paddingHorizontal: 15,
22 | borderBottomColor: '#ccc',
23 | borderBottomWidth: 1,
24 | borderRadius: 5,
25 | marginVertical: 5,
26 | },
27 | categoryTitle: {
28 | fontSize: AppTheme.fontSize.s16,
29 | },
30 | categoryColor: {
31 | width: 20,
32 | height: 20,
33 | borderRadius: 5,
34 | },
35 | modalAddCategory: {
36 | display: 'flex',
37 | alignItems: 'center',
38 | justifyContent: 'center',
39 | backgroundColor: '#fff',
40 | borderRadius: 5,
41 | padding: 15,
42 | },
43 | modalInputWrapper: {
44 | width: '100%',
45 | display: 'flex',
46 | flexDirection: 'row',
47 | justifyContent: 'space-between',
48 | },
49 | inputStyle: {
50 | width: '100%',
51 | height: 40,
52 | borderWidth: 1,
53 | borderColor: '#ccc',
54 | borderRadius: 5,
55 | paddingHorizontal: 10,
56 | marginVertical: 20,
57 | },
58 | modalColorPickWrapper: {
59 | display: 'flex',
60 | justifyContent: 'center',
61 | flexDirection: 'row',
62 | flexWrap: 'wrap',
63 | gap: 10,
64 | marginBottom: 10,
65 | marginTop: 10,
66 | },
67 | colorPickerItem: {
68 | width: 27,
69 | height: 27,
70 | },
71 | modalButtonWrapper: {
72 | width: '100%',
73 | display: 'flex',
74 | flexDirection: 'row',
75 | justifyContent: 'space-between',
76 | alignItems: 'center',
77 | },
78 | buttonSave: {
79 | width: '45%',
80 | backgroundColor: AppTheme.colors.primary_1,
81 | padding: 10,
82 | borderRadius: 5,
83 | alignItems: 'center',
84 | marginTop: 10,
85 | },
86 | buttonCancel: {
87 | width: '45%',
88 | backgroundColor: AppTheme.colors.neutral_50,
89 | padding: 10,
90 | borderRadius: 5,
91 | alignItems: 'center',
92 | marginTop: 10,
93 | },
94 | modalCategoryStyle: {
95 | width: '50%',
96 | height: '50%',
97 | },
98 | modalCategoryTitle: {
99 | display: 'flex',
100 | textAlign: 'center',
101 | alignItems: 'center',
102 | justifyContent: 'center',
103 | position: 'relative',
104 | width: '100%',
105 | },
106 | trashIcon: {
107 | width: 25,
108 | height: 25,
109 | position: 'absolute',
110 | right: 0,
111 | },
112 | });
113 |
114 | export default styles;
115 |
--------------------------------------------------------------------------------
/src/screens/Profile/ModalLogout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet } from 'react-native';
3 | import Modal from 'react-native-modal';
4 | import TextView from 'src/components/TextView';
5 | import Button from 'src/components/Button';
6 | import { View } from 'react-native-reanimated/mock';
7 | import { AppTheme } from 'src/utils/appConstant';
8 | import TaskComp from 'src/components/Task';
9 |
10 | const ModalLogout = ({ open, handleClose }) => {
11 | return (
12 |
17 |
18 |
19 | Logout
20 |
21 | Are you sure you want to log out ?
22 |
23 |
24 |
25 |
31 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default ModalLogout;
43 | const styles = StyleSheet.create({
44 | view: {
45 | justifyContent: 'flex-end',
46 | margin: 0,
47 | position: 'relative',
48 | },
49 | modalContentWrap: {
50 | width: '100%',
51 | height: '30%',
52 | backgroundColor: '#fefefe',
53 | borderTopLeftRadius: 30,
54 | borderTopRightRadius: 30,
55 | },
56 | modalContent: {
57 | height: '65%',
58 | display: 'flex',
59 | alignItems: 'center',
60 | },
61 | modalTitle: {
62 | color: AppTheme.colors.primary_1,
63 | fontWeight: 700,
64 | fontSize: AppTheme.fontSize.s20,
65 | marginTop: 30,
66 | },
67 | buttonWrap: {
68 | display: 'flex',
69 | flexDirection: 'row',
70 | justifyContent: 'center',
71 | gap: 20,
72 | },
73 | buttonCancel: {
74 | backgroundColor: '#feecee',
75 | paddingHorizontal: 20,
76 | height: 60,
77 | width: '40%',
78 | display: 'flex',
79 | justifyContent: 'center',
80 | alignItems: 'center',
81 | borderRadius: 50,
82 | },
83 | buttonDelete: {
84 | backgroundColor: AppTheme.colors.primary_1,
85 | paddingHorizontal: 20,
86 | height: 60,
87 | width: '40%',
88 | display: 'flex',
89 | justifyContent: 'center',
90 | alignItems: 'center',
91 | borderRadius: 50,
92 | },
93 | taskInfoWrap: {
94 | textAlign: 'center',
95 | marginTop: 20,
96 | },
97 | textCancel: {
98 | fontSize: 20,
99 | fontWeight: 600,
100 | }
101 | });
102 |
--------------------------------------------------------------------------------
/src/screens/SignUp/SignUp.styles.js:
--------------------------------------------------------------------------------
1 | import { StyleSheet, Dimensions } from 'react-native';
2 | import { AppTheme, Dimens, scaleSize } from '../../utils/appConstant';
3 | const { width, height } = Dimensions.get('window');
4 |
5 | export default StyleSheet.create({
6 | headerWrapper: {
7 | position: 'absolute',
8 | top: 0,
9 | },
10 | container: {
11 | flex: 1,
12 | alignItems: 'center',
13 | justifyContent: 'center',
14 | width: '100%',
15 | position: 'relative',
16 | backgroundColor: AppTheme.colors.white,
17 | },
18 | containerScrollView: {
19 | width: '90%',
20 | height: '100%',
21 | position: 'relative',
22 | alignItems: 'center',
23 | justifyContent: 'center',
24 | },
25 | textHeader: {
26 | fontSize: AppTheme.fontSize.s40,
27 | fontWeight: 700,
28 | color: AppTheme.colors.black,
29 | marginBottom: 100,
30 | },
31 | loginSocial: {
32 | display: 'flex',
33 | flexDirection: 'row',
34 | width: '100%',
35 | alignItems: 'center',
36 | justifyContent: 'center',
37 | gap: 10,
38 | paddingVertical: 10,
39 | borderWidth: 1,
40 | borderColor: AppTheme.colors.neutral_20,
41 | borderRadius: 10,
42 | marginBottom: 15,
43 | },
44 | dividerLine: {
45 | backgroundColor: AppTheme.colors.neutral_20,
46 | height: 1,
47 | flex: 1,
48 | },
49 | buttonTextStyle: {
50 | color: 'white',
51 | },
52 | buttonNext: {
53 | marginTop: 50,
54 | width: '100%',
55 | height: 50,
56 | backgroundColor: '#ff585d',
57 | borderRadius: 50,
58 | display: 'flex',
59 | alignItems: 'center',
60 | justifyContent: 'center',
61 | },
62 |
63 | bottomText: {
64 | position: 'absolute',
65 | bottom: 0,
66 | color: AppTheme.colors.neutral_40,
67 | },
68 | signInText: {
69 | color: '#ff585d',
70 | fontWeight: 700,
71 | },
72 |
73 | // login with pass
74 | textHeaderPass: {
75 | fontSize: AppTheme.fontSize.s40,
76 | fontWeight: 700,
77 | color: AppTheme.colors.black,
78 | marginBottom: 50,
79 | },
80 | keywordScrollView: {
81 | width,
82 | backgroundColor: 'transparent',
83 | flexGrow: 1,
84 | justifyContent: 'center',
85 | alignItems: 'center',
86 | },
87 |
88 | loginInput: {
89 | marginBottom: 20,
90 | // backgroundColor: AppTheme.colors.neutral_10,
91 | borderRadius: 10,
92 | },
93 |
94 | rememberCheckboxWrapper: {
95 | display: 'flex',
96 | justifyContent: 'center',
97 | alignItems: 'center',
98 | width: '100%',
99 | marginBottom: 10,
100 | },
101 |
102 | signUnBySocialWrapper: {
103 | display: 'flex',
104 | flexDirection: 'row',
105 | width: '100%',
106 | justifyContent: 'center',
107 | gap: 20,
108 | marginTop: 20,
109 | },
110 | signUpBySocialItem: {
111 | borderColor: AppTheme.colors.neutral_30,
112 | borderWidth: 1,
113 | borderRadius: 10,
114 | paddingHorizontal: 20,
115 | paddingVertical: 10,
116 | },
117 | });
118 |
--------------------------------------------------------------------------------
/src/components/CheckBox.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | forwardRef,
3 | memo,
4 | useCallback,
5 | useEffect,
6 | useImperativeHandle,
7 | useState,
8 | } from 'react';
9 | import { StyleSheet, View } from 'react-native';
10 | import Icon from './IconComponent';
11 | import { AppFont, AppTheme } from 'src/utils/appConstant';
12 | import TouchableDebounce from './TouchableDebounce';
13 | import TextView from './TextView';
14 | import { usePrevious } from 'src/utils/hookApi';
15 | import IconTick from 'src/assets/icons/login/ic_tick.svg';
16 |
17 | const CheckBox = (
18 | {
19 | defaultValue = false,
20 | title = '',
21 | containerStyle = {},
22 | onChange,
23 | onChangeDependencies = [],
24 | textStyle,
25 | checkBoxStyle,
26 | isCheckedStyle,
27 | },
28 | _ref,
29 | ) => {
30 | const prevDefaultValue = usePrevious(defaultValue);
31 | const [isChecked, setIsChecked] = useState(defaultValue);
32 | const handleSelected = useCallback(() => {
33 | const newVal = !isChecked;
34 | setIsChecked(newVal);
35 | onChange?.(newVal);
36 | }, [isChecked, ...onChangeDependencies]);
37 |
38 | useImperativeHandle(
39 | _ref,
40 | () => ({
41 | setIsChecked,
42 | isChecked,
43 | }),
44 | [isChecked],
45 | );
46 |
47 | useEffect(() => {
48 | if (prevDefaultValue !== defaultValue) {
49 | setIsChecked(defaultValue);
50 | }
51 | }, [prevDefaultValue, defaultValue]);
52 |
53 | return (
54 |
58 |
65 | {isChecked && (
66 | //
72 |
73 | )}
74 |
75 | {title}
76 |
77 | );
78 | };
79 | export default memo(forwardRef(CheckBox));
80 |
81 | const styles = StyleSheet.create({
82 | txt1: {
83 | marginLeft: AppTheme.gapSize.s8,
84 | color: AppTheme.colors.neutral_100,
85 | fontFamily: AppFont.Montserrat_Medium,
86 | fontSize: AppTheme.fontSize.s12,
87 | },
88 | vSelected: {
89 | backgroundColor: AppTheme.colors.secondary,
90 | borderColor: AppTheme.colors.neutral_70,
91 | },
92 | container: {
93 | flexDirection: 'row',
94 | alignItems: 'center',
95 | },
96 | viewCheckSquare: {
97 | width: AppTheme.gapSize.s18,
98 | height: AppTheme.gapSize.s18,
99 | borderRadius: 3,
100 | borderWidth: 2,
101 | borderColor: AppTheme.colors.neutral_70,
102 | justifyContent: 'center',
103 | alignItems: 'center',
104 | },
105 | });
106 |
--------------------------------------------------------------------------------
/src/components/ButtonIcon.js:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback, useMemo } from 'react';
2 | import { ActivityIndicator, StyleSheet } from 'react-native';
3 | import { AppTheme } from 'src/utils/appConstant';
4 | import TextView from './TextView';
5 | import TouchableDebounce from './TouchableDebounce';
6 | import FastImage from 'react-native-fast-image';
7 |
8 | const ButtonIcon = ({
9 | icon,
10 | iconSize,
11 | iconStyle,
12 | text = '',
13 | textStyle,
14 | iconColor,
15 | containerStyle,
16 | onPress,
17 | loading = false,
18 | loadingColor = AppTheme.colors.white,
19 | adjustsFontSizeToFit = false,
20 | numberOfLines = null,
21 | type = 'active', // inactive
22 | disabled = false,
23 | ...rest
24 | }) => {
25 | const renderIcon = useCallback(() => {
26 | if (!!icon && !loading) {
27 | return ;
28 | }
29 | if (loading) {
30 | return (
31 |
36 | );
37 | }
38 | return null;
39 | }, [icon, loading, loadingColor, iconStyle, iconSize, iconColor, icon]);
40 |
41 | const { backgroundColor, textColor } = useMemo(() => {
42 | switch (type) {
43 | case 'inactive':
44 | return {
45 | backgroundColor: AppTheme.colors.neutral_20,
46 | textColor: AppTheme.colors.neutral_40,
47 | };
48 | default:
49 | case 'active':
50 | return {
51 | backgroundColor: AppTheme.colors.primary_1,
52 | textColor: AppTheme.colors.white,
53 | };
54 | }
55 | }, [type]);
56 |
57 | const borderStyle = useMemo(() => {
58 | if (type != 'inactive') {
59 | return {};
60 | }
61 | return {
62 | borderWidth: 1,
63 | borderColor: AppTheme.colors.primary_1,
64 | };
65 | }, [type]);
66 |
67 | return (
68 |
80 | {renderIcon()}
81 |
86 | {text}
87 |
88 |
89 | );
90 | };
91 |
92 | export default memo(ButtonIcon);
93 |
94 | const styles = StyleSheet.create({
95 | disabledView: { opacity: 0.6 },
96 | ico1: { marginRight: AppTheme.gapSize.s8 },
97 | indicator: { marginRight: 8 },
98 | txt1: { fontWeight: '600' },
99 | containerStyle: {
100 | flexDirection: 'row',
101 | alignSelf: 'flex-start',
102 | alignItems: 'center',
103 | height: AppTheme.buttonHeight,
104 | paddingHorizontal: 16,
105 | borderRadius: AppTheme.gapSize.s12,
106 | justifyContent: 'center',
107 | },
108 | });
109 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------