Commit d049fb75 by tungnq

TODO: Base Code

parent f3010a1f
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow strict-local
*/
import React, {useEffect} from 'react';
import {View, Text} from 'react-native';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import rootReducer from './src/reducers/index';
import RootView from './src/RootView';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './src/saga/rootSaga';
// import FirebaseNotification from "./src/helper/FirebaseNotification";
import SplashScreen from 'react-native-splash-screen';
const sagaMiddleware = createSagaMiddleware();
let store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
const App = () => {
// useEffect(async () => {
// SplashScreen.hide();
// }, []);
return (
<Provider store={store}>
<RootView />
</Provider>
);
};
export default App;
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
import type {PropsWithChildren} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
type SectionProps = PropsWithChildren<{
title: string;
}>;
function Section({children, title}: SectionProps): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
}
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
module.exports = { module.exports = {
presets: ['module:@react-native/babel-preset'], presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'],
}; };
...@@ -6,6 +6,17 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); ...@@ -6,6 +6,17 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
* *
* @type {import('metro-config').MetroConfig} * @type {import('metro-config').MetroConfig}
*/ */
const config = {};
module.exports = mergeConfig(getDefaultConfig(__dirname), config); const defaultConfig = getDefaultConfig(__dirname);
const config = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: defaultConfig.resolver.assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
},
};
module.exports = mergeConfig(defaultConfig, config);
\ No newline at end of file
...@@ -11,7 +11,66 @@ ...@@ -11,7 +11,66 @@
}, },
"dependencies": { "dependencies": {
"react": "18.2.0", "react": "18.2.0",
"react-native": "0.74.2" "react-native": "0.74.2",
"@react-native-community/async-storage": "^1.12.1",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-community/netinfo": "^7.1.7",
"@react-native-community/picker": "^1.8.1",
"@react-native-community/slider": "^4.1.12",
"@react-navigation/bottom-tabs": "^6.0.9",
"@react-navigation/drawer": "^6.x",
"@react-navigation/material-top-tabs": "^6.0.6",
"@react-navigation/native": "^6.0.6",
"@react-navigation/stack": "^6.0.11",
"async": "^3.2.3",
"axios": "0.21.4",
"js-sha256": "^0.9.0",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"react-hook-form": "^7.24.2",
"react-native-autocomplete-input": "^5.0.2",
"react-native-calendars": "^1.1260.0",
"react-native-confirmation-code-field": "^7.1.0",
"react-native-datepicker": "^1.7.2",
"react-native-device-info": "^8.1.7",
"react-native-dropdown-picker": "^5.1.23",
"react-native-dropdownalert": "^4.3.0",
"react-native-elements": "^3.4.1",
"react-native-gesture-handler": "^1.10.3",
"react-native-gifted-charts": "^1.2.41",
"react-native-i18n": "^2.0.15",
"react-native-image-crop-picker": "^0.36.2",
"react-native-indicators": "^0.17.0",
"react-native-linear-gradient": "^2.6.2",
"react-native-material-textfield": "^0.16.1",
"react-native-modal": "^12.0.2",
"react-native-modal-dropdown": "^1.0.1",
"react-native-pager-view": "^5.4.0",
"react-native-permissions": "^3.6.1",
"react-native-progress": "^5.0.0",
"react-native-qrcode-scanner": "^1.5.5",
"react-native-reanimated": "3.12.1",
"react-native-reanimated-table": "^0.0.2",
"react-native-rename": "^2.9.0",
"react-native-render-html": "^6.1.0",
"react-native-responsive-fontsize": "^0.5.1",
"react-native-safe-area-context": "^3.2.0",
"react-native-screens": "^3.1.1",
"react-native-share": "^7.0.0",
"react-native-simple-radio-button": "^2.7.4",
"react-native-sound": "^0.11.1",
"react-native-splash-screen": "^3.2.0",
"react-native-svg": "12.1.1",
"react-native-svg-charts": "^5.4.0",
"react-native-swipe-list-view": "2.5.0",
"react-native-swiper": "^1.6.0",
"react-native-tab-view": "^3.0.1",
"react-native-touch-id": "^4.4.1",
"react-native-vector-icons": "^8.1.0",
"react-native-webview": "^13.12.2",
"react-redux": "^7.2.4",
"redux": "^4.1.0",
"redux-saga": "^1.1.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
...@@ -24,6 +83,7 @@ ...@@ -24,6 +83,7 @@
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-test-renderer": "^18.0.0", "@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3", "babel-jest": "^29.6.3",
"react-native-svg-transformer": "^1.5.1",
"eslint": "^8.19.0", "eslint": "^8.19.0",
"jest": "^29.6.3", "jest": "^29.6.3",
"prettier": "2.8.8", "prettier": "2.8.8",
......
import React, {useEffect, useRef, useState} from 'react';
import StackNavigation from './routers/StackNavigation';
import {connect} from 'react-redux';
import DropdownAlert from 'react-native-dropdownalert';
import DropdownManager from './components/DropdownAlert/DropdownManager';
import R from './assets/R';
import {HEIGHTXD, WIDTHXD} from './config/Functions';
import {SkypeIndicator} from 'react-native-indicators';
import Modal from 'react-native-modal';
import {saveUserToRedux} from './actions/users';
import I18n, {setLocation} from './helper/i18/i18n';
import KEY from './assets/AsynStorage';
import {changeLanguage} from './actions/language';
import AsyncStorage from '@react-native-community/async-storage';
import NoInternetComponent from './components/NoInternet';
const RootView = props => {
useEffect(() => {
setInitLanguage();
DropdownManager.register(
dropDownAlertRef.current,
dropDownAlertLongTimeRef.current,
);
}, []);
const dropDownAlertRef = useRef(null);
const dropDownAlertLongTimeRef = useRef(null);
const setInitLanguage = async () => {
// const laguage = await AsyncStorage.getItem(KEY.LANGUAGE);
const laguage = 'vi';
if (laguage) props.changeLanguage(laguage);
setLocation(I18n, laguage);
};
return (
<>
<StackNavigation isLoggedIn={props.isLoggedIn} />
<Modal isVisible={props.loadingModal.isVisible}>
<SkypeIndicator color={'white'} />
</Modal>
<DropdownAlert
inactiveStatusBarBackgroundColor={R.colors.main}
activeStatusBarBackgroundColor={R.colors.main}
warnImageSrc={R.images.iconWarn}
successImageSrc={R.images.iconSuccess}
errorImageSrc={R.images.iconError}
titleStyle={{color: '#fff'}}
messageStyle={{color: '#fff'}}
updateStatusBar={false}
closeInterval={1000}
ref={dropDownAlertRef}
warnColor={R.colors.orange400}
defaultContainer={{
borderBottomRightRadius: WIDTHXD(30),
borderBottomLeftRadius: WIDTHXD(30),
paddingTop: HEIGHTXD(30),
paddingVertical: HEIGHTXD(30),
paddingHorizontal: WIDTHXD(20),
}}
/>
<DropdownAlert
updateStatusBar={false}
inactiveStatusBarBackgroundColor={R.colors.colorMain}
activeStatusBarBackgroundColor={R.colors.colorMain}
warnImageSrc={R.images.iconWarn}
successImageSrc={R.images.iconSuccess}
errorImageSrc={R.images.iconError}
titleStyle={{color: '#fff'}}
messageStyle={{color: '#fff'}}
closeInterval={600000}
ref={dropDownAlertLongTimeRef}
warnColor={R.colors.orange400}
defaultContainer={{
borderBottomRightRadius: WIDTHXD(30),
borderBottomLeftRadius: WIDTHXD(30),
paddingTop: HEIGHTXD(30),
paddingVertical: HEIGHTXD(30),
paddingHorizontal: WIDTHXD(20),
}}
/>
<NoInternetComponent />
</>
);
};
const mapStateToProps = state => {
return {
loadingModal: state.ModalLoadingReducer,
};
};
export default connect(mapStateToProps, {
saveUserToRedux,
changeLanguage,
})(RootView);
import {
UPDATE_NOTIFICATION
} from './actionTypes';
export const updateNotification = () => {
return {
type: UPDATE_NOTIFICATION,
};
};
import {
UPDATE_REPORT
} from './actionTypes';
export const updateReport = () => {
return {
type: UPDATE_REPORT,
};
};
import {NEW_SCREEN} from './actionTypes';
export const newScreenInit = (body) => {
return {
type: NEW_SCREEN,
body,
};
};
import {PUSHNOTI, HIDENOTI} from './actionTypes';
export const showNotificaton = (data) => {
return {
type: PUSHNOTI,
data,
};
};
export const hideNotification = () => {
return {
type: HIDENOTI,
};
};
import { UPDATE_SYSTEM_CONFIG } from "./actionTypes";
export function updateSystemConfig(data) {
return {
type: UPDATE_SYSTEM_CONFIG,
data,
};
}
export const UPDATE_USER_INFO = "UPDATE_USER_INFO";
export const UPDATE_USER_SUCCESS = "UPDATE_USER_SUCCESS";
export const UPDATE_USER_DATA = "UPDATE_USER_DATA";
export const LOGIN = "LOGIN";
export const UPDATE_INFOR = "UPDATE_INFOR";
export const SHOWLOADING = "SHOWLOADING";
export const HIDELOADING = "HIDELOADING";
export const PUSHNOTI = "PUSHNOTI";
export const HIDENOTI = "HIDENOTI";
export const UPDATE_NOTIFICATION = "UPDATE_NOTIFICATION";
export const UPDATE_NOTIFICATION_SUCCESSED = "UPDATE_NOTIFICATION_SUCCESSED";
export const UPDATE_NOTIFICATION_FAIL = "UPDATE_NOTIeFICATION_FAIL";
export const UPDATE_REPORT_SUCCESSED = "UPDATE_REPORT_SUCCESSED";
export const UPDATE_REPORT = "UPDATE_REPORT";
export const SAVENAVIGATE = "SAVENAVIGATE";
export const NEW_SCREEN = "NEW_SCREEN";
export const CLEAR_SCREEN = "CLEAR_SCREEN";
export const CHANGE_LANGUAGE = "CHANGE_LANGUAGE";
export const WALLET_INFO = "WALLET_INFO";
export const UPDATE_WALLET = "UPDATE_WALLET";
export const UPDATE_WALLET_SUCCESS = "UPDATE_WALLET_SUCCESS";
export const UPDATE_SYSTEM_CONFIG = "UPDATE_SYSTEM_CONFIG";
export const ADD_PRODUCT = "ADD_PRODUCT";
export const REMOVE_PRODUCT = "REMOVE_PRODUCT";
export const CLEAR_CART = "CLEAR_CART";
import { REMOVE_PRODUCT, ADD_PRODUCT, CLEAR_CART } from "./actionTypes";
export const addProduct = (item) => {
return {
type: ADD_PRODUCT,
data: item,
};
};
export const removeProduct = (id) => {
return {
type: REMOVE_PRODUCT,
data: id,
};
};
export const clearCart = () => {
return {
type: CLEAR_CART,
};
};
import { CHANGE_LANGUAGE } from "./actionTypes";
export const changeLanguage = (language) => {
return {
type: CHANGE_LANGUAGE,
language,
};
};
import {
SHOWLOADING,
HIDELOADING
} from './actionTypes';
export const showLoading = () => {
return {
type: SHOWLOADING,
};
};
export const hideLoading = () => {
return {
type: HIDELOADING
};
};
import { LOGIN, WALLET_INFO, UPDATE_INFOR } from "./actionTypes";
export function saveUserToRedux(data) {
return {
type: LOGIN,
data,
};
}
export function saveWalletInfo(data) {
return {
type: WALLET_INFO,
data,
};
}
export function updateUser() {
return {
type: UPDATE_INFOR,
};
}
import { UPDATE_WALLET } from "./actionTypes";
export function updateWalletInfo() {
return {
type: UPDATE_WALLET,
};
}
import {PostLogin, PostData, GetData, PostFormData} from '../helpers';
import url from '../url';
export const listProvince = async params =>
GetData(url.listProvince, params)
.then(res => res)
.catch(err => err);
export const dropdownBank = async params =>
GetData(url.dropdownBank, params)
.then(res => res)
.catch(err => err);
export const dropdownBankCredit = async params =>
GetData(url.dropdownBankCredit, params)
.then(res => res)
.catch(err => err);
export const dropdownCreditType = async id =>
GetData(`${url.dropdownCreditType}/${id}`, {})
.then(res => res)
.catch(err => err);
import {
PostLogin,
PostData,
GetData,
PostFormData,
PostHaveKey,
} from '../helpers';
import url from '../url';
export const getInforGeneral = async params =>
GetData(url.getInforGeneral, params)
.then(res => res)
.catch(err => err);
export const getListInvestPackage = async params =>
GetData(url.getListInvestPackage, params)
.then(res => res)
.catch(err => err);
export const getConfigPoint = async params =>
GetData(url.getConfigPoint, params)
.then(res => res)
.catch(err => err);
export const changePoint = async params =>
PostData(url.changePoint, params)
.then(res => res)
.catch(err => err);
export const getListNoti = async params =>
PostData(url.getListNoti, params)
.then(res => res)
.catch(err => err);
export const detailNoti = async id =>
GetData(`${url.detailNoti}/${id}`, {})
.then(res => res)
.catch(err => err);
export const decryptData = async (body, key) =>
PostHaveKey(url.urlDecryptData, body, key)
.then(res => res)
.catch(err => null);
export const getKey = async body =>
GetData(url.getKey, body)
.then(res => res)
.catch(err => null);
export const listRanking = async params =>
GetData(url.listRanking, params)
.then(res => res)
.catch(err => err);
export const detailRanking = async id =>
GetData(`${url.detailRanking}/${id}`, {})
.then(res => res)
.catch(err => err);
export const getNewestVersionInfo = async id =>
GetData(`${url.getNewestVersionInfo}/${id}`, {})
.then(res => res)
.catch(err => err);
export const listBannerAPI = async params =>
PostData(url.listBanner, params)
.then(res => res)
.catch(err => err);
export const getInforSetting = async params =>
GetData(url.inforSetting, params)
.then(res => res)
.catch(err => err);
export const changeSetting = async params =>
PostData(url.changeSetting, params)
.then(res => res)
.catch(err => err);
export const systemConfig = async params =>
GetData(url.systemConfig, params)
.then(res => res)
.catch(err => err);
import {PostLogin, PostData, GetData, PostFormData} from '../helpers';
import url from '../url';
export const checkPhoneNumber = async params =>
PostLogin(url.checkPhoneNumber, params)
.then(res => res)
.catch(err => err);
export const verifyOTP = async params =>
PostLogin(url.verifyOTP, params)
.then(res => res)
.catch(err => err);
export const signUp = async body =>
PostLogin(url.signUp, body)
.then(res => res)
.catch(err => err);
export const login = async body =>
PostData(url.login, body)
.then(res => res)
.catch(err => err);
export const requestSendOTP = async body =>
PostData(url.urlrequestSendOTP, body)
.then(res => res)
.catch(err => err);
export const confirmOTP = async body =>
PostData(url.urlConfirmOTP, body)
.then(res => res)
.catch(err => err);
export const uploadFontIdentify = async body =>
PostFormData(url.urlFontIdentify, body)
.then(res => res)
.catch(err => err);
export const uploadBackIdentify = async body =>
PostFormData(url.urlBackIdentify, body)
.then(res => res)
.catch(err => err);
export const verifyCustomer = async body =>
PostData(url.verifyCustomer, body)
.then(res => res)
.catch(err => err);
export const logoutAPI = async body =>
PostData(url.logout, body)
.then(res => res)
.catch(err => err);
export const changePass = async body =>
PostData(url.changePass, body)
.then(res => res)
.catch(err => err);
export const myTeam = async body =>
PostData(url.myTeam, body)
.then(res => res)
.catch(err => err);
export const sendCodeChangePass = async param =>
GetData(url.sendCodeChangePass, param)
.then(res => res)
.catch(err => err);
//Secretcode
export const createSecretCode = async body =>
PostData(url.createSecretCode, body)
.then(res => res)
.catch(err => err);
export const updateSecretCode = async body =>
PostData(url.updateSecretCode, body)
.then(res => res)
.catch(err => err);
export const sendCodeForgetSecretKey = async params =>
GetData(url.sendCodeSecret, params)
.then(res => res)
.catch(err => err);
export const renewSecret = async body =>
PostData(url.renewSecret, body)
.then(res => res)
.catch(err => err);
export const getTeams = async body =>
PostData(url.renewSecret, body)
.then(res => res)
.catch(err => err);
export const myContractAPI = async params =>
GetData(url.myContract, params)
.then(res => res)
.catch(err => err);
export const reportCustomer = async body =>
PostData(url.reportCustomer, body)
.then(res => res)
.catch(err => err);
export const changePhone = async body =>
PostData(url.changePhone, body)
.then(res => res)
.catch(err => err);
export const sendOTPChangePhone = async body =>
PostData(url.sendOTPChangePhone, body)
.then(res => res)
.catch(err => err);
export const updateUserInfo = async body =>
PostData(url.updateCusInfor, body)
.then(res => res)
.catch(err => err);
export const changeAvatart = async body =>
PostData(url.changeAvatart, body)
.then(res => res)
.catch(err => err);
export const sendCodeForgetPassword = async body =>
PostData(url.sendCodeForgetPassword, body)
.then(res => res)
.catch(err => err);
export const updateCusForgetPassword = async body =>
PostData(url.updateCusForgetPassword, body)
.then(res => res)
.catch(err => err);
//Previous
export const getUserInfo = async param =>
GetData(url.getUserInfo, param)
.then(res => res)
.catch(err => err);
export const getProvinceList = async () =>
PostData(url.getProvinceList, {addressLevel: 1})
.then(res => res)
.catch(err => err);
export const getMyTeam = async params =>
PostData(url.myTeam, params)
.then(res => res)
.catch(err => err);
export const uploadAvatarAPI = async body =>
PostFormData(url.uploadAvatar, body)
.then(res => res)
.catch(err => err);
import {GetData, PostData} from '../helpers';
import url from '../url';
export const getWalletInfo = async params =>
GetData(url.getWalletInfo, params)
.then(res => res)
.catch(err => err);
export const depositsRequest = async params =>
PostData(url.depositsRequest, params)
.then(res => res)
.catch(err => err);
export const withdrawRequest = async params =>
PostData(url.withdrawRequest, params)
.then(res => res)
.catch(err => err);
export const getListTrans = async params =>
PostData(url.getListTrans, params)
.then(res => res)
.catch(err => err);
export const detailTrans = async id =>
GetData(`${url.detailTrans}/${id}`, {})
.then(res => res)
.catch(err => err);
export const inforBankTrans = async params =>
GetData(url.inforBankTrans, params)
.then(res => res)
.catch(err => err);
import KEY from '../assets/AsynStorage';
import axios from 'axios';
import AsyncStorage from '@react-native-community/async-storage';
import {DeviceEventEmitter} from 'react-native';
import {DEVICE_EVENT_KEY} from '../config/constants';
const API_KEY =
'eue823478uwuishdsfhjsd8939827389273897987wr837r98we7r8w9erwer7w9er7we8rw98er7';
axios.defaults.timeout = 10000;
export async function GetData(url, data) {
const token = await AsyncStorage.getItem(KEY.TOKEN);
let myRequest = {
method: 'get',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
'x-api-key': API_KEY,
},
params: {
...data,
},
timeout: 30 * 1000,
// withCredentials: true,
};
console.log('My request', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
console.log(error);
if (error.request.status === 403 || error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
export async function PostLogin(url, json) {
let myRequest = {
method: 'post',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'x-api-key': API_KEY,
},
timeout: 30 * 1000,
data: JSON.stringify(json),
};
console.log('post data mobile', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
console.log('error', error);
const err = {
message: data.message ? data.message : 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
});
}
export async function PostData(url, json, data) {
const token = await AsyncStorage.getItem(KEY.TOKEN);
let myRequest = {
method: 'post',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
'x-api-key': API_KEY,
},
timeout: 30 * 1000,
data: JSON.stringify(json),
params: {
...data,
},
};
console.log('post data mobile', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
if (error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
export async function PostFormData(url, data) {
const token = await AsyncStorage.getItem(KEY.TOKEN);
let myRequest = {
method: 'post',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + token,
'x-api-key': API_KEY,
},
timeout: 30 * 1000,
data: data,
};
console.log('post data mobile', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
if (error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
/**
*
* @param {*} url is link api
* @param {*} json is input format json to request server
* @param {*} isAuth is state auth
*/
export async function PutData(url, json, param) {
const token = await AsyncStorage.getItem(KEY.TOKEN);
let myRequest = {
method: 'put',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
'x-api-key': API_KEY,
},
data: JSON.stringify(json),
params: {
...param,
},
};
console.log('PutData', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
if (error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
/**
*
* @param {*} url is link api
* @param {*} json is input format json to request server
* @param {*} isAuth is state auth
*/
export async function DeleteData(url, json, param) {
const token = await AsyncStorage.getItem(KEY.TOKEN);
let myRequest = {
method: 'delete',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
'x-api-key': API_KEY,
},
data: JSON.stringify(json),
params: {
...param,
},
};
console.log('DeleteData', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
if (error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
export async function GetDataCommon(url, data) {
let myRequest = {
method: 'get',
url,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
params: {
...data,
},
timeout: 30 * 1000,
};
console.log('My request', myRequest);
return await axios(myRequest)
.then(response => response)
.then(response => response)
.catch(error => {
console.log('error', error);
if (error.request.status === 403 || error.request.status === 401) {
DeviceEventEmitter.emit(DEVICE_EVENT_KEY.LOGOUT_EVENT);
} else {
const data = JSON.parse(error.request._response);
const err = {
message: data.message
? data.message
: 'Có lỗi trong qúa trình xử lý!',
status: error.request.status,
};
return err;
}
});
}
export * from './helpers';
export * from './Status';
export * from './url';
export const root = 'https://apigw.dcvfinance.com/api/';
export default {
// user
checkPhoneNumber: `${root}app/auth/checkPhone`,
verifyOTP: `${root}app/auth/checkOtp`,
signUp: `${root}app/auth/loginNewUser`,
login: `${root}app/auth/login`,
logout: `${root}app/auth/logout`,
};
const KEY = {
TOKEN: '@TOKEN',
FIREBASE: '@Firebase',
ACCOUNT: '@ACCOUNT',
LANGUAGE: '@LANGUAGE',
TODOLIST: '@TODOLIST',
};
export default KEY;
import images from './images';
import colors from './colors';
import fonts from './fonts';
import fontsize from './fontsize';
const R = {
images,
colors,
fonts,
fontsize,
};
export default R;
const colors = {
main: '#00139B',
txtMain: '#006938',
label: '#5D5D5D',
color777: '#777777',
colorMainLight: '#5173B1',
colorBackground: '#E2E8F2',
borderGray: '#bbc2ce',
borderGraym: '#c9cfd9',
placeHolder: '#8D8D8D',
backgrPicker: '#E2E8F2',
dollar: '#07BA00',
colorBg: '#E8E8E8',
accent: '#A60014',
primary: '#0AC4BA',
secondary: '#00b33c',
black: '#000000',
white: '#FFFFFF',
orange: '#Fb9736',
lightBlue: '#1a8cff',
lightBlue1: '#008ae6',
lightBlue2: '#22AEFB',
rgbaBtn: '#79F8B5',
yellow: '#e6e600',
red: '#E3434F',
bgMain: '#9EF8C9',
colorButton1: '#43D75B',
colorTextLine: '#FFA412',
colorBgScreen: '#F7F8FA',
colorBgInputText: '#F2F2F2',
};
export default colors;
const fonts = {
RobotoThin: 'Roboto-Thin',
RobotoThinItalic: 'Roboto-ThinItalic',
RobotoItalic: 'Roboto-Italic',
RobotoBlack: 'Roboto-Black',
RobotoBlackItalic: 'Roboto-BlackItalic',
RobotoBold: 'Roboto-Bold',
RobotoBoldItalic: 'Roboto-BoldItalic',
RobotoLight: 'Roboto-Light',
RobotoLightItalic: 'Roboto-LightItalic',
RobotoMedium: 'Roboto-Medium',
RobotoMediumItalic: 'Roboto-MediumItalic',
RobotoRegular: 'Roboto-Regular',
MontserratRegular: 'Montserrat-Regular',
MontserratMedium: 'Montserrat-Medium',
MontserratSemiBold: 'Montserrat-SemiBold',
MontserratItalic: 'Montserrat-Italic',
MontserratMediumItalic: 'Montserrat-MediumItalic',
MontserratSemiBoldItalic: 'Montserrat-SemiBoldItalic',
};
export default fonts;
const fontsize = {
fontSizeLabel: 14,
fontSizeContent: 14,
fontSizeInputText: 16,
};
export default fontsize;
const images = {
iconWarn: require('./images/iconWarn.png'),
iconSuccess: require('./images/iconSuccess.png'),
iconError: require('./images/iconError.png'),
iconCamera: require('./images/iconCamera.png'),
icHome: require('./images/icHome.png'),
icAccount: require('./images/icAccount.png'),
bg_cannot_connect: require('./images/bg_cannot_connect.png'),
icGallery: require('./images/icGallery.png'),
};
export default images;
const sizes = {
// global sizes
small: 5,
base: 16,
font: 14,
radius: 6,
padding: 25,
// font sizes
big: 34,
h1: 26,
h2: 20,
h3: 18,
title: 16,
body: 14,
caption: 12,
};
export default sizes;
import sizes from './sizes';
import colors from './colors';
export {sizes, colors};
import {Alert} from 'react-native';
import I18n from '../helper/i18/i18n';
export const NotificationAlert = (string) => {
Alert.alert(I18n.t('Notification'), string);
};
export const confirmAlert = (content, callback) => {
Alert.alert(
I18n.t('Notification'),
content,
[
{
text: I18n.t('Cancel'),
style: 'cancel',
},
{
text: I18n.t('Ok'),
onPress: () => {
callback();
},
},
],
{cancelable: false},
);
};
import React, {PureComponent} from 'react';
import {Text} from 'react-native';
import {connect} from 'react-redux';
import I18n from '../helper/i18/i18n';
class AppText extends React.Component {
constructor(props) {
super(props);
this.state = {
i18n: I18n,
};
}
componentWillMount() {
const {language} = this.props;
if (language) this.setMainLocaleLanguage(language);
}
componentWillReceiveProps = (nextProps) => {
const {language} = nextProps;
if (language) this.setMainLocaleLanguage(language);
};
setMainLocaleLanguage = (language) => {
let i18n = this.state.i18n;
i18n.locale = language;
this.setState({i18n});
};
render() {
const {i18nKey, style} = this.props;
const {i18n} = this.state;
return (
<Text style={style}>
{i18nKey ? i18n.t(i18nKey) : this.props.children}
</Text>
);
}
}
const mapStateToProps = (state) => {
return {
language: state.languageReducer.language,
};
};
export default connect(mapStateToProps, null)(AppText);
import React, {Component} from 'react';
import {
StyleSheet,
TouchableOpacity,
View,
Text,
ImageBackground,
} from 'react-native';
import R from '../assets/R';
import {colors, sizes} from '../assets/theme';
import {
getFontSize,
getFontXD,
getWidth,
HEIGHT,
WIDTH,
} from '../config/Functions';
const Button = props => {
const {title, onPress, containerStyle, txtStyle, backgroundColor} = props;
return (
<TouchableOpacity
style={[
{
height: HEIGHT(40),
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F2B60D',
marginHorizontal: WIDTH(15),
borderRadius: HEIGHT(6),
marginTop: HEIGHT(30),
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
},
{...containerStyle},
]}
disabled={props.disabled}
onPress={onPress}>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Text
style={[
{
fontSize: getFontSize(18),
color: R.colors.white,
fontWeight: 'bold',
},
{...txtStyle},
]}>
{title}
</Text>
</View>
</TouchableOpacity>
);
};
export default Button;
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
Linking,
} from 'react-native';
import QRCodeScanner from 'react-native-qrcode-scanner';
import {RNCamera} from 'react-native-camera';
const ScanScreen = props => {
const onSuccess = e => {
console.log(e.data);
};
return (
<QRCodeScanner
onRead={onSuccess}
topContent={<Text style={styles.centerText}>Quét QR</Text>}
/>
);
};
const styles = StyleSheet.create({
centerText: {
flex: 1,
fontSize: 18,
padding: 32,
color: '#777',
},
textBold: {
fontWeight: '500',
color: '#000',
},
buttonText: {
fontSize: 21,
color: 'rgb(0,122,255)',
},
buttonTouchable: {
padding: 16,
},
});
export default ScanScreen;
class DropdownManager {
_defaultDropdown = null;
_defaultDropdownLongTime = null;
register(_ref, _refLongTime) {
if (!this._defaultDropdown) {
this._defaultDropdown = _ref;
}
if (!this._defaultDropdownLongTime) {
this._defaultDropdownLongTime = _refLongTime;
}
}
unregister(_ref, _refLongTime) {
if (!!this._defaultDropdown && this._defaultDropdown._id === _ref._id) {
this._defaultDropdown = null;
}
if (
!!this._defaultDropdownLongTime &&
this._defaultDropdownLongTime._id === _refLongTime._id
) {
this._defaultDropdownLongTime = null;
}
}
getDefault(isLongMessage = false) {
return isLongMessage
? this._defaultDropdownLongTime
: this._defaultDropdown;
}
}
export default new DropdownManager();
import DropdownManager from "./DropdownManager";
// Type display dropdown
export const TYPE = {
SUCCESS: "success",
INFO: "info",
WARN: "warn",
ERROR: "error",
};
/**
* To display dropdown Alert in top screen
* @param type type of Alert (check it in TYPE above)
* @param title title of Alert
* @param description description of Alert
*/
export function showAlert(type, title, description) {
const ref = DropdownManager.getDefault(description.length > 60);
if (!!ref) {
ref.alertWithType(type, title, description);
}
}
/**
* To hide dropdown Alert in top screen
*/
export function hideAlert() {
const ref = DropdownManager.getDefault();
if (!!ref) {
ref.closeAction();
}
}
import * as React from 'react';
import { StatusBar } from 'react-native';
import { useIsFocused } from '@react-navigation/native';
export function FocusAwareStatusBar(props) {
const isFocused = useIsFocused();
return isFocused ? <StatusBar {...props} /> : null;
}
import React, {useState} from 'react';
import {
Image,
Platform,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import R from '../../assets/R';
import {
getWidth,
HEIGHTXD,
WIDTHXD,
WIDTHXDICON,
HEIGHT,
getFontSize,
WIDTH,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import {useNavigation} from '@react-navigation/native';
import SnackBar from '../SnackBar';
const Header = props => {
const {title, isBack} = props;
const navigate = useNavigation();
return (
<View style={styles.headerContainer}>
<Text numberOfLines={1} style={styles.txtTitle}>
{title}
</Text>
{isBack && (
<TouchableOpacity
style={styles.btnBack}
onPress={() => navigate.goBack()}>
<Icon color={R.colors.black} name={'arrowleft'} size={22} />
</TouchableOpacity>
)}
</View>
);
};
export default Header;
const styles = StyleSheet.create({
headerContainer: {
height: HEIGHT(45),
width: '100%',
flexDirection: 'row',
alignItems: 'center',
marginBottom: 2,
justifyContent: 'center',
backgroundColor: R.colors.white,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 5,
},
txtTitle: {
flex: 1,
fontSize: getFontSize(16),
textAlign: 'center',
fontWeight: 'bold',
color: R.colors.black,
},
btnBack: {
position: 'absolute',
left: WIDTH(10),
width: WIDTH(35),
height: HEIGHT(30),
alignItems: 'center',
justifyContent: 'center',
},
});
import React from 'react';
import {View, Text, TextInput} from 'react-native';
import {WIDTHXD, getFontXD, WIDTH, HEIGHT} from '../../config/Functions';
import R from '../../assets/R';
import {RFValue} from 'react-native-responsive-fontsize';
const TextField = props => {
const {
title,
onChangeText,
isPassword,
maxLength,
isNumber,
value,
editable,
error,
} = props;
const renderMess = () => {
if (value && value.trim().length > 0) return `${title} không hợp lệ`;
return `Vui lòng nhập ${title ? title.toLowerCase() : messageError} `;
};
return (
<View style={{marginVertical: 5, marginHorizontal: WIDTH(15)}}>
<Text
style={{
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
}}>
{title ? title : ''}
</Text>
<TextInput
maxLength={maxLength ? maxLength : 256}
placeholderTextColor={R.colors.placeHolder}
editable={editable != null ? editable : true}
secureTextEntry={isPassword}
autoCapitalize="none"
value={value}
keyboardType={isNumber ? 'number-pad' : 'default'}
onChangeText={val => onChangeText(val)}
style={{
height: HEIGHT(35),
color: 'black',
borderRadius: HEIGHT(6),
fontSize: 16,
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: '#EFF3F5',
}}
/>
<View
style={{
height: HEIGHT(20),
marginTop: 5,
}}>
{error && !error.ref?.value && (
<Text
ellipsizeMode={'tail'}
numberOfLines={1}
style={{
flex: 1,
color: R.colors.red,
fontSize: RFValue(10),
}}>
{error.message ? error.message : renderMess()}
</Text>
)}
</View>
</View>
);
};
export default React.memo(TextField);
import React from 'react';
import {View, Text, TextInput} from 'react-native';
import {HEIGHTXD, WIDTHXD, getFontXD} from '../../config/Functions';
import R from '../../assets/R';
const TextField = props => {
const {title, onChangeText, maxLength, value, editable} = props;
return (
<View style={{marginVertical: 5}}>
<Text
style={{
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
}}>
{title ? title : ''}
</Text>
<TextInput
maxLength={maxLength}
textAlign={'left'}
editable={editable != null ? editable : true}
value={value}
onChangeText={val => onChangeText(val)}
multiline={true}
numberOfLines={3}
placeholderTextColor={R.colors.placeHolder}
autoCapitalize="none"
style={{
textAlignVertical: 'top',
textAlign: 'left',
color: 'black',
maxHeight: HEIGHTXD(259),
minHeight: HEIGHTXD(169),
borderRadius: 7,
borderWidth: 0.7,
borderColor: '#DBDBDB',
fontSize: 16,
paddingVertical: 10,
paddingHorizontal: 10,
backgroundColor: 'white',
shadowColor: '#AFA9A9',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.25,
shadowRadius: 1.84,
elevation: 1,
}}
/>
</View>
);
};
export default React.memo(TextField);
import React, {useState} from 'react';
import {View, Text, TextInput, TouchableOpacity} from 'react-native';
import {HEIGHTXD, WIDTHXD, getFontXD} from '../../config/Functions';
import R from '../../assets/R';
import I18n from '../../helper/i18/i18n';
import Icon from 'react-native-vector-icons/Feather';
import {Image} from 'react-native';
import {NotificationAlert} from '../Aleart';
const TextField = props => {
const [showPassword, setShowPassword] = useState(false);
const {
title,
onChangeText,
isPassword,
maxLength,
isNumber,
value,
editable,
error,
onBlur,
placeholder,
keyboardType,
placeHolderColor,
textColor,
tinColor,
fontSize,
borderBottomColor,
required,
autoCapitalize,
} = props;
return (
<View>
{title ? (
<Text
style={{
fontSize: R.fontsize.fontSizeLabel,
fontWeight: '700',
color: R.colors.black,
marginBottom: 8,
}}>
<Text>{title}</Text>
<TouchableOpacity
onPress={() => {
NotificationAlert(
'Đối với các ngân hàng Techcombank, SeaBank,Vietcombank, OCB, TPBANK cần nhập thông tin số tài khoản tín dụng.',
);
}}>
<Image
source={R.images.iconWarn}
style={{
width: 22,
height: 20,
marginLeft: 10,
marginTop: 10,
resizeMode: 'cover',
}}
/>
</TouchableOpacity>
</Text>
) : null}
<View style={{justifyContent: 'center'}}>
<TextInput
onBlur={onBlur}
maxLength={maxLength ? maxLength : 256}
placeholderTextColor={placeHolderColor ? placeHolderColor : '#8E8E8E'}
editable={editable != null ? editable : true}
placeholder={placeholder ? placeHolderColor : ''}
secureTextEntry={isPassword && !showPassword}
autoCapitalize={autoCapitalize ? autoCapitalize : 'none'}
value={value}
fontSize={13}
keyboardType={keyboardType}
onChangeText={val => {
if (keyboardType === 'number-pad') {
const text = val.replace(/[^0-9]/g, '');
onChangeText(text);
} else {
onChangeText(val);
}
}}
style={{
height: 42,
color: textColor ? textColor : R.colors.black,
borderBottomWidth: 0.5,
fontSize: fontSize ? fontSize : R.fontsize.fontSizeInputText,
paddingVertical: 5,
fontWeight: '400',
paddingHorizontal: 17,
backgroundColor: R.colors.colorBgInputText,
borderRadius: 10,
borderBottomColor: 'transparent',
}}
/>
{isPassword && (
<TouchableOpacity
style={{position: 'absolute', right: 17}}
onPress={() => setShowPassword(!showPassword)}>
<Icon
name={showPassword ? 'eye' : 'eye-off'}
size={20}
color={'#4B4B4B'}
/>
</TouchableOpacity>
)}
</View>
<View
style={{
height: 20,
marginTop: 5,
}}>
{error && (
<Text
style={{
color: tinColor ? tinColor : R.colors.red1,
fontSize: getFontXD(32),
}}>
{I18n.t('PleaseEnterField')}
</Text>
)}
</View>
</View>
);
};
export default React.memo(TextField);
import React from 'react';
import {
Animated,
FlatList,
Image,
StyleSheet,
TouchableHighlight,
View,
} from 'react-native';
import {SwipeListView} from 'react-native-swipe-list-view';
import {WIDTHXD, getWidth, HEIGHTXD, WIDTHXDICON} from '../../config/Functions';
import R from '../../assets/R';
import ItemEmpty from './ItemEmpty';
const rowSwipeAnimatedValues = [];
/**
* Displays a swipe use as FlatList
*@param listIcon list image icon in swipe (for example : [R.image.iconDelete] )
*@param widthListIcon with of list icon
*@callback onPressIcon call when you choice one of list icon return index of icon and index of item
*@param rightOfList end of list icon margin Right screen
*@param styleOfIcon custom style of icon
*@param data is a Array [Object1,Object2]
*/
export default class FlatListSwipe extends React.Component {
indexOfItem = 0;
rowMap = [];
constructor(props) {
super(props);
this.state = {
isDisable: false,
};
}
closeRowItem = () => {
if (this.rowMap[this.indexOfItem]) {
this.rowMap[this.indexOfItem].closeRow();
}
};
closeRow = (rowMap, rowKey) => {
if (rowMap[rowKey]) {
rowMap[rowKey].closeRow();
}
};
renderRightAction = (rowMap, image, indexOfIcon) => {
return (
<View style={{marginRight: indexOfIcon === 0 ? 10 : 0}}>
<TouchableHighlight
underlayColor="transparent"
key={indexOfIcon.toString()}
disabled={this.state.isDisable}
onPress={() => {
this.closeRow(rowMap, this.indexOfItem);
this.setState(
{
isDisable: true,
},
() => {
setTimeout(() => {
if (this.props.isListRequestPartners)
this.props.onPressIcon(indexOfIcon + 1, this.indexOfItem);
else this.props.onPressIcon(indexOfIcon, this.indexOfItem);
this.setState({
isDisable: false,
});
}, 1000);
},
);
}}
style={{
height: 65,
width: 55,
borderRadius: 10,
marginTop: 10,
backgroundColor: indexOfIcon === 0 ? R.colors.gray1 : R.colors.red1,
flex: 0,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={image}
style={[
styles.trash,
this.props.styleOfIcon !== null && this.props.styleOfIcon,
]}
/>
</TouchableHighlight>
</View>
);
};
onRowOpen = (rowKey, rowMap) => {
this.indexOfItem = rowKey;
this.rowMap = rowMap;
};
onSwipeValueChange = swipeData => {
const {key, value} = swipeData;
rowSwipeAnimatedValues[key].setValue(Math.abs(value));
};
convertData = data => {
// if (rowSwipeAnimatedValues['0'] === undefined) {
this.props.data.forEach((_, i) => {
rowSwipeAnimatedValues[`${i}`] = new Animated.Value(0);
});
// }
let dataTmp = [];
data.map((item, index) => {
dataTmp.push({...item, key: index.toString()});
});
return dataTmp;
};
renderHiddenItem = (data, rowMap) => {
return (
<Animated.View
style={[
styles.backRightBtnRight,
this.props.rightOfList && {right: this.props.rightOfList},
{
transform: [
{
translateX: rowSwipeAnimatedValues[data.item.key].interpolate({
inputRange: [0, getWidth()],
outputRange: [getWidth(), 0],
}),
},
],
},
]}>
{this.props.listIcons.map((item, index) =>
this.renderRightAction(rowMap, item, index),
)}
</Animated.View>
);
};
render() {
const data = this.convertData(this.props.data);
const {widthListIcon} = this.props;
return (
<View style={styles.container}>
<SwipeListView
{...this.props}
data={data}
showsVerticalScrollIndicator={false}
renderHiddenItem={this.renderHiddenItem}
rightOpenValue={-widthListIcon || -WIDTHXD(387)}
onRowOpen={this.onRowOpen}
onSwipeValueChange={this.onSwipeValueChange}
disableRightSwipe={true}
swipeToOpenPercent={10}
swipeToClosePercent={10}
ListEmptyComponent={!this.props.isLoading && <ItemEmpty />}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'transparent',
flex: 1,
},
backRightBtnRight: {
flexDirection: 'row',
flex: 1,
// right: 20
},
trash: {
height: 21,
width: 21,
},
});
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import {HEIGHT, getFont, getWidth} from '../../config/Functions';
import R from '../../assets/R';
import i18n from '../../helper/i18/i18n';
const ItemEmpty = props => {
let paddingHorizontal = props.paddingHorizontal ? props.paddingHorizontal : 0;
const {title} = props;
return (
<View
style={{...styles.container, width: getWidth() - paddingHorizontal * 2}}>
<View style={{alignItems: 'center'}}>
<Text style={{fontSize: getFont(16), color: R.colors.black}}>
{title || i18n.t('NullDataSearch')}
</Text>
</View>
</View>
);
};
ItemEmpty.defaultProps = {
paddingHorizontal: 0,
};
export default ItemEmpty;
const styles = StyleSheet.create({
container: {
width: getWidth(),
height: HEIGHT(50),
flexDirection: 'row',
paddingTop: HEIGHT(16),
paddingBottom: HEIGHT(10),
paddingRight: HEIGHT(10),
paddingLeft: HEIGHT(10),
alignItems: 'center',
justifyContent: 'center',
},
});
import * as React from 'react';
//@ts-ignore
import { ActivityIndicator, ActivityIndicatorProps, StyleSheet } from 'react-native';
const StyledIndicator: React.FunctionComponent<ActivityIndicatorProps> = (props: ActivityIndicatorProps) => {
return <ActivityIndicator color={'red'} size={'small'} style={styles.container} {...props} />;
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default StyledIndicator;
//@ts-ignore
import React, { useEffect, useRef, useState, useMemo } from 'react';
//@ts-ignore
import { FlatList, FlatListProps, RefreshControl, View, TextStyle, StyleProp, ViewStyle } from 'react-native';
import NoData from './StyledNoData';
import StyledIndicator from './StyledIndicator';
interface Props extends FlatListProps<any> {
[key: string]: any;
FlatListComponent?: React.FunctionComponent<any>;
loading?: boolean;
data: any[];
loadingMore?: boolean;
noDataText?: string;
ListHeaderComponent?: any;
scrollEnabled?: boolean;
noDataCanRefresh?: boolean;
i18Params?: any;
noDataTextI18Key?: any;
noDataStyle?: StyleProp<ViewStyle>;
customStyle?: any;
onLoadMore?(): void;
onNoDataRefresh?(): void;
}
const StyledList = (props: Props, ref: any) => {
const [momentumScrolled, setMomentumScrolled] = useState(false);
const list = useRef<FlatList>(null);
const { loading, loadingMore, data, ListHeaderComponent, refreshing, customStyle } = props;
const contentContainerStyle: StyleProp<ViewStyle> = {};
const hasData = data?.length !== 0;
if (!hasData) {
contentContainerStyle.flex = 1;
// contentContainerStyle.alignItems = 'center'
// contentContainerStyle.justifyContent = 'center'
}
let styles: StyleProp<ViewStyle>;
if (typeof ListHeaderComponent === 'undefined' && !hasData) {
styles = [contentContainerStyle, customStyle];
} else {
styles = customStyle;
}
function keyExtractor(item: any, i: any): string {
return `${i}`;
}
function handleRefresh() {
if (props.onRefresh) props.onRefresh();
}
// Bởi vì onEnReached call nhiều lần nên phải trick để chỉ call 1 lần thôi
useEffect(() => {
if (momentumScrolled) {
setMomentumScrolled(true);
if (props.onLoadMore) props.onLoadMore();
}
}, [momentumScrolled]);
function handleEndReached(info: any) {
if (!momentumScrolled) {
setMomentumScrolled(true);
}
}
function handleNoDataRefresh() {
const { onNoDataRefresh } = props;
if (onNoDataRefresh) onNoDataRefresh();
}
function onMomentumScrollBegin() {
setMomentumScrolled(false);
}
React.useImperativeHandle(ref, () => ({
scrollToTop: () => {
list?.current?.scrollToOffset({ animated: true, offset: 0 });
},
}));
function renderFooter() {
if (hasData && loadingMore) {
return (
<View style={{ alignItems: 'center', marginVertical: 8 }}>
<StyledIndicator size={24} />
</View>
);
}
return null;
}
function renderNoData() {
const { noDataText, noDataTextI18Key, noDataCanRefresh } = props;
return (
<NoData
customStyle={props.noDataStyle}
loading={loading}
text={"No data"}
canRefresh={noDataCanRefresh}
onRefresh={handleNoDataRefresh}
/>
);
}
const FlatListComponent: any = useMemo(() => {
return props?.FlatListComponent || FlatList;
}, [props?.FlatListComponent]);
return (
<FlatListComponent
ref={list}
contentContainerStyle={styles}
keyExtractor={keyExtractor}
initialNumToRender={10}
// onEndReached={handleEndReached}
onEndReachedThreshold={0.01}
onMomentumScrollBegin={onMomentumScrollBegin}
ListEmptyComponent={renderNoData}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={!!refreshing}
colors={['red']}
tintColor={"blue"}
onRefresh={handleRefresh}
/>
}
ListFooterComponent={renderFooter}
keyboardShouldPersistTaps={'never'}
{...props}
/>
);
};
export default React.memo(React.forwardRef(StyledList));
import * as React from 'react';
//@ts-ignore
import { ActivityIndicator,TouchableOpacity, Text,StyleProp, StyleSheet, TextStyle, View, ViewStyle } from 'react-native';
interface StyledListNoDataProps {
text?: string;
canRefresh?: boolean;
loading?: boolean;
onRefresh?(): any;
customStyle?: StyleProp<ViewStyle>;
customStyleText?: StyleProp<TextStyle>;
}
const StyledNoData: React.FunctionComponent<StyledListNoDataProps> = (props: StyledListNoDataProps) => {
return (
<View style={[styles.container, props.customStyle]}>
{props.loading ? (
<View style={{ alignItems: 'center' }}>
<ActivityIndicator />
</View>
) : (
<Text>Khong co data</Text>
)}
{!!props.canRefresh && !props.loading ? (
<TouchableOpacity onPress={props.onRefresh}>
<Text>Reload</Text>
</TouchableOpacity>
) : (
<View />
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
padding: 8,
},
text: {
fontWeight: '600',
fontSize: 14,
color: 'red',
textAlign: 'center',
},
textReload: {
margin: 12,
color: 'red',
},
});
export default StyledNoData;
import React, {useEffect, useState} from 'react';
import {View, Text, StyleSheet, TouchableOpacity, Image} from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import R from '../assets/R';
import AppText from './AppText';
const NoInternetComponent = props => {
const [isConnected, setConnect] = useState(true);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setConnect(state.isConnected);
});
return unsubscribe;
}, []);
return !isConnected ? (
<View style={styles.offlineContainer}>
<Image source={R.images.bg_cannot_connect} style={styles.imageStyle} />
<AppText i18nKey={'No_Internet'} style={styles.textStyle} />
<AppText i18nKey={'Check_Internet_Connect'} style={styles.subTextStyle} />
<TouchableOpacity
onPress={() => {
setTimeout(() => {
NetInfo.fetch().then(state => {
setConnect(state.isConnected);
});
}, 3000);
}}>
<AppText
i18nKey={'Retry'}
style={{alignSelf: 'center', fontSize: 20, color: 'blue'}}
/>
</TouchableOpacity>
</View>
) : (
<View style={{width: 0, height: 0}} />
);
};
const styles = StyleSheet.create({
offlineContainer: {
width: '100%',
height: '100%',
backgroundColor: 'white',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
},
offlineText: {
color: R.colors.white100,
},
textStyle: {
fontSize: 20,
color: 'black',
marginTop: 30,
},
subTextStyle: {
fontSize: 16,
color: R.colors.borderC,
marginVertical: 10,
},
imageStyle: {
width: '80%',
height: 200,
},
});
export default NoInternetComponent;
import React, {Component} from 'react';
import {
View,
Text,
Modal,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
TextInput,
Platform,
} from 'react-native';
import Autocomplete from 'react-native-autocomplete-input';
import AntDesign from 'react-native-vector-icons/AntDesign';
import {
HEIGHT,
WIDTH,
WIDTHXD,
getFontXD,
HEIGHTXD,
WIDTHXDICON,
} from '../../config/Functions';
import R from '../../assets/R';
import I18n from '../../helper/i18/i18n';
import AppText from '../AppText';
/**
* Displays a popup search with a list of data that returns the value of the selected item
*@param title title of popup search(string)
*@param iconStyle style of icon search(PropsStyle)
*@callback onValueChange call when you choice one of list item with param name and item
*@method setModalVisible to set show and hide this popup(param true to show and false to hide)
*@method changeName to find data with query (auto searh after 500ms)
*/
class ModalSearch extends Component {
state = {
modalVisible: false,
query: '',
name: '',
typing: false,
typingTimeout: 0,
reRender: false,
selectedIndex: -1,
};
listSearch = [];
/**
* This Function to open, close modal
*/
setModalVisible = visible => {
this.setState({
modalVisible: visible,
});
};
componentDidMount() {
this.loadData();
}
componentWillReceiveProps(nextProps) {
if (
nextProps.data !== this.props.data &&
(nextProps.data.length > 0 || nextProps.isNeedClearData)
) {
this.listSearch = nextProps.data;
this.setState({reRender: this.state.reRender});
}
}
/**
* This Function to load data with param search
*/
changeName = event => {
this.setState({name: event, selectedIndex: -1});
if (this.state.typingTimeout) {
clearTimeout(this.state.typingTimeout);
}
this.setState({
name: event,
query: event,
typing: false,
typingTimeout: setTimeout(async () => {
const {findData} = this.props;
this.listSearch = findData ? await findData(event) : [];
this.setState({reRender: !this.state.reRender});
}, 500),
});
};
/**
* This Function to search with query
* @param query value of input text
*/
/**
* This Function to load data first
*/
loadData = async () => {
const {findData} = this.props;
this.listSearch = findData ? await findData(' ') : [];
};
render() {
const {title, iconStyle, onValueChange} = this.props;
const {query} = this.state;
return (
<Modal
onRequestClose={() => {
this.setModalVisible(false);
}}
animationType="slide"
transparent={true}
visible={this.state.modalVisible}>
<TouchableWithoutFeedback
onPress={() => {
this.setModalVisible(false);
}}>
<View style={styles.opacity}>
<TouchableWithoutFeedback>
<View style={styles.modal}>
<Text style={styles.title}>{title}</Text>
<TouchableOpacity
onPress={() => {
this.setModalVisible(false);
}}
style={[styles.iconCloseStyle]}
hitSlop={{top: 20, left: 20, right: 20, bottom: 20}}>
<AntDesign name="close" size={WIDTHXD(50)} color="#000" />
</TouchableOpacity>
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
style={{
// width: WIDTHXD(870),
paddingRight: WIDTHXD(100),
height: HEIGHTXD(99),
}}
ref={ref => (this.refList = ref)}
inputContainerStyle={styles.inputBox}
placeholderTextColor="gray"
containerStyle={styles.autocompleteContainer}
data={this.listSearch}
keyExtractor={(item, index) => index.toString()}
defaultValue={query}
listStyle={styles.listStyle}
renderTextInput={iProps => (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
}}>
<AntDesign
name="search1"
size={WIDTHXDICON(50)}
color="#77869E"
/>
<TextInput
value={this.state.name}
onChangeText={e => this.changeName(e)}
{...iProps}
placeholderTextColor="#BAC7D3"
style={{
fontSize: getFontXD(36),
marginLeft: WIDTHXD(21.7),
color: '#000',
flex: 1,
height: '100%',
}}
/>
</View>
)}
// onChangeText={text => { this.changeName(text) }}
placeholder={'Tim kiem'}
listContainerStyle={{
marginTop: HEIGHTXD(37),
}}
renderItem={({item, index}) => {
let evenRow =
index ===
(this.listSearch && this.listSearch.length - 1);
let highlighted = this.state.selectedIndex === index;
return (
<TouchableOpacity
key={index.toString()}
style={[
styles.itemStyle,
highlighted && {backgroundColor: '#e3e8f2'},
]}
onPress={() => {
onValueChange &&
onValueChange(
item.text ? `${item.text}` : `${item.name}`,
item,
);
this.setState({
query: item.text ? item.text : item.name,
selectedIndex: index,
});
this.setModalVisible(false);
}}>
<Text style={styles.itemText}>
{item.text
? item.text && `${item.text}`
: item.name && `${item.name}`}
</Text>
</TouchableOpacity>
);
}}
flatListProps={{
ItemSeparatorComponent: () => (
<View
style={{
width: '100%',
height: 0.5,
backgroundColor: R.colors.borderD4,
}}
/>
),
ListEmptyComponent: () => (
<AppText style={styles.itemText} i18nKey={'NoData'} />
),
}}
/>
{this.state.name.length !== 0 && (
<TouchableOpacity
onPress={() => {
this.changeName('');
}}
hitSlop={{top: 20, bottom: 20, left: 50, right: 50}}
style={[
styles.iconStyle,
iconStyle !== null && iconStyle,
]}>
<AntDesign
name="close"
size={WIDTHXD(40)}
color="#BAC7D3"
/>
</TouchableOpacity>
)}
</View>
</View>
</TouchableWithoutFeedback>
</View>
</TouchableWithoutFeedback>
</Modal>
);
}
}
export default ModalSearch;
const styles = StyleSheet.create({
opacity: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#rgba(0,0,0,0.7)',
},
modal: {
backgroundColor: 'white',
width: WIDTHXD(1004),
borderRadius: WIDTHXD(30),
height: HEIGHTXD(1570),
paddingTop: HEIGHTXD(50),
paddingBottom: HEIGHTXD(54),
alignItems: 'center',
paddingHorizontal: WIDTHXD(45),
},
title: {
color: R.colors.indigo701,
fontSize: getFontXD(54),
marginBottom: HEIGHTXD(75),
alignSelf: 'center',
},
container: {
flexDirection: 'row',
alignItems: 'center',
width: WIDTHXD(914),
},
autocompleteContainer: {
zIndex: 1,
width: WIDTHXD(914),
},
itemText: {
fontSize: 16,
},
inputBox: {
width: WIDTHXD(914),
borderRadius: WIDTHXD(20),
height: HEIGHTXD(99),
paddingHorizontal: WIDTHXD(30),
paddingRight: WIDTHXD(100),
justifyContent: 'space-between',
borderWidth: 0.3,
borderColor: R.colors.borderD4,
flexDirection: 'row',
alignItems: 'center',
},
iconStyle: {
position: 'absolute',
right: WIDTHXD(20),
zIndex: 1,
height: HEIGHTXD(99),
width: WIDTHXD(60),
justifyContent: 'center',
alignItems: 'center',
top: 0,
},
listStyle: {
width: WIDTHXD(914),
marginLeft: 0,
// marginTop: 1,
// borderWidth: WIDTHXD(13),
borderColor: R.colors.borderD4,
borderRadius: WIDTHXD(20),
borderWidth: 0.3,
borderTopWidth: 0.3,
height: HEIGHTXD(1150),
},
itemStyle: {
paddingVertical: HEIGHT(6),
paddingHorizontal: WIDTH(10),
minHeight: HEIGHTXD(100),
// borderBottomWidth: 0.3,
// borderBottomColor: R.colors.iconGray
},
iconCloseStyle: {
position: 'absolute',
right: WIDTHXD(72),
zIndex: 1,
top: HEIGHTXD(67.5),
},
});
import React, {useState} from 'react';
import {
ActivityIndicator,
Image,
StyleSheet,
Text,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';
import R from '../../assets/R';
import {getFontXD, HEIGHTXD, WIDTHXD} from '../../config/Functions';
import Modal from 'react-native-modal';
import i18n from '../../helper/i18/i18n';
import {connect} from 'react-redux';
import {hideLoading, showLoading} from '../../actions/loadingAction';
import {uploadFile} from '../../apis/Functions/config';
import {showAlert, TYPE} from '../DropdownAlert';
import {saveUserToRedux} from '../../actions/users';
import {ASYNC_STORE_KEY} from '../../config/constants';
import AsyncStorage from '@react-native-community/async-storage';
import PickerImg from './PickerImg';
import {changeAvatart} from '../../apis/Functions/users';
const options = {
title: 'Select Avatar',
customButtons: [{name: 'fb', title: 'Choose Photo from Facebook'}],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
const PickerAvatar = props => {
const [isModalVisible, setModalVisible] = useState(false);
const [imgAvatar, setImgAvatar] = useState(props.userInfo?.avatar);
const [isUploadAvatar, setIsUploadAvatar] = useState(false);
const uploadAvatar = async photo => {
// setIsUploadAvatar(true);
// const data = new FormData();
// let fileName = photo.filename;
// if (!fileName || fileName === undefined) {
// let pathArray = photo.path.split('/');
// fileName = pathArray[pathArray.length - 1];
// }
// data.append('files', {
// name: fileName.replace(/HEIC/g, 'jpg'),
// type: photo.mime,
// size: photo.size,
// uri: photo.path,
// });
// const response = await uploadFile(data);
// if (response.status === 200) {
// if (response.data.code === 'OK' && response.data.data) {
// const res = await changeAvatart({avatar: response.data.data[0]?.url});
// if (res.data.code == 200) {
// setImgAvatar(response.data.data[0].url);
// showAlert(
// TYPE.SUCCESS,
// i18n.t('Notification'),
// i18n.t('ChangeAvatartSuccess'),
// );
// } else {
// showAlert(
// TYPE.ERROR,
// i18n.t('Notification'),
// i18n.t('ChangeAvatartFaild'),
// );
// }
// //call api update
// } else {
// showAlert(
// TYPE.ERROR,
// i18n.t('Notification'),
// response.data.error
// ? i18n.t(response.data.error)
// : i18n.t('CallAPIError'),
// );
// }
// } else {
// showAlert(TYPE.ERROR, i18n.t('Notification'), i18n.t('CallAPIError'));
// }
// setIsUploadAvatar(false);
};
return (
<TouchableOpacity onPress={() => setModalVisible(true)}>
<View style={styles.containerImg}>
<View
style={{
position: 'absolute',
bottom: 0,
width: 70,
height: 70,
borderRadius: 35,
justifyContent: 'center',
alignItems: 'center',
}}>
{imgAvatar ? (
<Image source={{uri: imgAvatar}} style={styles.imgAvatar} />
) : (
<Image source={R.images.icAccount} style={styles.imgAvatar} />
)}
{isUploadAvatar && (
<ActivityIndicator
color={'gray'}
size={'small'}
style={{
position: 'absolute',
}}
/>
)}
</View>
<View style={styles.iconPicker}>
<Image
style={{width: 16, height: 16, tintColor: R.colors.white}}
source={R.images.iconCamera}
/>
</View>
</View>
<PickerImg
isModalVisible={isModalVisible}
setModalVisible={setModalVisible}
cropping={true}
onClickImage={image => {
uploadAvatar(image);
}}
/>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
width: WIDTHXD(350),
height: HEIGHTXD(280),
backgroundColor: R.colors.white,
borderRadius: 10,
borderWidth: 2,
borderColor: '#DBDBDB',
borderStyle: 'dashed',
justifyContent: 'center',
alignItems: 'center',
},
imgAvatar: {
width: 70,
height: 70,
borderRadius: 35,
borderColor: R.colors.main,
backgroundColor: R.colors.gray1,
},
containerImg: {
width: 75,
height: 75,
},
iconPicker: {
width: 24,
height: 24,
position: 'absolute',
justifyContent: 'center',
alignItems: 'center',
right: 3,
backgroundColor: '#1754C7',
borderRadius: 12,
bottom: 0,
},
txt: {
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
paddingLeft: 10,
},
selectionImg: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
containerSelect: {
backgroundColor: 'white',
paddingTop: 5,
paddingBottom: 30,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
line: {
height: 0.5,
backgroundColor: R.colors.gray1,
width: '100%',
marginTop: 5,
},
imgIcon: {
width: 40,
height: 40,
},
txtTitleBtn: {
textAlign: 'center',
fontSize: 14,
color: R.colors.black,
},
});
const mapStateToProps = state => {
return {
userInfo: state.userReducer.userInfo,
};
};
export default connect(mapStateToProps, {
showLoading,
hideLoading,
saveUserToRedux,
})(PickerAvatar);
import React, {useState, useEffect} from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import DatePicker from 'react-native-datepicker';
import Icon from 'react-native-vector-icons/AntDesign';
import {HEIGHTXD, WIDTHXD, getFontXD, HEIGHT} from '../../config/Functions';
import R from '../../assets/R';
import I18n from '../../helper/i18/i18n';
import moment from 'moment';
/**
* Note
*
* placeholder: if have not value placeholder is visibled
* defaultValue : if have defaultValue it will be seted for value of date
* other you can make minDate,maxDate... with props of libary react-native-datepicker
*/
const PickerDate = props => {
const [time, setTime] = useState(new Date());
useEffect(() => {
const checkDefaultValue = () => {
if (
props.defaultValue === undefined ||
props.defaultValue === null ||
props.defaultValue == ''
) {
setTime(null);
} else {
// let str = props.defaultValue.split("-");
let date = new Date(moment(props.defaultValue, 'DD/MM/YYYY'));
setTime(date);
}
};
checkDefaultValue();
}, []);
return (
<>
{props.title ? (
<Text
style={{
fontSize: R.fontsize.fontSizeLabel,
fontWeight: '700',
color: R.colors.black,
marginBottom: 8,
}}>
<Text>{props.title}</Text>
{props.required && <Text style={{color: R.colors.red1}}> *</Text>}
</Text>
) : null}
{props.disable ? (
<View
style={{
flexDirection: 'row',
height: HEIGHT(40),
fontWeight: '400',
paddingHorizontal: 17,
borderRadius: 10,
borderBottomColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: R.colors.borderGray,
}}>
<Text
style={{
flex: 1,
color: R.colors.black,
fontSize: R.fontsize.fontSizeContent,
}}>
{props.defaultValue}
</Text>
<Icon name={'calendar'} size={24} color={'#4B4B4B'} />
</View>
) : (
<TouchableOpacity
style={{
flexDirection: 'row',
height: HEIGHT(40),
fontWeight: '400',
paddingHorizontal: 17,
borderRadius: 10,
borderWidth: 1,
borderColor: R.colors.borderGray,
}}>
<DatePicker
date={time}
format="DD/MM/YYYY"
confirmBtnText={I18n.t('Ok')}
cancelBtnText={I18n.t('Cancel')}
locale="vi"
style={{flex: 1}}
mode="date"
iconComponent={
<Icon name={'calendar'} size={24} color={'#4B4B4B'} />
}
placeholder={props.placeholder}
git
customStyles={{
dateIcon: {
position: 'absolute',
top: 4,
right: 10,
marginLeft: 0,
},
dateInput: {
alignItems: 'flex-start',
flex: 1,
width: '100%',
borderWidth: 0,
marginLeft: WIDTHXD(0),
color: R.colors.black,
},
dateText: {
color: R.colors.black,
fontSize: R.fontsize.fontSizeContent,
},
placeholderText: {
color: R.colors.colorNhap,
fontSize: 16,
},
}}
onDateChange={date => {
setTime(date);
props.pickDate(date);
}}
/>
</TouchableOpacity>
)}
<View
style={{
height: 20,
marginTop: 5,
}}>
{props.error ? (
<Text
style={{
color: R.colors.red1,
fontSize: getFontXD(32),
}}>
{`${I18n.t('PleaseSelect')}${props.title.toLowerCase()}`}
</Text>
) : null}
</View>
</>
);
};
export default PickerDate;
import React, {useState, useEffect} from 'react';
import {Text, View, StyleSheet, TouchableOpacity, Image} from 'react-native';
import DatePicker from 'react-native-datepicker';
import Icon from 'react-native-vector-icons/AntDesign';
import {HEIGHTXD, WIDTHXD, getFontXD, getWidth} from '../../config/Functions';
import R from '../../assets/R';
import I18n from '../../helper/i18/i18n';
import moment from 'moment';
/**
* Note
*
* placeholder: if have not value placeholder is visibled
* defaultValue : if have defaultValue it will be seted for value of date
* other you can make minDate,maxDate... with props of libary react-native-datepicker
*/
const PickerDate = props => {
const [time, setTime] = useState(new Date());
useEffect(() => {
const checkDefaultValue = () => {
if (
props.value === undefined ||
props.value === null ||
props.value == ''
) {
setTime(null);
} else {
// let str = props.defaultValue.split("-");
let date = new Date(moment(props.value, 'DD/MM/YYYY'));
setTime(date);
}
};
checkDefaultValue();
}, [props.value]);
return (
<DatePicker
date={time}
format="DD/MM/YYYY"
confirmBtnText={'Đồng ý'}
cancelBtnText={'Huỷ'}
locale="vi"
style={props.containerStyle}
mode="date"
iconComponent={
time ? (
<TouchableOpacity
style={{
marginBottom: 0,
position: 'absolute',
top: 10,
right: 0,
}}
onPress={() => {
setTime();
props.pickDate(null);
}}>
<Image
resizeMode="contain"
source={R.images.icClose}
style={{
width: 18,
height: 18,
tintColor: time ? R.colors.black : '#4B4B4B',
}}
size={22}
/>
</TouchableOpacity>
) : (
<Image
resizeMode="contain"
source={R.images.icDatePicker}
style={{
marginBottom: 0,
position: 'absolute',
top: 10,
right: 0,
width: 18,
height: 18,
}}
size={22}
color={'#4B4B4B'}
/>
)
}
placeholder={props.placeholder}
git
customStyles={{
dateInput: {
alignItems: 'flex-start',
flex: 1,
color: R.colors.black,
justifyContent: 'center',
borderWidth: 0,
},
dateText: {
color: time ? 'black' : '#575757',
fontSize: 16,
},
placeholderText: {
color: '#575757',
fontSize: 16,
},
}}
onDateChange={date => {
setTime(date);
props.pickDate(date);
}}
/>
);
};
export default PickerDate;
import React, {useState, useEffect} from 'react';
import {
View,
Text,
Image,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
Platform,
PermissionsAndroid,
} from 'react-native';
import R from '../../assets/R';
import {
HEIGHTXD,
WIDTHXD,
getFontXD,
requestCameraPermission,
getWidth,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import Modal from 'react-native-modal';
import ImagePicker from 'react-native-image-crop-picker';
import AppText from '../AppText';
const options = {
title: 'Select Avatar',
customButtons: [{name: 'fb', title: 'Choose Photo from Facebook'}],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
const PickerImgUni = props => {
const {title, height, width, callApi} = props;
const [isModalVisible, setModalVisible] = useState(false);
const [urlImg, setUrlImg] = useState('');
const checkPermissionAndroid = () => {
if (!PermissionsAndroid.check('CAMERA')) {
requestCameraPermission();
}
};
const onchoosGalery = () => {
ImagePicker.openPicker({
mediaType: 'photo',
multiple: false,
height: 350,
width: 575,
cropping: true,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
parseData(image);
});
};
const onCapture = () => {
if (Platform.OS == 'android') checkPermissionAndroid();
ImagePicker.openCamera({
mediaType: 'photo',
height: 350,
width: 575,
cropping: true,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
parseData(image);
});
};
const parseData = photo => {
const data = new FormData();
let fileName = photo.filename;
if (!fileName || fileName === undefined) {
let pathArray = photo.path.split('/');
fileName = pathArray[pathArray.length - 1];
}
data.append('files', {
name: fileName.replace(/HEIC/g, 'jpg'),
type: photo.mime,
size: photo.size,
uri: photo.path,
});
callApi(data);
};
return (
<View onPress={() => setModalVisible(true)}>
<View style={{marginBottom: 5}}>
<Text style={styles.txt}>{title}:</Text>
</View>
<TouchableOpacity onPress={() => setModalVisible(true)}>
{urlImg ? (
<Image
resizeMode={'contain'}
source={{uri: urlImg}}
style={styles.containerIdentify}
/>
) : (
<View style={styles.containerIdentify}>
<Image source={R.images.icCamera} style={styles.image} />
</View>
)}
</TouchableOpacity>
<Modal
isVisible={isModalVisible}
style={{margin: 0, justifyContent: 'flex-end'}}
onSwipeComplete={() => setModalVisible(false)}
swipeDirection={['up', 'left', 'right', 'down']}>
<TouchableWithoutFeedback onPress={() => setModalVisible(false)}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
<View style={styles.containerSelect}>
<AppText
i18nKey={'Select_source_image'}
style={{
textAlign: 'center',
fontSize: 16,
fontWeight: 'bold',
color: '#1473E6',
}}
/>
<View style={styles.line} />
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<TouchableOpacity style={styles.selectionImg} onPress={onCapture}>
<Image style={styles.imgIcon} source={R.images.iconCamera} />
<AppText i18nKey={'Take_photo'} style={styles.txtTitleBtn} />
</TouchableOpacity>
<TouchableOpacity
style={styles.selectionImg}
onPress={onchoosGalery}>
<Image style={styles.imgIcon} source={R.images.iconImg} />
<AppText i18nKey={'Photo_library'} style={styles.txtTitleBtn} />
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: R.colors.white,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
txt: {
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
},
selectionImg: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
containerSelect: {
backgroundColor: 'white',
paddingVertical: 20,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderRadius: 8,
},
line: {
height: 1,
backgroundColor: '#929292',
width: '100%',
marginTop: 5,
},
imgIcon: {
width: 40,
height: 40,
},
txtTitleBtn: {
textAlign: 'center',
fontSize: 16,
color: '#1473E6',
},
containerIdentify: {
height: HEIGHTXD(500),
marginHorizontal: 30,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 0.6,
borderStyle: 'dashed',
borderColor: R.colors.gray1,
backgroundColor: R.colors.colorBgScreen,
},
image: {
height: 48,
width: 48,
},
});
export default React.memo(PickerImgUni);
import React, {useState} from 'react';
import {
View,
Text,
Image,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
Platform,
PermissionsAndroid,
} from 'react-native';
import R from '../../assets/R';
import {
HEIGHTXD,
WIDTHXD,
getFontXD,
requestCameraPermission,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import Modal from 'react-native-modal';
import ImagePicker from 'react-native-image-crop-picker';
import AppText from '../AppText';
const options = {
title: 'Select Avatar',
customButtons: [{name: 'fb', title: 'Choose Photo from Facebook'}],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
const checkPermissionAndroid = () => {
if (!PermissionsAndroid.check('CAMERA')) {
requestCameraPermission();
}
};
const PickerImg = props => {
const {onClickImage, isModalVisible, setModalVisible} = props;
let option;
if (props.cropping) {
option = {
mediaType: 'photo',
multiple: false,
cropping: true,
width: 300,
height: 300,
};
} else {
option = {
mediaType: 'photo',
multiple: false,
};
}
const onChooseGallery = () => {
ImagePicker.openPicker(option).then(image => {
setModalVisible(false);
onClickImage(image);
});
};
const onCapture = () => {
if (Platform.OS == 'android') checkPermissionAndroid();
let option;
if (props.cropping) {
option = {
mediaType: 'photo',
multiple: false,
cropping: true,
width: 300,
height: 300,
};
} else {
option = {
mediaType: 'photo',
multiple: false,
};
}
ImagePicker.openCamera(option).then(image => {
setModalVisible(false);
onClickImage(image);
});
};
return (
<Modal
isVisible={isModalVisible}
style={{margin: 0, justifyContent: 'flex-end'}}
onSwipeComplete={() => setModalVisible(false)}
swipeDirection={['up', 'left', 'right', 'down']}>
<TouchableWithoutFeedback onPress={() => setModalVisible(false)}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
<View style={styles.containerSelect}>
<AppText
i18nKey={'Select_source_image'}
style={{
paddingVertical: 15,
fontSize: 16,
color: R.colors.black,
fontWeight: 'bold',
textAlign: 'center',
}}
/>
<View style={styles.line} />
<View
style={{
justifyContent: 'space-around',
flexDirection: 'row',
alignItems: 'center',
}}>
<TouchableOpacity style={styles.selectionImg} onPress={onCapture}>
<Image style={styles.imgIcon} source={R.images.iconCamera} />
<AppText i18nKey={'Take_photo'} style={styles.txtTitleBtn} />
</TouchableOpacity>
<TouchableOpacity
style={styles.selectionImg}
onPress={onChooseGallery}>
<Image style={styles.imgIcon} source={R.images.icGallery} />
<AppText i18nKey={'Photo_library'} style={styles.txtTitleBtn} />
</TouchableOpacity>
</View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
container: {
width: WIDTHXD(350),
height: HEIGHTXD(280),
backgroundColor: R.colors.white,
borderRadius: 5,
borderWidth: 2,
borderColor: '#DBDBDB',
borderStyle: 'dashed',
justifyContent: 'center',
alignItems: 'center',
},
txt: {
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
paddingLeft: 10,
},
selectionImg: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
containerSelect: {
backgroundColor: 'white',
paddingTop: 5,
paddingBottom: 30,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
line: {
height: 0.5,
backgroundColor: R.colors.gray1,
width: '100%',
marginTop: 5,
},
imgIcon: {
width: 40,
height: 40,
},
txtTitleBtn: {
textAlign: 'center',
fontSize: 14,
color: R.colors.black,
},
});
export default React.memo(PickerImg);
import React, {useState, useEffect} from 'react';
import {
View,
Text,
Image,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
Platform,
PermissionsAndroid,
} from 'react-native';
import R from '../../assets/R';
import {
HEIGHTXD,
WIDTHXD,
getFontXD,
requestCameraPermission,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import Modal from 'react-native-modal';
import Block from '../Block';
import ImagePicker from 'react-native-image-crop-picker';
import AppText from '../AppText';
const options = {
title: 'Select Avatar',
customButtons: [{name: 'fb', title: 'Choose Photo from Facebook'}],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
const PickerImgUni = props => {
const {title, height, width, onSelectImg} = props;
const [isModalVisible, setModalVisible] = useState(false);
const [urlImg, setUrlImg] = useState('');
// const [imgPicker, setImgPicker] = useState('');
const checkPermissionAndroid = () => {
if (!PermissionsAndroid.check('CAMERA')) {
requestCameraPermission();
}
};
const onchoosGalery = () => {
ImagePicker.openPicker({
mediaType: 'photo',
multiple: false,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
onSelectImg(image.path);
});
};
const onCapture = () => {
if (Platform.OS == 'android') checkPermissionAndroid();
ImagePicker.openCamera({
mediaType: 'photo',
width: 300,
height: 400,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
onSelectImg(image.path);
});
};
return (
<TouchableOpacity
onPress={() => setModalVisible(true)}
style={{
marginTop: 10,
width: WIDTHXD(480),
alignItems: 'center',
}}>
<View style={{width: WIDTHXD(480), marginBottom: 5}}>
<Text style={styles.txt}>{title}:</Text>
</View>
{urlImg ? (
<Image source={{uri: urlImg}} style={styles.container} />
) : (
<View style={styles.container}>
<Icon name={'plus'} size={40} color={'#DBDBDB'} />
</View>
)}
<Modal
isVisible={isModalVisible}
style={{margin: 0, justifyContent: 'flex-end'}}
onSwipeComplete={() => setModalVisible(false)}
swipeDirection={['up', 'left', 'right', 'down']}>
<TouchableWithoutFeedback onPress={() => setModalVisible(false)}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
<View style={styles.containerSelect}>
<AppText
i18nKey={'Select_source_image'}
style={{
textAlign: 'center',
fontSize: 16,
fontWeight: 'bold',
color: '#1473E6',
}}
/>
<View style={styles.line} />
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<TouchableOpacity style={styles.selectionImg} onPress={onCapture}>
<Image style={styles.imgIcon} source={R.images.iconCamera} />
<AppText i18nKey={'Take_photo'} style={styles.txtTitleBtn} />
</TouchableOpacity>
<TouchableOpacity
style={styles.selectionImg}
onPress={onchoosGalery}>
<Image style={styles.imgIcon} source={R.images.iconImg} />
<AppText i18nKey={'Photo_library'} style={styles.txtTitleBtn} />
</TouchableOpacity>
</View>
</View>
</Modal>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
width: WIDTHXD(350),
height: HEIGHTXD(320),
backgroundColor: R.colors.white,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
txt: {
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
},
selectionImg: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
containerSelect: {
height: HEIGHTXD(520),
backgroundColor: 'white',
paddingTop: 10,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
line: {
height: 1,
backgroundColor: '#929292',
width: '100%',
marginTop: 5,
},
imgIcon: {
width: 40,
height: 40,
},
txtTitleBtn: {
textAlign: 'center',
fontSize: 16,
color: '#1473E6',
},
});
export default React.memo(PickerImgUni);
import React, {Component} from 'react';
import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
TouchableHighlight,
Dimensions,
Platform,
} from 'react-native';
import ModalDropdown from 'react-native-modal-dropdown';
import R from '../../assets/R';
import {
WIDTHXD,
getFontXD,
HEIGHTXD,
getHeight,
WIDTHXDICON,
HEIGHT,
WIDTH,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
const data = [];
/**
* This Function to show piker with list date (for example [{name:'Picker1'},{name:'Picker2}])
* @callback onValueChange return value of item you choice
* @param value value of picker you choice
* @param defaultIndex defaultIndex of picker you choice
* @param containerStyle custom containerStyle of view
* @param data data value of date
* @param width width of picker
* @param height height of picker
* @param date value of date you choice
* @param heightItem height of picker Item
* @param maxHeight set height of list
* @param iconDropdown to set icon for dropdown
* @param iconDropdownStyle to style icon dropdown
* other you can make minDate,maxDate... with props of libary react-native-datepicker
*/
export default class PickerItem extends Component {
constructor(props) {
super(props);
this._button = null;
this._buttonFrame = null;
this.state = {
value: '',
showInBottom: true,
};
}
componentDidMount() {
this._dropdown.select(0);
}
_dropdownAdjustFrame = style => {
const {showInBottom} = this.state;
// alert(showInBottom);
let stylez = style;
if (!showInBottom) {
stylez.height += HEIGHTXD(99) * (6 - Math.min(this.props.data.length, 5));
} else {
stylez.height += HEIGHTXD(99);
}
// stylez.left += 150;
return stylez;
};
render() {
const {
width,
onValueChange,
containerStyle,
height,
value,
defaultValue,
data,
defaultIndex,
iconDropdown,
iconDropdownStyle,
disabled,
isTriangle,
textStyle,
iconSize,
iconColor,
} = this.props;
return (
<View style={styles.cell}>
<TouchableOpacity
disabled={disabled}
ref={button => {
this._button = button;
}}
onPress={() => {
this._dropdown.show();
// this._updatePosition();
}}
style={[
styles.pickerStyle,
containerStyle !== null && containerStyle,
height && {height},
width && {width},
]}>
<Text
numberOfLines={1}
style={[
styles.dropdown_row_text,
width && {width: width - WIDTHXD(125)},
textStyle ? textStyle : {},
]}>
{defaultValue || this.state.value}
</Text>
{iconDropdown || isTriangle ? (
<Icon
name={'up'}
size={iconSize ? iconSize : 20}
color={iconColor ? iconColor : R.colors.borderGray}
/>
) : (
<Icon
name={'down'}
size={iconSize ? iconSize : 20}
color={iconColor ? iconColor : R.colors.borderGray}
/>
)}
</TouchableOpacity>
<ModalDropdown
showsVerticalScrollIndicator={false}
saveScrollPosition={false}
ref={el => {
this._dropdown = el;
}}
style={[styles.dropdown, width && {width}]}
defaultValue={defaultValue || '0'}
defaultIndex={defaultIndex || 0}
textStyle={styles.dropdown_text}
dropdownStyle={[
styles.dropdown_dropdown,
{maxHeight: HEIGHTXD(99 * Math.min(data.length, 6) + 12)},
width && {width},
]}
options={data !== null && data}
onSelect={value => {
onValueChange && onValueChange(value, data[value]);
this.setState({value: data[value].name});
}}
renderRow={(option, index, isSelected) => (
<View
style={[
styles.dropdown_row,
{
backgroundColor:
option.value == value?.value ? '#C0C0C0' : '#f2f2f2',
},
]}>
<Text
numberOfLines={1}
style={[
styles.dropdown_row_text,
{
marginHorizontal: WIDTHXD(30),
// color: option.value == value?.value ? 'black' : 'black',
// fontWeight: option.value == value?.value ? '700' : '400',
fontWeight: '400',
color: 'black',
},
]}>
{`${option.name}`}
</Text>
</View>
)}
renderButtonText={rowData => this.renderButtonText(rowData)}
// adjustFrame={(style) => this._dropdownAdjustFrame(style)}
// renderSeparator={(sectionID, rowID, adjacentRowHighlighted) => this.renderSeparator(sectionID, rowID, adjacentRowHighlighted)}
/>
</View>
);
}
renderButtonText = () => ' ';
renderSeparator = rowID => {
if (rowID === data.length - 1) {
return [];
}
let key = `spr_${rowID}`;
return <View style={styles.dropdown_separator} key={key} />;
};
}
const styles = StyleSheet.create({
cell: {
flex: 0,
},
dropdown: {
alignSelf: 'center',
width: '100%',
height: HEIGHTXD(0),
},
dropdown_text: {
fontSize: 16,
},
dropdown_dropdown: {
width: '100%',
maxHeight: HEIGHTXD(200),
borderBottomLeftRadius: WIDTHXD(20),
borderBottomRightRadius: WIDTHXD(20),
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.2,
shadowRadius: 1.41,
elevation: 2,
marginTop: Platform.OS == 'ios' ? 0 : -21,
},
dropdown_row: {
flexDirection: 'row',
height: HEIGHTXD(100),
alignItems: 'center',
paddingHorizontal: 5,
},
dropdown_row_text: {
// marginHorizontal: 4,
fontSize: 16,
textAlignVertical: 'center',
},
dropdown_separator: {
borderBottomWidth: 0.3,
borderBottomColor: R.colors.iconGray,
},
pickerStyle: {
width: '100%',
height: HEIGHT(40),
flexDirection: 'row',
paddingHorizontal: WIDTH(10),
justifyContent: 'space-between',
alignItems: 'center',
alignSelf: 'center',
backgroundColor: 'white',
marginTop: 5,
borderRadius: 7,
borderWidth: 1,
borderColor: R.colors.borderGray,
fontSize: 16,
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: 'white',
},
});
// @flow
import React, {Component} from 'react';
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
import R from '../../assets/R';
import {HEIGHTXD, WIDTHXD, getFontXD} from '../../config/Functions';
import ModalSearch from './ModalSearch';
/**
* This Function to show piker search with list date (for example [{name:'Picker1'},{name:'Picker2}])
* This show view with input when touch it show a modal search
* @callback onValueChange return value of item you choice
* @param title title of popup search
* @param containerStyle custom containerStyle of view
* @param data style value of date
* @param width width of picker
* @param date value of date you choice
* @param value value of date you choice
* @param searchPickerStyle custom searchPickerStyle of TouchableOpacity
* @param placeholder value of placeholder
* @param textStyle custom text in Text
* other you can make minDate,maxDate... with props of libary react-native-datepicker
*/
class PickerSearch extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
item: null,
reRender: false,
};
}
componentDidMount = async () => {
const {value} = this.props;
if (value) {
this.setState({value});
}
};
componentWillReceiveProps(nextProps) {
if (
nextProps.data !== this.props.data &&
(nextProps.data.length > 0 || nextProps.isNeedClearData)
) {
this.setState({reRender: this.state.reRender});
}
}
onChangeText = text => {
this.ModalSearch && this.ModalSearch.changeName(text);
};
render() {
const {value, item} = this.state;
const {
textStyle,
containerStyle,
placeholder,
width,
data,
title,
onValueChange,
findData,
disabled,
height,
tempDisabled,
onShowPorm,
style,
} = this.props;
const choosed =
this.props.value && this.props.value != '' && this.props.value !== null
? true
: false;
return (
<View style={[styles.container, style ? style : {}]}>
<TouchableOpacity
disabled={disabled}
onPress={() => {
tempDisabled
? onShowPorm && onShowPorm()
: this.ModalSearch.setModalVisible(true);
}}
style={[
styles.searchPicker,
containerStyle !== null && containerStyle,
width && {width},
height && {height},
]}>
{value === '' && this.props.value && this.props.value === '' ? (
<Text>{placeholder !== null && placeholder}</Text>
) : (
<Text
numberOfLines={1}
style={[
styles.textStyle,
textStyle !== null && textStyle,
width && {width: width - WIDTHXD(150)},
]}>
{this.props.value !== null && this.props.value}
</Text>
)}
<TouchableOpacity
onPress={() => {
this.setState({value: '', item: null});
onValueChange && onValueChange('', null);
}}
hitSlop={{left: 20, top: 20, right: 20, bottom: 20}}
disabled={!choosed || disabled}>
<AntDesign
name={!choosed || disabled ? 'search1' : 'close'}
size={WIDTHXD(43)}
color={R.colors.iconGray}
/>
</TouchableOpacity>
</TouchableOpacity>
<ModalSearch
ref={ref => {
this.ModalSearch = ref;
}}
data={data}
isNeedClearData={this.props.isNeedClearData}
findData={findData !== null && findData}
onValueChange={(Value, item) => {
this.setState({value: Value, item: item});
onValueChange && onValueChange(item.value, item);
}}
title={title}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
borderRadius: HEIGHTXD(8),
flexDirection: 'row',
alignItems: 'center',
width: WIDTHXD(960),
},
searchPicker: {
width: WIDTHXD(960),
borderRadius: HEIGHTXD(20),
paddingHorizontal: WIDTHXD(36),
justifyContent: 'space-between',
flexDirection: 'row',
alignItems: 'center',
height: HEIGHTXD(99),
borderWidth: 0.3,
borderColor: R.colors.iconGray,
color: R.colors.black0,
},
textStyle: {
fontFamily: R.fonts.RobotoRegular,
width: WIDTHXD(800),
fontSize: 16,
},
});
export default PickerSearch;
import React, {useState, useEffect} from 'react';
import {
View,
Text,
Image,
StyleSheet,
TouchableOpacity,
TouchableWithoutFeedback,
Platform,
PermissionsAndroid,
} from 'react-native';
import R from '../../assets/R';
import {
HEIGHTXD,
WIDTHXD,
getFontXD,
requestCameraPermission,
getWidth,
} from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import Modal from 'react-native-modal';
import ImagePicker from 'react-native-image-crop-picker';
import AppText from '../AppText';
const options = {
title: 'Select Avatar',
customButtons: [{name: 'fb', title: 'Choose Photo from Facebook'}],
storageOptions: {
skipBackup: true,
path: 'images',
},
};
const PickerImgUni = props => {
const {title} = props;
const [isModalVisible, setModalVisible] = useState(false);
const [urlImg, setUrlImg] = useState('');
const checkPermissionAndroid = () => {
if (!PermissionsAndroid.check('CAMERA')) {
requestCameraPermission();
}
};
const onchoosGalery = () => {
ImagePicker.openPicker({
mediaType: 'photo',
multiple: false,
height: 350,
width: 575,
cropping: true,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
parseData(image);
});
};
const onCapture = () => {
if (Platform.OS == 'android') checkPermissionAndroid();
ImagePicker.openCamera({
mediaType: 'photo',
height: 350,
width: 575,
cropping: true,
}).then(image => {
setModalVisible(false);
setUrlImg(image.path);
parseData(image);
});
};
const parseData = photo => {
const data = new FormData();
let fileName = photo.filename;
if (!fileName || fileName === undefined) {
let pathArray = photo.path.split('/');
fileName = pathArray[pathArray.length - 1];
}
data.append('files', {
name: fileName.replace(/HEIC/g, 'jpg'),
type: photo.mime,
size: photo.size,
uri: photo.path,
});
callApi(data);
};
const callApi = () => {
console.log('call');
};
return (
<View onPress={() => setModalVisible(true)}>
<View style={{marginBottom: 5}}>
<Text style={styles.txt}>{title}:</Text>
</View>
<TouchableOpacity onPress={() => setModalVisible(true)}>
{urlImg ? (
<Image
resizeMode={'contain'}
source={{uri: urlImg}}
style={styles.containerIdentify}
/>
) : (
<View style={styles.containerIdentify}>
<Image source={R.images.icCamera} style={styles.image} />
</View>
)}
</TouchableOpacity>
<Modal
isVisible={isModalVisible}
style={{margin: 0, justifyContent: 'flex-end'}}
onSwipeComplete={() => setModalVisible(false)}
swipeDirection={['up', 'left', 'right', 'down']}>
<TouchableWithoutFeedback onPress={() => setModalVisible(false)}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
<View style={styles.containerSelect}>
<AppText
i18nKey={'Select_source_image'}
style={{
textAlign: 'center',
fontSize: 16,
fontWeight: 'bold',
color: '#1473E6',
}}
/>
<View style={styles.line} />
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<TouchableOpacity style={styles.selectionImg} onPress={onCapture}>
<Image style={styles.imgIcon} source={R.images.iconCamera} />
<AppText i18nKey={'Take_photo'} style={styles.txtTitleBtn} />
</TouchableOpacity>
<TouchableOpacity
style={styles.selectionImg}
onPress={onchoosGalery}>
<Image style={styles.imgIcon} source={R.images.iconImg} />
<AppText i18nKey={'Photo_library'} style={styles.txtTitleBtn} />
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: R.colors.white,
borderRadius: 5,
justifyContent: 'center',
alignItems: 'center',
},
txt: {
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
},
selectionImg: {
padding: 10,
justifyContent: 'center',
alignItems: 'center',
},
containerSelect: {
backgroundColor: 'white',
paddingVertical: 20,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
borderRadius: 8,
},
line: {
height: 1,
backgroundColor: '#929292',
width: '100%',
marginTop: 5,
},
imgIcon: {
width: 40,
height: 40,
},
txtTitleBtn: {
textAlign: 'center',
fontSize: 16,
color: '#1473E6',
},
containerIdentify: {
height: HEIGHTXD(500),
marginHorizontal: 30,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 0.6,
borderStyle: 'dashed',
borderColor: R.colors.gray1,
backgroundColor: R.colors.colorBgScreen,
},
image: {
height: 48,
width: 48,
},
});
export default React.memo(PickerImgUni);
import React, {useState, useEffect} from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
StatusBar,
TouchableWithoutFeedback,
} from 'react-native';
import R from '../assets/R';
import {getFontXD, HEIGHTXD} from '../config/Functions';
import LinearGradient from 'react-native-linear-gradient';
import Modal from 'react-native-modal';
import {connect} from 'react-redux';
import {showNotificaton, hideNotification} from '../actions/SnackBarAction';
import {useNavigation} from '@react-navigation/native';
import AppText from '../components/AppText';
const SnackBar = props => {
const navigate = useNavigation();
const {isOpen, title, content, screen, id_record} = props.snackReducer;
useEffect(() => {
if (isOpen)
setTimeout(() => {
props.hideNotification();
}, 6000);
}, [props.snackReducer]);
return (
<Modal
style={{padding: 0, margin: 0}}
animationInTiming={1500}
animationOutTiming={800}
backdropOpacity={0}
animationIn={'slideInDown'}
animationOut={'slideOutUp'}
isVisible={isOpen}>
<View style={styles.container}>
<View style={{flex: 1}}>
<Text style={styles.txtTitle}>{title}</Text>
<Text numberOfLines={2} style={styles.txt}>
{content}
</Text>
</View>
<View style={styles.row}>
<TouchableOpacity
onPress={() => props.hideNotification()}
style={styles.btn}>
<AppText i18nKey={'Close'} style={styles.txtBtn} />
</TouchableOpacity>
{screen && id_record ? (
<TouchableOpacity
onPress={() => {
navigate.navigate(screen, {id: id_record});
}}
style={[styles.btn, {marginLeft: 20}]}>
<AppText i18nKey={'Detail'} style={styles.txtBtn} />
</TouchableOpacity>
) : null}
</View>
</View>
<TouchableWithoutFeedback onPress={() => props.hideNotification()}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
</Modal>
);
};
const mapStateToProps = state => {
return {
snackReducer: state.SnackReducer,
};
};
export default connect(mapStateToProps, {showNotificaton, hideNotification})(
SnackBar,
);
const styles = StyleSheet.create({
container: {
marginTop: 10,
height: HEIGHTXD(380),
backgroundColor: R.colors.white,
marginHorizontal: 10,
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 10,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 3,
},
row: {
flexDirection: 'row',
justifyContent: 'flex-end',
},
txt: {
color: R.colors.black,
},
txtBtn: {
fontSize: 16,
color: R.colors.txtMain,
fontWeight: 'bold',
},
txtTitle: {
fontSize: 16,
color: R.colors.black,
fontWeight: 'bold',
},
});
import React, {useEffect, useState} from 'react';
import {
View,
Text,
Modal,
TouchableOpacity,
StyleSheet,
Image,
TouchableWithoutFeedback,
Linking,
Platform,
} from 'react-native';
import R from '../assets/R';
import {
getFontXD,
getHeight,
getWidth,
HEIGHTXD,
WIDTHXD,
} from '../config/Functions';
import I18n from '../helper/i18/i18n';
import DeviceInfo from 'react-native-device-info';
import {connect} from 'react-redux';
import {getNewestVersionInfo} from '../apis/Functions/home';
import {showLoading, hideLoading} from '../actions/loadingAction';
const VersionChecker = props => {
const [visible, setVisible] = useState(false);
const [isForceUpdate, setIsForceUpdate] = useState(false);
const [version, setVersion] = useState('1.0');
useEffect(() => {
checkVersion();
}, []);
const checkVersion = async () => {
const res = await getNewestVersionInfo(
Platform.OS == 'ios' ? 'ios' : 'android',
);
if (res.data.code == 200 && res.data.data) {
if (
res.data.data.value.version_name != DeviceInfo.getVersion() ||
res.data.data.value.build != DeviceInfo.getBuildNumber()
) {
setVersion(res.data.data.value.version_name);
setVisible(true);
setIsForceUpdate(res.data.data.value.is_require_update);
}
}
};
const onUpdatePressed = async () => {
try {
if (Platform.OS === 'ios') {
Linking.openURL(
'itms-services://?action=download-manifest&url=https://investing.dcvfinance.com/public/assets/app/DCV.plist',
);
setVisible(false);
} else {
Linking.openURL(
'https://play.google.com/store/apps/details?id=com.dcvcard',
);
}
} catch (error) {}
};
const onRequestClose = () => null;
const renderBackdrop = () => {
return (
<View
style={{
backgroundColor: 'rgba(0,0,0,0.30)',
// backgroundColor: 'red',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: getWidth(),
height: getHeight(),
}}
/>
);
};
const cancelUpdate = () => {
setVisible(false);
};
return (
<Modal
onRequestClose={() => onRequestClose()}
transparent
animationType="fade"
style={{position: 'absolute'}}
visible={visible}>
{renderBackdrop()}
<View pointerEvents="box-none" style={styles.containerStyle}>
<View style={styles.imageUpgradeContainer} zIndex={100}>
<Image
source={R.images.iconUpgrade}
style={[styles.imageUpgradeStyle, {tintColor: R.colors.main}]}
/>
</View>
<View style={styles.contentContainerStyle}>
<Text style={styles.titleStyle}>
{props.language.language == 'vi' ? 'Cập nhật' : 'Update'}
</Text>
<Text style={styles.versionLabelStyle}>
{I18n.t('Version')}
{': '}
{version}
</Text>
<Text style={styles.descStyle}>
{' '}
{props.language.language == 'vi'
? 'Đã có phiên bản DCVFinance mới. Cập nhật ngay để tiếp tục sử dụng và trải nghiệm những tính năng mới nhất của hệ thống!'
: 'A new version of DCVFinance is available. Update now to continue using and experiencing the latest system features!'}
</Text>
{isForceUpdate ? (
<TouchableOpacity
onPress={() => onUpdatePressed()}
style={styles.notNowContainerStyle}>
<Text style={[styles.textNotNowStyle, {color: R.colors.main}]}>
{props.language.language == 'vi' ? 'Cập nhật' : 'Update'}
</Text>
</TouchableOpacity>
) : (
<View
style={[
styles.notNowContainerStyle,
{
flexDirection: 'row',
marginHorizontal: WIDTHXD(100),
},
]}>
<TouchableOpacity
onPress={() => cancelUpdate()}
style={[styles.btnButton, {paddingRight: WIDTHXD(60)}]}>
<Text
style={[
styles.textNotNowStyle,
{color: R.colors.color777, textAlign: 'right'},
]}>
{props.language.language == 'vi' ? 'Bỏ qua' : 'Cancel'}
</Text>
</TouchableOpacity>
<View style={styles.dividerStyleVertical}></View>
<TouchableOpacity
onPress={() => onUpdatePressed()}
style={[styles.btnButton, {paddingLeft: WIDTHXD(60)}]}>
<Text
style={[
styles.textNotNowStyle,
{color: R.colors.main, textAlign: 'left'},
]}>
{I18n.t('Update')}
</Text>
</TouchableOpacity>
</View>
)}
</View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
imageUpgradeStyle: {
width: 50,
height: 50,
tintColor: R.colors.primaryColor,
},
imageUpgradeContainer: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
},
containerStyle: {
flex: 1,
width: getWidth() * 0.8,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
},
contentContainerStyle: {
marginTop: -40,
paddingTop: 40,
width: getWidth() * 0.9,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 15,
},
logoStyle: {
width: 65,
height: 65,
marginVertical: 20,
},
titleStyle: {
fontWeight: '600',
fontSize: 20,
color: 'black',
paddingHorizontal: 8,
textAlign: 'center',
},
versionLabelStyle: {
fontSize: 14,
color: R.colors.grey600,
marginTop: 5,
marginBottom: 15,
paddingHorizontal: 8,
textAlign: 'center',
},
descStyle: {
fontSize: getFontXD(46),
color: R.colors.grey900,
marginBottom: 20,
paddingHorizontal: 10,
textAlign: 'center',
},
notNowContainerStyle: {
height: HEIGHTXD(160),
width: '100%',
alignItems: 'center',
justifyContent: 'center',
},
btnButton: {
flex: 1,
},
textNotNowStyle: {
fontSize: getFontXD(46),
width: '100%',
textAlign: 'center',
color: R.colors.primaryColor,
},
starContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
height: 45,
},
dividerStyle: {
height: 0.5,
width: '100%',
backgroundColor: R.colors.borderC,
},
dividerStyleVertical: {
height: HEIGHTXD(160),
width: 0.5,
backgroundColor: R.colors.borderC,
},
});
const mapStateToProps = state => {
return {
language: state.languageReducer,
};
};
export default connect(mapStateToProps, {showLoading, hideLoading})(
VersionChecker,
);
import React from 'react';
import {
Dimensions,
Platform,
Alert,
Text,
PermissionsAndroid,
Linking,
FlatList,
} from 'react-native';
import moment from 'moment';
import _ from 'lodash';
import I18n from '../helper/i18/i18n';
import AppText from '../components/AppText';
import AsyncStorage from '@react-native-community/async-storage';
import {
LOGINSCREEN,
TRANSACTION_HISTORY_DETAIL,
MY_TEAM,
} from '../routers/ScreenNames';
import R from '../assets/R';
import KEY from '../assets/AsynStorage';
import {ASYNC_STORE_KEY} from './constants';
import {sha256, sha224} from 'js-sha256';
import {RFValue} from 'react-native-responsive-fontsize';
var Sound = require('react-native-sound');
export const soundStart = () => {
var whoosh = new Sound('sond_noti.mp3', Sound.MAIN_BUNDLE, error => {
if (error) {
console.log('failed to load the sound', error);
return;
}
whoosh.play(success => {
if (success) {
console.log('successfully finished playing');
} else {
console.log('playback failed due to audio decoding errors');
}
});
});
};
export const encryString = val => {
return val;
};
export const convertScreen = name => {
switch (name) {
case 'ADD_MEMBER_TEAM':
return MY_TEAM;
case 'ADMIN_APPROVE_PUSH_CARD':
case 'ADMIN_APPROVE_PULL_CARD':
return TRANSACTION_HISTORY_DETAIL;
// case "ADMIN_APPROVE_CREDIT_CARD":
// return BORROW_REQUEST;
// case "REQUEST_FINISH":
// return HISTORY_DETAIL;
// case "ADMIN_REJECT_REQUEST":
// return BORROW_REQUEST_DETAIL;
default:
return null;
}
};
export const logout = async navigation => {
await AsyncStorage.removeItem(KEY.ACCOUNT);
await AsyncStorage.removeItem(KEY.TOKEN);
navigation.reset({
index: 1,
routes: [{name: LOGINSCREEN}],
});
};
export const requestCameraPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Cool Photo App Camera Permission',
message:
'Cool Photo App needs access to your camera ' +
'so you can take awesome pictures.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('You can use the camera');
} else {
console.log('Camera permission denied');
}
} catch (err) {
console.warn(err);
}
};
export const NotificationAlert = string => {
Alert.alert(I18n.t('Notification'), string);
};
export const confirmAlert = (title, callback) => {
Alert.alert(
I18n.t('Notification'),
title,
[
{
text: I18n.t('Cancel'),
style: 'cancel',
},
{
text: I18n.t(Ok),
onPress: () => {
callback();
},
},
],
{cancelable: false},
);
};
const {width, height} = Dimensions.get('window');
export const getWidth = () => width;
export const getHeight = () => height;
export const getFontSize = fz => RFValue(fz - 2);
// Get size for xd
export const WIDTHXD = w => width * (w / 1125);
export const HEIGHTXD = h => height * (h / 2436);
export const getLineHeightXD = f => f / 3 + 2;
export const getFontXD = f => f / 3 + 2;
// Get size for figmar
export const WIDTH = w => width * (w / 1125) * 3;
export const HEIGHT = h => height * (h / 2436) * 3;
export const getLineHeight = f => f;
export const getFont = f => f - 1;
export const WIDTHXDICON = w => width * (w / 1125);
export const WIDTHNEW = w => width * (w / 1125) * 3;
export const HEIGHTNEW = h => height * (h / 2436) * 3;
export const validatePhone = str => {
let re = /^[0-9+]{9,11}$/;
return re.test(str);
};
// convert number 5000000=> 5.000.000
export const numberWithCommas = (x, c = '.') =>
Math.round(x)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, c ? c : '.');
export const isIos = Platform.OS === 'ios';
export const Gender = {
male: 0,
female: 1,
};
export const converType = type => {
if (type == 'DEPOSIT') return I18n.t('Deposit');
return I18n.t('Withdraw');
};
export const converStatus = status => {
switch (status) {
case 0:
return I18n.t('Waiting_for_Progress');
case 1:
return I18n.t('Success');
case 2:
return I18n.t('Cancel');
}
};
export const callNumber = phone => {
let phoneNumber = phone;
if (Platform.OS !== 'android') {
phoneNumber = `telprompt:${phone}`;
} else {
phoneNumber = `tel:${phone}`;
}
Linking.canOpenURL(phoneNumber)
.then(supported => {
if (!supported) {
Alert.alert(I18n.t('PhoneAvailable'));
} else {
return Linking.openURL(phoneNumber);
}
})
.catch(err => console.log(err));
};
export function numberFormat(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
}
export const toPriceVnd = (str, isTextView = false) => {
if (isTextView && (!str || str === '0' || str === 0)) return '0';
if (str) {
let stringPrice = str.toString().split('.');
let headStringPrice = `${stringPrice[0]
.toString()
.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1.')}`;
return stringPrice.length === 1
? headStringPrice
: headStringPrice.concat(`,${stringPrice[1]}`);
} else {
return '';
}
};
// cắt chuỗi dạng something...
export const ellipsis = (str = '', max = 30) =>
str.length > max ? `${str.substring(0, max)}...` : str;
// tính chiều rộng của item dưới dạng flatlist
export const itemWidth = (numColumns, padding) => {
let totalPadding = padding * (numColumns + 1);
let w = (width - totalPadding) / numColumns;
return w;
};
// chuyển string thành dạng viết hoa
export const toUpperCase = str => (str ? str.toUpperCase() : '');
export const sortType = {
sortDefault: 1,
latestNews: 2,
priceUp: 3,
priceDown: 4,
};
// delete item from array
export const removeItemFromArr2 = (items, index) => {
let fill = items.filter((e, i) => i !== index);
return fill;
};
export const removeItemFromArr = (items, index) => {
items.splice(index, 1);
return items;
};
// sum field of array object
export const totalByValue = (data, field) =>
data.length === 0
? 0
: data.map(item => item[field]).reduce((prev, next) => prev + next);
// show notification
export const popupOk = (title, msg, onPress = null) => {
Alert.alert(
title,
msg,
[{text: 'Ok', style: 'ok', onPress: onPress || (() => null)}],
{cancelable: false},
);
};
export const popupCancel = (title, msg, onPress = null) => {
Alert.alert(
title,
msg,
[
{text: 'Cancel', style: 'cancel'},
{text: 'Ok', style: 'ok', onPress: onPress || (() => null)},
],
{cancelable: false},
);
};
export const cutStringBetweenCharacters = (str, char1, char2, addFirst) => {
let re = `\\${char1}([^${char2}]+)\\${char2}`;
let reg = new RegExp(re);
let s = reg.exec(str) ? reg.exec(str)[1] : '';
return addFirst ? char1 + s : s;
};
export const mark = (str, char1, char2, tag, endTag) => {
let re = `\\${char1}(.*?)\\${char2}`;
let reg = new RegExp(re, 'gi');
return str.replace(reg, `<${tag}>$1</${endTag || tag}>12`);
};
export const cutStringBetweenCharacters2 = (str, char1, char2, addFirst) => {
let s = str.split(char1).pop().split(char2)[0];
return addFirst ? char1 + s : s;
};
export const replaceStrByIndex = (str, index, newStr) =>
str.substr(0, index) + newStr + str.substr(index + 1);
global.langCode = 'vi';
export const getLangCode = () => global.langCode;
export const setLangCode = langCode => {
global.langCode = langCode;
};
export const formatDateTime = time =>
moment(time).format('DD/MM/YYYY HH:mm:ss');
export const VideoMimeType = {
flv: 'video/x-flv',
mp4: 'video/mp4',
m3u8: 'application/x-mpegURL',
ts: 'video/MP2T',
'3gp': 'video/3gpp',
mov: 'video/quicktime',
avi: 'video/x-msvideo',
wmv: 'video/x-ms-wmv',
};
export const StringFromLastCharacter = (str, char) =>
str.substring(str.lastIndexOf(char) + 1);
export const toArrayBySeparators = (str, separators) => {
/**
* example: txt = "aaaa55bbb33cccc" => ["aaaa", "55", "bbb", "33", "cccc"]
* toArrayBySeparators(txt, [55,33])
*/
let reg = new RegExp(`(${separators.join('|')})`);
return str
.split(reg)
.filter(x => x.length > 0)
.map(x => x);
};
export const getRange = (startDate, endDate, type) => {
let fromDate = moment(startDate);
let toDate = moment(endDate);
let range = [];
let range2 = [];
// for (let i = 0; i < diff; i++) {
// console.log('diff: ', diff);
// range.push(moment(startDate).add(i, type))
// }
while (toDate > fromDate || fromDate.format('M') === toDate.format('M')) {
range.push(fromDate);
range2.push(fromDate.format('DD/MM'));
fromDate.add(1, type);
}
return range2;
};
export const getFirstAndLastWords = text => {
let t = text.split(' ');
return `${t[0]} ${t[t.length - 1]}`;
};
export const shortFullname = text => {
let arr = text.split(' ');
let name = '';
arr.forEach((e, i) => {
if (i === 0) {
name += `${e} `;
} else if (i === arr.length - 1) {
name += arr.length === 2 ? e : ` ${e}`;
} else {
name += `${e.charAt(0)}`;
}
});
return name;
};
export const convertTimeApi = date => {
const time = moment(date).format('YYYY-MM-DD');
return time;
};
export const convertDate = date => {
const time = moment(date).format('DD/MM/YYYY');
return time;
};
export const convertDateTime = date => {
const time = moment(date).format('hh:mm DD/MM/YYYY');
return time;
};
export const convertDateTimeNow = () => {
const time = moment().format('hh:mm DD/MM/YYYY');
return time;
};
export const convertTime = date => {
const temp = new Date(moment(date, 'YYYY-MM-DD'));
const time = moment(temp).format('DD/MM/YYYY');
return time;
};
export const convertTimeServer = date => {
const temp = new Date(moment(date, 'DD/MM/YYYY'));
const time = moment(temp).format('DD-MM-YYYY');
return time;
};
export const getTimeDDMM = time => {
let t1 = new Date(moment(time, 'DD/MM/YYYY').format('MM/DD/YYYY')).getTime();
return t1;
};
export const converStringToDate = time => {
let t1 = new Date(moment(time, 'YYYY-MM-DD').format('MM/DD/YYYY')).getTime();
const strDate = convertDate(t1);
return strDate;
};
export const checkPassWord = str => {
var regularExpression = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/;
return regularExpression.test(str);
};
export const sortDataByTime = data => {
data.sort((a, b) => {
let t1 = moment(a.time, 'DD-MM-YYYY').format('MM/YYYY');
let t2 = moment(b.time, 'DD-MM-YYYY').format('MM/YYYY');
if (t1 > t2) return -1;
else {
if (t1 < t2) return 1;
else return 0;
}
});
return data;
};
export const checkDifferMonth = (t1, t2) => t1 !== t2;
export const mapTimeVNToWorld = data => {
if (!data) return data;
// data 07/05/2019
let str = data && data.split('/');
if (str.length < 2) return data;
return `${str[1]}/${str[0]}/${str[2]}`;
};
export const checkFormatWhiteSpace = str => {
if (!str.replace(/\s/g, '').length) return true;
return false;
};
// check 1 mảng dạng [a,b,c] có phần tử nào null,undefined,empty ko.
// nếu có trả về index phần từ đầu tiên ko thì return true
export const checkFormatArray = array => {
let i = -1;
if (!_.isEmpty(array)) {
for (let j = 0; j < array.length; j++) {
if (_.isUndefined(array[j]) || _.isNull(array[j]) || array[j] === '') {
i = j;
break;
}
}
}
if (i === -1) {
return true;
} else {
return i;
}
};
export const checkFormatArrayWhiteSpace = array => {
let i = -1;
if (!_.isEmpty(array)) {
for (let j = 0; j < array.length; j++) {
if (checkFormatWhiteSpace(array[j])) {
i = j;
break;
}
}
}
if (i === -1) {
return true;
} else {
return i;
}
};
// check 1 phần từ có null, undefined hay empty ko
export const checkFormatItem = item => {
let i = -1;
if (
_.isUndefined(item) ||
_.isNull(item) ||
item === '' ||
item === 'NaN' ||
item === 0
) {
i = 0;
}
if (i === -1) {
return true;
} else {
return i;
}
};
export const convertDataStatement = data => {
let dataTmp = data;
data.map((item, index) => {
let date = mapTimeVNToWorld(item.transDate);
let month = moment(date).format('M');
let year = moment(date).format('YYYY');
let isShowMonth = false;
if (index === 0) {
isShowMonth = true;
} else if (data[index - 1]) {
if (
moment(mapTimeVNToWorld(data[index - 1].transDate)).format('YYYY') !==
year
) {
isShowMonth = true;
} else if (
moment(mapTimeVNToWorld(data[index - 1].transDate)).format('M') !==
month
) {
isShowMonth = true;
}
}
dataTmp[index] = {...data[index], isShowMonth};
});
return dataTmp;
};
export const convertTypeFile = name => {
if (name) {
const fileNameTmp = name.toLowerCase();
if (fileNameTmp.endsWith('.doc') || fileNameTmp.endsWith('.dox')) {
return 0;
}
if (fileNameTmp.endsWith('.xls') || fileNameTmp.endsWith('.xlsx')) {
return 1;
}
if (fileNameTmp.endsWith('.pdf')) {
return 2;
}
if (
fileNameTmp.endsWith('.heic') ||
fileNameTmp.endsWith('.jpeg') ||
fileNameTmp.endsWith('.jpg') ||
fileNameTmp.endsWith('.png') ||
fileNameTmp.endsWith('.bmp')
) {
return 3;
}
}
return 0;
};
export const getStartDateOfQuater = quarter => {
return moment().quarter(quarter).startOf('quarter').format('YYYY-MM-DD');
};
export const getEndDateOfQuater = quarter => {
return moment().quarter(quarter).endOf('quarter').format('YYYY-MM-DD');
};
export const changePositionElement = (element, array) => {
let restArray = array.filter(e => e != element);
restArray.splice(0, 0, element);
return restArray;
};
export const getMimeType = fileExt => {
switch (fileExt) {
case 'doc':
return 'application/msword';
case 'docx':
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
case 'ppt':
return 'application/vnd.ms-powerpoint';
case 'pptx':
return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
case 'xls':
return 'application/vnd.ms-excel';
case 'xlsx':
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
case 'pdf':
return 'application/pdf';
case 'png':
return 'image/png';
case 'bmp':
return 'application/x-MS-bmp';
case 'gif':
return 'image/gif';
case 'jpg':
return 'image/jpeg';
case 'jpeg':
return 'image/jpeg';
case 'avi':
return 'video/x-msvideo';
case 'aac':
return 'audio/x-aac';
case 'mp3':
return 'audio/mpeg';
case 'mp4':
return 'video/mp4';
case 'apk':
return 'application/vnd.Android.package-archive';
case 'txt':
case 'log':
case 'h':
case 'cpp':
case 'js':
case 'html':
return 'text/plain';
default:
return '*/*';
}
};
import i18n from "../helper/i18/i18n";
import R from "../assets/R";
export const HISTORY_STATUS = {
ALL: {
code: "ALL",
color: R.colors.secondary,
icon: R.images.icCompleted,
name: i18n.t("All"),
status: "-1",
},
COMPLETED: {
code: "COMPLETED",
color: R.colors.secondary,
icon: R.images.icCompleted,
name: i18n.t("Completed"),
status: "3",
},
IN_PROCESSING: {
code: "IN_PROCESSING",
color: R.colors.lightBlue,
icon: R.images.icInfo,
name: i18n.t("InProcessing"),
status: "4",
},
FAILED: {
code: "FAILED",
color: R.colors.red1,
icon: R.images.icFailed,
name: i18n.t("Failed"),
status: "7",
},
WATTING: {
code: "TRANS_WAIT_APPROVED",
color: R.colors.orange,
icon: R.images.icInfo,
name: i18n.t("InProcessing"),
status: "4",
},
};
export const SEX = {
MALE: 0,
FEMALE: 1,
};
export const TRANSACTION_TYPE = {
ALL: {
code: "ALL",
color: R.colors.secondary,
backgroundButton: R.images.bgDepositButton,
name: i18n.t("All"),
transferType: -1,
},
DEPOSIT: {
code: "PUSH",
color: R.colors.secondary,
backgroundButton: R.images.bgDepositButton,
name: i18n.t("Deposit"),
transferType: 2,
transferTypeTxt: "PUSH",
},
WITHDRAW: {
code: "PULL",
color: R.colors.red1,
backgroundButton: R.images.bgWithdrawButton,
name: i18n.t("Withdraw"),
transferType: 1,
transferTypeTxt: "PULL",
},
BORROW_REQUEST: {
code: "BORROW_REQUEST",
color: R.colors.gray1,
backgroundButton: R.images.bgBorrowRequest,
name: i18n.t("BorrowRequest"),
transferType: 3,
transferTypeTxt: "3",
},
};
export const ACCOUNT_BANK_TYPE = {
BANK: "Bank",
CREDIT: "Credit",
};
export const CELL_COUNT = 4;
export const SHARE_TYPE = {
ALL: 1,
FACEBOOK: 2,
TWITTER: 3,
};
export const RATINGS_TYPE = {
REFER_FRIEND: 1,
BENEFIT: 2,
};
export const LANGUAGE_LIST = [
{
id: 56,
name: i18n.t("Vietnamese"),
value: "vi",
code: "vi",
},
{
id: 57,
name: i18n.t("English"),
value: "en",
code: "en",
},
];
export const ASYNC_STORE_KEY = {
TOKEN: "@TOKEN",
FIREBASE: "@Firebase",
ACCOUNT: "@ACCOUNT",
LANGUAGE: "@LANGUAGE",
};
export const OTP_TYPE = {
CHECK_PHONE_NUMBER: 0,
FORGOT_PASSWORD: 1,
};
export const PROVINCE_LIST = [
{
id: 1,
code: "AG",
name: "An Giang",
},
{
id: 2,
code: "BR_VT",
name: "Bà Rịa - Vũng Tàu",
},
{
id: 3,
code: "BL",
name: "Bạc Liêu",
},
{
id: 4,
code: "BK",
name: "Bắc Kạn",
},
{
id: 5,
code: "BC",
name: "Bắc Giang",
},
{
id: 6,
code: "BN",
name: "Bắc Ninh",
},
{
id: 7,
code: "BT",
name: "Bến Tre",
},
{
id: 8,
code: "BD",
name: "Bình Dương",
},
{
id: 9,
code: "BD",
name: "Bình Định",
},
{
id: 10,
code: "BP",
name: "Bình Phước",
},
{
id: 11,
code: "HN",
name: "Hà Nội",
},
{
id: 12,
code: "HCM",
name: "Hồ Chí Minh",
},
];
export const DEVICE_EVENT_KEY = {
RELOAD_BALANCE_WALLET: "reloadBalanceWallet",
LOGOUT_EVENT: "logoutEvent",
};
import io from 'socket.io-client/dist/socket.io';
import {NetworkSetting} from './Setting';
export const connectSocket = () => {
const socket = io(NetworkSetting.SOCKETCHAT, {
jsonp: false,
// timeout: 10000,
// // transports: ['websocket'],
// autoConnect: false,
agent: '-',
// path: '/', // Whatever your path is
pfx: '-',
// // key: token, // Using token-based auth.
// // passphrase: cookie, // Using cookie auth.
reconnection: true,
'force new connection': true,
transports: ['websocket', 'polling'],
cert: '-',
ca: '-',
ciphers: '-',
rejectUnauthorized: '-',
perMessageDeflate: '-',
});
return socket;
};
export const SOCKET_EVENTS = {
EVENT: 'event',
CONNECTION: 'connection',
};
export const EVENT_STATUS = {
UNKNOW: 0,
ACTIVE: 1,
INACTIVE: 2,
};
export const ROOMS = {
COMMENT: 'COMMENT_',
};
export const NAMESPACE = {
COMMENT: '/comments',
HOME: '/homes',
SHARE: '/share',
REVIEW_DETAIL: '/review_detail',
COMMENT_REVIEW_DETAIL: '/comment_review_detail',
PROFILE: '/profile',
REPLY_COMMENT: '/reply-comments',
WALLET: '/wallet',
PRIVATE_CHAT: '/private_chat',
THREAD_CHAT: '/thread_chat',
};
export const MESSAGE_TYPE = {
TEXT: 1,
IMAGE: 2,
LOCATION: 3,
ALERT: 4,
};
export const MESSAGE_STATUS = {
SENT: 1,
PENDING: 2,
RECEIVER: 3,
READ: 4,
};
/* eslint-disable no-console */
import React, {useEffect} from 'react';
import {Platform, View, Alert} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import {ASYNC_STORE_KEY} from '../config/constants';
import messaging from '@react-native-firebase/messaging';
import {showNotificaton} from '../actions/SnackBarAction';
import {connect} from 'react-redux';
import {convertScreen} from '../config/Functions';
import {updateWalletInfo} from '../actions/walletAction';
const FirebaseNotification = props => {
// messaging().setBackgroundMessageHandler(async (remoteMessage) => {
// console.log("Message handled in the background!", remoteMessage);
// });
// messaging().onNotificationOpenedApp(async (remoteMessage) => {
// console.log("On notifi open app-----", remoteMessage);
// });
useEffect(() => {
checkPermission();
const unsubscribe = messaging().onMessage(async remoteMessage => {
console.log('A new FCM message arrived!', remoteMessage);
const {body, title} = remoteMessage.notification;
const {action_type, record_id} = remoteMessage.data;
console.log('action_type', action_type);
props.showNotificaton({
title,
content: body,
screen: convertScreen(action_type),
id_record: record_id,
});
props.updateWalletInfo();
});
// messaging().onNotificationOpenedApp((remoteMessage) => {
// console.log(
// 'Notification caused app to open from background state:',
// remoteMessage,
// );
// const {action_type, body, title, record_id} = remoteMessage.data;
// if (action_type == 'REDIRECT') {
// Linking.openURL(remoteMessage.data.redirect_to);
// } else {
// props.newScreenInit({
// screen: convertScreen(action_type),
// id_record: record_id,
// });
// }
// });
// messaging()
// .getInitialNotification()
// .then((remoteMessage) => {
// console.log(remoteMessage);
// if (remoteMessage) {
// console.log(
// 'Notification caused app to open from quit state:',
// remoteMessage.data,
// );
// const {action_type, body, title, record_id} = remoteMessage.data;
// if (action_type == 'REDIRECT') {
// Linking.openURL(remoteMessage.data.redirect_to);
// } else {
// props.newScreenInit({
// screen: convertScreen(action_type),
// id_record: record_id,
// });
// }
// }
// });
return unsubscribe;
}, []);
const checkPermission = async () => {
const enabled = await messaging().hasPermission();
if (enabled == 1) {
getFcmToken();
} else {
requestPermission();
}
};
const requestPermission = async () => {
try {
await messaging().requestPermission();
getFcmToken();
} catch (error) {
// If user do not allow Push Notification
console.log('Rejected');
}
};
const getFcmToken = async () => {
let fcmToken = await AsyncStorage.getItem(ASYNC_STORE_KEY.FIREBASE);
console.log('fcmToken save', fcmToken);
if (!fcmToken) {
fcmToken = await messaging().getToken();
console.log('fcmToken create', fcmToken);
if (fcmToken) {
AsyncStorage.setItem(ASYNC_STORE_KEY.FIREBASE, fcmToken);
}
}
};
return <View />;
};
const mapStateToProps = state => {
return {};
};
export default connect(mapStateToProps, {
showNotificaton,
updateWalletInfo,
})(FirebaseNotification);
import { concat } from "lodash";
import React, { Component, useState, useEffect } from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ScrollView,
Alert,
} from "react-native";
import io from "socket.io-client";
class SocketIO extends Component {
constructor(props) {
super(props);
this.socket = io("http://192.168.0.103:9000");
this.state = {
isRegistor: true,
room: "",
text: "",
listRooms: [],
name: "",
user: "",
ListMessage: [],
currentRoom: "",
};
}
componentDidMount() {
this.socket.on("server-send-rooms", (data) => {
this.setState({
listRooms: data,
});
});
this.socket.on("server-send-room-socket", (data) => {
this.setState({
currentRoom: data,
});
});
this.socket.on("server-send-message", (data) => {
const newList = this.state.ListMessage.concat(data);
this.setState({
ListMessage: newList,
});
});
}
onSend = () => {
this.socket.emit("client-send-message", this.state.text);
};
onInitRoom = () => {
this.socket.emit("create-room", this.state.room);
};
render() {
const { isRegistor, currentRoom, listRooms, user, ListMessage } =
this.state;
return (
<View style={{ flex: 1, marginTop: 50, paddingHorizontal: 20 }}>
<View style={styles.footer}>
<TextInput
onChangeText={(val) => this.setState({ room: val })}
style={styles.txtInput}
/>
<TouchableOpacity
onPress={() => this.onInitRoom()}
style={styles.btn}
>
<Text>To room</Text>
</TouchableOpacity>
</View>
<Text style={styles.txtRoom}>{currentRoom}</Text>
<View style={styles.row}>
<View style={styles.wrapLeft}>
{listRooms.map((e) => (
<View style={{ marginTop: 10 }}>
<Text>{e}</Text>
</View>
))}
</View>
<View style={styles.wrapChat}>
{ListMessage.map((e) => (
<View style={{ marginTop: 10 }}>
<Text>{e}</Text>
</View>
))}
</View>
</View>
<View style={styles.footer}>
<TextInput
onChangeText={(val) => this.setState({ text: val })}
style={styles.txtInput}
/>
<TouchableOpacity onPress={() => this.onSend()} style={styles.btn}>
<Text>Send</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
wrapTop: {
height: 50,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
wrap: {
flexDirection: "row",
},
item: {
borderWidth: 0.6,
borderRadius: 5,
marginRight: 10,
paddingHorizontal: 5,
height: 40,
justifyContent: "center",
},
wrapChat: {
borderWidth: 0.6,
marginVertical: 20,
borderRadius: 10,
flex: 2,
},
wrapLeft: {
borderWidth: 0.6,
marginVertical: 20,
borderRadius: 10,
flex: 1,
marginRight: 10,
},
footer: {
flexDirection: "row",
alignItems: "center",
height: 50,
justifyContent: "space-between",
marginBottom: 20,
},
txtInput: {
flex: 1,
borderWidth: 0.6,
borderRadius: 10,
marginRight: 10,
height: 40,
},
btn: {
backgroundColor: "pink",
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
},
row: {
flexDirection: "row",
height: 400,
},
txtRoom: {
textAlign: "center",
fontSize: 22,
fontWeight: "600",
},
});
export default SocketIO;
import {concat} from 'lodash';
import React, {Component, useState, useEffect} from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
ScrollView,
Alert,
} from 'react-native';
import io from 'socket.io-client';
class SocketIO extends Component {
constructor(props) {
super(props);
this.socket = io('http://192.168.0.103:9000');
this.state = {
isRegistor: true,
room: '',
text: '',
listUser: [],
name: '',
user: '',
ListMessage: [],
};
}
componentDidMount() {
this.socket.on('server-send-color', (msg) => {
this.setState({bgColor: msg});
});
this.socket.on('server-send-register-faild', () => {
Alert.alert('Đăng ký thất bại', 'Username đã tồn tại!');
});
this.socket.on('server-send-register-success', (data) => {
this.setState({
isRegistor: false,
user: data,
});
});
this.socket.on('server-send-list-user', (data) => {
this.setState({
listUser: data,
});
});
this.socket.on('server-send-message', (data) => {
const newData = this.state.ListMessage.concat(data);
this.setState({
ListMessage: newData,
});
});
}
onSend = () => {
this.socket.emit('client-send-message', this.state.text);
};
onRegister = () => {
this.socket.emit('client-send-register', this.state.name);
};
onLogout = () => {
this.socket.emit('client-send-logout');
this.setState({
isRegistor: true,
listUser: [],
user: '',
});
};
onFocus = () => {
console.log('Bat dau');
};
onInitRoom = () => {
console.log('room', this.state.room);
this.socket.emit('create-room', this.state.room);
};
render() {
const {isRegistor, listUser, user, ListMessage} = this.state;
if (isRegistor)
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<TextInput
autoCapitalize={'none'}
style={{
width: 200,
height: 50,
borderWidth: 0.6,
borderRadius: 10,
paddingHorizontal: 10,
fontSize: 20,
}}
onChangeText={(val) => this.setState({room: val})}
/>
<TouchableOpacity
onPress={() => this.onInitRoom()}
style={{
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
backgroundColor: 'pink',
marginTop: 20,
}}>
<Text>To room</Text>
</TouchableOpacity>
<View style={{height: 100}} />
<TextInput
autoCapitalize={'none'}
style={{
width: 200,
height: 50,
borderWidth: 0.6,
borderRadius: 10,
paddingHorizontal: 10,
fontSize: 20,
}}
onChangeText={(val) => this.setState({name: val})}
/>
<TouchableOpacity
onPress={() => this.onRegister()}
style={{
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
backgroundColor: 'pink',
marginTop: 20,
}}>
<Text>Dăng ký</Text>
</TouchableOpacity>
</View>
);
return (
<View style={{flex: 1, marginTop: 50, paddingHorizontal: 20}}>
<View style={styles.wrapTop}>
<Text style={{fontSize: 20}}>Hello {user}</Text>
<TouchableOpacity onPress={() => this.onLogout()}>
<Text style={{fontSize: 20, color: 'red'}}>Logout</Text>
</TouchableOpacity>
</View>
<View style={{height: 60}}>
<ScrollView horizontal>
{listUser.map((e) => (
<View style={styles.item}>
<Text style={{fontSize: 20}}>{e}</Text>
</View>
))}
</ScrollView>
</View>
<View style={styles.wrapChat}>
{ListMessage.map((e) => (
<View style={{marginTop: 10}}>
<Text>{e.message}</Text>
</View>
))}
</View>
<View style={styles.footer}>
<TextInput
onChangeText={(val) => this.setState({text: val})}
style={styles.txtInput}
onFocus={() => this.onFocus()}
onEndEditing={() => {
console.log('ket thu');
}}
/>
<TouchableOpacity onPress={() => this.onSend()} style={styles.btn}>
<Text>Send</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
wrapTop: {
height: 50,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
wrap: {
flexDirection: 'row',
},
item: {
borderWidth: 0.6,
borderRadius: 5,
marginRight: 10,
paddingHorizontal: 5,
height: 40,
justifyContent: 'center',
},
wrapChat: {
borderWidth: 0.6,
height: 400,
marginVertical: 20,
borderRadius: 10,
},
footer: {
flexDirection: 'row',
alignItems: 'center',
height: 50,
justifyContent: 'space-between',
marginBottom: 20,
},
txtInput: {
flex: 1,
borderWidth: 0.6,
borderRadius: 10,
marginRight: 10,
height: 40,
},
btn: {
backgroundColor: 'pink',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
},
});
export default SocketIO;
import I18n from "react-native-i18n";
import { I18nManager } from "react-native";
import en from "./locales/en";
import vi from "./locales/vn";
I18n.translations = {
en,
vi,
};
I18n.fallbacks = true;
export default I18n;
export function setLocation(i18n, location) {
I18nManager.allowRTL(false);
const defaultLanguage = { languageTag: location, isRTL: false };
const { languageTag, isRTL } = defaultLanguage;
i18n.locale = languageTag;
return i18n;
}
export default {
EnterPhoneNumberToLogin: 'Enter phone number to login',
Enter: 'Enter',
PhoneNumber: 'Phone number',
VerifyCodeWillSendPhoneNumber:
'The verification code will be sent to the phone number\n you just entered',
Login: 'Login',
ForgotPassword: 'Forgot password?',
TermsOfUse: 'Terms of use',
PrivacyPolicy: 'Privacy policy',
Password: 'Password',
ChangeOtherPhoneNumber: 'Change other phone number',
VerifyCodeWillSend: 'A real 4-digit code will be sent to the phone number ',
ResendVerifyCode: 'Resend verify code',
Continue: 'Next',
FirstLastName: 'First and last name',
CreateCreditCardSuccess: 'Create card credit success',
UpdateCreditCardSuccess: 'Update card credit success',
ReferralCode: 'Referral code (if any)',
And: 'and',
PleaseEnterField: 'Please enter this field',
PleaseSelect: 'Please select ',
PleaseEnter: 'Please enter ',
Go: 'Go',
Upgrade: 'Upgrade',
InvestPackage: 'Invest Package',
AccountBalance: 'Account balance',
Birthday: 'Birthday',
Sex: 'Sex',
Male: 'Male',
Female: 'Female',
IdentifyInfo: 'Identify Information',
IdentifyID: 'Identify ID',
DateOfIdentifyId: 'Date of identify ID',
PlaceOfIdentifyId: 'Place of identify ID',
Hometown: 'Hometown',
Home: 'Home',
Notification: 'Notification',
Account: 'Account',
PersonalInfo: 'Personal Infor',
AboutUs: 'About us',
AccountSupport: 'Support',
Setting: 'Setting',
Logout: 'Logout',
Email: 'Email',
ProvinceCity: 'Province/City',
ContactAddress: 'Contact address',
Update: 'Update',
OnlineSupport: 'Online support',
SupportByOperator: 'Support by operator',
FanpageFacebook: 'Fanpage Facebook',
Terms2: 'Terms',
Version: 'Version',
SupportPhoneNumber: 'Support phone number',
WorkTime: 'Work time',
Address: 'Address',
ChangePassword: 'Change password',
CommonSetting: 'Common setting',
OldPassword: 'Old password',
NewPassword: 'New password',
ConfirmNewPassword: 'Confirm new password',
ReceiveNotification: 'Receive notification',
Language: 'Language',
English: 'English',
Vietnamese: 'Vietnamese',
LogoutConfirm: 'Do you really want to sign out?',
No: 'No',
ResendVerifyCode: 'Resend verify code',
VerifyCode: 'Verification code',
GetVerifyCode: 'Get verification code',
Confirm: 'Confirm',
NullDataSearch: 'No data',
No_Internet: 'No internet',
Select_source_image: 'Select the source of the image',
Photo_library: 'Photo library',
Take_photo: 'Take a photo',
};
export default {
EnterPhoneNumberToLogin: 'Nhập số điện thoại để đăng nhập',
Enter: 'Nhập',
PhoneNumber: 'Số điện thoại',
VerifyCodeWillSendPhoneNumber:
'Mã xác thực sẽ gửi về số điện thoại\n mà bạn vừa nhập',
Login: 'Đăng nhập',
LoginFalse: 'Đăng nhập thấi bại',
ForgotPassword: 'Quên mật khẩu?',
TermsOfUse: 'Điều khoản sử dụng',
PrivacyPolicy: ' Chính sách bảo mật',
Password: 'Mật khẩu',
ChangeOtherPhoneNumber: 'Đổi số điện thoại khác',
VerifyCodeWillSend: 'Mã các thực gồm 6 chữ số sẽ được gửi đến số điện thoại ',
ResendVerifyCode: 'Gửi lại mã xác nhận',
Continue: 'Tiếp tục',
FirstLastName: 'Họ và tên',
ReferralCode: 'Mã giới thiệu (nếu có)',
And: 'và',
PleaseEnterField: 'Vui lòng nhập trường này',
PleaseSelect: 'Vui lòng chọn ',
PleaseEnter: 'Vui lòng nhập ',
Go: 'Đi vào',
Upgrade: 'Nâng cấp',
Home: 'Trang chủ',
Notification: 'Thông báo',
Account: 'Tài khoản',
PersonalInfo: 'Thông tin cá nhân',
AboutUs: 'Về chúng tôi',
AccountSupport: 'Hỗ trợ tài khoản',
Setting: 'Cài đặt',
Logout: 'Đăng xuất',
No: 'Không',
Birthday: 'Ngày sinh',
Sex: 'Giới tính',
Male: 'Nam',
Female: 'Nữ',
IdentifyInfo: 'Thông tin định danh',
IdentifyID: 'Số CMND',
DateOfIdentifyId: 'Ngày cấp',
PlaceOfIdentifyId: 'Nơi cấp',
Hometown: 'Quê quán',
Update: 'Cập nhật',
Ok: 'Đồng ý',
Cancel: 'Hủy',
ProvinceCity: 'Tỉnh/Thành phố',
ContactAddress: 'Địa chỉ liên hệ',
OnlineSupport: 'Hỗ trợ trực tuyến',
SupportByOperator: 'Hỗ trợ qua tổng đài viên',
FanpageFacebook: 'Fanpage Facebook',
Terms2: 'Điều khoản sử dụng',
Version: 'Phiên bản',
SupportPhoneNumber: 'Số điện thoại viên',
WorkTime: 'Thời gian làm việc',
Address: 'Địa chỉ',
NullDataSearch: 'Không có dữ liệu',
ChangePassword: 'Đổi mật khẩu',
CommonSetting: 'Thiết lập chung',
OldPassword: 'Mật khẩu cũ',
NewPassword: 'Mật khẩu mới',
ConfirmNewPassword: 'Xác nhận mật khẩu mới',
ReceiveNotification: 'Nhận thông báo',
Language: 'Ngôn ngữ',
English: 'Tiếng Anh',
Vietnamese: 'Tiếng Việt',
LogoutConfirm: 'Bạn thật sự muốn đăng xuất không?',
GetVerifyCode: 'Nhận mã xác thực',
VerifyCode: 'Mã xác thực',
Confirm: 'Xác nhận',
No_Internet: 'Không có kết nốt Internet',
Check_Internet_Connect: 'Kiểm tra lại đường truyền',
Retry: 'Thử lại',
Select_source_image: 'Chọn nguồn lấy ảnh',
Photo_library: 'Thư viện ảnh',
Take_photo: 'Chụp ảnh',
};
import {
ADD_PRODUCT,
REMOVE_PRODUCT,
CLEAR_CART,
} from "../actions/actionTypes";
const initialState = [];
// @ts-ignore
export default function userReducer(state = initialState, action) {
switch (action.type) {
case ADD_PRODUCT: {
let newList = state.concat(action.data);
return newList;
}
case REMOVE_PRODUCT: {
const newList = state.filter((e) => e.id != action.data);
return newList;
}
case CLEAR_CART: {
return [];
}
default:
return state;
}
}
import {SHOWLOADING, HIDELOADING} from '../actions/actionTypes';
const initialState = {
isVisible: false,
};
// @ts-ignore
export default function ModalLoadingReducer(state = initialState, action) {
switch (action.type) {
case SHOWLOADING: {
return {isVisible: true};
}
case HIDELOADING: {
return {isVisible: false};
}
default:
return state;
}
}
import { NEW_SCREEN, CLEAR_SCREEN } from "../actions/actionTypes";
const initialState = {
screen: null,
id: null,
};
// @ts-ignore
export default function ScreenInitReducer(state = initialState, action) {
switch (action.type) {
case NEW_SCREEN: {
return { screen: action.body.screen, id: action.body.id_record };
}
default:
return state;
}
}
import {PUSHNOTI, HIDENOTI} from '../actions/actionTypes';
const initialState = {
isOpen: false,
screen: '',
title: '',
content: '',
id_record: '',
};
// @ts-ignore
export default function SnackReducer(state = initialState, action) {
switch (action.type) {
case PUSHNOTI: {
return {...action.data, isOpen: true};
}
case HIDENOTI: {
return {...state, isOpen: false};
}
default:
return state;
}
}
import { UPDATE_SYSTEM_CONFIG } from "../actions/actionTypes";
const initialState = {
point_exchange_ratio: 20,
point_exchange_minimum: 1000,
trans_fee: 0.025,
pull_card_fee: 0.02,
insurance_fee: 0,
is_insurrance: true,
customer_f1_percent: 10.0,
customer_f2_percent: 3.0,
customer_f3_percent: 2.0,
};
// @ts-ignore
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_SYSTEM_CONFIG: {
return { ...action.data };
}
default:
return state;
}
}
import userReducer from "./userReducer";
import { combineReducers } from "redux";
import ModalLoadingReducer from "./ModalLoading";
import SnackReducer from "./SnackReducer";
import ScreenInitReducer from "./ScreenInit";
import languageReducer from "./languageReducer";
import walletReducer from "./walletReducer";
import SystemConfigReducer from "./SystemConfigReducer";
import cartReducer from "./CartReducer";
// @ts-ignore
const rootReducer = combineReducers({
userReducer,
ModalLoadingReducer,
SnackReducer,
ScreenInitReducer,
languageReducer,
walletReducer,
SystemConfigReducer,
cartReducer,
});
export default rootReducer;
import { CHANGE_LANGUAGE } from "./../actions/actionTypes";
var initialState = {
language: "vi",
};
var languageReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_LANGUAGE: {
return { language: action.language };
}
default:
return state;
}
};
export default languageReducer;
import { LOGIN, UPDATE_USER_SUCCESS } from "../actions/actionTypes";
const initialState = {
isSignedIn: false,
expiredTime: new Date(),
userInfo: {},
walletInfo: {},
};
// @ts-ignore
export default function userReducer(state = initialState, action) {
switch (action.type) {
case LOGIN: {
let user = action.data;
return { ...action.data, isSignedIn: user !== null, userInfo: user };
}
case UPDATE_USER_SUCCESS: {
let user = action.data;
return { ...state, userInfo: user };
}
default:
return state;
}
}
import { UPDATE_WALLET_SUCCESS } from "../actions/actionTypes";
const initialState = {
id: 1.0,
amount: 0,
point: 0,
benifit_yesterday: null,
total_notification_not_read: 0,
};
// @ts-ignore
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_WALLET_SUCCESS: {
return { ...action.data };
}
default:
return state;
}
}
import React, {Component, useState} from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Button,
TouchableWithoutFeedback,
Image,
} from 'react-native';
import Icon from 'react-native-vector-icons/Entypo';
import R from '../assets/R';
import Modal from 'react-native-modal';
import {useNavigation} from '@react-navigation/native';
import {
DEPOSIT,
MY_WALLET,
WITHDRAW,
BORROW_REQUEST,
} from '../routers/ScreenNames';
import AppText from '../components/AppText';
const WalletModal = props => {
const [isModalVisible, setModalVisible] = useState(false);
const navigate = useNavigation();
const toggleModal = () => {
setModalVisible(!isModalVisible);
};
return (
<View style={styles.container}>
<View style={styles.wraper}>
<TouchableOpacity onPress={toggleModal} style={styles.btn}>
<Icon name={'plus'} size={16} color={R.colors.white} />
</TouchableOpacity>
</View>
<Modal style={{margin: 0}} isVisible={isModalVisible}>
<View style={{flex: 1}}>
<TouchableWithoutFeedback onPress={toggleModal}>
<View style={{flex: 1}}></View>
</TouchableWithoutFeedback>
<View style={styles.footer}>
<TouchableOpacity
onPress={() => {
navigate.navigate(DEPOSIT);
toggleModal();
}}
style={styles.wraper1}>
<View style={styles.containerIcon}>
<Image style={styles.imgIcon} source={R.images.icDeposit} />
</View>
<AppText i18nKey={'Deposit'} style={styles.txt}></AppText>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
navigate.navigate(WITHDRAW);
toggleModal();
}}
style={styles.wraper1}>
<View style={styles.containerIcon}>
<Image style={styles.imgIcon} source={R.images.icWithdraw} />
</View>
<AppText i18nKey={'Withdraw'} style={styles.txt}></AppText>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
navigate.navigate(MY_WALLET);
toggleModal();
}}
style={styles.wraper1}>
<View style={styles.containerIcon}>
<Image
style={{height: 28, width: 28}}
source={R.images.icWallet}
/>
</View>
<AppText i18nKey={'Wallet'} style={styles.txt}></AppText>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
navigate.navigate(BORROW_REQUEST);
toggleModal();
}}
style={styles.wraper1}>
<View style={styles.containerIcon}>
<Image
style={{height: 28, width: 28}}
source={R.images.icRequestLoan}
/>
</View>
<AppText i18nKey={'Loan'} style={styles.txt}></AppText>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
};
export default WalletModal;
const styles = StyleSheet.create({
btn: {
backgroundColor: R.colors.main,
width: 70,
height: 70,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 3,
borderRadius: 40,
},
container: {
flex: 1,
},
wraper: {
backgroundColor: R.colors.white,
width: 50,
height: 50,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
position: 'absolute',
top: -25,
borderRadius: 25,
},
footer: {
backgroundColor: 'white',
height: 165,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
justifyContent: 'space-around',
flexDirection: 'row',
},
imgIcon: {
width: 35,
height: 35,
resizeMode: 'contain',
},
wraper1: {
justifyContent: 'center',
alignItems: 'center',
},
containerIcon: {
height: 60,
width: 60,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#D7E6FF',
borderRadius: 10,
},
txt: {
fontSize: 12,
color: R.colors.main,
fontWeight: '600',
marginTop: 12,
},
});
export const LOGINSCREEN = 'LOGINSCREEN';
export const HOMESCREEN = 'HOMESCREEN';
export const TABNAVIGATOR = 'TABNAVIGATOR';
export const ENTER_PASSWORD = 'ENTER_PASSWORD';
export const CONFIRM_OTP = 'CONFIRM_OTP';
export const CHANGE_PHONE_NUMBER_OTP = 'CHANGE_PHONE_NUMBER_OTP';
import React, {Fragment, useRef, useEffect, useState} from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import TabNavigator from './TabNavigation';
import Login from '../screens/login';
import * as ScreenName from './ScreenNames';
const Stack = createStackNavigator();
function MyStack(props) {
return (
<Stack.Navigator
screenOptions={{
headerStatusBarHeight: 0,
}}
headerMode={'none'}
initialRouteName={ScreenName.LOGINSCREEN}>
<Stack.Screen name={ScreenName.LOGINSCREEN} component={Login} />
<Stack.Screen name={ScreenName.TABNAVIGATOR} component={TabNavigator} />
</Stack.Navigator>
);
}
export default function App(props) {
return (
<Fragment>
<NavigationContainer independent={true}>
<MyStack />
</NavigationContainer>
</Fragment>
);
}
import React, {useEffect, useState} from 'react';
import {DeviceEventEmitter, Image, View} from 'react-native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import i18n from '../helper/i18/i18n';
import {connect} from 'react-redux';
import R from '../assets/R';
import Account from '../screens/account';
import Home from '../screens/home';
const Tab = createBottomTabNavigator();
const TabNavigator = props => {
const [reload, setReload] = useState(false);
useEffect(() => {
let setLanguage = DeviceEventEmitter.addListener('setLanguage', value => {
setReload(!reload);
});
return () => {
setLanguage.remove();
};
}, []);
return (
<Tab.Navigator
initialRouteName="Screen5"
screenOptions={{headerShown: false}}
tabBarOptions={{
showIcon: true,
showLabel: true,
activeTintColor: R.colors.main,
style: {
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.29,
shadowRadius: 2,
elevation: 7,
justifyContent: 'center',
},
}}>
<Tab.Screen
name="HomeScreen1"
component={Home}
options={{
tabBarLabel: i18n.t('Home'),
tabBarIcon: ({color, size}) => (
<Image
source={R.images.icHome}
style={{width: size, height: size, tintColor: color}}
/>
),
}}
/>
<Tab.Screen
name="AccountScreen"
component={Account}
options={{
tabBarLabel: i18n.t('Account'),
tabBarIcon: ({color, size}) => (
<Image
source={R.images.icAccount}
style={{width: size, height: size - 3, tintColor: color}}
resizeMode={'contain'}
/>
),
}}
/>
</Tab.Navigator>
);
};
const mapStateToProps = state => {
return {
user: state.userReducer,
};
};
export default connect(mapStateToProps, {})(TabNavigator);
import {call, all} from 'redux-saga/effects';
import {watchUpdateUser} from './userSaga';
import {watchUpdateWallet} from './walletSaga';
export default function* rootSaga() {
yield all([call(watchUpdateUser), call(watchUpdateWallet)]);
}
import {put, takeLatest} from 'redux-saga/effects';
import {getInforGeneral} from '../apis/Functions/home';
import {UPDATE_USER_SUCCESS, UPDATE_INFOR} from '../actions/actionTypes';
function* updateUser(action) {
try {
const res = yield getInforGeneral();
if (res.data.code == 200 && res.data.data) {
yield put({
type: UPDATE_USER_SUCCESS,
data: res.data.data,
});
}
} catch (error) {}
}
export function* watchUpdateUser() {
yield takeLatest(UPDATE_INFOR, updateUser);
}
import {put, takeLatest} from 'redux-saga/effects';
// @ts-ignore
import {UPDATE_WALLET, UPDATE_WALLET_SUCCESS} from '../actions/actionTypes';
import {getWalletInfo} from '../apis/Functions/wallet';
function* updateWallet() {
try {
const res = yield getWalletInfo();
if (res.data.code == 200 && res.data.data) {
yield put({
type: UPDATE_WALLET_SUCCESS,
data: res.data.data,
});
}
} catch (error) {}
}
export function* watchUpdateWallet() {
yield takeLatest(UPDATE_WALLET, updateWallet);
}
import React, {useEffect, useState} from 'react';
import AccountView from './view';
import {useNavigation} from '@react-navigation/native';
import {connect} from 'react-redux';
import {saveUserToRedux, saveWalletInfo} from '../../actions/users';
import {showLoading, hideLoading} from '../../actions/loadingAction';
import {showAlert, TYPE} from '../../components/DropdownAlert';
const Account = props => {
return <AccountView />;
};
const mapStateToProps = state => {
return {
userInfo: state.userReducer.userInfo,
};
};
export default connect(mapStateToProps, {
saveUserToRedux,
saveWalletInfo,
showLoading,
hideLoading,
})(Account);
import React, {Component} from 'react';
import {View, Text, SafeAreaView, StyleSheet} from 'react-native';
import Header from '../../components/Header/Header';
import i18n from '../../helper/i18/i18n';
import R from '../../assets/R';
import PickerItem from '../../components/Picker/PickerItem';
import DatePicker from '../../components/Picker/PickerDate';
import PickerAvatar from '../../components/Picker/PickerAvatar';
import {WIDTH} from '../../config/Functions';
import AppText from '../../components/AppText';
const AccountView = props => {
return (
<SafeAreaView style={styles.container}>
<Header title={i18n.t('Account')} />
<View style={styles.body}>
<Text>{i18n.t('Account')}</Text>
</View>
</SafeAreaView>
);
};
export default AccountView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
body: {
flex: 1,
paddingHorizontal: WIDTH(10),
},
});
import React, {Component} from 'react';
import {View, Text} from 'react-native';
import Homeview from './view';
const Home = props => {
return <Homeview />;
};
export default Home;
import React, {Component} from 'react';
import {View, Text, SafeAreaView, StyleSheet} from 'react-native';
import Header from '../../components/Header/Header';
import i18n from '../../helper/i18/i18n';
import R from '../../assets/R';
const HomeView = props => {
return (
<SafeAreaView style={styles.container}>
<View style={styles.container}>
<Header title={i18n.t('Home')} />
<Text>Home screen</Text>
</View>
</SafeAreaView>
);
};
export default HomeView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
});
import {useNavigation} from '@react-navigation/native';
import React from 'react';
import {Text, View} from 'react-native';
import LoginView from './view';
import {TABNAVIGATOR} from '../../routers/ScreenNames';
const Login = ({params}) => {
const navigate = useNavigation();
const onLogin = () => {
navigate.navigate(TABNAVIGATOR);
console.log('Hello');
};
return <LoginView onLogin={onLogin} />;
};
export default Login;
import React from 'react';
import {Text, View, StyleSheet, SafeAreaView} from 'react-native';
import TextField from '../../components/Input/TextField';
import {useForm, Controller} from 'react-hook-form';
import R from '../../assets/R';
import Header from '../../components/Header/Header';
import Button from '../../components/Button';
const LoginView = ({onLogin}) => {
const {
control,
handleSubmit,
formState: {errors},
} = useForm();
return (
<SafeAreaView style={styles.container}>
<Header />
<View style={styles.container}>
<Controller
control={control}
rules={{
required: true,
maxLength: 225,
}}
render={({field: {onChange, onBlur, value}}) => (
<TextField
title={'Mã phẩm giống'}
required
value={value}
onChangeText={onChange}
error={errors.code}
/>
)}
name="code"
/>
<Button title={'Lưu'} onPress={handleSubmit(onLogin)} />
</View>
</SafeAreaView>
);
};
export default LoginView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
});
import React from 'react';
import {Text, View} from 'react-native';
import LoginView from './view';
const Login = ({params}) => <LoginView />;
export default Login;
import React from 'react';
import {Text, View, TouchableOpacity} from 'react-native';
const LoginView = ({params}) => (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity>
<Text>Login</Text>
</TouchableOpacity>
</View>
);
export default LoginView;
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment