finshed sign up
parent
aebc1e4b79
commit
51b02065ee
|
@ -5,10 +5,20 @@ module.exports = {
|
|||
[
|
||||
'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: ['.'],
|
||||
alias: {
|
||||
"@redux": "./src/redux",
|
||||
'@redux': './src/redux',
|
||||
'@lang': './src/lang',
|
||||
'@pages': './src/pages',
|
||||
'@api': './src/api',
|
||||
|
@ -20,7 +30,8 @@ module.exports = {
|
|||
'@navigation': './src/navigation',
|
||||
'@configs': './src/configs',
|
||||
'@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-vector-icons": "^10.0.2",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"realm": "^12.3.1",
|
||||
"redux": "^4.2.1"
|
||||
},
|
||||
|
@ -19494,6 +19495,14 @@
|
|||
"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": {
|
||||
"version": "18.2.0",
|
||||
"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-vector-icons": "^10.0.2",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-string-replace": "^1.1.1",
|
||||
"realm": "^12.3.1",
|
||||
"redux": "^4.2.1"
|
||||
},
|
||||
|
|
|
@ -11,6 +11,8 @@ import configDarkTheme from '@configs/colors';
|
|||
import Navigation from '@navigation/navigation';
|
||||
import {MyStatusBar} from '@components/MyStatusBar';
|
||||
import {SafeAreaView} from 'react-native';
|
||||
import {appVarActions} from '@configs/appVarReducer';
|
||||
import {saveVarChanges} from '@helper/appData';
|
||||
|
||||
const App = () => {
|
||||
useEffect(() => {
|
||||
|
@ -52,7 +54,7 @@ const OtherProviders = () => {
|
|||
|
||||
return (
|
||||
<NavigationContainer theme={navigationTheme}>
|
||||
<GluestackUIProvider config={configDarkTheme}>
|
||||
<GluestackUIProvider config={themeConfig}>
|
||||
<MainComponent />
|
||||
</GluestackUIProvider>
|
||||
</NavigationContainer>
|
||||
|
|
|
@ -120,6 +120,7 @@ export function MyButton({
|
|||
alignItems: 'center',
|
||||
padding: 10,
|
||||
borderRadius: 10,
|
||||
opacity: disabled ? currentTheme.opacity[60] : 1,
|
||||
}}>
|
||||
<ButtonText />
|
||||
</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 {useSelector} from 'react-redux';
|
||||
import {RootState} from '@redux/store';
|
||||
import {Text} from '@gluestack-ui/themed';
|
||||
import {useRef, useState} from 'react';
|
||||
import {ReactNode, useRef, useState} from 'react';
|
||||
|
||||
interface MyIconInputProps {
|
||||
text: string;
|
||||
|
@ -13,6 +19,9 @@ interface MyIconInputProps {
|
|||
value?: string;
|
||||
onChangeText?: (text: string) => void;
|
||||
disableContainer?: boolean;
|
||||
maxLength?: number;
|
||||
rightComponent?: ReactNode;
|
||||
helper?: ReactNode;
|
||||
}
|
||||
|
||||
export function MyIconInput({
|
||||
|
@ -23,43 +32,107 @@ export function MyIconInput({
|
|||
value,
|
||||
onChangeText,
|
||||
disableContainer,
|
||||
maxLength,
|
||||
rightComponent,
|
||||
helper,
|
||||
}: MyIconInputProps) {
|
||||
const currentTheme = useSelector(
|
||||
(state: RootState) => state.nonSaveVariables.theme.colors,
|
||||
);
|
||||
|
||||
const [password, setPassword] = useState('');
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
|
||||
const togglePasswordVisibility = () => {
|
||||
setIsPasswordVisible(!isPasswordVisible);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
!disableContainer && {
|
||||
backgroundColor: currentTheme.backgroundDark300,
|
||||
borderRadius: 10,
|
||||
},
|
||||
]}>
|
||||
<View
|
||||
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>
|
||||
<Text size="sm" color={currentTheme.textLight200}>
|
||||
{text}
|
||||
</Text>
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: currentTheme.backgroundDark400,
|
||||
borderRadius: 10,
|
||||
}}>
|
||||
<TextInput
|
||||
style={{
|
||||
backgroundColor: currentTheme.backgroundDark400,
|
||||
flex: 1,
|
||||
height: 40,
|
||||
borderRadius: 10,
|
||||
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 style={{paddingLeft: 10, paddingRight: 20}}>{helper}</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export interface MyInputErrorProps {
|
||||
iconName?: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export function MyInputError({
|
||||
iconName,
|
||||
text,
|
||||
}: MyInputErrorProps): React.ReactElement<MyInputErrorProps> {
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {Text} from '@gluestack-ui/themed';
|
||||
import {VStack} 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 = useToast();
|
||||
const ToastDetails = [
|
||||
|
@ -37,7 +47,7 @@ const Toast = () => {
|
|||
description: 'Please enter a valid email address',
|
||||
},
|
||||
];
|
||||
};
|
||||
}; */
|
||||
|
||||
interface toastType {
|
||||
id?: string;
|
||||
|
@ -46,7 +56,7 @@ interface toastType {
|
|||
title: any;
|
||||
description: any;
|
||||
isClosable?: boolean;
|
||||
rest: any;
|
||||
rest?: any;
|
||||
}
|
||||
|
||||
interface alertType extends toastType {
|
||||
|
@ -64,9 +74,10 @@ function showToast(toast: any, item: toastType) {
|
|||
rest,
|
||||
}: alertType) => (
|
||||
<Alert
|
||||
maxWidth="95%"
|
||||
alignSelf="center"
|
||||
flexDirection="row"
|
||||
// maxWidth="95%"
|
||||
//alignSelf="center"
|
||||
//flexDirection="row"
|
||||
bg="$secondary600"
|
||||
action={action}
|
||||
variant={variant}
|
||||
{...rest}>
|
||||
|
@ -85,15 +96,17 @@ function showToast(toast: any, item: toastType) {
|
|||
? '$white'
|
||||
: variant !== 'outline'
|
||||
? '$black'
|
||||
: null
|
||||
: '$white'
|
||||
}>
|
||||
{title}
|
||||
</Text>
|
||||
</HStack>
|
||||
{isClosable ? (
|
||||
<MyButton
|
||||
type="secondary"
|
||||
text="X"
|
||||
<MyIconButton
|
||||
MyIconProps={{
|
||||
name: 'close',
|
||||
size: 24,
|
||||
}}
|
||||
onPress={() => toast.close(id)}
|
||||
/>
|
||||
) : /*<IconButton
|
||||
|
@ -113,7 +126,7 @@ function showToast(toast: any, item: toastType) {
|
|||
? '$white'
|
||||
: variant !== 'outline'
|
||||
? '$black'
|
||||
: null
|
||||
: '$white'
|
||||
}>
|
||||
{description}
|
||||
</Text>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
type EventID = string;
|
||||
import { EventID, EventType } from '@event/types';
|
||||
|
||||
|
||||
|
||||
interface BasicEvent {
|
||||
id: EventID;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
type: EventType | "cluster";
|
||||
}
|
||||
|
||||
interface PA_Point_Cluster extends BasicEvent {
|
||||
|
|
|
@ -6,12 +6,15 @@ import {AccountName} from './types';
|
|||
|
||||
import {getVersionByNum, VersionType} from '@helper/version';
|
||||
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 AppVarMaxBackups: number = 10;
|
||||
export const maxCachedUsers = 30;
|
||||
export const maxCachedEvents = 30;
|
||||
|
||||
export enum appStatus {
|
||||
IS_LOADING,
|
||||
|
@ -31,6 +34,7 @@ export interface NON_SAVE_VARS {
|
|||
theme: ThemeTokensType;
|
||||
connectionStatus: connectionStatus;
|
||||
cachedUsers: {[key: AccountName]: User};
|
||||
cachedEvents: {[key: EventID]: PAEvent};
|
||||
chats: {[key: roomId]: chatEntity};
|
||||
chatActivity: roomId[];
|
||||
selectedChat: roomId | 'none';
|
||||
|
@ -43,6 +47,7 @@ export const non_save_vars: NON_SAVE_VARS = {
|
|||
theme: configDarkTheme.tokens,
|
||||
connectionStatus: connectionStatus.UNKNOWN,
|
||||
cachedUsers: {},
|
||||
cachedEvents: {},
|
||||
chats: {},
|
||||
chatActivity: [],
|
||||
selectedChat: 'none',
|
||||
|
|
|
@ -5,8 +5,9 @@ import {ThemeTokensType} from '@configs/colors';
|
|||
|
||||
import {chatEntity, roomId} from '@configs/chat/types';
|
||||
import {User} from '@user/types';
|
||||
import {AccountName} from './types';
|
||||
import {PA_Point} from '@components/map/cluster/getData';
|
||||
import {AccountName, EventId} from './types';
|
||||
import {PA_Point} from '@components/map/types';
|
||||
import {PAEvent} from '@event/types';
|
||||
|
||||
export const appNonSaveVariablesSlice = createSlice({
|
||||
name: 'non_save_vars',
|
||||
|
@ -24,6 +25,13 @@ export const appNonSaveVariablesSlice = createSlice({
|
|||
removeCachedUser: (state, action: PayloadAction<AccountName>) => {
|
||||
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>) => {
|
||||
state.selectedChat = action.payload;
|
||||
},
|
||||
|
|
|
@ -17,11 +17,14 @@ export type XAuthorization = string;
|
|||
//export type UserId = string;
|
||||
//export type WebSocketSessionId = string;
|
||||
|
||||
export type EventId = string;
|
||||
|
||||
export const accountNameOptions = {
|
||||
minLength: 4,
|
||||
maxLength: 24,
|
||||
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,
|
||||
minBits: 50,
|
||||
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',
|
||||
LOGOUT = '/user/logout',
|
||||
SIGN_UP = '/user/signup',
|
||||
CHECK_ACCOUNT_NAME = '/user/check/:accountName',
|
||||
/*REGISTER_STEP_1 = '/admin/users/email',
|
||||
REGISTER_RESEND_MAIL = '/admin/users/email/resend',
|
||||
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 {
|
||||
path: apiBackendRequest.APP_START;
|
||||
|
||||
|
@ -180,7 +190,8 @@ type FetchTypes =
|
|||
| LOGIN*/
|
||||
| GET_USER_PROFILE
|
||||
| APP_START
|
||||
| LOGOUT;
|
||||
| LOGOUT
|
||||
| CHECK_ACCOUNT_NAME;
|
||||
/*
|
||||
function isA(obj: any): obj is REGISTER_STEP_1 {
|
||||
return obj.request !== undefined;
|
||||
|
|
|
@ -40,6 +40,10 @@ export const DBMigration: {[key in databaseNames]: any} = {
|
|||
chatRoomInfos: (Schema: typeof DBSchemas.chatRoomInfos) => {
|
||||
const callback: MigrationCallback = (oldRealm, newRealm) => {};
|
||||
|
||||
return callback;
|
||||
},events: (Schema: typeof DBSchemas.events) => {
|
||||
const callback: MigrationCallback = (oldRealm, newRealm) => {};
|
||||
|
||||
return callback;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import chat from './schemas/chat';
|
||||
import users from './schemas/users';
|
||||
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 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 {filterParam} from './get';
|
||||
|
||||
export type databaseNames = 'users' | 'chat' | 'chatRoomInfos';
|
||||
export type databaseNames = 'users' | 'chat' | 'chatRoomInfos' | 'events';
|
||||
export type possibleDBKeys = string;
|
||||
|
||||
export interface databaseConf<props, enums> {
|
||||
|
@ -31,10 +31,10 @@ export interface databaseNameSuffix {
|
|||
export function mergeDBName(nameObj: databaseNameSuffix, web?: 'web'): string {
|
||||
if (web === 'web') {
|
||||
return nameObj.suffix === undefined
|
||||
? nameObj.name + '-' + MyUserManager.getSelectedUserId()
|
||||
? nameObj.name + '-' + MyUserManager.getSelectedUserAccount()
|
||||
: nameObj.name +
|
||||
'-' +
|
||||
MyUserManager.getSelectedUserId() +
|
||||
MyUserManager.getSelectedUserAccount() +
|
||||
('_' + nameObj.suffix);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ export default interface LangFormat {
|
|||
title: string;
|
||||
description: string;
|
||||
inputUsername: string;
|
||||
error: string;
|
||||
};
|
||||
signUpStepPhoneNumber: {
|
||||
title: string;
|
||||
|
@ -79,12 +80,17 @@ export default interface LangFormat {
|
|||
title: string;
|
||||
description: string;
|
||||
inputPassword: string;
|
||||
errorLength: string;
|
||||
errorPasswordInvalid: string;
|
||||
};
|
||||
signUpStepAccountName: {
|
||||
title: string;
|
||||
description: string;
|
||||
inputAccountName: string;
|
||||
buttonGetStarted: string;
|
||||
signUpError: {[key: number]: string};
|
||||
errorLength: string;
|
||||
errorAccountNameInvalid: string;
|
||||
};
|
||||
};
|
||||
profile: {
|
||||
|
|
|
@ -65,6 +65,7 @@ export const lang: LangFormat = {
|
|||
title: "Let's get started, what's your name?",
|
||||
description: 'The name will be displayed on your profil overview',
|
||||
inputUsername: 'Username',
|
||||
error: 'At least ${minLength} characters are required',
|
||||
},
|
||||
signUpStepPhoneNumber: {
|
||||
title: 'Create your account using your phone number',
|
||||
|
@ -77,8 +78,11 @@ export const lang: LangFormat = {
|
|||
},
|
||||
signUpStepPassword: {
|
||||
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',
|
||||
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: {
|
||||
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.',
|
||||
inputAccountName: 'ACCOUNT NAME',
|
||||
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: {
|
||||
|
@ -119,13 +132,13 @@ export const lang: LangFormat = {
|
|||
changeUsername: {
|
||||
username: 'USERNAME',
|
||||
info: 'You can use a-z, 0-9 and underscores.',
|
||||
info2: 'Minimum length is 3 characters.',
|
||||
info2: 'Minimum length is ${minLength} characters.',
|
||||
},
|
||||
changePassword: {
|
||||
currentPassword: 'CURRENT PASSWORD',
|
||||
newPassword: '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.',
|
||||
},
|
||||
help: {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {useSelector} from 'react-redux';
|
|||
import {Map} from '@pages/map/map';
|
||||
|
||||
import {EventID} from '@components/map/types';
|
||||
import EventPage from '@pages/event/EventPage';
|
||||
|
||||
export const MapTabName = 'Map';
|
||||
|
||||
|
@ -47,13 +48,20 @@ function MapTab() {
|
|||
/>
|
||||
<MapStack.Screen
|
||||
name="Event"
|
||||
options={{headerShown: true}}
|
||||
component={MapScreen}
|
||||
options={{
|
||||
animation: 'slide_from_right',
|
||||
title: 'lang',
|
||||
headerShown: true,
|
||||
headerStyle: headerStyle,
|
||||
headerShadowVisible: false,
|
||||
headerTitleAlign: 'center',
|
||||
}}
|
||||
component={EventPage}
|
||||
/>
|
||||
<MapStack.Screen
|
||||
name="EventStore"
|
||||
options={{headerShown: true}}
|
||||
component={MapScreen}
|
||||
component={EventPage}
|
||||
/>
|
||||
</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 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 {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
|
||||
import {Position} from '@rnmapbox/maps/src/types/Position';
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
navigateToHome,
|
||||
} from '@navigation/registration/registration';
|
||||
import {useNavigation} from '@react-navigation/native';
|
||||
import {RootState} from '@redux/store';
|
||||
import {RootState, store} from '@redux/store';
|
||||
import MyUserManager from '@user/MyUserManager';
|
||||
import {useState} from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
@ -60,8 +60,6 @@ export function Login() {
|
|||
text={lang.buttonLogin}
|
||||
style={{marginBottom: 20}}
|
||||
onPress={() => {
|
||||
console.log('login');
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
makeRequest({
|
||||
|
@ -95,13 +93,18 @@ export function Login() {
|
|||
console.log('reason', reason);
|
||||
setIsLoading(false);
|
||||
|
||||
let text =
|
||||
store.getState().appVariables.lang.registration
|
||||
.signUpStepAccountName.signUpError[
|
||||
reason.status as number
|
||||
];
|
||||
|
||||
showToast(toast, {
|
||||
title: 'Failed',
|
||||
variant: 'solid',
|
||||
action: 'error',
|
||||
description: undefined,
|
||||
description: text,
|
||||
isClosable: true,
|
||||
rest: {colorScheme: 'primary'},
|
||||
});
|
||||
});
|
||||
}}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import {MyButton} from '@components/MyButton';
|
||||
import {MyIconInput} from '@components/MyInput';
|
||||
import {MyButton, MyIconButton} from '@components/MyButton';
|
||||
import {MyIcon} from '@components/MyIcon';
|
||||
import {MyIconInput, MyInputError} from '@components/MyInput';
|
||||
import {MyScreenContainer} from '@components/MyScreenContainer';
|
||||
import {MyTitle} from '@components/MyTitle';
|
||||
import showToast from '@components/MyToast';
|
||||
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 {apiBackendRequest, makeRequest} from '@helper/request';
|
||||
import {RootScreenNavigationProp} from '@navigation/navigation';
|
||||
|
@ -13,10 +21,11 @@ import {
|
|||
import {useNavigation} from '@react-navigation/native';
|
||||
import {RootState, store} from '@redux/store';
|
||||
import MyUserManager from '@user/MyUserManager';
|
||||
import {useState} from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {Text} from 'react-native';
|
||||
import {View} from 'react-native';
|
||||
import {useSelector} from 'react-redux';
|
||||
import reactStringReplace from 'react-string-replace';
|
||||
|
||||
function Title({text, description}: {text: string; description?: string}) {
|
||||
return (
|
||||
|
@ -38,6 +47,17 @@ export function SignUpStepUsername() {
|
|||
);
|
||||
|
||||
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 (
|
||||
<MyScreenContainer
|
||||
|
@ -55,13 +75,22 @@ export function SignUpStepUsername() {
|
|||
text={lang.signUpStepUsername.inputUsername}
|
||||
iconName="person"
|
||||
value={username}
|
||||
onChangeText={text => setUsername(text)}
|
||||
onChangeText={text => {
|
||||
setUsername(text);
|
||||
setInputTouched(true);
|
||||
}}
|
||||
maxLength={userNameOptions.maxLength}
|
||||
helper={
|
||||
inputTouched &&
|
||||
usernameValid && <MyInputError text={errorText.join('')} />
|
||||
}
|
||||
/>
|
||||
|
||||
<MyButton
|
||||
type="secondary"
|
||||
text={lang.buttonNext}
|
||||
style={{marginBottom: 20}}
|
||||
disabled={username.length < userNameOptions.minLength}
|
||||
onPress={() => {
|
||||
let rp = {...registerProcess};
|
||||
|
||||
|
@ -173,6 +202,49 @@ export function SignUpStepPassword() {
|
|||
);
|
||||
|
||||
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 (
|
||||
<MyScreenContainer
|
||||
|
@ -182,22 +254,40 @@ export function SignUpStepPassword() {
|
|||
<ContentContainer>
|
||||
<Title
|
||||
text={lang.signUpStepPassword.title}
|
||||
description={lang.signUpStepPassword.description}
|
||||
description={descriptionText.join('')}
|
||||
/>
|
||||
|
||||
<View style={{gap: 12, marginTop: 20}}>
|
||||
<MyIconInput
|
||||
text={lang.signUpStepPassword.inputPassword}
|
||||
iconName="lock"
|
||||
secureTextEntry
|
||||
secureTextEntry={!isPasswordVisible}
|
||||
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
|
||||
type="secondary"
|
||||
text={lang.buttonNext}
|
||||
style={{marginBottom: 2}}
|
||||
disabled={!passwordValid()}
|
||||
onPress={() => {
|
||||
let rp = {...registerProcess};
|
||||
|
||||
|
@ -216,11 +306,22 @@ export function SignUpStepPassword() {
|
|||
);
|
||||
}
|
||||
|
||||
enum AccountNameAvailable {
|
||||
Loading,
|
||||
Available,
|
||||
NotAvailable,
|
||||
}
|
||||
|
||||
export function SignUpStepAccountName() {
|
||||
const lang = useSelector(
|
||||
(state: RootState) => state.appVariables.lang.registration,
|
||||
);
|
||||
const currentTheme = useSelector(
|
||||
(state: RootState) => state.nonSaveVariables.theme.colors,
|
||||
);
|
||||
|
||||
const navigation = useNavigation<RootScreenNavigationProp>();
|
||||
const toast = useToast();
|
||||
|
||||
const registerProcess = useSelector(
|
||||
(state: RootState) => state.appVariables.preferences.RegisterProcess,
|
||||
|
@ -228,6 +329,72 @@ export function SignUpStepAccountName() {
|
|||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
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 (
|
||||
<MyScreenContainer
|
||||
|
@ -246,7 +413,17 @@ export function SignUpStepAccountName() {
|
|||
text={lang.signUpStepAccountName.inputAccountName}
|
||||
iconName="person"
|
||||
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
|
||||
|
@ -254,15 +431,19 @@ export function SignUpStepAccountName() {
|
|||
text={lang.signUpStepAccountName.buttonGetStarted}
|
||||
style={{marginBottom: 2}}
|
||||
isLoading={isLoading}
|
||||
disabled={
|
||||
!accountNameValid() ||
|
||||
isAccountNameAvailable !== AccountNameAvailable.Available
|
||||
}
|
||||
onPress={() => {
|
||||
setIsLoading(true);
|
||||
|
||||
let rp = {...registerProcess};
|
||||
|
||||
rp.AccountName = accountName;
|
||||
|
||||
store.dispatch(appVarActions.setRegisterProcess(rp));
|
||||
|
||||
console.log('registerProcess', rp);
|
||||
|
||||
makeRequest({
|
||||
path: apiBackendRequest.SIGN_UP,
|
||||
request: rp,
|
||||
|
@ -273,8 +454,6 @@ export function SignUpStepAccountName() {
|
|||
},
|
||||
})
|
||||
.then(resp => {
|
||||
console.log('response', resp);
|
||||
|
||||
MyUserManager.createNewMyUser(
|
||||
accountName,
|
||||
resp.response.Username,
|
||||
|
@ -288,8 +467,27 @@ export function SignUpStepAccountName() {
|
|||
console.log('catch', err);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('error', error);
|
||||
.catch(reason => {
|
||||
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
|
||||
? createUserProp(
|
||||
SourceProp.offline,
|
||||
new Blob([usr[usrDBKeys.ProfilePictureBinaryLQ]]),
|
||||
Buffer.from(usr[usrDBKeys.ProfilePictureBinaryLQ]),
|
||||
//new Blob([usr[usrDBKeys.ProfilePictureBinaryLQ]]),
|
||||
)
|
||||
: createUserProp(
|
||||
SourceProp.online,
|
||||
|
@ -76,7 +77,8 @@ async function getUser(
|
|||
usr[usrDBKeys.ProfilePictureBinaryHQ].byteLength !== 0
|
||||
? createUserProp(
|
||||
SourceProp.offline,
|
||||
new Blob([usr[usrDBKeys.ProfilePictureBinaryHQ]]),
|
||||
Buffer.from(usr[usrDBKeys.ProfilePictureBinaryHQ]),
|
||||
//new Blob([usr[usrDBKeys.ProfilePictureBinaryHQ]]),
|
||||
)
|
||||
: createUserProp(
|
||||
SourceProp.online,
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
// ... other configs, if any
|
||||
"baseUrl": ".",
|
||||
"target": "ESNext",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react-native",
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"@redux/*": ["src/redux/*"],
|
||||
"@lang/*": ["src/lang/*"],
|
||||
|
@ -18,7 +23,8 @@
|
|||
"@navigation/*": ["src/navigation/*"],
|
||||
"@configs/*": ["src/configs/*"],
|
||||
"@helper/*": ["src/helper/*"],
|
||||
"@user/*": ["src/user/*"]
|
||||
"@user/*": ["src/user/*"],
|
||||
"@event/*": ["src/event/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue