finshed sign up
parent
aebc1e4b79
commit
51b02065ee
|
@ -5,10 +5,20 @@ module.exports = {
|
||||||
[
|
[
|
||||||
'module-resolver',
|
'module-resolver',
|
||||||
{
|
{
|
||||||
extensions: ['.ios.js', '.android.js', '.ios.jsx', '.android.jsx', '.js', '.jsx', '.json', '.ts', '.tsx'],
|
extensions: [
|
||||||
|
'.ios.js',
|
||||||
|
'.android.js',
|
||||||
|
'.ios.jsx',
|
||||||
|
'.android.jsx',
|
||||||
|
'.js',
|
||||||
|
'.jsx',
|
||||||
|
'.json',
|
||||||
|
'.ts',
|
||||||
|
'.tsx',
|
||||||
|
],
|
||||||
root: ['.'],
|
root: ['.'],
|
||||||
alias: {
|
alias: {
|
||||||
"@redux": "./src/redux",
|
'@redux': './src/redux',
|
||||||
'@lang': './src/lang',
|
'@lang': './src/lang',
|
||||||
'@pages': './src/pages',
|
'@pages': './src/pages',
|
||||||
'@api': './src/api',
|
'@api': './src/api',
|
||||||
|
@ -20,7 +30,8 @@ module.exports = {
|
||||||
'@navigation': './src/navigation',
|
'@navigation': './src/navigation',
|
||||||
'@configs': './src/configs',
|
'@configs': './src/configs',
|
||||||
'@helper': './src/helper',
|
'@helper': './src/helper',
|
||||||
'@user': './src/user'
|
'@user': './src/user',
|
||||||
|
'@event': './src/event',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"react-native-user-agent": "^2.3.1",
|
"react-native-user-agent": "^2.3.1",
|
||||||
"react-native-vector-icons": "^10.0.2",
|
"react-native-vector-icons": "^10.0.2",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^8.1.3",
|
||||||
|
"react-string-replace": "^1.1.1",
|
||||||
"realm": "^12.3.1",
|
"realm": "^12.3.1",
|
||||||
"redux": "^4.2.1"
|
"redux": "^4.2.1"
|
||||||
},
|
},
|
||||||
|
@ -19494,6 +19495,14 @@
|
||||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-string-replace": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-string-replace/-/react-string-replace-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-26TUbLzLfHQ5jO5N7y3Mx88eeKo0Ml0UjCQuX4BMfOd/JX+enQqlKpL1CZnmjeBRvQE8TR+ds9j1rqx9CxhKHQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-test-renderer": {
|
"node_modules/react-test-renderer": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz",
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
"react-native-user-agent": "^2.3.1",
|
"react-native-user-agent": "^2.3.1",
|
||||||
"react-native-vector-icons": "^10.0.2",
|
"react-native-vector-icons": "^10.0.2",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^8.1.3",
|
||||||
|
"react-string-replace": "^1.1.1",
|
||||||
"realm": "^12.3.1",
|
"realm": "^12.3.1",
|
||||||
"redux": "^4.2.1"
|
"redux": "^4.2.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,8 @@ import configDarkTheme from '@configs/colors';
|
||||||
import Navigation from '@navigation/navigation';
|
import Navigation from '@navigation/navigation';
|
||||||
import {MyStatusBar} from '@components/MyStatusBar';
|
import {MyStatusBar} from '@components/MyStatusBar';
|
||||||
import {SafeAreaView} from 'react-native';
|
import {SafeAreaView} from 'react-native';
|
||||||
|
import {appVarActions} from '@configs/appVarReducer';
|
||||||
|
import {saveVarChanges} from '@helper/appData';
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -52,7 +54,7 @@ const OtherProviders = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationContainer theme={navigationTheme}>
|
<NavigationContainer theme={navigationTheme}>
|
||||||
<GluestackUIProvider config={configDarkTheme}>
|
<GluestackUIProvider config={themeConfig}>
|
||||||
<MainComponent />
|
<MainComponent />
|
||||||
</GluestackUIProvider>
|
</GluestackUIProvider>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
|
|
|
@ -120,6 +120,7 @@ export function MyButton({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: 10,
|
padding: 10,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
|
opacity: disabled ? currentTheme.opacity[60] : 1,
|
||||||
}}>
|
}}>
|
||||||
<ButtonText />
|
<ButtonText />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import {KeyboardTypeOptions, TextInput, View} from 'react-native';
|
import {
|
||||||
|
KeyboardTypeOptions,
|
||||||
|
StyleSheet,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
import {MyIcon} from './MyIcon';
|
import {MyIcon} from './MyIcon';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
import {RootState} from '@redux/store';
|
import {RootState} from '@redux/store';
|
||||||
import {Text} from '@gluestack-ui/themed';
|
import {Text} from '@gluestack-ui/themed';
|
||||||
import {useRef, useState} from 'react';
|
import {ReactNode, useRef, useState} from 'react';
|
||||||
|
|
||||||
interface MyIconInputProps {
|
interface MyIconInputProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -13,6 +19,9 @@ interface MyIconInputProps {
|
||||||
value?: string;
|
value?: string;
|
||||||
onChangeText?: (text: string) => void;
|
onChangeText?: (text: string) => void;
|
||||||
disableContainer?: boolean;
|
disableContainer?: boolean;
|
||||||
|
maxLength?: number;
|
||||||
|
rightComponent?: ReactNode;
|
||||||
|
helper?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MyIconInput({
|
export function MyIconInput({
|
||||||
|
@ -23,42 +32,106 @@ export function MyIconInput({
|
||||||
value,
|
value,
|
||||||
onChangeText,
|
onChangeText,
|
||||||
disableContainer,
|
disableContainer,
|
||||||
|
maxLength,
|
||||||
|
rightComponent,
|
||||||
|
helper,
|
||||||
}: MyIconInputProps) {
|
}: MyIconInputProps) {
|
||||||
const currentTheme = useSelector(
|
const currentTheme = useSelector(
|
||||||
(state: RootState) => state.nonSaveVariables.theme.colors,
|
(state: RootState) => state.nonSaveVariables.theme.colors,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setIsPasswordVisible(!isPasswordVisible);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
!disableContainer && {
|
!disableContainer && {
|
||||||
backgroundColor: currentTheme.backgroundDark300,
|
backgroundColor: currentTheme.backgroundDark300,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<View style={{marginLeft: 12}}>
|
<View
|
||||||
<MyIcon name={iconName} size={30} />
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<View style={{marginLeft: 12}}>
|
||||||
|
<MyIcon name={iconName} size={30} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={{flex: 1, margin: 12, gap: 2}}>
|
||||||
|
<Text size="sm" color={currentTheme.textLight200}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: currentTheme.backgroundDark400,
|
||||||
|
borderRadius: 10,
|
||||||
|
}}>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
height: 40,
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
}}
|
||||||
|
keyboardType={keyboardType}
|
||||||
|
secureTextEntry={secureTextEntry}
|
||||||
|
value={value}
|
||||||
|
onChangeText={onChangeText}
|
||||||
|
maxLength={maxLength}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{rightComponent && (
|
||||||
|
<View style={{paddingRight: 10}}>{rightComponent}</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={{flex: 1, margin: 12, gap: 2}}>
|
<View style={{paddingLeft: 10, paddingRight: 20}}>{helper}</View>
|
||||||
<Text size='sm' color={currentTheme.textLight200}>{text}</Text>
|
</View>
|
||||||
<TextInput
|
);
|
||||||
style={{
|
}
|
||||||
backgroundColor: currentTheme.backgroundDark400,
|
|
||||||
height: 40,
|
export interface MyInputErrorProps {
|
||||||
borderRadius: 10,
|
iconName?: string;
|
||||||
paddingLeft: 10,
|
text: string;
|
||||||
}}
|
}
|
||||||
keyboardType={keyboardType}
|
|
||||||
secureTextEntry={secureTextEntry}
|
export function MyInputError({
|
||||||
value={value}
|
iconName,
|
||||||
onChangeText={onChangeText}
|
text,
|
||||||
/>
|
}: MyInputErrorProps): React.ReactElement<MyInputErrorProps> {
|
||||||
</View>
|
const theme = useSelector(
|
||||||
|
(state: RootState) => state.nonSaveVariables.theme.colors,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
|
paddingRight: 10,
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 10,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<MyIcon
|
||||||
|
name={iconName === undefined ? 'info' : iconName}
|
||||||
|
color={theme.red500}
|
||||||
|
size={18}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text color="$red500">{text}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
import {Alert, CloseIcon, useToast} from '@gluestack-ui/themed';
|
import {
|
||||||
|
Alert,
|
||||||
|
CloseIcon,
|
||||||
|
InfoIcon,
|
||||||
|
Toast,
|
||||||
|
ToastDescription,
|
||||||
|
ToastTitle,
|
||||||
|
useToast,
|
||||||
|
} from '@gluestack-ui/themed';
|
||||||
import {HStack} from '@gluestack-ui/themed';
|
import {HStack} from '@gluestack-ui/themed';
|
||||||
import {Text} from '@gluestack-ui/themed';
|
import {Text} from '@gluestack-ui/themed';
|
||||||
import {VStack} from '@gluestack-ui/themed';
|
import {VStack} from '@gluestack-ui/themed';
|
||||||
import {AlertIcon} from '@gluestack-ui/themed';
|
import {AlertIcon} from '@gluestack-ui/themed';
|
||||||
import {MyButton} from './MyButton';
|
import {MyButton, MyIconButton} from './MyButton';
|
||||||
|
import {AlertText} from '@gluestack-ui/themed';
|
||||||
|
|
||||||
|
/*
|
||||||
const Toast = () => {
|
const Toast = () => {
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const ToastDetails = [
|
const ToastDetails = [
|
||||||
|
@ -37,7 +47,7 @@ const Toast = () => {
|
||||||
description: 'Please enter a valid email address',
|
description: 'Please enter a valid email address',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
}; */
|
||||||
|
|
||||||
interface toastType {
|
interface toastType {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -46,7 +56,7 @@ interface toastType {
|
||||||
title: any;
|
title: any;
|
||||||
description: any;
|
description: any;
|
||||||
isClosable?: boolean;
|
isClosable?: boolean;
|
||||||
rest: any;
|
rest?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface alertType extends toastType {
|
interface alertType extends toastType {
|
||||||
|
@ -64,9 +74,10 @@ function showToast(toast: any, item: toastType) {
|
||||||
rest,
|
rest,
|
||||||
}: alertType) => (
|
}: alertType) => (
|
||||||
<Alert
|
<Alert
|
||||||
maxWidth="95%"
|
// maxWidth="95%"
|
||||||
alignSelf="center"
|
//alignSelf="center"
|
||||||
flexDirection="row"
|
//flexDirection="row"
|
||||||
|
bg="$secondary600"
|
||||||
action={action}
|
action={action}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
{...rest}>
|
{...rest}>
|
||||||
|
@ -85,15 +96,17 @@ function showToast(toast: any, item: toastType) {
|
||||||
? '$white'
|
? '$white'
|
||||||
: variant !== 'outline'
|
: variant !== 'outline'
|
||||||
? '$black'
|
? '$black'
|
||||||
: null
|
: '$white'
|
||||||
}>
|
}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
{isClosable ? (
|
{isClosable ? (
|
||||||
<MyButton
|
<MyIconButton
|
||||||
type="secondary"
|
MyIconProps={{
|
||||||
text="X"
|
name: 'close',
|
||||||
|
size: 24,
|
||||||
|
}}
|
||||||
onPress={() => toast.close(id)}
|
onPress={() => toast.close(id)}
|
||||||
/>
|
/>
|
||||||
) : /*<IconButton
|
) : /*<IconButton
|
||||||
|
@ -113,7 +126,7 @@ function showToast(toast: any, item: toastType) {
|
||||||
? '$white'
|
? '$white'
|
||||||
: variant !== 'outline'
|
: variant !== 'outline'
|
||||||
? '$black'
|
? '$black'
|
||||||
: null
|
: '$white'
|
||||||
}>
|
}>
|
||||||
{description}
|
{description}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
type EventID = string;
|
import { EventID, EventType } from '@event/types';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface BasicEvent {
|
interface BasicEvent {
|
||||||
id: EventID;
|
id: EventID;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
|
type: EventType | "cluster";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PA_Point_Cluster extends BasicEvent {
|
interface PA_Point_Cluster extends BasicEvent {
|
||||||
|
|
|
@ -6,12 +6,15 @@ import {AccountName} from './types';
|
||||||
|
|
||||||
import {getVersionByNum, VersionType} from '@helper/version';
|
import {getVersionByNum, VersionType} from '@helper/version';
|
||||||
import configDarkTheme, {ThemeTokensType} from '@configs/colors';
|
import configDarkTheme, {ThemeTokensType} from '@configs/colors';
|
||||||
|
import { EventID, PAEvent } from '@event/types';
|
||||||
|
import { PA_Point } from '@components/map/types';
|
||||||
|
|
||||||
|
|
||||||
import {PA_Point} from '@components/map/cluster/getData';
|
|
||||||
|
|
||||||
export const APP_VERSION = getVersionByNum(1);
|
export const APP_VERSION = getVersionByNum(1);
|
||||||
export const AppVarMaxBackups: number = 10;
|
export const AppVarMaxBackups: number = 10;
|
||||||
export const maxCachedUsers = 30;
|
export const maxCachedUsers = 30;
|
||||||
|
export const maxCachedEvents = 30;
|
||||||
|
|
||||||
export enum appStatus {
|
export enum appStatus {
|
||||||
IS_LOADING,
|
IS_LOADING,
|
||||||
|
@ -31,6 +34,7 @@ export interface NON_SAVE_VARS {
|
||||||
theme: ThemeTokensType;
|
theme: ThemeTokensType;
|
||||||
connectionStatus: connectionStatus;
|
connectionStatus: connectionStatus;
|
||||||
cachedUsers: {[key: AccountName]: User};
|
cachedUsers: {[key: AccountName]: User};
|
||||||
|
cachedEvents: {[key: EventID]: PAEvent};
|
||||||
chats: {[key: roomId]: chatEntity};
|
chats: {[key: roomId]: chatEntity};
|
||||||
chatActivity: roomId[];
|
chatActivity: roomId[];
|
||||||
selectedChat: roomId | 'none';
|
selectedChat: roomId | 'none';
|
||||||
|
@ -43,6 +47,7 @@ export const non_save_vars: NON_SAVE_VARS = {
|
||||||
theme: configDarkTheme.tokens,
|
theme: configDarkTheme.tokens,
|
||||||
connectionStatus: connectionStatus.UNKNOWN,
|
connectionStatus: connectionStatus.UNKNOWN,
|
||||||
cachedUsers: {},
|
cachedUsers: {},
|
||||||
|
cachedEvents: {},
|
||||||
chats: {},
|
chats: {},
|
||||||
chatActivity: [],
|
chatActivity: [],
|
||||||
selectedChat: 'none',
|
selectedChat: 'none',
|
||||||
|
|
|
@ -5,8 +5,9 @@ import {ThemeTokensType} from '@configs/colors';
|
||||||
|
|
||||||
import {chatEntity, roomId} from '@configs/chat/types';
|
import {chatEntity, roomId} from '@configs/chat/types';
|
||||||
import {User} from '@user/types';
|
import {User} from '@user/types';
|
||||||
import {AccountName} from './types';
|
import {AccountName, EventId} from './types';
|
||||||
import {PA_Point} from '@components/map/cluster/getData';
|
import {PA_Point} from '@components/map/types';
|
||||||
|
import {PAEvent} from '@event/types';
|
||||||
|
|
||||||
export const appNonSaveVariablesSlice = createSlice({
|
export const appNonSaveVariablesSlice = createSlice({
|
||||||
name: 'non_save_vars',
|
name: 'non_save_vars',
|
||||||
|
@ -24,6 +25,13 @@ export const appNonSaveVariablesSlice = createSlice({
|
||||||
removeCachedUser: (state, action: PayloadAction<AccountName>) => {
|
removeCachedUser: (state, action: PayloadAction<AccountName>) => {
|
||||||
delete state.cachedUsers[action.payload];
|
delete state.cachedUsers[action.payload];
|
||||||
},
|
},
|
||||||
|
setCachedEvent: (state, action: PayloadAction<PAEvent>) => {
|
||||||
|
state.cachedEvents[action.payload.UUID] = action.payload;
|
||||||
|
},
|
||||||
|
removeCachedEvent: (state, action: PayloadAction<EventId>) => {
|
||||||
|
delete state.cachedEvents[action.payload];
|
||||||
|
},
|
||||||
|
|
||||||
setSelectedChat: (state, action: PayloadAction<roomId>) => {
|
setSelectedChat: (state, action: PayloadAction<roomId>) => {
|
||||||
state.selectedChat = action.payload;
|
state.selectedChat = action.payload;
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,11 +17,14 @@ export type XAuthorization = string;
|
||||||
//export type UserId = string;
|
//export type UserId = string;
|
||||||
//export type WebSocketSessionId = string;
|
//export type WebSocketSessionId = string;
|
||||||
|
|
||||||
|
export type EventId = string;
|
||||||
|
|
||||||
export const accountNameOptions = {
|
export const accountNameOptions = {
|
||||||
minLength: 4,
|
minLength: 4,
|
||||||
maxLength: 24,
|
maxLength: 24,
|
||||||
isAllowed: (text: string): boolean => {
|
isAllowed: (text: string): boolean => {
|
||||||
return text.match('^[a-zA-Z0-9_.]+$') !== null;
|
// allows usernames that start and end with a lowercase letter or digit, with optional dots or underscores in the middle, and it is case-insensitive
|
||||||
|
return text.match(/^[a-z0-9](?:[._]*[a-z0-9])*$/i) !== null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +41,17 @@ export const passwordOptions = {
|
||||||
maxLength: 64,
|
maxLength: 64,
|
||||||
minBits: 50,
|
minBits: 50,
|
||||||
isAllowed: (text: string): boolean => {
|
isAllowed: (text: string): boolean => {
|
||||||
return /\W/.test(text) && /[a-zA-Z]/.test(text) && /[a-zA-Z]/.test(text);
|
// return /\W/.test(text) && /[a-zA-Z]/.test(text) && /[a-zA-Z]/.test(text);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Contains at least one uppercase letter
|
||||||
|
Contains at least one lowercase letter
|
||||||
|
Contains at least one digit (number)
|
||||||
|
Contains at least one special character
|
||||||
|
*/
|
||||||
|
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+={}\[\]|;:'",.<>\/?]).*$/.test(
|
||||||
|
text,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
import {maxCachedEvents} from '@configs/appNonSaveVar';
|
||||||
|
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
|
||||||
|
import {AccountName, XAuthorization} from '@configs/types';
|
||||||
|
import {makeRequest, apiBackendRequest} from '@helper/request';
|
||||||
|
import BigDataManager from '@helper/storage/BigDataManager';
|
||||||
|
import {RootState, store} from '@redux/store';
|
||||||
|
import {useSelector} from 'react-redux';
|
||||||
|
import {
|
||||||
|
BasicEventProp,
|
||||||
|
createEventProp,
|
||||||
|
EventBannerPicture,
|
||||||
|
EventBannerPictureType,
|
||||||
|
EventID,
|
||||||
|
EventType,
|
||||||
|
NumToEventType,
|
||||||
|
PAEvent,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
import {SourceProp} from '@user/types';
|
||||||
|
|
||||||
|
let cachedEventList: EventID[] = [];
|
||||||
|
|
||||||
|
async function getEvent(
|
||||||
|
UUID: EventID,
|
||||||
|
save?: boolean,
|
||||||
|
): Promise<PAEvent | undefined> {
|
||||||
|
if (UUID === 'none') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event: PAEvent | undefined;
|
||||||
|
|
||||||
|
let state = store.getState();
|
||||||
|
let eventIsInCache = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
const eve = state.nonSaveVariables.cachedEvents[UUID];
|
||||||
|
if (eve !== undefined) {
|
||||||
|
event = eve;
|
||||||
|
eventIsInCache = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (event === undefined) {
|
||||||
|
const eveDBKeys = BigDataManager.databases.events.keys;
|
||||||
|
const eve = await BigDataManager.databases.events.getEntry(UUID);
|
||||||
|
|
||||||
|
if (eve !== undefined && eve !== null) {
|
||||||
|
let EventBannerPicture: EventBannerPicture = {
|
||||||
|
lq:
|
||||||
|
eve[eveDBKeys.EventBannerPictureBinaryLQ].byteLength !== 0
|
||||||
|
? createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
//Buffer.from(eve[eveDBKeys.EventBannerPictureBinaryLQ]),
|
||||||
|
new Blob([eve[eveDBKeys.EventBannerPictureBinaryLQ] as unknown as string]),
|
||||||
|
)
|
||||||
|
: createEventProp(
|
||||||
|
SourceProp.online,
|
||||||
|
undefined,
|
||||||
|
eve[eveDBKeys.EventBannerPicture],
|
||||||
|
),
|
||||||
|
hq:
|
||||||
|
eve[eveDBKeys.EventBannerPictureBinaryHQ].byteLength !== 0
|
||||||
|
? createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
//Buffer.from(eve[eveDBKeys.EventBannerPictureBinaryHQ]),
|
||||||
|
new Blob([eve[eveDBKeys.EventBannerPictureBinaryHQ] as unknown as string]),
|
||||||
|
)
|
||||||
|
: createEventProp(
|
||||||
|
SourceProp.online,
|
||||||
|
undefined,
|
||||||
|
eve[eveDBKeys.EventBannerPicture],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
event = {
|
||||||
|
UUID,
|
||||||
|
Name: createEventProp(SourceProp.offline, eve[eveDBKeys.Name]),
|
||||||
|
Description: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.Description],
|
||||||
|
),
|
||||||
|
StartDate: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.StartDate],
|
||||||
|
),
|
||||||
|
EndDate: createEventProp(SourceProp.offline, eve[eveDBKeys.EndDate]),
|
||||||
|
Latitude: createEventProp(SourceProp.offline, eve[eveDBKeys.Latitude]),
|
||||||
|
Longitude: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.Longitude],
|
||||||
|
),
|
||||||
|
Type: createEventProp(SourceProp.offline, NumToEventType(eve[eveDBKeys.Type])),
|
||||||
|
Theme: createEventProp(SourceProp.offline, eve[eveDBKeys.Theme]),
|
||||||
|
FriendList: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.FriendList],
|
||||||
|
),
|
||||||
|
UserLength: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.UserLength],
|
||||||
|
),
|
||||||
|
ButtonAction: createEventProp(
|
||||||
|
SourceProp.offline,
|
||||||
|
eve[eveDBKeys.ButtonAction],
|
||||||
|
),
|
||||||
|
lastUpdateTimestamp: eve[eveDBKeys.lastUpdateTimestamp],
|
||||||
|
EventBannerPicture,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === undefined) {
|
||||||
|
try {
|
||||||
|
/*const resp = await makeRequest({
|
||||||
|
path: apiBackendRequest.GET_USER_PROFILE,
|
||||||
|
requestGET: {':UUID': UUID},
|
||||||
|
response: {
|
||||||
|
Description: '',
|
||||||
|
FollowersCount: 0,
|
||||||
|
FollowingCount: 0,
|
||||||
|
Eventname: '',
|
||||||
|
XpLevel: 0,
|
||||||
|
XpPoints: 0,
|
||||||
|
AvatarUrl: '',
|
||||||
|
},
|
||||||
|
});*/
|
||||||
|
|
||||||
|
/* Name: BasicEventProp<string>;
|
||||||
|
Description: BasicEventProp<string>;
|
||||||
|
StartDate: BasicEventProp<timestamp>;
|
||||||
|
EndDate: BasicEventProp<timestamp>;
|
||||||
|
Latitude: BasicEventProp<number>;
|
||||||
|
Longitude: BasicEventProp<number>;
|
||||||
|
Type: BasicEventProp<EventType>;
|
||||||
|
Theme: BasicEventProp<EventThemes>;
|
||||||
|
lastUpdateTimestamp: timestamp;
|
||||||
|
FriendList: BasicEventProp<string[]>;
|
||||||
|
UserLength: BasicEventProp<number>;
|
||||||
|
EventBannerPicture: EventBannerPicture;
|
||||||
|
ButtonAction: BasicEventProp<string>; */
|
||||||
|
|
||||||
|
const resp = {response:{
|
||||||
|
Name: 'test event',
|
||||||
|
Description: 'test description',
|
||||||
|
StartDate: 1702230005,
|
||||||
|
EndDate: new Date().getTime() + 100000,
|
||||||
|
Latitude: 48.7758,
|
||||||
|
Longitude: 9.1829,
|
||||||
|
Type: 0,
|
||||||
|
Theme: 0,
|
||||||
|
FriendList: [],
|
||||||
|
UserLength: 5,
|
||||||
|
pic: 'https://www.w3schools.com/w3css/img_lights.jpg',
|
||||||
|
ButtonAction: '{"url":"https://www.w3schools.com/w3css/img_lights.jpg"}',
|
||||||
|
}};
|
||||||
|
|
||||||
|
event = {
|
||||||
|
UUID,
|
||||||
|
Description: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.Description,
|
||||||
|
),
|
||||||
|
lastUpdateTimestamp: Math.floor(new Date().getTime() / 1000),
|
||||||
|
EventBannerPicture: {
|
||||||
|
lq: createEventProp(
|
||||||
|
SourceProp.online,
|
||||||
|
undefined,
|
||||||
|
resp.response.pic + "?type=low",
|
||||||
|
),
|
||||||
|
hq: createEventProp(
|
||||||
|
SourceProp.online,
|
||||||
|
undefined,
|
||||||
|
resp.response.pic,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Name: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.Name,
|
||||||
|
),
|
||||||
|
StartDate: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.StartDate,
|
||||||
|
),
|
||||||
|
EndDate: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.EndDate,
|
||||||
|
),
|
||||||
|
Latitude: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.Latitude,
|
||||||
|
),
|
||||||
|
Longitude: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.Longitude,
|
||||||
|
),
|
||||||
|
Type: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
NumToEventType(resp.response.Type),
|
||||||
|
),
|
||||||
|
Theme: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.Theme,
|
||||||
|
),
|
||||||
|
FriendList: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.FriendList,
|
||||||
|
),
|
||||||
|
UserLength: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.UserLength,
|
||||||
|
),
|
||||||
|
ButtonAction: createEventProp(
|
||||||
|
SourceProp.cached,
|
||||||
|
resp.response.ButtonAction,
|
||||||
|
),
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//BigDataManager.setEntry('events', event);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventIsInCache === false && event !== undefined) {
|
||||||
|
console.log('save in cache');
|
||||||
|
store.dispatch(appNonSaveVarActions.setCachedEvent(event));
|
||||||
|
cachedEventList.push(event.UUID);
|
||||||
|
|
||||||
|
if (cachedEventList.length > maxCachedEvents) {
|
||||||
|
let eveId = cachedEventList[0];
|
||||||
|
cachedEventList.shift();
|
||||||
|
console.log('eveId', eveId);
|
||||||
|
|
||||||
|
store.dispatch(appNonSaveVarActions.removeCachedEvent(eveId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GetParam {
|
||||||
|
CACHE = 0,
|
||||||
|
SAVE,
|
||||||
|
}
|
||||||
|
|
||||||
|
let getEventList: {[key: AccountName]: GetParam} = {};
|
||||||
|
|
||||||
|
async function refreshEvents() {
|
||||||
|
for (let UUID in getEventList) {
|
||||||
|
const param = getEventList[UUID];
|
||||||
|
delete getEventList[UUID];
|
||||||
|
|
||||||
|
await getEvent(UUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(refreshEvents, 500);
|
||||||
|
|
||||||
|
function addEventToGetQueue(UUID: EventID, param: GetParam) {
|
||||||
|
if (getEventList[UUID] === undefined) {
|
||||||
|
getEventList[UUID] = param;
|
||||||
|
} else if (getEventList[UUID] < param) {
|
||||||
|
getEventList[UUID] = param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEventSelector(UUID: EventID) {
|
||||||
|
addEventToGetQueue(UUID, GetParam.CACHE);
|
||||||
|
|
||||||
|
const myEvent = useSelector(
|
||||||
|
(state: RootState) => state.nonSaveVariables.cachedEvents[UUID],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (myEvent === undefined) {
|
||||||
|
return initUndefinedEvent(UUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return myEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEventSelectorPicture(UUID: EventID): EventBannerPicture {
|
||||||
|
addEventToGetQueue(UUID, GetParam.CACHE);
|
||||||
|
|
||||||
|
const myEvent = useSelector(
|
||||||
|
(state: RootState) =>
|
||||||
|
state.nonSaveVariables.cachedEvents[UUID]?.EventBannerPicture,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (myEvent === undefined) {
|
||||||
|
return {
|
||||||
|
lq: createEventProp(SourceProp.online),
|
||||||
|
hq: createEventProp(SourceProp.online),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return myEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initUndefinedEvent(UUID: EventID): PAEvent {
|
||||||
|
return {
|
||||||
|
UUID,
|
||||||
|
Name: createEventProp(SourceProp.online),
|
||||||
|
Description: createEventProp(SourceProp.online),
|
||||||
|
StartDate: createEventProp(SourceProp.online),
|
||||||
|
EndDate: createEventProp(SourceProp.online),
|
||||||
|
Latitude: createEventProp(SourceProp.online),
|
||||||
|
Longitude: createEventProp(SourceProp.online),
|
||||||
|
Type: createEventProp(SourceProp.online),
|
||||||
|
Theme: createEventProp(SourceProp.online),
|
||||||
|
lastUpdateTimestamp: 0,
|
||||||
|
FriendList: createEventProp(SourceProp.online),
|
||||||
|
UserLength: createEventProp(SourceProp.online),
|
||||||
|
EventBannerPicture: {
|
||||||
|
lq: createEventProp(SourceProp.online),
|
||||||
|
hq: createEventProp(SourceProp.online),
|
||||||
|
},
|
||||||
|
ButtonAction: createEventProp(SourceProp.online),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const EventManager = {
|
||||||
|
getEvent,
|
||||||
|
getEventSelector,
|
||||||
|
getEventSelectorPicture,
|
||||||
|
initUndefinedEvent,
|
||||||
|
};
|
||||||
|
export default EventManager;
|
|
@ -0,0 +1,71 @@
|
||||||
|
import {SourceProp} from '@user/types';
|
||||||
|
|
||||||
|
import {EventId, timestamp} from '@configs/types';
|
||||||
|
|
||||||
|
export type EventID = string;
|
||||||
|
export type EventType = 'event' | 'eventStore';
|
||||||
|
|
||||||
|
export enum EventThemes {
|
||||||
|
default = 0,
|
||||||
|
small = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BasicEventProp<T1> {
|
||||||
|
source: SourceProp;
|
||||||
|
url?: string;
|
||||||
|
data?: T1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEventProp<T1>(
|
||||||
|
source: SourceProp,
|
||||||
|
data?: T1,
|
||||||
|
url?: string,
|
||||||
|
): BasicEventProp<T1> {
|
||||||
|
return {source, data, url};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventBannerPictureType = BasicEventProp<Blob | undefined>;
|
||||||
|
|
||||||
|
export interface EventBannerPicture {
|
||||||
|
lq: EventBannerPictureType; //low quality
|
||||||
|
hq?: EventBannerPictureType; //high quality
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PAEvent {
|
||||||
|
UUID: EventId;
|
||||||
|
Name: BasicEventProp<string>;
|
||||||
|
Description: BasicEventProp<string>;
|
||||||
|
StartDate: BasicEventProp<timestamp>;
|
||||||
|
EndDate: BasicEventProp<timestamp>;
|
||||||
|
Latitude: BasicEventProp<number>;
|
||||||
|
Longitude: BasicEventProp<number>;
|
||||||
|
Type: BasicEventProp<EventType>;
|
||||||
|
Theme: BasicEventProp<EventThemes>;
|
||||||
|
lastUpdateTimestamp: timestamp;
|
||||||
|
FriendList: BasicEventProp<string[]>;
|
||||||
|
UserLength: BasicEventProp<number>;
|
||||||
|
EventBannerPicture: EventBannerPicture;
|
||||||
|
ButtonAction: BasicEventProp<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NumToEventType(num: number): EventType {
|
||||||
|
switch (num) {
|
||||||
|
case 0:
|
||||||
|
return 'event';
|
||||||
|
case 1:
|
||||||
|
return 'eventStore';
|
||||||
|
default:
|
||||||
|
return 'event';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EventTypeToNum(type: EventType): number {
|
||||||
|
switch (type) {
|
||||||
|
case 'event':
|
||||||
|
return 0;
|
||||||
|
case 'eventStore':
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ export enum apiBackendRequest {
|
||||||
GET_USER_PROFILE = '/users/:accountName',
|
GET_USER_PROFILE = '/users/:accountName',
|
||||||
LOGOUT = '/user/logout',
|
LOGOUT = '/user/logout',
|
||||||
SIGN_UP = '/user/signup',
|
SIGN_UP = '/user/signup',
|
||||||
|
CHECK_ACCOUNT_NAME = '/user/check/:accountName',
|
||||||
/*REGISTER_STEP_1 = '/admin/users/email',
|
/*REGISTER_STEP_1 = '/admin/users/email',
|
||||||
REGISTER_RESEND_MAIL = '/admin/users/email/resend',
|
REGISTER_RESEND_MAIL = '/admin/users/email/resend',
|
||||||
REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId',
|
REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId',
|
||||||
|
@ -147,6 +148,15 @@ interface GET_USER_PROFILE extends defaultRequest {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CHECK_ACCOUNT_NAME extends defaultRequest {
|
||||||
|
path: apiBackendRequest.CHECK_ACCOUNT_NAME;
|
||||||
|
|
||||||
|
requestGET: {
|
||||||
|
':accountName': AccountName;
|
||||||
|
};
|
||||||
|
response: {};
|
||||||
|
}
|
||||||
|
|
||||||
interface APP_START extends defaultRequest {
|
interface APP_START extends defaultRequest {
|
||||||
path: apiBackendRequest.APP_START;
|
path: apiBackendRequest.APP_START;
|
||||||
|
|
||||||
|
@ -180,7 +190,8 @@ type FetchTypes =
|
||||||
| LOGIN*/
|
| LOGIN*/
|
||||||
| GET_USER_PROFILE
|
| GET_USER_PROFILE
|
||||||
| APP_START
|
| APP_START
|
||||||
| LOGOUT;
|
| LOGOUT
|
||||||
|
| CHECK_ACCOUNT_NAME;
|
||||||
/*
|
/*
|
||||||
function isA(obj: any): obj is REGISTER_STEP_1 {
|
function isA(obj: any): obj is REGISTER_STEP_1 {
|
||||||
return obj.request !== undefined;
|
return obj.request !== undefined;
|
||||||
|
|
|
@ -40,6 +40,10 @@ export const DBMigration: {[key in databaseNames]: any} = {
|
||||||
chatRoomInfos: (Schema: typeof DBSchemas.chatRoomInfos) => {
|
chatRoomInfos: (Schema: typeof DBSchemas.chatRoomInfos) => {
|
||||||
const callback: MigrationCallback = (oldRealm, newRealm) => {};
|
const callback: MigrationCallback = (oldRealm, newRealm) => {};
|
||||||
|
|
||||||
|
return callback;
|
||||||
|
},events: (Schema: typeof DBSchemas.events) => {
|
||||||
|
const callback: MigrationCallback = (oldRealm, newRealm) => {};
|
||||||
|
|
||||||
return callback;
|
return callback;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import chat from './schemas/chat';
|
import chat from './schemas/chat';
|
||||||
import users from './schemas/users';
|
import users from './schemas/users';
|
||||||
import chatRoomInfos from './schemas/chatRoomInfos';
|
import chatRoomInfos from './schemas/chatRoomInfos';
|
||||||
|
import events from './schemas/events';
|
||||||
|
|
||||||
const DBSchemas = {users, chat, chatRoomInfos};
|
const DBSchemas = {users, chat, chatRoomInfos, events};
|
||||||
export const SkipDBSchemas = [chat.details.name];
|
export const SkipDBSchemas = [chat.details.name];
|
||||||
|
|
||||||
export type databaseConfType = typeof DBSchemas[keyof typeof DBSchemas];
|
export type databaseConfType = typeof DBSchemas[keyof typeof DBSchemas];
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import {filterParam, getAllEntries, getEntry} from '../get';
|
||||||
|
import {DBMigration} from '../migration';
|
||||||
|
import {setEntry} from '../set';
|
||||||
|
|
||||||
|
import {databaseConf, possibleDBKeys} from '../types';
|
||||||
|
|
||||||
|
enum keys {
|
||||||
|
UUID = 'a',
|
||||||
|
Name = 'b',
|
||||||
|
Description = 'c',
|
||||||
|
StartDate = 'd',
|
||||||
|
EndDate = 'e',
|
||||||
|
Latitude = 'f',
|
||||||
|
Longitude = 'g',
|
||||||
|
Type = 'h',
|
||||||
|
Theme = 'i',
|
||||||
|
lastUpdateTimestamp = 'j',
|
||||||
|
FriendList = 'k',
|
||||||
|
UserLength = 'l',
|
||||||
|
EventBannerPicture = 'm', //URL
|
||||||
|
EventBannerPictureBinaryLQ = 'n',
|
||||||
|
EventBannerPictureBinaryHQ = 'o',
|
||||||
|
ButtonAction = 'p',
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = 'events';
|
||||||
|
const primaryKey: keyof typeof propsDefault = keys.UUID;
|
||||||
|
|
||||||
|
const propsType: {[key in keyof typeof propsDefault]: string} = {
|
||||||
|
[keys.UUID]: 'string',
|
||||||
|
[keys.Name]: 'string',
|
||||||
|
[keys.Description]: 'string',
|
||||||
|
[keys.StartDate]: 'int',
|
||||||
|
[keys.EndDate]: 'int',
|
||||||
|
[keys.Latitude]: 'double',
|
||||||
|
[keys.Longitude]: 'double',
|
||||||
|
[keys.Type]: 'int',
|
||||||
|
[keys.Theme]: 'int',
|
||||||
|
[keys.lastUpdateTimestamp]: 'int',
|
||||||
|
[keys.FriendList]: 'string[]',
|
||||||
|
[keys.UserLength]: 'int',
|
||||||
|
[keys.EventBannerPicture]: 'string', //URL
|
||||||
|
[keys.EventBannerPictureBinaryLQ]: 'data',
|
||||||
|
[keys.EventBannerPictureBinaryHQ]: 'data',
|
||||||
|
[keys.ButtonAction]: 'string',
|
||||||
|
};
|
||||||
|
|
||||||
|
const propsDefault = {
|
||||||
|
[keys.UUID]: '',
|
||||||
|
[keys.Name]: '',
|
||||||
|
[keys.Description]: '',
|
||||||
|
[keys.StartDate]: 0,
|
||||||
|
[keys.EndDate]: 0,
|
||||||
|
[keys.Latitude]: 0,
|
||||||
|
[keys.Longitude]: 0,
|
||||||
|
[keys.Type]: 0,
|
||||||
|
[keys.Theme]: 0,
|
||||||
|
[keys.lastUpdateTimestamp]: 0,
|
||||||
|
[keys.FriendList]: ['none'],
|
||||||
|
[keys.UserLength]: 0,
|
||||||
|
[keys.EventBannerPicture]: '', //URL
|
||||||
|
[keys.EventBannerPictureBinaryLQ]: new ArrayBuffer(0),
|
||||||
|
[keys.EventBannerPictureBinaryHQ]: new ArrayBuffer(0),
|
||||||
|
[keys.ButtonAction]: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const thisSchema: databaseConf<typeof propsDefault, typeof keys> = {
|
||||||
|
filePath: name,
|
||||||
|
version: 1,
|
||||||
|
keys,
|
||||||
|
migration: () => {
|
||||||
|
return DBMigration[name](thisSchema);
|
||||||
|
},
|
||||||
|
setEntry: (val: typeof thisSchema.defaultProps, suffix?: string) => {
|
||||||
|
return setEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
|
||||||
|
thisSchema,
|
||||||
|
val,
|
||||||
|
suffix,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getEntry: (key: possibleDBKeys, suffix?: string) => {
|
||||||
|
return getEntry<typeof thisSchema, typeof thisSchema.defaultProps>(
|
||||||
|
thisSchema,
|
||||||
|
key,
|
||||||
|
suffix,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getAllEntries: (filter?: filterParam, suffix?: string) => {
|
||||||
|
return getAllEntries<typeof thisSchema, typeof thisSchema.defaultProps>(
|
||||||
|
thisSchema,
|
||||||
|
filter,
|
||||||
|
suffix,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
defaultProps: propsDefault,
|
||||||
|
details: {
|
||||||
|
name,
|
||||||
|
properties: propsType,
|
||||||
|
primaryKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default thisSchema;
|
|
@ -1,7 +1,7 @@
|
||||||
import MyUserManager from '@user/MyUserManager';
|
import MyUserManager from '@user/MyUserManager';
|
||||||
import {filterParam} from './get';
|
import {filterParam} from './get';
|
||||||
|
|
||||||
export type databaseNames = 'users' | 'chat' | 'chatRoomInfos';
|
export type databaseNames = 'users' | 'chat' | 'chatRoomInfos' | 'events';
|
||||||
export type possibleDBKeys = string;
|
export type possibleDBKeys = string;
|
||||||
|
|
||||||
export interface databaseConf<props, enums> {
|
export interface databaseConf<props, enums> {
|
||||||
|
@ -31,10 +31,10 @@ export interface databaseNameSuffix {
|
||||||
export function mergeDBName(nameObj: databaseNameSuffix, web?: 'web'): string {
|
export function mergeDBName(nameObj: databaseNameSuffix, web?: 'web'): string {
|
||||||
if (web === 'web') {
|
if (web === 'web') {
|
||||||
return nameObj.suffix === undefined
|
return nameObj.suffix === undefined
|
||||||
? nameObj.name + '-' + MyUserManager.getSelectedUserId()
|
? nameObj.name + '-' + MyUserManager.getSelectedUserAccount()
|
||||||
: nameObj.name +
|
: nameObj.name +
|
||||||
'-' +
|
'-' +
|
||||||
MyUserManager.getSelectedUserId() +
|
MyUserManager.getSelectedUserAccount() +
|
||||||
('_' + nameObj.suffix);
|
('_' + nameObj.suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@ export default interface LangFormat {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
inputUsername: string;
|
inputUsername: string;
|
||||||
|
error: string;
|
||||||
};
|
};
|
||||||
signUpStepPhoneNumber: {
|
signUpStepPhoneNumber: {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -79,12 +80,17 @@ export default interface LangFormat {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
inputPassword: string;
|
inputPassword: string;
|
||||||
|
errorLength: string;
|
||||||
|
errorPasswordInvalid: string;
|
||||||
};
|
};
|
||||||
signUpStepAccountName: {
|
signUpStepAccountName: {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
inputAccountName: string;
|
inputAccountName: string;
|
||||||
buttonGetStarted: string;
|
buttonGetStarted: string;
|
||||||
|
signUpError: {[key: number]: string};
|
||||||
|
errorLength: string;
|
||||||
|
errorAccountNameInvalid: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
profile: {
|
profile: {
|
||||||
|
|
|
@ -65,6 +65,7 @@ export const lang: LangFormat = {
|
||||||
title: "Let's get started, what's your name?",
|
title: "Let's get started, what's your name?",
|
||||||
description: 'The name will be displayed on your profil overview',
|
description: 'The name will be displayed on your profil overview',
|
||||||
inputUsername: 'Username',
|
inputUsername: 'Username',
|
||||||
|
error: 'At least ${minLength} characters are required',
|
||||||
},
|
},
|
||||||
signUpStepPhoneNumber: {
|
signUpStepPhoneNumber: {
|
||||||
title: 'Create your account using your phone number',
|
title: 'Create your account using your phone number',
|
||||||
|
@ -77,8 +78,11 @@ export const lang: LangFormat = {
|
||||||
},
|
},
|
||||||
signUpStepPassword: {
|
signUpStepPassword: {
|
||||||
title: "You'll need a password",
|
title: "You'll need a password",
|
||||||
description: 'Make sure it’s 8 characters or more.',
|
description: 'Make sure it’s ${minLength} characters or more.',
|
||||||
inputPassword: 'PASSWORD',
|
inputPassword: 'PASSWORD',
|
||||||
|
errorLength: 'Password must be at least ${minLength} characters long',
|
||||||
|
errorPasswordInvalid:
|
||||||
|
'Must include at least on uppercase letter, one lowercase letter, one number and one special character',
|
||||||
},
|
},
|
||||||
signUpStepAccountName: {
|
signUpStepAccountName: {
|
||||||
title: 'Next, create your account name',
|
title: 'Next, create your account name',
|
||||||
|
@ -86,6 +90,15 @@ export const lang: LangFormat = {
|
||||||
'Your account name is unique and is used for friends to find you.',
|
'Your account name is unique and is used for friends to find you.',
|
||||||
inputAccountName: 'ACCOUNT NAME',
|
inputAccountName: 'ACCOUNT NAME',
|
||||||
buttonGetStarted: 'Get Started',
|
buttonGetStarted: 'Get Started',
|
||||||
|
errorLength: 'Account name must be at least ${minLength} characters long',
|
||||||
|
errorAccountNameInvalid:
|
||||||
|
'Account name can only contain \n20a-z, 0-9, underscores and dots',
|
||||||
|
signUpError: {
|
||||||
|
400: 'Invalid account name',
|
||||||
|
401: 'Invalid credentials',
|
||||||
|
500: 'Server error',
|
||||||
|
502: 'Server not reachable',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
profile: {
|
profile: {
|
||||||
|
@ -119,13 +132,13 @@ export const lang: LangFormat = {
|
||||||
changeUsername: {
|
changeUsername: {
|
||||||
username: 'USERNAME',
|
username: 'USERNAME',
|
||||||
info: 'You can use a-z, 0-9 and underscores.',
|
info: 'You can use a-z, 0-9 and underscores.',
|
||||||
info2: 'Minimum length is 3 characters.',
|
info2: 'Minimum length is ${minLength} characters.',
|
||||||
},
|
},
|
||||||
changePassword: {
|
changePassword: {
|
||||||
currentPassword: 'CURRENT PASSWORD',
|
currentPassword: 'CURRENT PASSWORD',
|
||||||
newPassword: 'NEW PASSWORD',
|
newPassword: 'NEW PASSWORD',
|
||||||
repeatNewPassword: 'REPEAT NEW PASSWORD',
|
repeatNewPassword: 'REPEAT NEW PASSWORD',
|
||||||
info: 'Make sure it’s 8 characters or more.',
|
info: 'Make sure it’s ${minLength} characters or more.',
|
||||||
info2: 'You will be logged out after changing your password.',
|
info2: 'You will be logged out after changing your password.',
|
||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {useSelector} from 'react-redux';
|
||||||
import {Map} from '@pages/map/map';
|
import {Map} from '@pages/map/map';
|
||||||
|
|
||||||
import {EventID} from '@components/map/types';
|
import {EventID} from '@components/map/types';
|
||||||
|
import EventPage from '@pages/event/EventPage';
|
||||||
|
|
||||||
export const MapTabName = 'Map';
|
export const MapTabName = 'Map';
|
||||||
|
|
||||||
|
@ -47,13 +48,20 @@ function MapTab() {
|
||||||
/>
|
/>
|
||||||
<MapStack.Screen
|
<MapStack.Screen
|
||||||
name="Event"
|
name="Event"
|
||||||
options={{headerShown: true}}
|
options={{
|
||||||
component={MapScreen}
|
animation: 'slide_from_right',
|
||||||
|
title: 'lang',
|
||||||
|
headerShown: true,
|
||||||
|
headerStyle: headerStyle,
|
||||||
|
headerShadowVisible: false,
|
||||||
|
headerTitleAlign: 'center',
|
||||||
|
}}
|
||||||
|
component={EventPage}
|
||||||
/>
|
/>
|
||||||
<MapStack.Screen
|
<MapStack.Screen
|
||||||
name="EventStore"
|
name="EventStore"
|
||||||
options={{headerShown: true}}
|
options={{headerShown: true}}
|
||||||
component={MapScreen}
|
component={EventPage}
|
||||||
/>
|
/>
|
||||||
</MapStack.Navigator>
|
</MapStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {Text} from '@gluestack-ui/themed';
|
||||||
|
|
||||||
|
function EventPage() {
|
||||||
|
return <Text>EventPage</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventPage;
|
|
@ -5,7 +5,8 @@ import React, {useState} from 'react'; // Add useState import
|
||||||
|
|
||||||
import DisplayMarkerList from '@components/map/DisplayMarkerList';
|
import DisplayMarkerList from '@components/map/DisplayMarkerList';
|
||||||
|
|
||||||
import getLocationData, {PA_Point} from '@components/map/cluster/getData';
|
import getLocationData from '@components/map/cluster/getData';
|
||||||
|
import {PA_Point} from '@components/map/types';
|
||||||
import {store} from '@redux/store';
|
import {store} from '@redux/store';
|
||||||
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
|
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
|
||||||
import {Position} from '@rnmapbox/maps/src/types/Position';
|
import {Position} from '@rnmapbox/maps/src/types/Position';
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
navigateToHome,
|
navigateToHome,
|
||||||
} from '@navigation/registration/registration';
|
} from '@navigation/registration/registration';
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
import {RootState} from '@redux/store';
|
import {RootState, store} from '@redux/store';
|
||||||
import MyUserManager from '@user/MyUserManager';
|
import MyUserManager from '@user/MyUserManager';
|
||||||
import {useState} from 'react';
|
import {useState} from 'react';
|
||||||
import {View} from 'react-native';
|
import {View} from 'react-native';
|
||||||
|
@ -60,8 +60,6 @@ export function Login() {
|
||||||
text={lang.buttonLogin}
|
text={lang.buttonLogin}
|
||||||
style={{marginBottom: 20}}
|
style={{marginBottom: 20}}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
console.log('login');
|
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
makeRequest({
|
makeRequest({
|
||||||
|
@ -95,13 +93,18 @@ export function Login() {
|
||||||
console.log('reason', reason);
|
console.log('reason', reason);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
|
let text =
|
||||||
|
store.getState().appVariables.lang.registration
|
||||||
|
.signUpStepAccountName.signUpError[
|
||||||
|
reason.status as number
|
||||||
|
];
|
||||||
|
|
||||||
showToast(toast, {
|
showToast(toast, {
|
||||||
title: 'Failed',
|
title: 'Failed',
|
||||||
variant: 'solid',
|
variant: 'solid',
|
||||||
action: 'error',
|
action: 'error',
|
||||||
description: undefined,
|
description: text,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
rest: {colorScheme: 'primary'},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
import {MyButton} from '@components/MyButton';
|
import {MyButton, MyIconButton} from '@components/MyButton';
|
||||||
import {MyIconInput} from '@components/MyInput';
|
import {MyIcon} from '@components/MyIcon';
|
||||||
|
import {MyIconInput, MyInputError} from '@components/MyInput';
|
||||||
import {MyScreenContainer} from '@components/MyScreenContainer';
|
import {MyScreenContainer} from '@components/MyScreenContainer';
|
||||||
import {MyTitle} from '@components/MyTitle';
|
import {MyTitle} from '@components/MyTitle';
|
||||||
|
import showToast from '@components/MyToast';
|
||||||
import {appVarActions} from '@configs/appVarReducer';
|
import {appVarActions} from '@configs/appVarReducer';
|
||||||
|
import {
|
||||||
|
accountNameOptions,
|
||||||
|
passwordOptions,
|
||||||
|
userNameOptions,
|
||||||
|
} from '@configs/types';
|
||||||
|
import {Spinner, set, useToast} from '@gluestack-ui/themed';
|
||||||
import {ToBase64} from '@helper/base64';
|
import {ToBase64} from '@helper/base64';
|
||||||
import {apiBackendRequest, makeRequest} from '@helper/request';
|
import {apiBackendRequest, makeRequest} from '@helper/request';
|
||||||
import {RootScreenNavigationProp} from '@navigation/navigation';
|
import {RootScreenNavigationProp} from '@navigation/navigation';
|
||||||
|
@ -13,10 +21,11 @@ import {
|
||||||
import {useNavigation} from '@react-navigation/native';
|
import {useNavigation} from '@react-navigation/native';
|
||||||
import {RootState, store} from '@redux/store';
|
import {RootState, store} from '@redux/store';
|
||||||
import MyUserManager from '@user/MyUserManager';
|
import MyUserManager from '@user/MyUserManager';
|
||||||
import {useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {Text} from 'react-native';
|
import {Text} from 'react-native';
|
||||||
import {View} from 'react-native';
|
import {View} from 'react-native';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
|
import reactStringReplace from 'react-string-replace';
|
||||||
|
|
||||||
function Title({text, description}: {text: string; description?: string}) {
|
function Title({text, description}: {text: string; description?: string}) {
|
||||||
return (
|
return (
|
||||||
|
@ -38,6 +47,17 @@ export function SignUpStepUsername() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
|
const [inputTouched, setInputTouched] = useState(false);
|
||||||
|
|
||||||
|
const usernameValid = username.length < userNameOptions.minLength;
|
||||||
|
|
||||||
|
const errorText = reactStringReplace(
|
||||||
|
lang.signUpStepUsername.error,
|
||||||
|
'${minLength}',
|
||||||
|
(match, i) => {
|
||||||
|
return userNameOptions.minLength.toString();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyScreenContainer
|
<MyScreenContainer
|
||||||
|
@ -55,13 +75,22 @@ export function SignUpStepUsername() {
|
||||||
text={lang.signUpStepUsername.inputUsername}
|
text={lang.signUpStepUsername.inputUsername}
|
||||||
iconName="person"
|
iconName="person"
|
||||||
value={username}
|
value={username}
|
||||||
onChangeText={text => setUsername(text)}
|
onChangeText={text => {
|
||||||
|
setUsername(text);
|
||||||
|
setInputTouched(true);
|
||||||
|
}}
|
||||||
|
maxLength={userNameOptions.maxLength}
|
||||||
|
helper={
|
||||||
|
inputTouched &&
|
||||||
|
usernameValid && <MyInputError text={errorText.join('')} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MyButton
|
<MyButton
|
||||||
type="secondary"
|
type="secondary"
|
||||||
text={lang.buttonNext}
|
text={lang.buttonNext}
|
||||||
style={{marginBottom: 20}}
|
style={{marginBottom: 20}}
|
||||||
|
disabled={username.length < userNameOptions.minLength}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
let rp = {...registerProcess};
|
let rp = {...registerProcess};
|
||||||
|
|
||||||
|
@ -173,6 +202,49 @@ export function SignUpStepPassword() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
const [inputTouched, setInputTouched] = useState(false);
|
||||||
|
|
||||||
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => {
|
||||||
|
setIsPasswordVisible(!isPasswordVisible);
|
||||||
|
};
|
||||||
|
|
||||||
|
const passwordValid = () => {
|
||||||
|
if (password.length < passwordOptions.minLength) {
|
||||||
|
return false;
|
||||||
|
} else if (!passwordOptions.isAllowed(password)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const descriptionText = reactStringReplace(
|
||||||
|
lang.signUpStepPassword.description,
|
||||||
|
'${minLength}',
|
||||||
|
() => {
|
||||||
|
return passwordOptions.minLength.toString();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const errorLengthText = reactStringReplace(
|
||||||
|
lang.signUpStepPassword.errorLength,
|
||||||
|
'${minLength}',
|
||||||
|
() => {
|
||||||
|
return passwordOptions.minLength.toString();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const errorText = () => {
|
||||||
|
if (password.length < passwordOptions.minLength) {
|
||||||
|
return errorLengthText.join('');
|
||||||
|
} else if (!passwordOptions.isAllowed(password)) {
|
||||||
|
return lang.signUpStepPassword.errorPasswordInvalid;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyScreenContainer
|
<MyScreenContainer
|
||||||
|
@ -182,22 +254,40 @@ export function SignUpStepPassword() {
|
||||||
<ContentContainer>
|
<ContentContainer>
|
||||||
<Title
|
<Title
|
||||||
text={lang.signUpStepPassword.title}
|
text={lang.signUpStepPassword.title}
|
||||||
description={lang.signUpStepPassword.description}
|
description={descriptionText.join('')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={{gap: 12, marginTop: 20}}>
|
<View style={{gap: 12, marginTop: 20}}>
|
||||||
<MyIconInput
|
<MyIconInput
|
||||||
text={lang.signUpStepPassword.inputPassword}
|
text={lang.signUpStepPassword.inputPassword}
|
||||||
iconName="lock"
|
iconName="lock"
|
||||||
secureTextEntry
|
secureTextEntry={!isPasswordVisible}
|
||||||
value={password}
|
value={password}
|
||||||
onChangeText={text => setPassword(text)}
|
maxLength={passwordOptions.maxLength}
|
||||||
|
onChangeText={text => {
|
||||||
|
setPassword(text);
|
||||||
|
setInputTouched(true);
|
||||||
|
}}
|
||||||
|
rightComponent={
|
||||||
|
<MyIconButton
|
||||||
|
MyIconProps={{
|
||||||
|
name: isPasswordVisible ? 'visibility-off' : 'visibility',
|
||||||
|
size: 24,
|
||||||
|
}}
|
||||||
|
onPress={togglePasswordVisibility}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
helper={
|
||||||
|
inputTouched &&
|
||||||
|
!passwordValid() && <MyInputError text={errorText()} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MyButton
|
<MyButton
|
||||||
type="secondary"
|
type="secondary"
|
||||||
text={lang.buttonNext}
|
text={lang.buttonNext}
|
||||||
style={{marginBottom: 2}}
|
style={{marginBottom: 2}}
|
||||||
|
disabled={!passwordValid()}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
let rp = {...registerProcess};
|
let rp = {...registerProcess};
|
||||||
|
|
||||||
|
@ -216,11 +306,22 @@ export function SignUpStepPassword() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AccountNameAvailable {
|
||||||
|
Loading,
|
||||||
|
Available,
|
||||||
|
NotAvailable,
|
||||||
|
}
|
||||||
|
|
||||||
export function SignUpStepAccountName() {
|
export function SignUpStepAccountName() {
|
||||||
const lang = useSelector(
|
const lang = useSelector(
|
||||||
(state: RootState) => state.appVariables.lang.registration,
|
(state: RootState) => state.appVariables.lang.registration,
|
||||||
);
|
);
|
||||||
|
const currentTheme = useSelector(
|
||||||
|
(state: RootState) => state.nonSaveVariables.theme.colors,
|
||||||
|
);
|
||||||
|
|
||||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
const registerProcess = useSelector(
|
const registerProcess = useSelector(
|
||||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||||
|
@ -228,6 +329,72 @@ export function SignUpStepAccountName() {
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [accountName, setAccountName] = useState('');
|
const [accountName, setAccountName] = useState('');
|
||||||
|
const [isAccountNameAvailable, setIsAccountNameAvailable] = useState(
|
||||||
|
AccountNameAvailable.Loading,
|
||||||
|
);
|
||||||
|
const [inputTouched, setInputTouched] = useState(false);
|
||||||
|
|
||||||
|
const accountNameValid = () => {
|
||||||
|
if (accountName.length < accountNameOptions.minLength) {
|
||||||
|
return false;
|
||||||
|
} else if (!accountNameOptions.isAllowed(accountName)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorText = () => {
|
||||||
|
if (accountName.length < accountNameOptions.minLength) {
|
||||||
|
return reactStringReplace(
|
||||||
|
lang.signUpStepAccountName.errorLength,
|
||||||
|
'${minLength}',
|
||||||
|
() => {
|
||||||
|
return accountNameOptions.minLength.toString();
|
||||||
|
},
|
||||||
|
).join('');
|
||||||
|
} else if (!accountNameOptions.isAllowed(accountName)) {
|
||||||
|
return lang.signUpStepAccountName.errorAccountNameInvalid;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rightComponent = () => {
|
||||||
|
const closeIcon = (
|
||||||
|
<MyIcon name="close" size={24} color={currentTheme.red600} />
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!accountNameValid()) {
|
||||||
|
return closeIcon;
|
||||||
|
} else if (isAccountNameAvailable === AccountNameAvailable.Loading) {
|
||||||
|
return <Spinner />;
|
||||||
|
} else if (isAccountNameAvailable === AccountNameAvailable.Available) {
|
||||||
|
return <MyIcon name="check" size={24} color={currentTheme.green400} />;
|
||||||
|
} else {
|
||||||
|
return closeIcon;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!accountNameValid()) return;
|
||||||
|
|
||||||
|
const delay = 400;
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
makeRequest({
|
||||||
|
path: apiBackendRequest.CHECK_ACCOUNT_NAME,
|
||||||
|
requestGET: {':accountName': accountName},
|
||||||
|
response: {},
|
||||||
|
})
|
||||||
|
.then(() => setIsAccountNameAvailable(AccountNameAvailable.Available))
|
||||||
|
.catch(() =>
|
||||||
|
setIsAccountNameAvailable(AccountNameAvailable.NotAvailable),
|
||||||
|
);
|
||||||
|
}, delay);
|
||||||
|
|
||||||
|
// Cleanup the timeout on component unmount or when inputValue changes
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
|
}, [accountName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyScreenContainer
|
<MyScreenContainer
|
||||||
|
@ -246,7 +413,17 @@ export function SignUpStepAccountName() {
|
||||||
text={lang.signUpStepAccountName.inputAccountName}
|
text={lang.signUpStepAccountName.inputAccountName}
|
||||||
iconName="person"
|
iconName="person"
|
||||||
value={accountName}
|
value={accountName}
|
||||||
onChangeText={text => setAccountName(text)}
|
onChangeText={text => {
|
||||||
|
setAccountName(text);
|
||||||
|
setInputTouched(true);
|
||||||
|
setIsAccountNameAvailable(AccountNameAvailable.Loading);
|
||||||
|
}}
|
||||||
|
maxLength={accountNameOptions.maxLength}
|
||||||
|
rightComponent={rightComponent()}
|
||||||
|
helper={
|
||||||
|
inputTouched &&
|
||||||
|
!accountNameValid() && <MyInputError text={errorText()} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MyButton
|
<MyButton
|
||||||
|
@ -254,15 +431,19 @@ export function SignUpStepAccountName() {
|
||||||
text={lang.signUpStepAccountName.buttonGetStarted}
|
text={lang.signUpStepAccountName.buttonGetStarted}
|
||||||
style={{marginBottom: 2}}
|
style={{marginBottom: 2}}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
disabled={
|
||||||
|
!accountNameValid() ||
|
||||||
|
isAccountNameAvailable !== AccountNameAvailable.Available
|
||||||
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
let rp = {...registerProcess};
|
let rp = {...registerProcess};
|
||||||
|
|
||||||
rp.AccountName = accountName;
|
rp.AccountName = accountName;
|
||||||
|
|
||||||
store.dispatch(appVarActions.setRegisterProcess(rp));
|
store.dispatch(appVarActions.setRegisterProcess(rp));
|
||||||
|
|
||||||
console.log('registerProcess', rp);
|
|
||||||
|
|
||||||
makeRequest({
|
makeRequest({
|
||||||
path: apiBackendRequest.SIGN_UP,
|
path: apiBackendRequest.SIGN_UP,
|
||||||
request: rp,
|
request: rp,
|
||||||
|
@ -273,8 +454,6 @@ export function SignUpStepAccountName() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
console.log('response', resp);
|
|
||||||
|
|
||||||
MyUserManager.createNewMyUser(
|
MyUserManager.createNewMyUser(
|
||||||
accountName,
|
accountName,
|
||||||
resp.response.Username,
|
resp.response.Username,
|
||||||
|
@ -288,8 +467,27 @@ export function SignUpStepAccountName() {
|
||||||
console.log('catch', err);
|
console.log('catch', err);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(reason => {
|
||||||
console.log('error', error);
|
console.log('error', reason);
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
|
||||||
|
let text =
|
||||||
|
store.getState().appVariables.lang.registration
|
||||||
|
.signUpStepAccountName.signUpError[
|
||||||
|
reason.status as number
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('text', text, reason.status);
|
||||||
|
|
||||||
|
showToast(toast, {
|
||||||
|
title: 'Failed',
|
||||||
|
variant: 'solid',
|
||||||
|
action: 'error',
|
||||||
|
description: text,
|
||||||
|
isClosable: true,
|
||||||
|
//rest: {colorScheme: 'primary'},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -65,7 +65,8 @@ async function getUser(
|
||||||
usr[usrDBKeys.ProfilePictureBinaryLQ].byteLength !== 0
|
usr[usrDBKeys.ProfilePictureBinaryLQ].byteLength !== 0
|
||||||
? createUserProp(
|
? createUserProp(
|
||||||
SourceProp.offline,
|
SourceProp.offline,
|
||||||
new Blob([usr[usrDBKeys.ProfilePictureBinaryLQ]]),
|
Buffer.from(usr[usrDBKeys.ProfilePictureBinaryLQ]),
|
||||||
|
//new Blob([usr[usrDBKeys.ProfilePictureBinaryLQ]]),
|
||||||
)
|
)
|
||||||
: createUserProp(
|
: createUserProp(
|
||||||
SourceProp.online,
|
SourceProp.online,
|
||||||
|
@ -76,7 +77,8 @@ async function getUser(
|
||||||
usr[usrDBKeys.ProfilePictureBinaryHQ].byteLength !== 0
|
usr[usrDBKeys.ProfilePictureBinaryHQ].byteLength !== 0
|
||||||
? createUserProp(
|
? createUserProp(
|
||||||
SourceProp.offline,
|
SourceProp.offline,
|
||||||
new Blob([usr[usrDBKeys.ProfilePictureBinaryHQ]]),
|
Buffer.from(usr[usrDBKeys.ProfilePictureBinaryHQ]),
|
||||||
|
//new Blob([usr[usrDBKeys.ProfilePictureBinaryHQ]]),
|
||||||
)
|
)
|
||||||
: createUserProp(
|
: createUserProp(
|
||||||
SourceProp.online,
|
SourceProp.online,
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
// ... other configs, if any
|
// ... other configs, if any
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react-native",
|
||||||
|
"strict": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@redux/*": ["src/redux/*"],
|
"@redux/*": ["src/redux/*"],
|
||||||
"@lang/*": ["src/lang/*"],
|
"@lang/*": ["src/lang/*"],
|
||||||
|
@ -18,7 +23,8 @@
|
||||||
"@navigation/*": ["src/navigation/*"],
|
"@navigation/*": ["src/navigation/*"],
|
||||||
"@configs/*": ["src/configs/*"],
|
"@configs/*": ["src/configs/*"],
|
||||||
"@helper/*": ["src/helper/*"],
|
"@helper/*": ["src/helper/*"],
|
||||||
"@user/*": ["src/user/*"]
|
"@user/*": ["src/user/*"],
|
||||||
|
"@event/*": ["src/event/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue