react-native upgrade to 0.71.1
parent
8b7f244812
commit
15f779a13b
|
@ -1,4 +1,5 @@
|
|||
apply plugin: "com.android.application"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
@ -316,4 +317,4 @@ project.ext.vectoricons = [
|
|||
iconFontNames: [ 'MaterialIcons.ttf', 'MaterialCommunityIcons.ttf', 'FontAwesome.ttf', 'Ionicons.ttf' ] // Name of the font files you want to copy
|
||||
]
|
||||
|
||||
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
|
|
|
@ -2,18 +2,13 @@
|
|||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
buildToolsVersion = "33.0.0"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 31
|
||||
targetSdkVersion = 31
|
||||
compileSdkVersion = 33
|
||||
targetSdkVersion = 33
|
||||
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
} else {
|
||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
}
|
||||
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||
ndkVersion = "23.1.7779620"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
|
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
|
@ -23,13 +23,14 @@
|
|||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-esnext": "^1.1.3",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"idb": "^7.1.1",
|
||||
"native-base": "^3.4.23",
|
||||
"password-quality-calculator": "^1.0.4",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "18.1.0",
|
||||
"react-native": "0.70.6",
|
||||
"react-native": "0.71.1",
|
||||
"react-native-encrypted-storage": "^4.0.3",
|
||||
"react-native-gesture-handler": "^2.8.0",
|
||||
"react-native-reanimated": "^2.13.0",
|
||||
"react-native-gesture-handler": "^2.8.0",
|
||||
"react-native-safe-area-context": "^4.4.1",
|
||||
"react-native-screens": "^3.18.2",
|
||||
"react-native-svg": "^13.6.0",
|
||||
|
@ -37,7 +38,8 @@
|
|||
"react-native-vector-icons": "^9.2.0",
|
||||
"react-native-web": "^0.18.10",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-string-replace": "^1.1.0"
|
||||
"react-string-replace": "^1.1.0",
|
||||
"realm": "^11.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.9",
|
||||
|
@ -97,7 +99,7 @@
|
|||
"<rootDir>"
|
||||
],
|
||||
"setupFiles": [
|
||||
"./tests/setup.js"
|
||||
"./mock.js"
|
||||
]
|
||||
},
|
||||
"browser": [
|
||||
|
|
|
@ -15,15 +15,25 @@ import {useEffect} from 'react';
|
|||
import {initAppData} from '@caj/helper/appData';
|
||||
import {appStatus} from '@caj/configs/appNonSaveVar';
|
||||
import {appNonSaveVarActions} from '@caj/configs/appNonSaveVarReducer';
|
||||
import BigDataManager from '@caj/helper/storage/BigDataManager';
|
||||
|
||||
const AnimationView = animated(View);
|
||||
|
||||
function onAppStart() {
|
||||
initAppData().then(() => {
|
||||
console.log('finish');
|
||||
setTimeout(() => {
|
||||
store.dispatch(appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING));
|
||||
}, 250);
|
||||
BigDataManager.initDatabase()
|
||||
.then(() => {
|
||||
console.log('finish');
|
||||
setTimeout(() => {
|
||||
store.dispatch(
|
||||
appNonSaveVarActions.setAppStatus(appStatus.APP_RUNNING),
|
||||
);
|
||||
}, 250);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Database Error! Can't start App :(");
|
||||
});
|
||||
|
||||
//store.dispatch(actions.loadPreferences(appVar));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
import React, {useState, useEffect} from 'react';
|
||||
|
||||
import {StyleSheet, Appearance} from 'react-native';
|
||||
import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {useSelector, useDispatch} from 'react-redux';
|
||||
import {RootState} from '@caj/redux/store';
|
||||
import {appVarActions} from '@caj/configs/appVarReducer';
|
||||
|
||||
import imgSrc from '@caj/img/maimg.png';
|
||||
import {placeholder} from '@caj/lang/default';
|
||||
import {getBackgroundColor} from '@caj/configs/colors';
|
||||
import {saveVarChanges} from '@caj/helper/appData';
|
||||
|
||||
import {Box, Input, VStack, Center, Avatar, Text, Button} from 'native-base';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import {
|
||||
LinkingOptions,
|
||||
NavigationContainer,
|
||||
useNavigation,
|
||||
} from '@react-navigation/native';
|
||||
import {
|
||||
createNativeStackNavigator,
|
||||
NativeStackNavigationProp,
|
||||
} from '@react-navigation/native-stack';
|
||||
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
height: '100%',
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const linking: LinkingOptions<{
|
||||
Maps: string;
|
||||
Home: string;
|
||||
Chat: string;
|
||||
Settings: string;
|
||||
}> = {
|
||||
prefixes: ['http://'],
|
||||
config: {
|
||||
screens: {
|
||||
Maps: 'mapss',
|
||||
Home: 'account',
|
||||
Chat: 'chats',
|
||||
Settings: 'settingss',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type HomeStackNavigatorParamList = {
|
||||
Account: undefined;
|
||||
Maps: undefined;
|
||||
Chat: undefined;
|
||||
Test: undefined;
|
||||
Settings: undefined;
|
||||
};
|
||||
|
||||
export type HomeScreenNavigationProp =
|
||||
NativeStackNavigationProp<HomeStackNavigatorParamList>;
|
||||
|
||||
export default function Navigation() {
|
||||
const theme = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.theme,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<Tab.Navigator screenOptions={{headerShown: false}}>
|
||||
<Tab.Screen name="Home" component={HomeStackScreen} />
|
||||
<Tab.Screen name="Settings" component={SettingsScreen} />
|
||||
<Tab.Screen
|
||||
name="Chat"
|
||||
options={{headerShown: true, tabBarStyle: {display: 'none'}}}
|
||||
component={ChatScreen}
|
||||
/>
|
||||
</Tab.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function ChatScreen() {
|
||||
return (
|
||||
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Text>Chat!</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function HomeScreen() {
|
||||
const navigation = useNavigation<HomeScreenNavigationProp>();
|
||||
return (
|
||||
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Text>Home screen</Text>
|
||||
<Button onPress={() => navigation.navigate('Test')}>Go to Test</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export type SettingsStackNavigatorParamList = {
|
||||
Chat: undefined;
|
||||
Settings: undefined;
|
||||
};
|
||||
|
||||
export type SettingsScreenNavigationProp =
|
||||
NativeStackNavigationProp<SettingsStackNavigatorParamList>;
|
||||
|
||||
function SettingsScreen() {
|
||||
const navigation = useNavigation<SettingsScreenNavigationProp>();
|
||||
const navigation2 = useNavigation<HomeScreenNavigationProp>();
|
||||
return (
|
||||
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Text>Settings screen</Text>
|
||||
<Button onPress={() => navigation2.navigate('Maps')}>Go to Chat</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const HomeStack = createNativeStackNavigator<HomeStackNavigatorParamList>();
|
||||
|
||||
function HomeStackScreen() {
|
||||
return (
|
||||
<HomeStack.Navigator>
|
||||
<HomeStack.Screen
|
||||
name="Maps"
|
||||
options={{headerShown: false}}
|
||||
component={HomeScreen}
|
||||
/>
|
||||
<HomeStack.Screen name="Test" component={ChatScreen} />
|
||||
</HomeStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const SettingsStack = createNativeStackNavigator<HomeStackNavigatorParamList>();
|
||||
|
||||
function SettingsStackScreen() {
|
||||
return (
|
||||
<SettingsStack.Navigator>
|
||||
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
|
||||
<SettingsStack.Screen name="Chat" component={ChatScreen} />
|
||||
</SettingsStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
|
@ -0,0 +1,30 @@
|
|||
import {AccountName, Username} from '@caj/helper/types';
|
||||
import {RootState} from '@caj/redux/store';
|
||||
import {HStack, Text} from 'native-base';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
export default function NameDisplay(props: {
|
||||
UserName: Username;
|
||||
AccountName: AccountName;
|
||||
fontSize?: number;
|
||||
}) {
|
||||
const theme = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.theme,
|
||||
);
|
||||
const fontSize = props.fontSize || 15;
|
||||
const lineHeight = fontSize * 1.25;
|
||||
|
||||
return (
|
||||
<HStack space={fontSize / 30} alignItems={'flex-end'}>
|
||||
<Text fontSize={fontSize} color="primary.400">
|
||||
{props.UserName !== '' ? props.UserName : '----'}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={fontSize / 1.5}
|
||||
fontFamily={'Outfit-Light'}
|
||||
color="light.400">
|
||||
{props.AccountName !== '' ? props.AccountName : '----'}
|
||||
</Text>
|
||||
</HStack>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {AccountName, Username} from '@caj/helper/types';
|
||||
import {AccountName, Username} from '@caj/configs/types';
|
||||
import {RootState} from '@caj/redux/store';
|
||||
import {HStack, Text} from 'native-base';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
|
|
@ -0,0 +1,960 @@
|
|||
import {RegisterProcess, ThemeMode} from '@caj/configs/appVar';
|
||||
import {appVarActions} from '@caj/configs/appVarReducer';
|
||||
import {defaultHeaderStyle} from '@caj/configs/colors';
|
||||
import {SlideFromLeftView} from '@caj/helper/animations';
|
||||
import {saveVarChanges} from '@caj/helper/appData';
|
||||
import {apiBackendRequest, makeRequest} from '@caj/helper/request';
|
||||
import {
|
||||
accountNameOptions,
|
||||
EMail,
|
||||
emailOptions,
|
||||
passwordOptions,
|
||||
userNameOptions,
|
||||
XToken,
|
||||
} from '@caj/helper/types';
|
||||
import {RootScreenNavigationProp} from '@caj/Navigation';
|
||||
import {RootState, store} from '@caj/redux/store';
|
||||
import {useNavigation} from '@react-navigation/native';
|
||||
import {
|
||||
createNativeStackNavigator,
|
||||
NativeStackNavigationProp,
|
||||
} from '@react-navigation/native-stack';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Center,
|
||||
Container,
|
||||
FormControl,
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Input,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
Spinner,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
useTheme,
|
||||
useToast,
|
||||
VStack,
|
||||
WarningOutlineIcon,
|
||||
} from 'native-base';
|
||||
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import {baseFontSize} from 'native-base/lib/typescript/theme/tools';
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
import ConfirmationCodeField from './ConfirmationCodeField';
|
||||
import NameDisplay from './NameDisplay';
|
||||
import showToast from './Toast';
|
||||
import {NativeSyntheticEvent, TextInputFocusEventData} from 'react-native';
|
||||
|
||||
const validateEmail = (email: EMail) => {
|
||||
return emailOptions.isAllowed(email);
|
||||
};
|
||||
|
||||
export default function NotLoggedIn() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const theme = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.theme,
|
||||
);
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
|
||||
return (
|
||||
<Box alignItems="center" mt={5}>
|
||||
<Text color="primary.400">{lang.appName}</Text>
|
||||
<Text color="white.100">{lang.appNameDesc}</Text>
|
||||
<VStack mt={5} space={4} alignItems="center">
|
||||
<Button
|
||||
w="72"
|
||||
colorScheme="primary"
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
onPress={() => {
|
||||
navigation.navigate('Register', {screen: 'RegStepOne'});
|
||||
}}>
|
||||
Sign up
|
||||
</Button>
|
||||
<Button
|
||||
w="72"
|
||||
colorScheme="black"
|
||||
variant={theme === ThemeMode.Darkest ? 'outline' : 'subtle'}
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl', color: 'white.900'}}>
|
||||
Log in
|
||||
</Button>
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export function RegisterScreenAnim(props: any) {
|
||||
return (
|
||||
<SlideFromLeftView>
|
||||
<RegisterScreen {...props} />
|
||||
</SlideFromLeftView>
|
||||
);
|
||||
}
|
||||
|
||||
export type LoginStackNavigatorParamList = {
|
||||
RegStepOne: undefined;
|
||||
RegStepTwo: undefined;
|
||||
RegStepFinal: undefined;
|
||||
};
|
||||
|
||||
const LoginStack = createNativeStackNavigator<LoginStackNavigatorParamList>();
|
||||
|
||||
export type LoginScreenNavigationProp =
|
||||
NativeStackNavigationProp<LoginStackNavigatorParamList>;
|
||||
|
||||
function RegisterScreen() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const theme = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.theme,
|
||||
);
|
||||
|
||||
return (
|
||||
<LoginStack.Navigator>
|
||||
<LoginStack.Screen
|
||||
name="RegStepOne"
|
||||
options={{
|
||||
animation: 'slide_from_left',
|
||||
title: lang.account.registration.registration,
|
||||
headerShown: true,
|
||||
...defaultHeaderStyle(theme, 'registration'),
|
||||
}}
|
||||
component={StepOne}
|
||||
/>
|
||||
<LoginStack.Screen
|
||||
name="RegStepTwo"
|
||||
options={{
|
||||
animation: 'slide_from_right',
|
||||
title: lang.account.registration.registration,
|
||||
headerShown: true,
|
||||
...defaultHeaderStyle(theme, 'registration'),
|
||||
}}
|
||||
component={StepTwo}
|
||||
/>
|
||||
<LoginStack.Screen
|
||||
name="RegStepFinal"
|
||||
options={{
|
||||
animation: 'slide_from_right',
|
||||
title: lang.account.registration.registration,
|
||||
headerShown: true,
|
||||
...defaultHeaderStyle(theme, 'registration'),
|
||||
}}
|
||||
component={StepFinal}
|
||||
/>
|
||||
</LoginStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
function Agreement() {
|
||||
const toast = useToast();
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
|
||||
const textColor = useColorModeValue('blue.700', 'cyan.400');
|
||||
|
||||
let replacedText = reactStringReplace(
|
||||
lang.account.registration.info,
|
||||
'${TermsOfUse}',
|
||||
(match, i) => (
|
||||
<Text
|
||||
key={match + i}
|
||||
color={textColor}
|
||||
bold
|
||||
onPress={() => {
|
||||
showToast(toast, {
|
||||
title: lang.account.registration.termsOfUse,
|
||||
variant: 'solid',
|
||||
status: 'info',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
}}>
|
||||
{lang.account.registration.termsOfUse}
|
||||
</Text>
|
||||
),
|
||||
);
|
||||
|
||||
replacedText = reactStringReplace(
|
||||
replacedText,
|
||||
'${privacyPolicy}',
|
||||
(match, i) => (
|
||||
<Text
|
||||
key={match + i}
|
||||
color={textColor}
|
||||
bold
|
||||
onPress={() => {
|
||||
showToast(toast, {
|
||||
title: lang.account.registration.privacyPolicy,
|
||||
variant: 'solid',
|
||||
status: 'info',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
}}>
|
||||
{lang.account.registration.privacyPolicy}
|
||||
</Text>
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<Text textAlign={'justify'} color={'white.900'}>
|
||||
{replacedText}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
function resendMail(email: EMail, toast: any): Promise<XToken> {
|
||||
return new Promise<XToken>((resolve, reject) => {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_RESEND_MAIL,
|
||||
|
||||
requestHeader: {},
|
||||
request: {
|
||||
Email: email,
|
||||
},
|
||||
//response: {
|
||||
// XToken: undefined,
|
||||
//},
|
||||
})
|
||||
.then(resp => {
|
||||
console.log(1);
|
||||
let token =
|
||||
store.getState().appVariables.preferences.RegisterProcess.XToken;
|
||||
if (token !== undefined /*resp.response.XToken !== undefined*/) {
|
||||
showToast(toast, {
|
||||
title: store.getState().appVariables.lang.info,
|
||||
variant: 'solid',
|
||||
status: 'info',
|
||||
description:
|
||||
store.getState().appVariables.lang.account.registration.stepTwo
|
||||
.resend[2],
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
|
||||
resolve(token);
|
||||
} else {
|
||||
reject(500);
|
||||
showToast(toast, {
|
||||
title: store.getState().appVariables.lang.error,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: 'XToken is undefined',
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(resp => {
|
||||
let text = 'unknown error ' + resp.status;
|
||||
if (resp.status !== undefined) {
|
||||
const _text =
|
||||
store.getState().appVariables.lang.account.registration.stepTwo
|
||||
.resendError[resp.status as number];
|
||||
if (_text !== undefined) text = _text;
|
||||
}
|
||||
|
||||
showToast(toast, {
|
||||
title: store.getState().appVariables.lang.error,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: text,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
|
||||
reject(resp.status);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function StepOne() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const regPro = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const toast = useToast();
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
|
||||
const initNoErrors = {
|
||||
wrongFormat: false,
|
||||
alreadyExists: false,
|
||||
noEntered: false,
|
||||
unknown: undefined,
|
||||
};
|
||||
|
||||
const [errors, setErrors] = useState(initNoErrors);
|
||||
|
||||
const isError =
|
||||
errors.wrongFormat ||
|
||||
errors.alreadyExists ||
|
||||
errors.noEntered ||
|
||||
errors.unknown !== undefined;
|
||||
|
||||
const errorText = () => {
|
||||
if (errors.wrongFormat) {
|
||||
return lang.account.registration.stepOne.addressInvalid;
|
||||
}
|
||||
if (errors.alreadyExists) {
|
||||
return lang.account.registration.stepOne.addressExists;
|
||||
}
|
||||
if (errors.noEntered) {
|
||||
return lang.account.registration.stepOne.noMailEntered;
|
||||
}
|
||||
if (errors.unknown !== undefined) {
|
||||
return errors.unknown;
|
||||
}
|
||||
};
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const [values, setValues] = useState({email: regPro.EMail});
|
||||
|
||||
useEffect(() => {
|
||||
if (regPro.isRegistering === 'stepTwo') {
|
||||
setLoading(true);
|
||||
setErrors(initNoErrors);
|
||||
|
||||
setTimeout(nextStep, 500);
|
||||
} else if (regPro.isRegistering === 'stepFinal') {
|
||||
setLoading(true);
|
||||
setErrors(initNoErrors);
|
||||
|
||||
setTimeout(nextStep, 500);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const nextStep = () => {
|
||||
setLoading(true);
|
||||
setErrors(initNoErrors);
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_1,
|
||||
requestHeader: {},
|
||||
request: {
|
||||
Email: values.email,
|
||||
},
|
||||
response: {
|
||||
XToken: undefined,
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
let rp = {...regPro};
|
||||
rp.isRegistering = 'stepTwo';
|
||||
rp.EMail = values.email;
|
||||
rp.XToken = resp.response.XToken;
|
||||
|
||||
dispatch(appVarActions.setRegisterProcess(rp));
|
||||
saveVarChanges();
|
||||
|
||||
showToast(toast, {
|
||||
title: lang.account.registration.stepOne.success,
|
||||
variant: 'solid',
|
||||
status: 'success',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
|
||||
navigation.navigate('Register', {screen: 'RegStepTwo'});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(resp => {
|
||||
if (resp.status === 401 || resp.status === 204) {
|
||||
if (regPro.XToken !== undefined) {
|
||||
let rp = {...regPro};
|
||||
|
||||
if (resp.status === 401) {
|
||||
rp.isRegistering = 'stepTwo';
|
||||
rp.EMail = values.email;
|
||||
|
||||
dispatch(appVarActions.setRegisterProcess(rp));
|
||||
saveVarChanges();
|
||||
|
||||
navigation.navigate('Register', {screen: 'RegStepTwo'});
|
||||
} else if (resp.status === 204) {
|
||||
rp.isRegistering = 'stepFinal';
|
||||
rp.EMail = values.email;
|
||||
|
||||
dispatch(appVarActions.setRegisterProcess(rp));
|
||||
saveVarChanges();
|
||||
|
||||
navigation.navigate('Register', {screen: 'RegStepFinal'});
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
} else {
|
||||
resendMail(values.email, toast)
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
showToast(toast, {
|
||||
title: 'Error',
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: resp.status,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView keyboardShouldPersistTaps="handled">
|
||||
<Box alignItems="center" mt={5}>
|
||||
<FormControl
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
isRequired
|
||||
isDisabled={isLoading}
|
||||
isInvalid={isError}>
|
||||
<FormControl.Label>
|
||||
{lang.account.registration.stepOne.title}
|
||||
</FormControl.Label>
|
||||
<Input
|
||||
autoFocus
|
||||
placeholder={lang.account.registration.stepOne.title}
|
||||
value={values.email}
|
||||
autoCapitalize={'none'}
|
||||
maxLength={emailOptions.maxLength}
|
||||
keyboardType={'email-address'}
|
||||
onChangeText={text => {
|
||||
const mail = text.replaceAll(' ', '');
|
||||
setValues({email: mail});
|
||||
|
||||
if (errors.noEntered && mail !== '') {
|
||||
let err = errors;
|
||||
err.noEntered = false;
|
||||
setErrors({...err});
|
||||
}
|
||||
if (errors.wrongFormat && validateEmail(mail)) {
|
||||
let err = errors;
|
||||
err.wrongFormat = false;
|
||||
setErrors({...err});
|
||||
}
|
||||
if (errors.alreadyExists) {
|
||||
let err = errors;
|
||||
err.alreadyExists = false;
|
||||
setErrors({...err});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{errorText()}
|
||||
</FormControl.ErrorMessage>
|
||||
</FormControl>
|
||||
<Container marginY={5}>
|
||||
<Agreement />
|
||||
</Container>
|
||||
<Button
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
colorScheme="primary"
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
if (values.email === '') {
|
||||
let err = errors;
|
||||
err.noEntered = true;
|
||||
setErrors({...err});
|
||||
} else if (validateEmail(values.email)) {
|
||||
nextStep();
|
||||
} else {
|
||||
let err = errors;
|
||||
err.wrongFormat = true;
|
||||
setErrors({...err});
|
||||
}
|
||||
}}>
|
||||
{lang.account.registration.stepOne.button}
|
||||
</Button>
|
||||
</Box>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
function StepTwo() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const regPro = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const cellCount = 6;
|
||||
const {colors} = useTheme();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
|
||||
const initNoErrors = {
|
||||
noEntered: false,
|
||||
};
|
||||
|
||||
const [errors, setErrors] = useState(initNoErrors);
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const [values, setValues] = useState({code: ''});
|
||||
|
||||
const headerText = () => {
|
||||
return reactStringReplace(
|
||||
lang.account.registration.stepTwo.title,
|
||||
'${EMail}',
|
||||
(match, i) => (
|
||||
<Text key={match + i} color={'primary.400'}>
|
||||
{regPro.EMail}
|
||||
</Text>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const resendText = () => {
|
||||
return reactStringReplace(
|
||||
lang.account.registration.stepTwo.resend[0],
|
||||
'${resend}',
|
||||
(match, i) => (
|
||||
<Text
|
||||
key={match + i}
|
||||
color={'primary.400'}
|
||||
onPress={() => {
|
||||
setLoading(true);
|
||||
resendMail(
|
||||
store.getState().appVariables.preferences.RegisterProcess.EMail,
|
||||
toast,
|
||||
)
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}}>
|
||||
{lang.account.registration.stepTwo.resend[1]}
|
||||
</Text>
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const validate = (text: string) => {
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_2,
|
||||
|
||||
requestHeader: {},
|
||||
requestGET: {':verifyId': text, ':xToken': regPro.XToken || ''},
|
||||
})
|
||||
.then(resp => {
|
||||
let rp = {...regPro};
|
||||
rp.isRegistering = 'stepFinal';
|
||||
|
||||
dispatch(appVarActions.setRegisterProcess(rp));
|
||||
saveVarChanges();
|
||||
|
||||
showToast(toast, {
|
||||
title: lang.account.registration.stepTwo.success,
|
||||
variant: 'solid',
|
||||
status: 'success',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
|
||||
navigation.navigate('Register', {screen: 'RegStepFinal'});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(resp => {
|
||||
let text = 'unknown error ' + resp.status;
|
||||
if (resp.status !== undefined) {
|
||||
const _text =
|
||||
lang.account.registration.stepTwo.verificationError[
|
||||
resp.status as number
|
||||
];
|
||||
if (_text !== undefined) text = _text;
|
||||
}
|
||||
|
||||
showToast(toast, {
|
||||
title: lang.error,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: text,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
if (resp.status === 422) {
|
||||
let rp = {...regPro};
|
||||
rp.isRegistering = false;
|
||||
|
||||
dispatch(appVarActions.setRegisterProcess(rp));
|
||||
saveVarChanges();
|
||||
navigation.navigate('Register', {screen: 'RegStepOne'});
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView keyboardShouldPersistTaps="handled">
|
||||
<Box alignItems="center" mt={5}>
|
||||
<Center w="75%" maxW="1000px">
|
||||
<Text>{headerText()}</Text>
|
||||
<Box>
|
||||
<FormControl isDisabled={isLoading} isInvalid={errors.noEntered}>
|
||||
<ConfirmationCodeField
|
||||
cellCount={cellCount}
|
||||
charType="number"
|
||||
disabled={isLoading}
|
||||
rest={{mt: 5}}
|
||||
onChange={(text: string) => {
|
||||
setValues({code: text});
|
||||
setErrors(initNoErrors);
|
||||
}}
|
||||
onFinish={(text: string) => validate(text)}
|
||||
/>
|
||||
|
||||
<FormControl.ErrorMessage
|
||||
leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{lang.account.registration.stepTwo.noCodeEntered}
|
||||
</FormControl.ErrorMessage>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Center>
|
||||
<Container marginY={5}>
|
||||
<Agreement />
|
||||
</Container>
|
||||
<Button
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
colorScheme="primary"
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
if (values.code.length === cellCount) {
|
||||
validate(values.code);
|
||||
} else {
|
||||
let err = {...errors};
|
||||
err.noEntered = true;
|
||||
setErrors(err);
|
||||
}
|
||||
}}>
|
||||
{lang.account.registration.stepTwo.button}
|
||||
</Button>
|
||||
<Container marginY={5}>
|
||||
<Text textAlign={'justify'} color={'white.900'}>
|
||||
{resendText()}
|
||||
</Text>
|
||||
</Container>
|
||||
</Box>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
function StepFinal() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const regPro = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {colors} = useTheme();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
interface inputElementType {
|
||||
label: string;
|
||||
input: string;
|
||||
autoCapitalize: 'none' | 'words';
|
||||
errorIndex: 'none' | string;
|
||||
errorTextObject: any;
|
||||
isPassword: boolean | 'passwordRepeat';
|
||||
minLength: number;
|
||||
maxLength: number;
|
||||
onTextChange: any;
|
||||
textChangeTimeout: number;
|
||||
isAllowed: any;
|
||||
}
|
||||
|
||||
const accountNameRef = useRef(setTimeout(() => {}));
|
||||
const accountNameFetchRef = useRef(setTimeout(() => {}));
|
||||
|
||||
const accountName = {
|
||||
label: lang.account.registration.stepFinal.accountName,
|
||||
input: '',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.accountNameError,
|
||||
minLength: accountNameOptions.minLength,
|
||||
maxLength: accountNameOptions.maxLength,
|
||||
isAllowed: accountNameOptions.isAllowed,
|
||||
isPassword: false,
|
||||
onTextChange: (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
||||
let text = e.nativeEvent.text;
|
||||
let self = accountName;
|
||||
|
||||
clearTimeout(accountNameRef.current);
|
||||
|
||||
let obj = {...valuesAccountName};
|
||||
accountNameRef.current = setTimeout(() => {
|
||||
obj.input = text;
|
||||
|
||||
if (text.length < self.minLength) obj.errorIndex = 'tooShort';
|
||||
else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
|
||||
else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
|
||||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesAccountName(obj);
|
||||
}, 50);
|
||||
|
||||
clearTimeout(accountNameFetchRef.current);
|
||||
accountNameFetchRef.current = setTimeout(() => {
|
||||
console.log(obj);
|
||||
|
||||
if (obj.errorIndex === 'none') {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK,
|
||||
request: {AccountName: obj.input},
|
||||
})
|
||||
.then(resp => {
|
||||
console.log('OK');
|
||||
})
|
||||
.catch(resp => {
|
||||
if (resp.status !== undefined) {
|
||||
obj.errorIndex = resp.status;
|
||||
setValuesAccountName(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 750);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesAccountName, setValuesAccountName] = useState(accountName);
|
||||
|
||||
const userNameRef = useRef(setTimeout(() => {}));
|
||||
|
||||
const userName = {
|
||||
label: lang.account.registration.stepFinal.userName,
|
||||
input: '',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.userNameError,
|
||||
minLength: userNameOptions.minLength,
|
||||
maxLength: userNameOptions.maxLength,
|
||||
isAllowed: userNameOptions.isAllowed,
|
||||
isPassword: false,
|
||||
onTextChange: (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
||||
let text = e.nativeEvent.text;
|
||||
let self = userName;
|
||||
|
||||
clearTimeout(userNameRef.current);
|
||||
|
||||
userNameRef.current = setTimeout(() => {
|
||||
let obj = {...valuesUserName};
|
||||
obj.input = text;
|
||||
|
||||
if (text.length < self.minLength) obj.errorIndex = 'tooShort';
|
||||
else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
|
||||
else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
|
||||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesUserName(obj);
|
||||
}, 50);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesUserName, setValuesUserName] = useState(userName);
|
||||
|
||||
const passwordRef = useRef(setTimeout(() => {}));
|
||||
|
||||
const password = {
|
||||
label: lang.account.registration.stepFinal.password,
|
||||
input: '',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.passwordError,
|
||||
minLength: passwordOptions.minLength,
|
||||
maxLength: passwordOptions.maxLength,
|
||||
isAllowed: passwordOptions.isAllowed,
|
||||
isPassword: true,
|
||||
onTextChange: (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
||||
let text = e.nativeEvent.text;
|
||||
let self = password;
|
||||
|
||||
clearTimeout(passwordRef.current);
|
||||
|
||||
passwordRef.current = setTimeout(() => {
|
||||
let obj = {...valuesPassword};
|
||||
obj.input = text;
|
||||
|
||||
if (text.length < self.minLength) obj.errorIndex = 'tooShort';
|
||||
else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
|
||||
else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
|
||||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesPassword(obj);
|
||||
|
||||
let objRe = {...valuesPasswordRe};
|
||||
if (text !== valuesPasswordRe.input) objRe.errorIndex = 'noMatch';
|
||||
else objRe.errorIndex = 'none';
|
||||
|
||||
setValuesPasswordRe(objRe);
|
||||
}, 50);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesPassword, setValuesPassword] = useState(password);
|
||||
|
||||
const passwordReRef = useRef(setTimeout(() => {}));
|
||||
|
||||
const passwordRe = {
|
||||
label: lang.account.registration.stepFinal.passwordRepeat,
|
||||
input: '',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.passwordError,
|
||||
minLength: passwordOptions.minLength,
|
||||
maxLength: passwordOptions.maxLength,
|
||||
isAllowed: passwordOptions.isAllowed,
|
||||
isPassword: 'passwordRepeat',
|
||||
onTextChange: (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
||||
let text = e.nativeEvent.text;
|
||||
let self = passwordRe;
|
||||
|
||||
clearTimeout(passwordReRef.current);
|
||||
|
||||
passwordReRef.current = setTimeout(() => {
|
||||
let obj = {...valuesPasswordRe};
|
||||
obj.input = text;
|
||||
console.log(text, valuesPassword.input);
|
||||
|
||||
if (text !== valuesPassword.input) obj.errorIndex = 'noMatch';
|
||||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesPasswordRe(obj);
|
||||
}, 50);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesPasswordRe, setValuesPasswordRe] = useState(passwordRe);
|
||||
|
||||
const inputElement = (
|
||||
val: inputElementType,
|
||||
set: React.Dispatch<React.SetStateAction<inputElementType>>,
|
||||
valConst: inputElementType,
|
||||
autofocus?: boolean,
|
||||
) => {
|
||||
const isPassword =
|
||||
val.isPassword === true || val.isPassword === 'passwordRepeat';
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
isRequired
|
||||
isDisabled={isLoading}
|
||||
isInvalid={val.errorIndex !== 'none'}>
|
||||
<FormControl.Label>{val.label}</FormControl.Label>
|
||||
<Input
|
||||
autoFocus={autofocus}
|
||||
type={isPassword && showPassword === false ? 'password' : 'text'}
|
||||
placeholder={''}
|
||||
keyboardType={'default'}
|
||||
onChange={valConst.onTextChange}
|
||||
InputRightElement={
|
||||
isPassword ? (
|
||||
<IconButton
|
||||
mr="2"
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
icon={
|
||||
<MaterialIcons
|
||||
size={20}
|
||||
name={showPassword ? 'visibility' : 'visibility-off'}
|
||||
/>
|
||||
}
|
||||
borderRadius="full"
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{val.errorTextObject[val.errorIndex] !== undefined
|
||||
? val.errorTextObject[val.errorIndex]
|
||||
.replaceAll('$(minLength)', val.minLength)
|
||||
.replaceAll('$(maxLength)', val.maxLength)
|
||||
: val.errorTextObject[val.errorIndex] !== 'none'
|
||||
? lang.error
|
||||
: null}
|
||||
</FormControl.ErrorMessage>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView keyboardShouldPersistTaps="handled">
|
||||
<VStack space={3} alignItems="center" mt={5}>
|
||||
<Container maxWidth={'100%'} alignItems={'center'}>
|
||||
<Center>
|
||||
<Text>{lang.account.registration.stepFinal.displayName}</Text>
|
||||
<NameDisplay
|
||||
UserName={
|
||||
valuesUserName.input !== ''
|
||||
? valuesUserName.input
|
||||
: lang.account.registration.stepFinal.userName
|
||||
}
|
||||
AccountName={
|
||||
valuesAccountName.input !== ''
|
||||
? valuesAccountName.input
|
||||
: lang.account.registration.stepFinal.accountName
|
||||
}
|
||||
/>
|
||||
</Center>
|
||||
</Container>
|
||||
{inputElement(valuesUserName, setValuesUserName, userName)}
|
||||
{inputElement(valuesAccountName, setValuesAccountName, accountName)}
|
||||
{inputElement(valuesPassword, setValuesPassword, password)}
|
||||
{inputElement(valuesPasswordRe, setValuesPasswordRe, passwordRe)}
|
||||
</VStack>
|
||||
<Center>
|
||||
<Container mt={5}>
|
||||
<Agreement />
|
||||
</Container>
|
||||
<Button
|
||||
marginY={5}
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
colorScheme="primary"
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {}}>
|
||||
{lang.account.registration.stepFinal.button}
|
||||
</Button>
|
||||
</Center>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
|
@ -11,7 +11,7 @@ import {
|
|||
passwordOptions,
|
||||
userNameOptions,
|
||||
XToken,
|
||||
} from '@caj/helper/types';
|
||||
} from '@caj/configs/types';
|
||||
import {RootScreenNavigationProp} from '@caj/Navigation';
|
||||
import {RootState, store} from '@caj/redux/store';
|
||||
import {useNavigation} from '@react-navigation/native';
|
||||
|
@ -25,25 +25,20 @@ import {
|
|||
Center,
|
||||
Container,
|
||||
FormControl,
|
||||
Heading,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Input,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
Spinner,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
useTheme,
|
||||
useToast,
|
||||
VStack,
|
||||
WarningOutlineIcon,
|
||||
Progress,
|
||||
} from 'native-base';
|
||||
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
import {baseFontSize} from 'native-base/lib/typescript/theme/tools';
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
|
@ -52,6 +47,17 @@ import NameDisplay from './NameDisplay';
|
|||
import showToast from './Toast';
|
||||
import {NativeSyntheticEvent, TextInputFocusEventData} from 'react-native';
|
||||
|
||||
import PasswordQualityCalculator from '@caj/helper/password-quality-calculator/PasswordQualityCalculator';
|
||||
|
||||
// [ optional ] list of about 10000 most common passwords, 86kb (gzip 32kb)
|
||||
import MostPopularPasswords from '@caj/helper/password-quality-calculator/MostPopularPasswords';
|
||||
import MyUserManager from '@caj/user/MyUserManager';
|
||||
|
||||
const Buffer = require('buffer').Buffer;
|
||||
|
||||
// Load the popular passwords list
|
||||
PasswordQualityCalculator.PopularPasswords.load(MostPopularPasswords);
|
||||
|
||||
const validateEmail = (email: EMail) => {
|
||||
return emailOptions.isAllowed(email);
|
||||
};
|
||||
|
@ -86,7 +92,10 @@ export default function NotLoggedIn() {
|
|||
colorScheme="black"
|
||||
variant={theme === ThemeMode.Darkest ? 'outline' : 'subtle'}
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl', color: 'white.900'}}>
|
||||
_text={{fontSize: 'xl', color: 'white.900'}}
|
||||
onPress={() => {
|
||||
navigation.navigate('Register', {screen: 'Login'});
|
||||
}}>
|
||||
Log in
|
||||
</Button>
|
||||
</VStack>
|
||||
|
@ -106,6 +115,7 @@ export type LoginStackNavigatorParamList = {
|
|||
RegStepOne: undefined;
|
||||
RegStepTwo: undefined;
|
||||
RegStepFinal: undefined;
|
||||
Login: undefined;
|
||||
};
|
||||
|
||||
const LoginStack = createNativeStackNavigator<LoginStackNavigatorParamList>();
|
||||
|
@ -151,6 +161,16 @@ function RegisterScreen() {
|
|||
}}
|
||||
component={StepFinal}
|
||||
/>
|
||||
<LoginStack.Screen
|
||||
name="Login"
|
||||
options={{
|
||||
animation: 'slide_from_right',
|
||||
title: lang.account.login.title,
|
||||
headerShown: true,
|
||||
...defaultHeaderStyle(theme, 'registration'),
|
||||
}}
|
||||
component={Login}
|
||||
/>
|
||||
</LoginStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
@ -218,20 +238,35 @@ function resendMail(email: EMail, toast: any): Promise<XToken> {
|
|||
return new Promise<XToken>((resolve, reject) => {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_RESEND_MAIL,
|
||||
|
||||
requestHeader: {},
|
||||
requestHeader: {
|
||||
'X-Token':
|
||||
store.getState().appVariables.preferences.RegisterProcess.XToken,
|
||||
},
|
||||
request: {
|
||||
Email: email,
|
||||
},
|
||||
//response: {
|
||||
// XToken: undefined,
|
||||
//},
|
||||
response: {
|
||||
XToken: undefined,
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
console.log(1);
|
||||
let token =
|
||||
store.getState().appVariables.preferences.RegisterProcess.XToken;
|
||||
if (token !== undefined /*resp.response.XToken !== undefined*/) {
|
||||
if (token === undefined) token = '';
|
||||
|
||||
if (resp.response.XToken !== undefined || token !== undefined) {
|
||||
if (resp.response.XToken !== undefined) {
|
||||
token = resp.response.XToken;
|
||||
|
||||
let regPro = {
|
||||
...store.getState().appVariables.preferences.RegisterProcess,
|
||||
};
|
||||
regPro.XToken = token;
|
||||
store.dispatch(appVarActions.setRegisterProcess(regPro));
|
||||
|
||||
saveVarChanges();
|
||||
}
|
||||
|
||||
showToast(toast, {
|
||||
title: store.getState().appVariables.lang.info,
|
||||
variant: 'solid',
|
||||
|
@ -257,7 +292,7 @@ function resendMail(email: EMail, toast: any): Promise<XToken> {
|
|||
}
|
||||
})
|
||||
.catch(resp => {
|
||||
let text = 'unknown error ' + resp.status;
|
||||
let text = 'Error ' + resp.status;
|
||||
if (resp.status !== undefined) {
|
||||
const _text =
|
||||
store.getState().appVariables.lang.account.registration.stepTwo
|
||||
|
@ -342,6 +377,12 @@ function StepOne() {
|
|||
setLoading(true);
|
||||
setErrors(initNoErrors);
|
||||
|
||||
let rp = {...regPro};
|
||||
|
||||
if (rp.EMail !== values.email) {
|
||||
rp.XToken = undefined;
|
||||
}
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_1,
|
||||
requestHeader: {},
|
||||
|
@ -353,7 +394,6 @@ function StepOne() {
|
|||
},
|
||||
})
|
||||
.then(resp => {
|
||||
let rp = {...regPro};
|
||||
rp.isRegistering = 'stepTwo';
|
||||
rp.EMail = values.email;
|
||||
rp.XToken = resp.response.XToken;
|
||||
|
@ -376,8 +416,6 @@ function StepOne() {
|
|||
.catch(resp => {
|
||||
if (resp.status === 401 || resp.status === 204) {
|
||||
if (regPro.XToken !== undefined) {
|
||||
let rp = {...regPro};
|
||||
|
||||
if (resp.status === 401) {
|
||||
rp.isRegistering = 'stepTwo';
|
||||
rp.EMail = values.email;
|
||||
|
@ -400,26 +438,51 @@ function StepOne() {
|
|||
} else {
|
||||
resendMail(values.email, toast)
|
||||
.then(() => {
|
||||
navigation.navigate('Register', {screen: 'RegStepTwo'});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
return;
|
||||
} else if (resp.status === 422) {
|
||||
showToast(toast, {
|
||||
title: lang.error,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: lang.account.registration.stepOne.addressExists,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
} else {
|
||||
showToast(toast, {
|
||||
title: 'Error',
|
||||
title: lang.error,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: resp.status,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onButtonPress = () => {
|
||||
if (values.email === '') {
|
||||
let err = errors;
|
||||
err.noEntered = true;
|
||||
setErrors({...err});
|
||||
} else if (validateEmail(values.email)) {
|
||||
nextStep();
|
||||
} else {
|
||||
let err = errors;
|
||||
err.wrongFormat = true;
|
||||
setErrors({...err});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView keyboardShouldPersistTaps="handled">
|
||||
<Box alignItems="center" mt={5}>
|
||||
|
@ -434,8 +497,10 @@ function StepOne() {
|
|||
</FormControl.Label>
|
||||
<Input
|
||||
autoFocus
|
||||
autoComplete="email"
|
||||
placeholder={lang.account.registration.stepOne.title}
|
||||
value={values.email}
|
||||
onSubmitEditing={onButtonPress}
|
||||
autoCapitalize={'none'}
|
||||
maxLength={emailOptions.maxLength}
|
||||
keyboardType={'email-address'}
|
||||
|
@ -475,19 +540,7 @@ function StepOne() {
|
|||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
if (values.email === '') {
|
||||
let err = errors;
|
||||
err.noEntered = true;
|
||||
setErrors({...err});
|
||||
} else if (validateEmail(values.email)) {
|
||||
nextStep();
|
||||
} else {
|
||||
let err = errors;
|
||||
err.wrongFormat = true;
|
||||
setErrors({...err});
|
||||
}
|
||||
}}>
|
||||
onPress={onButtonPress}>
|
||||
{lang.account.registration.stepOne.button}
|
||||
</Button>
|
||||
</Box>
|
||||
|
@ -588,7 +641,7 @@ function StepTwo() {
|
|||
setLoading(false);
|
||||
})
|
||||
.catch(resp => {
|
||||
let text = 'unknown error ' + resp.status;
|
||||
let text = 'Error ' + resp.status;
|
||||
if (resp.status !== undefined) {
|
||||
const _text =
|
||||
lang.account.registration.stepTwo.verificationError[
|
||||
|
@ -704,6 +757,7 @@ function StepFinal() {
|
|||
onTextChange: any;
|
||||
textChangeTimeout: number;
|
||||
isAllowed: any;
|
||||
autoComplete?: 'name' | 'username-new' | 'password-new';
|
||||
}
|
||||
|
||||
const accountNameRef = useRef(setTimeout(() => {}));
|
||||
|
@ -712,6 +766,7 @@ function StepFinal() {
|
|||
const accountName = {
|
||||
label: lang.account.registration.stepFinal.accountName,
|
||||
input: '',
|
||||
autoComplete: 'username-new',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.accountNameError,
|
||||
minLength: accountNameOptions.minLength,
|
||||
|
@ -734,28 +789,25 @@ function StepFinal() {
|
|||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesAccountName(obj);
|
||||
|
||||
accountNameFetchRef.current = setTimeout(() => {
|
||||
if (obj.errorIndex === 'none') {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK,
|
||||
request: {AccountName: obj.input},
|
||||
})
|
||||
.then(resp => {})
|
||||
.catch(resp => {
|
||||
if (resp.status !== undefined) {
|
||||
obj.errorIndex = resp.status;
|
||||
setValuesAccountName({...obj});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 750);
|
||||
}, 50);
|
||||
|
||||
clearTimeout(accountNameFetchRef.current);
|
||||
accountNameFetchRef.current = setTimeout(() => {
|
||||
console.log(obj);
|
||||
|
||||
if (obj.errorIndex === 'none') {
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK,
|
||||
request: {AccountName: obj.input},
|
||||
})
|
||||
.then(resp => {
|
||||
console.log('OK');
|
||||
})
|
||||
.catch(resp => {
|
||||
if (resp.status !== undefined) {
|
||||
obj.errorIndex = resp.status;
|
||||
setValuesAccountName(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 750);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesAccountName, setValuesAccountName] = useState(accountName);
|
||||
|
@ -765,6 +817,7 @@ function StepFinal() {
|
|||
const userName = {
|
||||
label: lang.account.registration.stepFinal.userName,
|
||||
input: '',
|
||||
autoComplete: 'name',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.userNameError,
|
||||
minLength: userNameOptions.minLength,
|
||||
|
@ -797,6 +850,7 @@ function StepFinal() {
|
|||
const password = {
|
||||
label: lang.account.registration.stepFinal.password,
|
||||
input: '',
|
||||
autoComplete: 'password-new',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.passwordError,
|
||||
minLength: passwordOptions.minLength,
|
||||
|
@ -813,28 +867,56 @@ function StepFinal() {
|
|||
let obj = {...valuesPassword};
|
||||
obj.input = text;
|
||||
|
||||
let PasswordQuality = PasswordQualityCalculator(text);
|
||||
setPasswordQuality(PasswordQuality);
|
||||
|
||||
if (text.length < self.minLength) obj.errorIndex = 'tooShort';
|
||||
else if (text.length > self.maxLength) obj.errorIndex = 'tooLong';
|
||||
else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid';
|
||||
else if (passwordOptions.minBits >= PasswordQuality)
|
||||
obj.errorIndex = 'weak';
|
||||
else obj.errorIndex = 'none';
|
||||
|
||||
setValuesPassword(obj);
|
||||
|
||||
let objRe = {...valuesPasswordRe};
|
||||
if (text !== valuesPasswordRe.input) objRe.errorIndex = 'noMatch';
|
||||
else objRe.errorIndex = 'none';
|
||||
if (valuesPasswordRe.input !== '') {
|
||||
if (text !== valuesPasswordRe.input) objRe.errorIndex = 'noMatch';
|
||||
else objRe.errorIndex = 'none';
|
||||
|
||||
setValuesPasswordRe(objRe);
|
||||
setValuesPasswordRe(objRe);
|
||||
}
|
||||
}, 50);
|
||||
},
|
||||
} as inputElementType;
|
||||
const [valuesPassword, setValuesPassword] = useState(password);
|
||||
const [passwordQuality, setPasswordQuality] = useState(0);
|
||||
|
||||
let passwordQualityIndex = 0;
|
||||
if (passwordQuality >= 128) passwordQualityIndex = 4;
|
||||
else if (passwordQuality >= 100) passwordQualityIndex = 3;
|
||||
else if (passwordQuality >= 80) passwordQualityIndex = 2;
|
||||
else if (passwordQuality >= 50) passwordQualityIndex = 1;
|
||||
let passwordQualityPercent = (passwordQuality / 128.0) * 100;
|
||||
passwordQualityPercent =
|
||||
passwordQualityPercent >= 100.0 ? 100 : passwordQualityPercent;
|
||||
|
||||
const passwordQualityColor = 'hsl(' + passwordQualityPercent + ', 100%, 50%)';
|
||||
|
||||
const passwordQualityText =
|
||||
lang.account.registration.stepFinal.passwordQuality.replace(
|
||||
'${quality}',
|
||||
lang.account.registration.stepFinal.passwordQualityList[
|
||||
passwordQualityIndex
|
||||
],
|
||||
);
|
||||
|
||||
const passwordReRef = useRef(setTimeout(() => {}));
|
||||
|
||||
const passwordRe = {
|
||||
label: lang.account.registration.stepFinal.passwordRepeat,
|
||||
input: '',
|
||||
autoComplete: 'password-new',
|
||||
errorIndex: 'none',
|
||||
errorTextObject: lang.account.registration.stepFinal.passwordError,
|
||||
minLength: passwordOptions.minLength,
|
||||
|
@ -850,7 +932,6 @@ function StepFinal() {
|
|||
passwordReRef.current = setTimeout(() => {
|
||||
let obj = {...valuesPasswordRe};
|
||||
obj.input = text;
|
||||
console.log(text, valuesPassword.input);
|
||||
|
||||
if (text !== valuesPassword.input) obj.errorIndex = 'noMatch';
|
||||
else obj.errorIndex = 'none';
|
||||
|
@ -873,12 +954,13 @@ function StepFinal() {
|
|||
return (
|
||||
<FormControl
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
maxW={350}
|
||||
isRequired
|
||||
isDisabled={isLoading}
|
||||
isInvalid={val.errorIndex !== 'none'}>
|
||||
<FormControl.Label>{val.label}</FormControl.Label>
|
||||
<Input
|
||||
autoComplete={val.autoComplete}
|
||||
autoFocus={autofocus}
|
||||
type={isPassword && showPassword === false ? 'password' : 'text'}
|
||||
placeholder={''}
|
||||
|
@ -888,10 +970,12 @@ function StepFinal() {
|
|||
isPassword ? (
|
||||
<IconButton
|
||||
mr="2"
|
||||
focusable={false}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
icon={
|
||||
<MaterialIcons
|
||||
size={20}
|
||||
color={colors.white[200]}
|
||||
name={showPassword ? 'visibility' : 'visibility-off'}
|
||||
/>
|
||||
}
|
||||
|
@ -904,8 +988,8 @@ function StepFinal() {
|
|||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{val.errorTextObject[val.errorIndex] !== undefined
|
||||
? val.errorTextObject[val.errorIndex]
|
||||
.replaceAll('$(minLength)', val.minLength)
|
||||
.replaceAll('$(maxLength)', val.maxLength)
|
||||
.replaceAll('${minLength}', val.minLength)
|
||||
.replaceAll('${maxLength}', val.maxLength)
|
||||
: val.errorTextObject[val.errorIndex] !== 'none'
|
||||
? lang.error
|
||||
: null}
|
||||
|
@ -937,6 +1021,20 @@ function StepFinal() {
|
|||
{inputElement(valuesUserName, setValuesUserName, userName)}
|
||||
{inputElement(valuesAccountName, setValuesAccountName, accountName)}
|
||||
{inputElement(valuesPassword, setValuesPassword, password)}
|
||||
|
||||
<Box w="75%" maxW={350}>
|
||||
<Text mx={4} mb={1} fontSize={13} color="light.400">
|
||||
{passwordQualityText}
|
||||
</Text>
|
||||
<Progress
|
||||
value={passwordQualityPercent}
|
||||
mx={4}
|
||||
_filledTrack={{
|
||||
bg: passwordQualityColor,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{inputElement(valuesPasswordRe, setValuesPasswordRe, passwordRe)}
|
||||
</VStack>
|
||||
<Center>
|
||||
|
@ -951,10 +1049,400 @@ function StepFinal() {
|
|||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {}}>
|
||||
onPress={() => {
|
||||
function showToastNow(text: string) {
|
||||
showToast(toast, {
|
||||
title: text,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
}
|
||||
|
||||
valuesUserName.onTextChange({
|
||||
nativeEvent: {text: valuesUserName.input},
|
||||
});
|
||||
|
||||
valuesAccountName.onTextChange({
|
||||
nativeEvent: {text: valuesAccountName.input},
|
||||
});
|
||||
|
||||
valuesPassword.onTextChange({
|
||||
nativeEvent: {text: valuesPassword.input},
|
||||
});
|
||||
|
||||
let obj = {...valuesPasswordRe};
|
||||
if (obj.input !== valuesPassword.input) obj.errorIndex = 'noMatch';
|
||||
else obj.errorIndex = 'none';
|
||||
setValuesPasswordRe(obj);
|
||||
|
||||
setLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
if (
|
||||
valuesAccountName.input === '' ||
|
||||
valuesUserName.input === '' ||
|
||||
valuesPassword.input === '' ||
|
||||
valuesPasswordRe.input === '' ||
|
||||
valuesUserName.errorIndex !== 'none' ||
|
||||
valuesAccountName.errorIndex !== 'none' ||
|
||||
valuesPassword.errorIndex !== 'none' ||
|
||||
valuesPasswordRe.errorIndex !== 'none'
|
||||
) {
|
||||
/*showToastNow(
|
||||
lang.account.registration.stepFinal.noAllFieldsEntered,
|
||||
);*/
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let base64PW = new Buffer(valuesPassword.input).toString(
|
||||
'base64',
|
||||
);
|
||||
|
||||
let xToken =
|
||||
store.getState().appVariables.preferences.RegisterProcess
|
||||
.XToken;
|
||||
|
||||
if (xToken === undefined) xToken = '';
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.REGISTER_STEP_FINAL,
|
||||
requestHeader: {
|
||||
'X-Token': xToken,
|
||||
},
|
||||
request: {
|
||||
AccountName: valuesAccountName.input,
|
||||
Username: valuesUserName.input,
|
||||
Password: base64PW,
|
||||
},
|
||||
response: {
|
||||
XAuthorization: '',
|
||||
UserId: '',
|
||||
WebSocketSessionId: '',
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
if (
|
||||
resp.response.XAuthorization !== '' &&
|
||||
resp.response.UserId !== '' &&
|
||||
resp.response.WebSocketSessionId !== ''
|
||||
) {
|
||||
let regPro = {
|
||||
...store.getState().appVariables.preferences
|
||||
.RegisterProcess,
|
||||
};
|
||||
regPro.isRegistering = false;
|
||||
store.dispatch(appVarActions.setRegisterProcess(regPro));
|
||||
|
||||
MyUserManager.createNewMyUser(
|
||||
resp.response.UserId,
|
||||
valuesAccountName.input,
|
||||
valuesUserName.input,
|
||||
store.getState().appVariables.preferences.RegisterProcess
|
||||
.EMail,
|
||||
resp.response.XAuthorization,
|
||||
resp.response.WebSocketSessionId,
|
||||
);
|
||||
navigation.popToTop();
|
||||
navigation.goBack();
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(resp => {
|
||||
let errorText =
|
||||
lang.account.registration.stepFinal.registerError[
|
||||
resp.status
|
||||
] || 'Error ' + resp.status;
|
||||
showToastNow(errorText);
|
||||
setLoading(false);
|
||||
});
|
||||
}, 300);
|
||||
}}>
|
||||
{lang.account.registration.stepFinal.button}
|
||||
</Button>
|
||||
</Center>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
function Login() {
|
||||
const lang = useSelector((state: RootState) => state.appVariables.lang);
|
||||
const regPro = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||
);
|
||||
|
||||
const {colors} = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const toast = useToast();
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
|
||||
const initNoErrors = {
|
||||
wrongFormat: false,
|
||||
alreadyExists: false,
|
||||
noEntered: false,
|
||||
unknown: undefined,
|
||||
};
|
||||
|
||||
const [errorsMail, setErrorsMail] = useState(initNoErrors);
|
||||
const [errorsPassword, setErrorsPassword] = useState(initNoErrors);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const isErrorMail =
|
||||
errorsMail.wrongFormat || errorsMail.alreadyExists || errorsMail.noEntered;
|
||||
|
||||
const isErrorPassword = errorsPassword.noEntered;
|
||||
|
||||
const errorTextMail = () => {
|
||||
if (errorsMail.wrongFormat) {
|
||||
return lang.account.registration.stepOne.addressInvalid;
|
||||
}
|
||||
if (errorsMail.noEntered) {
|
||||
return lang.account.registration.stepOne.noMailEntered;
|
||||
}
|
||||
if (errorsMail.unknown !== undefined) {
|
||||
return errorsMail.unknown;
|
||||
}
|
||||
};
|
||||
|
||||
const errorTextPassword = () => {
|
||||
if (errorsPassword.noEntered) {
|
||||
return lang.account.registration.stepFinal.passwordError.required;
|
||||
}
|
||||
if (errorsPassword.unknown !== undefined) {
|
||||
return errorsPassword.unknown;
|
||||
}
|
||||
};
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const [values, setValues] = useState({email: regPro.EMail, password: ''});
|
||||
|
||||
const onButtonPress = () => {
|
||||
if (values.email === '') {
|
||||
let err = {...errorsMail};
|
||||
err.noEntered = true;
|
||||
setErrorsMail({...err});
|
||||
}
|
||||
if (values.password === '') {
|
||||
let err = {...errorsPassword};
|
||||
err.noEntered = true;
|
||||
setErrorsPassword({...err});
|
||||
} else if (validateEmail(values.email)) {
|
||||
setLoading(true);
|
||||
|
||||
if (values.password.length < passwordOptions.minLength) {
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
|
||||
showToast(toast, {
|
||||
title: lang.account.login.wrongEmPw,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
let base64PW = new Buffer(values.password).toString('base64');
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.LOGIN,
|
||||
requestHeader: {},
|
||||
request: {
|
||||
Email: values.email,
|
||||
Password: base64PW,
|
||||
},
|
||||
response: {
|
||||
XAuthorization: '',
|
||||
UserId: '',
|
||||
WebSocketSessionId: '',
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
setLoading(false);
|
||||
|
||||
let accName = 'ga';
|
||||
let userName = 'ga';
|
||||
MyUserManager.createNewMyUser(
|
||||
resp.response.UserId,
|
||||
accName,
|
||||
userName,
|
||||
values.email,
|
||||
resp.response.XAuthorization,
|
||||
resp.response.WebSocketSessionId,
|
||||
)
|
||||
.then(() => {
|
||||
showToast(toast, {
|
||||
title: lang.account.login.success,
|
||||
variant: 'solid',
|
||||
status: 'success',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
navigation.goBack();
|
||||
})
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
showToast(toast, {
|
||||
title: lang.account.login.failed,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(resp => {
|
||||
setLoading(false);
|
||||
|
||||
if (resp.status === 422) {
|
||||
showToast(toast, {
|
||||
title: lang.account.login.wrongEmPw,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
} else {
|
||||
showToast(toast, {
|
||||
title: lang.error + ' ' + resp.status,
|
||||
variant: 'solid',
|
||||
status: 'error',
|
||||
description: undefined,
|
||||
isClosable: true,
|
||||
rest: {},
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let err = {...errorsMail};
|
||||
err.wrongFormat = true;
|
||||
setErrorsMail({...err});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView keyboardShouldPersistTaps="handled">
|
||||
<Box alignItems="center" mt={5}>
|
||||
<FormControl
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
isRequired
|
||||
isDisabled={isLoading}
|
||||
isInvalid={isErrorMail}>
|
||||
<FormControl.Label>
|
||||
{lang.account.registration.stepOne.title}
|
||||
</FormControl.Label>
|
||||
<Input
|
||||
autoFocus
|
||||
autoComplete="email"
|
||||
placeholder={lang.account.registration.stepOne.title}
|
||||
value={values.email}
|
||||
onSubmitEditing={onButtonPress}
|
||||
autoCapitalize={'none'}
|
||||
maxLength={emailOptions.maxLength}
|
||||
keyboardType={'email-address'}
|
||||
onChangeText={text => {
|
||||
const mail = text.replaceAll(' ', '');
|
||||
let val = {...values};
|
||||
val.email = mail;
|
||||
setValues(val);
|
||||
|
||||
if (errorsMail.noEntered && mail !== '') {
|
||||
let err = errorsMail;
|
||||
err.noEntered = false;
|
||||
setErrorsMail({...err});
|
||||
}
|
||||
if (errorsMail.wrongFormat && validateEmail(mail)) {
|
||||
let err = errorsMail;
|
||||
err.wrongFormat = false;
|
||||
setErrorsMail({...err});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{errorTextMail()}
|
||||
</FormControl.ErrorMessage>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
marginBottom={5}
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
isRequired
|
||||
isDisabled={isLoading}
|
||||
isInvalid={isErrorPassword}>
|
||||
<FormControl.Label>
|
||||
{lang.account.registration.stepFinal.password}
|
||||
</FormControl.Label>
|
||||
<Input
|
||||
autoFocus
|
||||
autoComplete="password"
|
||||
placeholder={lang.account.registration.stepFinal.password}
|
||||
value={values.password}
|
||||
onSubmitEditing={onButtonPress}
|
||||
autoCapitalize={'none'}
|
||||
maxLength={emailOptions.maxLength}
|
||||
type={showPassword === false ? 'password' : 'text'}
|
||||
keyboardType={
|
||||
showPassword === true ? 'visible-password' : undefined
|
||||
}
|
||||
InputRightElement={
|
||||
<IconButton
|
||||
mr="2"
|
||||
focusable={false}
|
||||
onPress={() => setShowPassword(!showPassword)}
|
||||
icon={
|
||||
<MaterialIcons
|
||||
size={20}
|
||||
color={colors.white[200]}
|
||||
name={showPassword ? 'visibility' : 'visibility-off'}
|
||||
/>
|
||||
}
|
||||
borderRadius="full"
|
||||
/>
|
||||
}
|
||||
onChangeText={password => {
|
||||
let val = {...values};
|
||||
val.password = password;
|
||||
setValues(val);
|
||||
|
||||
if (errorsPassword.noEntered && password !== '') {
|
||||
let err = errorsPassword;
|
||||
err.noEntered = false;
|
||||
setErrorsMail({...err});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
{errorTextPassword()}
|
||||
</FormControl.ErrorMessage>
|
||||
</FormControl>
|
||||
<Button
|
||||
w="75%"
|
||||
maxW="350px"
|
||||
colorScheme="primary"
|
||||
rounded="xl"
|
||||
_text={{fontSize: 'xl'}}
|
||||
isLoading={isLoading}
|
||||
onPress={onButtonPress}>
|
||||
{lang.account.login.title}
|
||||
</Button>
|
||||
</Box>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {EMail, XToken} from '@caj/helper/types';
|
||||
import {EMail, UserId, XToken} from '@caj/configs/types';
|
||||
import {VersionType} from '@caj/helper/version';
|
||||
import { MyUserAccount } from '@caj/user/types';
|
||||
import {APP_VERSION} from './appNonSaveVar';
|
||||
|
||||
export enum ThemeMode {
|
||||
|
@ -54,6 +55,8 @@ export interface PREFERENCES_VARS {
|
|||
version: VersionType;
|
||||
theme: ThemeMode;
|
||||
RegisterProcess: RegisterProcess;
|
||||
selectedAccount: UserId | "none";
|
||||
accounts: {[key: UserId]: MyUserAccount};
|
||||
}
|
||||
|
||||
export const preferences_vars_default: PREFERENCES_VARS = {
|
||||
|
@ -64,4 +67,6 @@ export const preferences_vars_default: PREFERENCES_VARS = {
|
|||
XToken: undefined,
|
||||
EMail: '',
|
||||
},
|
||||
selectedAccount: "none",
|
||||
accounts: {},
|
||||
};
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
import {non_save_vars, NON_SAVE_VARS} from './appNonSaveVar';
|
||||
import LangFormat from '@caj/lang/default';
|
||||
import {lang as defaultLang} from '@caj/lang/en';
|
||||
import { UserId } from './types';
|
||||
import { MyUserAccount } from '@caj/user/types';
|
||||
|
||||
export interface appVariablesState {
|
||||
preferences: PREFERENCES_VARS;
|
||||
|
@ -37,6 +39,12 @@ export const appVariablesSlice = createSlice({
|
|||
setRegisterProcess: (state, action: PayloadAction<RegisterProcess>) => {
|
||||
state.preferences.RegisterProcess = action.payload;
|
||||
},
|
||||
setCurrentAccount:(state, action: PayloadAction<UserId>) => {
|
||||
state.preferences.selectedAccount = action.payload;
|
||||
},
|
||||
setAccount:(state, action: PayloadAction<MyUserAccount>) => {
|
||||
state.preferences.accounts[action.payload.UserId] = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import {ThemeMode} from './appVar';
|
||||
|
||||
export type EMail = string;
|
||||
|
||||
export type langCode = 'en' | string;
|
||||
|
||||
export type timestamp = number;
|
||||
|
||||
export type AccountName = string;
|
||||
export type Username = string;
|
||||
export type Username = string | undefined;
|
||||
export type Password = string;
|
||||
export type UserAgent = string;
|
||||
export type XToken = string;
|
||||
export type verifyId = string;
|
||||
|
||||
export type SessionId = string;
|
||||
export type XAuthorization = string;
|
||||
export type UserId = string;
|
||||
export type WebSocketSessionId = string;
|
||||
|
||||
|
@ -18,6 +24,7 @@ export const accountNameOptions = {
|
|||
return text.match('^[a-zA-Z0-9_.]+$') !== null;
|
||||
},
|
||||
};
|
||||
|
||||
export const userNameOptions = {
|
||||
minLength: 2,
|
||||
maxLength: 24,
|
||||
|
@ -29,8 +36,9 @@ export const userNameOptions = {
|
|||
export const passwordOptions = {
|
||||
minLength: 6,
|
||||
maxLength: 64,
|
||||
minBits: 50,
|
||||
isAllowed: (text: string): boolean => {
|
||||
return true;
|
||||
return /\W/.test(text) && /[a-zA-Z]/.test(text) && /[a-zA-Z]/.test(text);
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import {createSlice} from '@reduxjs/toolkit';
|
||||
import type {PayloadAction} from '@reduxjs/toolkit';
|
||||
|
||||
import LangFormat from '@caj/lang/default';
|
||||
import {lang as defaultLang} from '@caj/lang/en';
|
||||
import {UserId} from './types';
|
||||
import {MyUserAccount} from '@caj/user/types';
|
||||
|
||||
export interface userList {
|
||||
myUserList: {[key: UserId]: MyUserAccount};
|
||||
}
|
||||
|
||||
const initialState: userList = {
|
||||
myUserList: {},
|
||||
};
|
||||
|
||||
export const usersSlice = createSlice({
|
||||
name: 'users',
|
||||
initialState,
|
||||
reducers: {
|
||||
setAccount: (state, action: PayloadAction<MyUserAccount>) => {
|
||||
state.myUserList[action.payload.UserId] = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
const {actions} = usersSlice;
|
||||
export const usersActions = actions;
|
||||
|
||||
export default usersSlice.reducer;
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,638 @@
|
|||
import * as PopularPasswords from './PopularPasswords.js';
|
||||
|
||||
const PatternID = {
|
||||
LowerAlpha: 'L',
|
||||
UpperAlpha: 'U',
|
||||
Digit: 'D',
|
||||
Special: 'S',
|
||||
High: 'H',
|
||||
Other: 'X',
|
||||
|
||||
Dictionary: 'W',
|
||||
Repetition: 'R',
|
||||
Number: 'N',
|
||||
DiffSeq: 'C',
|
||||
|
||||
All: "LUDSHXWRNC"
|
||||
};
|
||||
const PrintableAsciiSpecial = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
||||
const UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const LowerCase = "abcdefghijklmnopqrstuvwxyz";
|
||||
const Digits = "0123456789";
|
||||
const HighAnsiChars = (() => {
|
||||
let sbHighAnsi = [], ch;
|
||||
for(ch = 0x00A1; ch <= 0x00AC; ++ch)
|
||||
sbHighAnsi.push(ch);
|
||||
for(ch = 0x00AE; ch < 0x00FF; ++ch)
|
||||
sbHighAnsi.push(ch);
|
||||
sbHighAnsi.push(0x00FF);
|
||||
return String.fromCharCode.apply(null, sbHighAnsi);
|
||||
})();
|
||||
|
||||
function Assert(ok, msg) {
|
||||
if (!ok) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QeCharType {
|
||||
constructor(chTypeID, strAlphabet, bIsConsecutive) {
|
||||
let nChars;
|
||||
if (typeof strAlphabet === 'string') {
|
||||
if(strAlphabet.length === 0) throw new Error();
|
||||
} else if (typeof strAlphabet === 'number') {
|
||||
if(nChars <= 0) throw new RangeError();
|
||||
nChars = strAlphabet;
|
||||
strAlphabet = null;
|
||||
bIsConsecutive = false;
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
this.TypeID = chTypeID;
|
||||
this.Alphabet = strAlphabet;
|
||||
this.CharCount = nChars || this.Alphabet.length;
|
||||
this.m_chFirst = (bIsConsecutive ? this.Alphabet.charCodeAt(0) : null);
|
||||
this.m_chLast = (bIsConsecutive ? this.Alphabet.charCodeAt(this.CharCount - 1) : null);
|
||||
|
||||
this.CharSize = Math.log2(this.CharCount);
|
||||
nChars || Assert((this.m_chLast - this.m_chFirst) == (this.CharCount - 1) || !bIsConsecutive);
|
||||
}
|
||||
|
||||
Contains(ch)
|
||||
{
|
||||
if(this.m_chLast !== null)
|
||||
return ((ch >= this.m_chFirst) && (ch <= this.m_chLast));
|
||||
|
||||
if (this.Alphabet.length === 0) throw new Error('Don\'t call for catch-none set')
|
||||
return (this.Alphabet.indexOf(String.fromCharCode(ch)) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
class EntropyEncoder {
|
||||
constructor(strAlphabet, uBaseWeight,
|
||||
uCharWeight, uOccExclThreshold)
|
||||
{
|
||||
if(strAlphabet === null || strAlphabet.length === 0) throw new Error();
|
||||
|
||||
this.m_strAlph = strAlphabet;
|
||||
this.m_uBaseWeight = uBaseWeight;
|
||||
this.m_uCharWeight = uCharWeight;
|
||||
this.m_uOccExclThreshold = uOccExclThreshold;
|
||||
|
||||
this.m_dHisto = {}
|
||||
}
|
||||
|
||||
Reset()
|
||||
{
|
||||
this.m_dHisto = {};
|
||||
}
|
||||
|
||||
Write(ch)
|
||||
{
|
||||
Assert(this.m_strAlph.indexOf(ch) >= 0);
|
||||
|
||||
let uOcc = this.m_dHisto[ch] || 0;
|
||||
Assert(ch in this.m_dHisto || uOcc === 0);
|
||||
this.m_dHisto[ch] = uOcc + 1;
|
||||
}
|
||||
|
||||
GetOutputSize()
|
||||
{
|
||||
let uTotalWeight = this.m_uBaseWeight * this.m_strAlph.length;
|
||||
for (let u of Object.values(this.m_dHisto))
|
||||
{
|
||||
Assert(u >= 1);
|
||||
if(u > this.m_uOccExclThreshold)
|
||||
uTotalWeight += (u - this.m_uOccExclThreshold) * this.m_uCharWeight;
|
||||
}
|
||||
|
||||
let dSize = 0.0, dTotalWeight = uTotalWeight;
|
||||
for (let u of Object.values(this.m_dHisto))
|
||||
{
|
||||
let uWeight = this.m_uBaseWeight;
|
||||
if(u > this.m_uOccExclThreshold)
|
||||
uWeight += (u - this.m_uOccExclThreshold) * this.m_uCharWeight;
|
||||
|
||||
dSize -= u * Math.log2(uWeight / dTotalWeight);
|
||||
}
|
||||
|
||||
return dSize;
|
||||
}
|
||||
}
|
||||
|
||||
class MultiEntropyEncoder {
|
||||
constructor() {
|
||||
this.m_dEncs = {};
|
||||
}
|
||||
|
||||
AddEncoder(chTypeID, ec)
|
||||
{
|
||||
Assert(ec);
|
||||
|
||||
Assert(!(chTypeID in this.m_dEncs));
|
||||
this.m_dEncs[chTypeID] = ec;
|
||||
}
|
||||
|
||||
Reset()
|
||||
{
|
||||
for(let ec of Object.values(this.m_dEncs)) {
|
||||
ec.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
Write(chTypeID, chData)
|
||||
{
|
||||
let ec = this.m_dEncs[chTypeID];
|
||||
if(!ec)
|
||||
return false;
|
||||
|
||||
ec.Write(chData);
|
||||
return true;
|
||||
}
|
||||
|
||||
GetOutputSize()
|
||||
{
|
||||
let d = 0.0;
|
||||
|
||||
for (let ec of Object.values(this.m_dEncs))
|
||||
{
|
||||
d += ec.GetOutputSize();
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
class QePatternInstance
|
||||
{
|
||||
constructor(iPosition, nLength, chPatternID, dblCost) {
|
||||
let ctSingle;
|
||||
if (typeof dblCost === 'number') {
|
||||
this.Position = iPosition;
|
||||
this.Length = nLength;
|
||||
this.PatternID = chPatternID;
|
||||
this.Cost = dblCost;
|
||||
this.SingleCharType = null;
|
||||
} else {
|
||||
ctSingle = chPatternID;
|
||||
|
||||
this.Position = iPosition;
|
||||
this.Length = nLength;
|
||||
this.PatternID = ctSingle.TypeID;
|
||||
this.Cost = ctSingle.CharSize;
|
||||
this.SingleCharType = ctSingle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QePathState {
|
||||
constructor(iPosition, lPath) {
|
||||
this.Position = iPosition;
|
||||
this.Path = lPath;
|
||||
}
|
||||
}
|
||||
|
||||
let m_objSyncInit;
|
||||
let m_lCharTypes;
|
||||
|
||||
function EnsureInitialized() {
|
||||
if(m_lCharTypes == null)
|
||||
{
|
||||
let strSpecial = PrintableAsciiSpecial;
|
||||
strSpecial = strSpecial + " ";
|
||||
|
||||
let nSp = strSpecial.length;
|
||||
let nHi = HighAnsiChars.length;
|
||||
|
||||
m_lCharTypes = [];
|
||||
|
||||
m_lCharTypes.push(new QeCharType(PatternID.LowerAlpha,
|
||||
LowerCase, true));
|
||||
m_lCharTypes.push(new QeCharType(PatternID.UpperAlpha,
|
||||
UpperCase, true));
|
||||
m_lCharTypes.push(new QeCharType(PatternID.Digit,
|
||||
Digits, true));
|
||||
m_lCharTypes.push(new QeCharType(PatternID.Special,
|
||||
strSpecial, false));
|
||||
m_lCharTypes.push(new QeCharType(PatternID.High,
|
||||
HighAnsiChars, false));
|
||||
m_lCharTypes.push(new QeCharType(PatternID.Other,
|
||||
0x10000 - (2 * 26) - 10 - nSp - nHi));
|
||||
}
|
||||
}
|
||||
|
||||
function GetCharType(ch) {
|
||||
let nTypes = m_lCharTypes.length;
|
||||
Assert((nTypes > 0) && (m_lCharTypes[nTypes - 1].CharCount > 256));
|
||||
|
||||
for(let i = 0; i < (nTypes - 1); ++i)
|
||||
{
|
||||
if(m_lCharTypes[i].Contains(ch))
|
||||
return m_lCharTypes[i];
|
||||
}
|
||||
|
||||
return m_lCharTypes[nTypes - 1];
|
||||
}
|
||||
|
||||
function ComputePathCost(l, vPassword, ecPattern, mcData)
|
||||
{
|
||||
ecPattern.Reset();
|
||||
for(let i = 0; i < l.length; ++i)
|
||||
ecPattern.Write(l[i].PatternID);
|
||||
let dblPatternCost = ecPattern.GetOutputSize();
|
||||
|
||||
mcData.Reset();
|
||||
let dblDataCost = 0.0;
|
||||
for(let pi of l)
|
||||
{
|
||||
let tChar = pi.SingleCharType;
|
||||
if(tChar != null)
|
||||
{
|
||||
let ch = vPassword[pi.Position];
|
||||
if(!mcData.Write(tChar.TypeID, ch))
|
||||
dblDataCost += pi.Cost;
|
||||
}
|
||||
else dblDataCost += pi.Cost;
|
||||
}
|
||||
dblDataCost += mcData.GetOutputSize();
|
||||
|
||||
return (dblPatternCost + dblDataCost);
|
||||
}
|
||||
|
||||
function FindRepetitions(vPassword, vPatterns)
|
||||
{
|
||||
let v = stringToArray(vPassword);
|
||||
let n = vPassword.length;
|
||||
|
||||
let chErased = 0xffff;
|
||||
for(let m = (n / 2); m >= 3; --m)
|
||||
{
|
||||
for(let x1 = 0; x1 <= (n - (2 * m)); ++x1)
|
||||
{
|
||||
let bFoundRep = false;
|
||||
|
||||
for(let x2 = (x1 + m); x2 <= (n - m); ++x2)
|
||||
{
|
||||
if(PartsEqual(v, x1, x2, m))
|
||||
{
|
||||
let dblCost = Math.log2(x1 + 1) + Math.log2(m);
|
||||
vPatterns[x2].push(new QePatternInstance(x2, m,
|
||||
PatternID.Repetition, dblCost));
|
||||
|
||||
chErased = ErasePart(v, x2, m, chErased);
|
||||
|
||||
bFoundRep = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFoundRep) chErased = ErasePart(v, x1, m, chErased);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function PartsEqual(v, x1, x2, nLength)
|
||||
{
|
||||
for(let i = 0; i < nLength; ++i)
|
||||
{
|
||||
if(v[x1 + i] != v[x2 + i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function ErasePart(v, i, n, chErased)
|
||||
{
|
||||
for(let j = 0; j < n; ++j) {
|
||||
v[i + j] = chErased;
|
||||
--chErased;
|
||||
}
|
||||
|
||||
return chErased;
|
||||
}
|
||||
|
||||
function stringToArray(string) {
|
||||
let array = [];
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
array.push(string.charCodeAt(i));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function FindNumbers(vPassword, vPatterns)
|
||||
{
|
||||
let n = vPassword.length;
|
||||
let sb = [];
|
||||
|
||||
for(let i = 0; i < n; ++i)
|
||||
{
|
||||
let ch = vPassword.charCodeAt(i);
|
||||
if((ch >= 0x30) && (ch <= 0x39)) sb.push(ch);
|
||||
else
|
||||
{
|
||||
AddNumberPattern(vPatterns, sb, i - sb.length);
|
||||
sb = [];
|
||||
}
|
||||
}
|
||||
AddNumberPattern(vPatterns, sb, n - sb.length);
|
||||
}
|
||||
|
||||
function AddNumberPattern(vPatterns, sb, i)
|
||||
{
|
||||
if(sb.length <= 2) return;
|
||||
let strNumber = String.fromCharCode.apply(null,sb);
|
||||
|
||||
let nZeros = 0;
|
||||
for(let j = 0; j < strNumber.length; ++j)
|
||||
{
|
||||
if(strNumber.charCodeAt(j) != 0x30) break;
|
||||
++nZeros;
|
||||
}
|
||||
|
||||
let dblCost = Math.log2(nZeros + 1);
|
||||
if(nZeros < strNumber.length)
|
||||
{
|
||||
let strNonZero = strNumber.substring(nZeros);
|
||||
|
||||
dblCost += Math.log2(parseFloat(strNonZero));
|
||||
}
|
||||
|
||||
vPatterns[i].push(new QePatternInstance(i, strNumber.length,
|
||||
PatternID.Number, dblCost));
|
||||
}
|
||||
|
||||
function FindDiffSeqs(vPassword, vPatterns) {
|
||||
let n = vPassword.length;
|
||||
let d = Infinity, p = 0;
|
||||
|
||||
for(let i = 1; i <= n; ++i)
|
||||
{
|
||||
let dCur = ((i == n) ? Infinity :
|
||||
(vPassword.charCodeAt(i) - vPassword.charCodeAt(i - 1)));
|
||||
if(dCur != d)
|
||||
{
|
||||
if((i - p) >= 3) // At least 3 chars involved
|
||||
{
|
||||
let ct = GetCharType(vPassword.charCodeAt(p));
|
||||
let dblCost = ct.CharSize + Math.log2(i - p - 1);
|
||||
|
||||
vPatterns[p].push(new QePatternInstance(p,
|
||||
i - p, PatternID.DiffSeq, dblCost));
|
||||
}
|
||||
|
||||
d = dCur;
|
||||
p = i - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function DecodeLeet(str) {
|
||||
let newstr = '';
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
let char = str.charAt(i);
|
||||
let decoded = DecodeLeetChar(char);
|
||||
newstr += decoded;
|
||||
}
|
||||
return newstr;
|
||||
}
|
||||
|
||||
function FindPopularPasswords(vPassword, vPatterns) {
|
||||
let n = vPassword.length;
|
||||
|
||||
let vLower = vPassword.toLowerCase();
|
||||
let vLeet = DecodeLeet(vLower);
|
||||
|
||||
for(let nSubLen = Math.min(n, PopularPasswords.getMaxLength()); nSubLen >= 3; --nSubLen) {
|
||||
if (!PopularPasswords.ContainsLength(nSubLen)) continue;
|
||||
|
||||
for(let i = 0; i <= (n - nSubLen); ++i)
|
||||
{
|
||||
let vSub = vLower.substring(i, i + nSubLen);
|
||||
|
||||
if (!vSub || vSub.indexOf('\u0000') !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!EvalAddPopularPasswordPattern(vPatterns, vPassword,
|
||||
i, vSub, 0.0))
|
||||
{
|
||||
let vLeetSub = vLeet.substring(i, nSubLen);
|
||||
if(EvalAddPopularPasswordPattern(vPatterns, vPassword,
|
||||
i, vLeetSub, 1.5))
|
||||
{
|
||||
vLower = StringClear(vLower, i, nSubLen); // Not vLeet
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vLower = StringClear(vLower, i, nSubLen); // Not vLeet
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function StringClear(str, pos, count) {
|
||||
let erased = ''
|
||||
for (let i = 0;i < count;i++) {
|
||||
erased += '\u0000'
|
||||
}
|
||||
return str.substring(0, pos) + erased + str.substring(pos + count);
|
||||
}
|
||||
|
||||
function DecodeLeetChar(chLeet) {
|
||||
if((chLeet.charCodeAt(0) >= 0x00C0) && (chLeet.charCodeAt(0) <= 0x00C6)) return 'a';
|
||||
if((chLeet.charCodeAt(0) >= 0x00C8) && (chLeet.charCodeAt(0) <= 0x00CB)) return 'e';
|
||||
if((chLeet.charCodeAt(0) >= 0x00CC) && (chLeet.charCodeAt(0) <= 0x00CF)) return 'i';
|
||||
if((chLeet.charCodeAt(0) >= 0x00D2) && (chLeet.charCodeAt(0) <= 0x00D6)) return 'o';
|
||||
if((chLeet.charCodeAt(0) >= 0x00D9) && (chLeet.charCodeAt(0) <= 0x00DC)) return 'u';
|
||||
if((chLeet.charCodeAt(0) >= 0x00E0) && (chLeet.charCodeAt(0) <= 0x00E6)) return 'a';
|
||||
if((chLeet.charCodeAt(0) >= 0x00E8) && (chLeet.charCodeAt(0) <= 0x00EB)) return 'e';
|
||||
if((chLeet.charCodeAt(0) >= 0x00EC) && (chLeet.charCodeAt(0) <= 0x00EF)) return 'i';
|
||||
if((chLeet.charCodeAt(0) >= 0x00F2) && (chLeet.charCodeAt(0) <= 0x00F6)) return 'o';
|
||||
if((chLeet.charCodeAt(0) >= 0x00F9) && (chLeet.charCodeAt(0) <= 0x00FC)) return 'u';
|
||||
|
||||
switch(chLeet)
|
||||
{
|
||||
case '4':
|
||||
case '@':
|
||||
case '?':
|
||||
case '^':
|
||||
case '\u00AA': return 'a';
|
||||
case '8':
|
||||
case '\u00DF': return 'b';
|
||||
case '(':
|
||||
case '{':
|
||||
case '[':
|
||||
case '<':
|
||||
case '\u00A2':
|
||||
case '\u00A9':
|
||||
case '\u00C7':
|
||||
case '\u00E7': return 'c';
|
||||
case '\u00D0':
|
||||
case '\u00F0': return 'd';
|
||||
case '3':
|
||||
case '\u20AC':
|
||||
case '&':
|
||||
case '\u00A3': return 'e';
|
||||
case '6':
|
||||
case '9': return 'g';
|
||||
case '#': return 'h';
|
||||
case '1':
|
||||
case '!':
|
||||
case '|':
|
||||
case '\u00A1':
|
||||
case '\u00A6': return 'i';
|
||||
case '\u00D1':
|
||||
case '\u00F1': return 'n';
|
||||
case '0':
|
||||
case '*':
|
||||
case '\u00A4': // Currency
|
||||
case '\u00B0': // Degree
|
||||
case '\u00D8':
|
||||
case '\u00F8': return 'o';
|
||||
case '\u00AE': return 'r';
|
||||
case '$':
|
||||
case '5':
|
||||
case '\u00A7': return 's';
|
||||
case '+':
|
||||
case '7': return 't';
|
||||
case '\u00B5': return 'u';
|
||||
case '%':
|
||||
case '\u00D7': return 'x';
|
||||
case '\u00A5':
|
||||
case '\u00DD':
|
||||
case '\u00FD':
|
||||
case '\u00FF': return 'y';
|
||||
case '2': return 'z';
|
||||
default: return chLeet;
|
||||
}
|
||||
}
|
||||
|
||||
function EvalAddPopularPasswordPattern(vPatterns, vPassword, i, sub, dblCostPerMod)
|
||||
{
|
||||
let IsPopularPassword = PopularPasswords.IsPopularPassword(sub);
|
||||
let uDictSize = PopularPasswords.GetDictSize(sub.length);
|
||||
if(!IsPopularPassword)
|
||||
return false;
|
||||
|
||||
let n = sub.length;
|
||||
let d = HammingDist(sub, 0, vPassword, i, n);
|
||||
|
||||
let dblCost = Math.log2(uDictSize);
|
||||
|
||||
// dblCost += Math.log2(n binom d)
|
||||
let k = Math.min(d, n - d);
|
||||
for(let j = n; j > (n - k); --j)
|
||||
dblCost += Math.log2(j);
|
||||
for(let j = k; j >= 2; --j)
|
||||
dblCost -= Math.log2(j);
|
||||
|
||||
dblCost += dblCostPerMod * d;
|
||||
|
||||
vPatterns[i].push(new QePatternInstance(i, n, PatternID.Dictionary,
|
||||
dblCost));
|
||||
return true;
|
||||
}
|
||||
|
||||
function HammingDist(v1, iOffset1, v2, iOffset2, nLength)
|
||||
{
|
||||
let nDist = 0;
|
||||
for(let i = 0; i < nLength; ++i)
|
||||
{
|
||||
if(v1.charCodeAt(iOffset1 + i) !== v2.charCodeAt(iOffset2 + i)) ++nDist;
|
||||
}
|
||||
|
||||
return nDist;
|
||||
}
|
||||
|
||||
export default function PasswordQualityCalculator(vPassword)
|
||||
{
|
||||
if (typeof vPassword !== 'string' || vPassword.length === 0) return 0;
|
||||
|
||||
EnsureInitialized();
|
||||
|
||||
let n = vPassword.length;
|
||||
let vPatterns = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
vPatterns[i] = [
|
||||
new QePatternInstance(i, 1, GetCharType(vPassword.charCodeAt(i)))
|
||||
];
|
||||
}
|
||||
|
||||
FindRepetitions(vPassword, vPatterns);
|
||||
FindNumbers(vPassword, vPatterns);
|
||||
FindDiffSeqs(vPassword, vPatterns);
|
||||
FindPopularPasswords(vPassword, vPatterns);
|
||||
|
||||
// Encoders must not be static, because the entropy estimation
|
||||
// may run concurrently in multiple threads and the encoders are
|
||||
// not read-only
|
||||
let ecPattern = new EntropyEncoder(PatternID.All, 0, 1, 0);
|
||||
let mcData = new MultiEntropyEncoder();
|
||||
|
||||
for(let i = 0; i < (m_lCharTypes.length - 1); ++i)
|
||||
{
|
||||
// Let m be the alphabet size. In order to ensure that two same
|
||||
// characters cost at least as much as a single character, for
|
||||
// the probability p and weight w of the character it must hold:
|
||||
// -log(1/m) >= -2*log(p)
|
||||
// <=> log(1/m) <= log(p^2) <=> 1/m <= p^2 <=> p >= sqrt(1/m);
|
||||
// sqrt(1/m) = (1+w)/(m+w)
|
||||
// <=> m+w = (1+w)*sqrt(m) <=> m+w = sqrt(m) + w*sqrt(m)
|
||||
// <=> w*(1-sqrt(m)) = sqrt(m) - m <=> w = (sqrt(m)-m)/(1-sqrt(m))
|
||||
// <=> w = (sqrt(m)-m)*(1+sqrt(m))/(1-m)
|
||||
// <=> w = (sqrt(m)-m+m-m*sqrt(m))/(1-m) <=> w = sqrt(m)
|
||||
let uw = Math.sqrt(m_lCharTypes[i].CharCount) | 0;
|
||||
|
||||
mcData.AddEncoder(m_lCharTypes[i].TypeID, new EntropyEncoder(
|
||||
m_lCharTypes[i].Alphabet, 1, uw, 1));
|
||||
}
|
||||
|
||||
let dblMinCost = Infinity;
|
||||
let tStart = Date.now();
|
||||
|
||||
let sRec = [];
|
||||
sRec.push(new QePathState(0, []));
|
||||
|
||||
|
||||
while(sRec.length > 0)
|
||||
{
|
||||
let tDiff = Date.now() - tStart;
|
||||
if(tDiff > 500) break;
|
||||
|
||||
let s = sRec.pop();
|
||||
|
||||
if(s.Position >= n)
|
||||
{
|
||||
Assert(s.Position === n);
|
||||
|
||||
let dblCost = ComputePathCost(s.Path, vPassword,
|
||||
ecPattern, mcData);
|
||||
if(dblCost < dblMinCost) dblMinCost = dblCost;
|
||||
}
|
||||
else
|
||||
{
|
||||
let lSubs = vPatterns[s.Position];
|
||||
for(let i = lSubs.length - 1; i >= 0; --i)
|
||||
{
|
||||
let pi = lSubs[i];
|
||||
Assert(pi.Position == s.Position);
|
||||
Assert(pi.Length >= 1);
|
||||
|
||||
let lNewPath = [];
|
||||
lNewPath.push(...s.Path);
|
||||
lNewPath.push(pi);
|
||||
|
||||
let sNew = new QePathState(s.Position +
|
||||
pi.Length, lNewPath);
|
||||
sRec.push(sNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.ceil(dblMinCost);
|
||||
}
|
||||
|
||||
PasswordQualityCalculator.PopularPasswords = PopularPasswords;
|
|
@ -0,0 +1,47 @@
|
|||
const m_dicts = {};
|
||||
|
||||
export function getMaxLength() {
|
||||
let iMaxLen = 0;
|
||||
for (let iLen of Object.keys(m_dicts)) {
|
||||
if (parseInt(iLen) > iMaxLen) iMaxLen = parseInt(iLen);
|
||||
}
|
||||
return iMaxLen;
|
||||
}
|
||||
|
||||
export function ContainsLength(nLength) {
|
||||
return nLength in m_dicts;
|
||||
}
|
||||
|
||||
export function IsPopularPassword(password) {
|
||||
if (password == null) throw new Error();
|
||||
if (password.length == 0) { return false; }
|
||||
|
||||
if (!(password.length in m_dicts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_dicts[password.length].includes(password);
|
||||
}
|
||||
|
||||
export function GetDictSize(length) {
|
||||
|
||||
if (!(length in m_dicts)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_dicts[length].length;
|
||||
}
|
||||
|
||||
export function load(passwordList) {
|
||||
for (let pw of passwordList) {
|
||||
if (pw.length in m_dicts) {
|
||||
m_dicts[pw.length].push(pw);
|
||||
} else {
|
||||
m_dicts[pw.length] = [pw];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
m_dicts = {}
|
||||
}
|
|
@ -10,13 +10,14 @@ import {
|
|||
AccountName,
|
||||
EMail,
|
||||
Password,
|
||||
SessionId,
|
||||
XAuthorization,
|
||||
UserId,
|
||||
Username,
|
||||
verifyId,
|
||||
WebSocketSessionId,
|
||||
XToken,
|
||||
} from './types';
|
||||
} from '../configs/types';
|
||||
import MyUserManager from '@caj/user/MyUserManager';
|
||||
|
||||
export const apiPath = {
|
||||
backend: {
|
||||
|
@ -30,19 +31,24 @@ export enum apiBackendRequest {
|
|||
REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId',
|
||||
REGISTER_STEP_FINAL = '/admin/users',
|
||||
REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK = '/admin/users/validation/accountname',
|
||||
LOGIN = '/user',
|
||||
GET_USER_PROFILE = '/users/:userId',
|
||||
APP_START = '/appstart',
|
||||
}
|
||||
|
||||
type requestGET = {[key: string]: string};
|
||||
type requestHeader = {[key: string]: string | undefined};
|
||||
|
||||
export interface defaultRequest {
|
||||
status?: 200 | 400 | 422 | 500;
|
||||
error?: string;
|
||||
requestGET?: requestGET;
|
||||
requestHeader?: requestHeader;
|
||||
}
|
||||
|
||||
interface REGISTER_STEP_1 extends defaultRequest {
|
||||
path: apiBackendRequest.REGISTER_STEP_1;
|
||||
requestHeader: {};
|
||||
|
||||
request: {
|
||||
Email: EMail;
|
||||
};
|
||||
|
@ -52,18 +58,20 @@ interface REGISTER_STEP_1 extends defaultRequest {
|
|||
}
|
||||
interface REGISTER_RESEND_MAIL extends defaultRequest {
|
||||
path: apiBackendRequest.REGISTER_RESEND_MAIL;
|
||||
requestHeader: {};
|
||||
requestHeader: {
|
||||
'X-Token': XToken | undefined;
|
||||
};
|
||||
request: {
|
||||
Email: EMail;
|
||||
};
|
||||
// response: {
|
||||
// XToken: XToken | undefined;
|
||||
//};
|
||||
response: {
|
||||
XToken: XToken | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
interface REGISTER_STEP_2 extends defaultRequest {
|
||||
path: apiBackendRequest.REGISTER_STEP_2;
|
||||
requestHeader: {};
|
||||
|
||||
requestGET: {
|
||||
':xToken': XToken;
|
||||
':verifyId': verifyId;
|
||||
|
@ -84,7 +92,7 @@ interface REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK extends defaultRequest {
|
|||
interface REGISTER_STEP_FINAL extends defaultRequest {
|
||||
path: apiBackendRequest.REGISTER_STEP_FINAL;
|
||||
requestHeader: {
|
||||
XToken: XToken;
|
||||
'X-Token': XToken;
|
||||
};
|
||||
request: {
|
||||
AccountName: AccountName;
|
||||
|
@ -92,18 +100,70 @@ interface REGISTER_STEP_FINAL extends defaultRequest {
|
|||
Password: Password;
|
||||
};
|
||||
response?: {
|
||||
SessionId: SessionId;
|
||||
XAuthorization: XAuthorization;
|
||||
UserId: UserId;
|
||||
WebSocketSessionId: WebSocketSessionId;
|
||||
};
|
||||
}
|
||||
|
||||
interface LOGIN extends defaultRequest {
|
||||
path: apiBackendRequest.LOGIN;
|
||||
request: {
|
||||
Email: EMail;
|
||||
Password: Password;
|
||||
};
|
||||
response?: {
|
||||
XAuthorization: XAuthorization;
|
||||
UserId: UserId;
|
||||
WebSocketSessionId: WebSocketSessionId;
|
||||
};
|
||||
}
|
||||
|
||||
interface GET_USER_PROFILE extends defaultRequest {
|
||||
path: apiBackendRequest.GET_USER_PROFILE;
|
||||
|
||||
requestGET: {
|
||||
':userId': UserId;
|
||||
};
|
||||
response: {
|
||||
AccountName: AccountName;
|
||||
Username: Username;
|
||||
Description: string;
|
||||
FollowersCount: number;
|
||||
FollowingCount: number;
|
||||
XpLevel: number;
|
||||
XpPoints: number;
|
||||
AvatarUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface APP_START extends defaultRequest {
|
||||
path: apiBackendRequest.APP_START;
|
||||
|
||||
response: {
|
||||
TokenValid: boolean;
|
||||
AccountName: AccountName;
|
||||
Username: Username;
|
||||
Description: string;
|
||||
FollowersCount: number;
|
||||
FollowingCount: number;
|
||||
XpLevel: number;
|
||||
XpPoints: number;
|
||||
AccountStatus: number;
|
||||
AvatarUrl: string;
|
||||
Events: any;
|
||||
};
|
||||
}
|
||||
|
||||
type FetchTypes =
|
||||
| REGISTER_STEP_1
|
||||
| REGISTER_RESEND_MAIL
|
||||
| REGISTER_STEP_2
|
||||
| REGISTER_STEP_FINAL
|
||||
| REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK;
|
||||
| REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK
|
||||
| LOGIN
|
||||
| GET_USER_PROFILE
|
||||
| APP_START;
|
||||
|
||||
function isA(obj: any): obj is REGISTER_STEP_1 {
|
||||
return obj.request !== undefined;
|
||||
|
@ -130,9 +190,20 @@ export function makeRequest<T1 extends FetchTypes>(type: T1): Promise<T1> {
|
|||
'X-Language': store.getState().appVariables.lang.details.langCode,
|
||||
};
|
||||
|
||||
if (type.requestHeader !== undefined) {
|
||||
for (let key in type.requestHeader) {
|
||||
let val = (type.requestHeader as requestHeader)[key];
|
||||
|
||||
if (val !== undefined) headers[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android' || Platform.OS === 'ios')
|
||||
headers['User-Agent'] = getUserAgent();
|
||||
|
||||
const SessionId = MyUserManager.getSessionId();
|
||||
if (SessionId !== undefined) headers['X-Authorization'] = SessionId;
|
||||
|
||||
const requestOptions: RequestInit = {
|
||||
method: makeRequestObj.request !== undefined ? 'POST' : 'GET',
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import {initDatabase} from './bdm/init';
|
||||
|
||||
const BigDataManager = {initDatabase};
|
||||
export default BigDataManager;
|
|
@ -0,0 +1,18 @@
|
|||
import Realm from 'realm';
|
||||
import DBSchemas from './schemas';
|
||||
|
||||
export const initDatabase = (): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
for (const key in DBSchemas) {
|
||||
const Schema = DBSchemas[key as keyof typeof DBSchemas];
|
||||
Realm.open({
|
||||
schema: [Schema.details],
|
||||
schemaVersion: Schema.version,
|
||||
path: Schema.filePath,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import {openDB, deleteDB, wrap, unwrap} from 'idb';
|
||||
import DBSchemas from './schemas';
|
||||
|
||||
export const initDatabase = (): Promise<void> => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
setTimeout(async () => {
|
||||
for (const key in DBSchemas) {
|
||||
const Schema = DBSchemas[key as keyof typeof DBSchemas];
|
||||
await openDB(Schema.details.name, Schema.version, {
|
||||
upgrade: Schema.migration,
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import {MigrationCallback} from 'realm';
|
||||
|
||||
export const usersDBMigration: MigrationCallback = (oldRealm, newRealm) => {
|
||||
/*// only apply this change if upgrading to schemaVersion 2
|
||||
if (oldRealm.schemaVersion < 2) {
|
||||
const oldObjects = oldRealm.objects('Person');
|
||||
const newObjects = newRealm.objects('Person');
|
||||
// loop through all objects and set the fullName property in the new schema
|
||||
for (const objectIndex in oldObjects) {
|
||||
const oldObject = oldObjects[objectIndex];
|
||||
const newObject = newObjects[objectIndex];
|
||||
newObject.fullName = `${oldObject.firstName} ${oldObject.lastName}`;
|
||||
}
|
||||
}*/
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
import {IDBPDatabase, IDBPTransaction, StoreNames} from 'idb';
|
||||
import DBSchemas from './schemas';
|
||||
|
||||
interface migrationArguments {
|
||||
db: IDBPDatabase<unknown>;
|
||||
oldVersion: number;
|
||||
newVersion: number | null;
|
||||
transaction: IDBPTransaction<unknown, StoreNames<unknown>[], 'versionchange'>;
|
||||
event: IDBVersionChangeEvent;
|
||||
}
|
||||
|
||||
type upgradeFunc = (
|
||||
database: IDBPDatabase<unknown>,
|
||||
oldVersion: number,
|
||||
newVersion: number | null,
|
||||
transaction: IDBPTransaction<unknown, StoreNames<unknown>[], 'versionchange'>,
|
||||
event: IDBVersionChangeEvent,
|
||||
) => void;
|
||||
|
||||
export const usersDBMigration: upgradeFunc = (
|
||||
db,
|
||||
oldVersion,
|
||||
newVersion,
|
||||
transaction,
|
||||
event,
|
||||
) => {
|
||||
const Schema = DBSchemas['users'];
|
||||
|
||||
if (oldVersion == 0) {
|
||||
// perform the initialization
|
||||
db.createObjectStore(Schema.details.name, {
|
||||
keyPath: Schema.details.primaryKey,
|
||||
});
|
||||
} else if (newVersion !== null) {
|
||||
let ver = oldVersion;
|
||||
|
||||
while (ver < newVersion) {
|
||||
console.log('upgrade from v', ver, ' to v', ver + 1);
|
||||
|
||||
ver++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const chatDBMigration: upgradeFunc = (
|
||||
db,
|
||||
oldVersion,
|
||||
newVersion,
|
||||
transaction,
|
||||
event,
|
||||
) => {
|
||||
const Schema = DBSchemas['chat'];
|
||||
|
||||
if (oldVersion == 0) {
|
||||
// perform the initialization
|
||||
db.createObjectStore(Schema.details.name, {
|
||||
keyPath: Schema.details.primaryKey,
|
||||
});
|
||||
} else if (newVersion !== null) {
|
||||
let ver = oldVersion;
|
||||
|
||||
while (ver < newVersion) {
|
||||
console.log('upgrade from v', ver, ' to v', ver + 1);
|
||||
|
||||
ver++;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import {usersDBMigration} from './migration';
|
||||
import {chatDBMigration} from './migration.web';
|
||||
|
||||
type databaseNames = 'users' | 'chat';
|
||||
|
||||
interface databaseConf {
|
||||
filePath: string;
|
||||
version: number;
|
||||
migration?: any;
|
||||
details: {
|
||||
name: databaseNames;
|
||||
properties: any;
|
||||
primaryKey: string;
|
||||
};
|
||||
}
|
||||
|
||||
const users: databaseConf = {
|
||||
filePath: 'users',
|
||||
version: 1,
|
||||
migration: usersDBMigration,
|
||||
details: {
|
||||
name: 'users',
|
||||
properties: {
|
||||
UserId: 'string',
|
||||
AccountName: 'string',
|
||||
Username: 'string',
|
||||
},
|
||||
primaryKey: 'UserId',
|
||||
},
|
||||
};
|
||||
|
||||
const chat: databaseConf = {
|
||||
filePath: 'chat',
|
||||
version: 1,
|
||||
migration: chatDBMigration,
|
||||
details: {
|
||||
name: 'chat',
|
||||
properties: {
|
||||
UserId: 'string',
|
||||
msg: 'string',
|
||||
},
|
||||
primaryKey: 'UserId',
|
||||
},
|
||||
};
|
||||
|
||||
const DBSchemas = {users, chat};
|
||||
export default DBSchemas;
|
|
@ -22,8 +22,15 @@ export default interface LangFormat {
|
|||
error: string;
|
||||
success: string;
|
||||
account: {
|
||||
login: {
|
||||
title: string;
|
||||
wrongEmPw: string;
|
||||
failed: string;
|
||||
success: string;
|
||||
};
|
||||
registration: {
|
||||
registration: string;
|
||||
|
||||
info: string;
|
||||
privacyPolicy: string;
|
||||
termsOfUse: string;
|
||||
|
@ -55,7 +62,11 @@ export default interface LangFormat {
|
|||
accountNameError: {[key: string]: string};
|
||||
userNameError: {[key: string]: string};
|
||||
passwordError: {[key: string]: string};
|
||||
passwordQuality: string;
|
||||
passwordQualityList: {[key: string]: string};
|
||||
noAllFieldsEntered: string;
|
||||
button: string;
|
||||
registerError: {[key: string]: string};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -21,13 +21,20 @@ export const lang: LangFormat = {
|
|||
error: 'Error',
|
||||
success: 'Success',
|
||||
account: {
|
||||
login: {
|
||||
title: 'Log in',
|
||||
wrongEmPw: 'Wrong E-Mail or password',
|
||||
failed: 'Login failed. Try again or contact support.',
|
||||
success: 'Successfully logged in!',
|
||||
},
|
||||
registration: {
|
||||
registration: 'Registration',
|
||||
|
||||
info: 'By registering, you agree to our ${TermsOfUse}. You can find out how we collect and use your data in our ${privacyPolicy}.',
|
||||
privacyPolicy: 'privacy policy',
|
||||
termsOfUse: 'Terms of Use',
|
||||
stepOne: {
|
||||
title: 'Enter E-Mail',
|
||||
title: 'E-Mail',
|
||||
success: 'A verification has sent to your E-Mail!',
|
||||
addressExists: 'The E-Mail you entered is already in use.',
|
||||
addressInvalid: 'The address you entered has an invalid format.',
|
||||
|
@ -44,7 +51,7 @@ export const lang: LangFormat = {
|
|||
'E-Mail verification has resent',
|
||||
],
|
||||
resendError: {
|
||||
400: 'The E-Mail is already verified!',
|
||||
400: 'Email is already in the registration process by another user!',
|
||||
401: 'Your device have changed. Please use another E-Mail address.',
|
||||
429: 'Too many requests in a too small period of time in a row',
|
||||
},
|
||||
|
@ -63,29 +70,44 @@ export const lang: LangFormat = {
|
|||
accountName: 'AccountName',
|
||||
password: 'Password',
|
||||
passwordRepeat: 'Repeat password',
|
||||
displayName: 'Other users see you like this:',
|
||||
displayName: 'Other users will see you like this:',
|
||||
accountNameError: {
|
||||
tooLong: 'Too long. Max length are $(maxLength) character.',
|
||||
tooShort: 'Too short. Min length are $(minLength) character.',
|
||||
tooLong: 'Too long. Max length are ${maxLength} character.',
|
||||
tooShort: 'Too short. Min length are ${minLength} character.',
|
||||
required: 'This field is required',
|
||||
invalid:
|
||||
'Account names can only contain letters, numbers, underscores (_) and dots (.)',
|
||||
exists: 'The account name you entered already exists.',
|
||||
422: 'This name is already in use :(',
|
||||
},
|
||||
userNameError: {
|
||||
tooLong: 'Too long. Max length are $(maxLength) character.',
|
||||
tooShort: 'Too short. Min length are $(minLength) character.',
|
||||
tooLong: 'Too long. Max length are ${maxLength} character.',
|
||||
tooShort: 'Too short. Min length are ${minLength} character.',
|
||||
required: 'This field is required',
|
||||
},
|
||||
passwordError: {
|
||||
noMatch: 'Passwords do not match',
|
||||
tooLong: 'Too long. Max length are $(maxLength) character.',
|
||||
tooShort: 'Too short. Min length are $(minLength) character.',
|
||||
tooLong: 'Too long. Max length are ${maxLength} character.',
|
||||
tooShort: 'Too short. Min length are ${minLength} character.',
|
||||
required: 'This field is required',
|
||||
weak: 'Password is too weak',
|
||||
invalid:
|
||||
"Passwords must have at least one letter, one number, and one special character. It's for your security. :)",
|
||||
},
|
||||
|
||||
passwordQuality: 'Password quality: ${quality}',
|
||||
passwordQualityList: {
|
||||
0: 'Very weak',
|
||||
1: 'Weak',
|
||||
2: 'Moderate',
|
||||
3: 'Strong',
|
||||
4: 'Very strong',
|
||||
},
|
||||
noAllFieldsEntered: 'Please fill all fields',
|
||||
button: 'Finish registration',
|
||||
registerError: {
|
||||
400: 'Sign up process expired please try again',
|
||||
401: 'User agent and/or IP has changed',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import appNonSaveVarReducer from '@caj/configs/appNonSaveVarReducer';
|
||||
import {configureStore} from '@reduxjs/toolkit';
|
||||
|
||||
import appVariablesReducer from '../configs/appVarReducer';
|
||||
import appVariablesReducer from '@caj/configs/appVarReducer';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import {appVarActions} from '@caj/configs/appVarReducer';
|
||||
import {
|
||||
AccountName,
|
||||
EMail,
|
||||
XAuthorization,
|
||||
UserId,
|
||||
Username,
|
||||
WebSocketSessionId,
|
||||
} from '@caj/configs/types';
|
||||
import {saveVarChanges} from '@caj/helper/appData';
|
||||
import {apiBackendRequest, makeRequest} from '@caj/helper/request';
|
||||
import {store} from '@caj/redux/store';
|
||||
import {MyUserAccount, createUserProp, SourceProp} from './types';
|
||||
|
||||
function createNewMyUser(
|
||||
UserId: UserId,
|
||||
AccountName: AccountName,
|
||||
Username: Username,
|
||||
EMail: EMail,
|
||||
SessionId: XAuthorization,
|
||||
WebSocketSessionId: WebSocketSessionId,
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let user: MyUserAccount = {
|
||||
UserId,
|
||||
AccountName: createUserProp(SourceProp.offline, AccountName),
|
||||
Username: createUserProp(SourceProp.offline, Username),
|
||||
Description: createUserProp(SourceProp.online),
|
||||
FollowersCount: createUserProp(SourceProp.online),
|
||||
FollowingCount: createUserProp(SourceProp.online),
|
||||
XpLevel: createUserProp(SourceProp.online),
|
||||
XpPoints: createUserProp(SourceProp.online),
|
||||
EMail,
|
||||
SessionId,
|
||||
WebSocketSessionId,
|
||||
lastUpdateTimestamp: createUserProp(
|
||||
SourceProp.offline,
|
||||
Math.floor(new Date().getTime() / 1000),
|
||||
),
|
||||
ProfilePicture: {
|
||||
hq: createUserProp(SourceProp.offline),
|
||||
lq: createUserProp(SourceProp.offline),
|
||||
},
|
||||
userSettings: {
|
||||
lang: store.getState().appVariables.lang.details.langCode,
|
||||
theme: store.getState().appVariables.preferences.theme,
|
||||
},
|
||||
};
|
||||
|
||||
console.log('SessionId', SessionId);
|
||||
|
||||
createMyUser(user);
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.APP_START,
|
||||
requestGET: {':userId': UserId},
|
||||
response: {
|
||||
AccountName: '',
|
||||
Description: '',
|
||||
FollowersCount: 0,
|
||||
FollowingCount: 0,
|
||||
Username: '',
|
||||
XpLevel: 0,
|
||||
XpPoints: 0,
|
||||
AvatarUrl: '',
|
||||
AccountStatus: 0,
|
||||
Events: {},
|
||||
TokenValid: false,
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
let user = {...getMyUser(UserId)};
|
||||
console.log(user);
|
||||
user.AccountName = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.AccountName,
|
||||
);
|
||||
user.Username = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.Username,
|
||||
);
|
||||
user.Description = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.Description,
|
||||
);
|
||||
user.FollowersCount = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.FollowersCount,
|
||||
);
|
||||
user.FollowingCount = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.FollowingCount,
|
||||
);
|
||||
user.XpLevel = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.XpLevel,
|
||||
);
|
||||
user.XpPoints = createUserProp(
|
||||
SourceProp.offline,
|
||||
resp.response.XpPoints,
|
||||
);
|
||||
|
||||
setMyUser(user);
|
||||
resolve();
|
||||
})
|
||||
.catch(resp => {
|
||||
console.error(resp.status);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createMyUser(user: MyUserAccount) {
|
||||
store.dispatch(appVarActions.setAccount(user));
|
||||
store.dispatch(appVarActions.setCurrentAccount(user.UserId));
|
||||
saveVarChanges();
|
||||
}
|
||||
|
||||
function getMyUser(userId: UserId): MyUserAccount {
|
||||
return store.getState().appVariables.preferences.accounts[userId];
|
||||
}
|
||||
|
||||
function setMyUser(user: MyUserAccount) {
|
||||
store.dispatch(appVarActions.setAccount(user));
|
||||
saveVarChanges();
|
||||
}
|
||||
|
||||
function getSessionId(userId?: UserId): XAuthorization | undefined {
|
||||
const preferences = store.getState().appVariables.preferences;
|
||||
let user = preferences.accounts[userId || preferences.selectedAccount];
|
||||
|
||||
if (user === undefined) return undefined;
|
||||
|
||||
let SessionId = user.SessionId;
|
||||
|
||||
console.log(userId || preferences.selectedAccount);
|
||||
console.log(preferences.accounts[userId || preferences.selectedAccount]);
|
||||
|
||||
return SessionId;
|
||||
}
|
||||
|
||||
const MyUserManager = {createNewMyUser, getSessionId};
|
||||
export default MyUserManager;
|
|
@ -0,0 +1,5 @@
|
|||
import {UserId, XAuthorization} from '@caj/configs/types';
|
||||
import {store} from '@caj/redux/store';
|
||||
|
||||
const UserManager = {};
|
||||
export default UserManager;
|
|
@ -0,0 +1,62 @@
|
|||
import {ThemeMode} from '@caj/configs/appVar';
|
||||
import {
|
||||
AccountName,
|
||||
EMail,
|
||||
langCode,
|
||||
XAuthorization,
|
||||
timestamp,
|
||||
UserId,
|
||||
Username,
|
||||
WebSocketSessionId,
|
||||
} from '@caj/configs/types';
|
||||
|
||||
export enum SourceProp {
|
||||
online = -1,
|
||||
offline = 0,
|
||||
cached = 1,
|
||||
}
|
||||
|
||||
interface BasicUserProp<T1> {
|
||||
source: SourceProp;
|
||||
url?: string;
|
||||
data?: T1;
|
||||
}
|
||||
|
||||
export interface ProfilePicture {
|
||||
lq: BasicUserProp<string>;
|
||||
hq: BasicUserProp<string>;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
UserId: UserId;
|
||||
|
||||
ProfilePicture: ProfilePicture;
|
||||
lastUpdateTimestamp: BasicUserProp<timestamp>;
|
||||
AccountName: BasicUserProp<AccountName>;
|
||||
Username: BasicUserProp<Username>;
|
||||
Description: BasicUserProp<string>;
|
||||
FollowersCount: BasicUserProp<number>;
|
||||
FollowingCount: BasicUserProp<number>;
|
||||
XpLevel: BasicUserProp<number>;
|
||||
XpPoints: BasicUserProp<number>;
|
||||
}
|
||||
|
||||
export interface MyUserAccount extends User {
|
||||
EMail: EMail;
|
||||
SessionId: XAuthorization;
|
||||
WebSocketSessionId: WebSocketSessionId;
|
||||
userSettings: userSettings;
|
||||
}
|
||||
|
||||
export interface userSettings {
|
||||
theme: ThemeMode;
|
||||
lang: langCode;
|
||||
}
|
||||
|
||||
export function createUserProp<T1>(
|
||||
source: SourceProp,
|
||||
data?: T1,
|
||||
url?: string,
|
||||
): BasicUserProp<T1> {
|
||||
return {source, data, url};
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
},
|
||||
"target": "ESNext",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react-native",
|
||||
"strict": true,
|
||||
|
|
Loading…
Reference in New Issue