appstart, create event, common inputs

master
Netcup Gituser 2023-12-17 22:53:39 +01:00
parent 00045692ad
commit cc569a8187
22 changed files with 815 additions and 343 deletions

View File

@ -0,0 +1,259 @@
import {useEffect, useState} from 'react';
import {MyIconInput, MyInputError} from './MyInput';
import {useSelector} from 'react-redux';
import {RootState} from '@redux/store';
import {
accountNameOptions,
passwordOptions,
userNameOptions,
} from '@configs/types';
import reactStringReplace from 'react-string-replace';
import {MyIconButton} from './MyButton';
import {MyIcon} from './MyIcon';
import {Spinner} from '@gluestack-ui/themed';
import {apiBackendRequest, makeRequest} from '@helper/request';
export function usernameValid(username: string) {
if (username.length < userNameOptions.minLength) {
return false;
} else if (!userNameOptions.isAllowed(username)) {
return false;
} else {
return true;
}
}
interface MyUsernameInputProps {
username: string;
setUsername: (username: string) => void;
}
export function MyUsernameInput(props: MyUsernameInputProps) {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.commonInputs.username,
);
const [inputTouched, setInputTouched] = useState(false);
const errorText = () => {
if (props.username.length < userNameOptions.minLength) {
return reactStringReplace(
lang.errorLength,
'${minLength}',
(match, i) => {
return userNameOptions.minLength.toString();
},
).join('');
} else if (!userNameOptions.isAllowed(props.username)) {
return lang.errorUsernameInvalid;
}
return '';
};
return (
<MyIconInput
iconName="person"
text={lang.label}
value={props.username}
onChangeText={text => {
props.setUsername(text);
setInputTouched(true);
}}
maxLength={userNameOptions.maxLength}
helper={
inputTouched &&
!usernameValid(props.username) && <MyInputError text={errorText()} />
}
/>
);
}
export function passwordValid(password: string) {
if (password.length < passwordOptions.minLength) {
return false;
} else if (!passwordOptions.isAllowed(password)) {
return false;
} else {
return true;
}
}
interface MyPasswordInputProps {
password: string;
setPassword: (password: string) => void;
disableContainer?: boolean;
}
export function MyPasswordInput(props: MyPasswordInputProps) {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.commonInputs.password,
);
const [inputTouched, setInputTouched] = useState(false);
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const togglePasswordVisibility = () =>
setIsPasswordVisible(!isPasswordVisible);
const errorText = () => {
if (props.password.length < passwordOptions.minLength) {
return reactStringReplace(lang.errorLength, '${minLength}', () => {
return passwordOptions.minLength.toString();
}).join('');
} else if (!passwordOptions.isAllowed(props.password)) {
return lang.errorPasswordInvalid;
} else {
return '';
}
};
return (
<MyIconInput
text={lang.label}
iconName="lock"
secureTextEntry={!isPasswordVisible}
value={props.password}
maxLength={passwordOptions.maxLength}
onChangeText={text => {
props.setPassword(text);
setInputTouched(true);
}}
rightComponent={
<MyIconButton
MyIconProps={{
name: isPasswordVisible ? 'visibility-off' : 'visibility',
size: 24,
}}
onPress={togglePasswordVisibility}
/>
}
helper={
inputTouched &&
!passwordValid(props.password) && <MyInputError text={errorText()} />
}
disableContainer={props.disableContainer}
/>
);
}
export function accountNameValid(accountName: string) {
if (accountName.length < accountNameOptions.minLength) {
return false;
} else if (!accountNameOptions.isAllowed(accountName)) {
return false;
} else {
return true;
}
}
export enum AccountNameAvailable {
Loading,
Available,
NotAvailable,
}
interface MyAccountNameInputProps {
accountName: string;
setAccountName: (accountName: string) => void;
checkAccountNameAvailability?: boolean;
isAccountNameAvailable?: AccountNameAvailable;
setIsAccountNameAvailable?: (
isAccountNameAvailable: AccountNameAvailable,
) => void;
}
export function MyAccountNameInput(props: MyAccountNameInputProps) {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.commonInputs.accountName,
);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
const [inputTouched, setInputTouched] = useState(false);
const rightComponent = () => {
const closeIcon = (
<MyIcon name="close" size={24} color={currentTheme.red600} />
);
if (!accountNameValid(props.accountName)) {
return closeIcon;
} else if (props.isAccountNameAvailable === AccountNameAvailable.Loading) {
return <Spinner />;
} else if (
props.isAccountNameAvailable === AccountNameAvailable.Available
) {
return <MyIcon name="check" size={24} color={currentTheme.green400} />;
} else {
return closeIcon;
}
};
const errorText = () => {
if (props.accountName.length < accountNameOptions.minLength) {
return reactStringReplace(lang.errorLength, '${minLength}', () => {
return accountNameOptions.minLength.toString();
}).join('');
} else if (!accountNameOptions.isAllowed(props.accountName)) {
return lang.errorAccountNameInvalid;
}
return '';
};
if (props.checkAccountNameAvailability) {
useEffect(() => {
if (!accountNameValid(props.accountName)) return;
const delay = 400;
const timeoutId = setTimeout(() => {
makeRequest({
path: apiBackendRequest.CHECK_ACCOUNT_NAME,
requestGET: {':accountName': props.accountName},
response: {},
})
.then(() => {
if (props.checkAccountNameAvailability) {
if (props.setIsAccountNameAvailable) {
props.setIsAccountNameAvailable(AccountNameAvailable.Available);
}
}
})
.catch(() => {
if (props.setIsAccountNameAvailable) {
props.setIsAccountNameAvailable(
AccountNameAvailable.NotAvailable,
);
}
});
}, delay);
// Cleanup the timeout on component unmount or when inputValue changes
return () => clearTimeout(timeoutId);
}, [props.accountName]);
}
return (
<MyIconInput
text={lang.label}
iconName="person"
value={props.accountName}
onChangeText={text => {
props.setAccountName(text);
setInputTouched(true);
if (props.setIsAccountNameAvailable) {
props.setIsAccountNameAvailable(AccountNameAvailable.Loading);
}
}}
maxLength={accountNameOptions.maxLength}
rightComponent={props.checkAccountNameAvailability && rightComponent()}
helper={
inputTouched &&
!accountNameValid(props.accountName) && (
<MyInputError text={errorText()} />
)
}
/>
);
}

View File

@ -2,14 +2,12 @@
import {chatEntity, roomId} from '@configs/chat/types';
import {User} from '@user/types';
import {AccountName} from './types';
import {UserId} 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 {EventID, PAEvent} from '@event/types';
import {PA_Point} from '@components/map/types';
export const APP_VERSION = getVersionByNum(1);
export const AppVarMaxBackups: number = 10;
@ -33,7 +31,7 @@ export interface NON_SAVE_VARS {
appStatus: appStatus;
theme: ThemeTokensType;
connectionStatus: connectionStatus;
cachedUsers: {[key: AccountName]: User};
cachedUsers: {[key: UserId]: User};
cachedEvents: {[key: EventID]: PAEvent};
chats: {[key: roomId]: chatEntity};
chatActivity: roomId[];

View File

@ -5,7 +5,7 @@ import {ThemeTokensType} from '@configs/colors';
import {chatEntity, roomId} from '@configs/chat/types';
import {SourceProp, User} from '@user/types';
import {AccountName, EventId} from './types';
import {EventId, UserId} from './types';
import {PA_Point} from '@components/map/types';
import {PAEvent, createEventProp} from '@event/types';
@ -20,9 +20,9 @@ export const appNonSaveVariablesSlice = createSlice({
state.theme = action.payload;
},
setCachedUser: (state, action: PayloadAction<User>) => {
state.cachedUsers[action.payload.AccountName] = action.payload;
state.cachedUsers[action.payload.UserId] = action.payload;
},
removeCachedUser: (state, action: PayloadAction<AccountName>) => {
removeCachedUser: (state, action: PayloadAction<UserId>) => {
delete state.cachedUsers[action.payload];
},
setCachedEvent: (state, action: PayloadAction<PAEvent>) => {
@ -31,10 +31,16 @@ export const appNonSaveVariablesSlice = createSlice({
removeCachedEvent: (state, action: PayloadAction<EventId>) => {
delete state.cachedEvents[action.payload];
},
setJoinedEvent: (state, action: PayloadAction<{id: EventId, isJoined: number}>) => {
state.cachedEvents[action.payload.id].isJoined = createEventProp(SourceProp.cached, action.payload.isJoined);
setJoinedEvent: (
state,
action: PayloadAction<{id: EventId; isJoined: number}>,
) => {
state.cachedEvents[action.payload.id].isJoined = createEventProp(
SourceProp.cached,
action.payload.isJoined,
);
},
setSelectedChat: (state, action: PayloadAction<roomId>) => {
state.selectedChat = action.payload;
},

View File

@ -1,4 +1,4 @@
import {XToken, AccountName, Username} from '@configs/types';
import {AccountName, Username, UserId} from '@configs/types';
import {VersionType} from '@helper/version';
import {MyUserAccount} from '@user/types';
import {APP_VERSION} from './appNonSaveVar';
@ -53,8 +53,8 @@ export interface PREFERENCES_VARS {
version: VersionType;
theme: ThemeMode;
RegisterProcess: RegisterProcess;
selectedAccount: AccountName | 'none';
accounts: {[key: AccountName]: MyUserAccount};
selectedAccount: UserId | 'none';
accounts: {[key: UserId]: MyUserAccount};
}
export const preferences_vars_default: PREFERENCES_VARS = {

View File

@ -7,7 +7,7 @@ import {
} from './appVar';
import LangFormat from '@lang/default';
import {lang as defaultLang} from '@lang/en';
import {AccountName} from './types';
import {UserId} from './types';
import {MyUserAccount} from '@user/types';
import {ThemeMode} from './colors';
@ -21,6 +21,17 @@ const initialState: appVariablesState = {
lang: defaultLang,
};
/*
store.dispatch(
appVarActions.setAccountName({
name: response.response.accountName,
uuid: "",
})
)
to save changes:
helper/appData.ts
*/
export const appVariablesSlice = createSlice({
name: 'appVariables',
initialState,
@ -37,11 +48,25 @@ export const appVariablesSlice = createSlice({
setRegisterProcess: (state, action: PayloadAction<RegisterProcess>) => {
state.preferences.RegisterProcess = action.payload;
},
setCurrentAccount: (state, action: PayloadAction<AccountName>) => {
setCurrentAccount: (state, action: PayloadAction<UserId>) => {
state.preferences.selectedAccount = action.payload;
},
setAccount: (state, action: PayloadAction<MyUserAccount>) => {
state.preferences.accounts[action.payload.AccountName] = action.payload;
state.preferences.accounts[action.payload.UserId] = action.payload;
},
setAccountName: (
state,
action: PayloadAction<{userId: string; name: string}>,
) => {
state.preferences.accounts[action.payload.userId].AccountName.data =
action.payload.name;
},
setUsername: (
state,
action: PayloadAction<{userId: string; username: string}>,
) => {
state.preferences.accounts[action.payload.userId].Username.data =
action.payload.username;
},
setDBEK: (state, action: PayloadAction<number>) => {
state.preferences.dbek = action.payload;

View File

@ -14,7 +14,7 @@ export type XToken = string;
//export type verifyId = string;
export type XAuthorization = string;
//export type UserId = string;
export type UserId = string;
//export type WebSocketSessionId = string;
export type EventId = string;
@ -32,7 +32,8 @@ export const userNameOptions = {
minLength: 2,
maxLength: 24,
isAllowed: (text: string): boolean => {
return true;
// 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;
},
};

View File

@ -7,7 +7,6 @@ import {AppVarMaxBackups, APP_VERSION} from '@configs/appNonSaveVar';
import {appVarActions} from '@configs/appVarReducer';
import {store} from '@redux/store';
import {getData, setData} from './storage/appData';
import {getVersionByType, stepUpVersionCalc} from './version';
const APP_CHANGE_BACKUP = 'appVerChangeBackup';

View File

@ -8,6 +8,7 @@ import {
Password,
XAuthorization,
Username,
UserId,
} from '../configs/types';
import MyUserManager from '@user/MyUserManager';
@ -20,7 +21,7 @@ export const apiPath = {
export enum apiBackendRequest {
LOGIN = '/user/login',
APP_START = '/user',
GET_USER_PROFILE = '/users/:accountName',
GET_USER_PROFILE = '/users/:userId',
LOGOUT = '/user/logout',
SIGN_UP = '/user/signup',
CHECK_ACCOUNT_NAME = '/user/check/:accountName',
@ -112,6 +113,7 @@ interface LOGIN extends defaultRequest {
};
response?: {
XAuthorization: XAuthorization;
UserId: UserId;
Username: Username;
};
}
@ -125,6 +127,7 @@ interface SIGN_UP extends defaultRequest {
};
response?: {
XAuthorization: XAuthorization;
UserId: UserId;
Username: Username;
};
}
@ -133,7 +136,7 @@ interface GET_USER_PROFILE extends defaultRequest {
path: apiBackendRequest.GET_USER_PROFILE;
requestGET: {
':accountName': AccountName;
':userId': UserId;
};
response: {
//AccountName: AccountName;

View File

@ -13,6 +13,28 @@ export default interface LangFormat {
join: string;
quit: string;
};
eventCreateion: {
newEventTitle: string;
newPublicEventTitle: string;
newPrivateEventTitle: string;
name: string;
uploadImage: string;
description: string;
location: string;
create: string;
website: string;
datetime: string;
openingHoursFlag: string;
};
days: {
monday: string;
tuesday: string;
wednesday: string;
thursday: string;
friday: string;
saturday: string;
sunday: string;
};
navigation: {
home: {
profile: {
@ -41,6 +63,23 @@ export default interface LangFormat {
info: string;
error: string;
success: string;
commonInputs: {
username: {
label: string;
errorLength: string;
errorUsernameInvalid: string;
};
accountName: {
label: string;
errorLength: string;
errorAccountNameInvalid: string;
};
password: {
label: string;
errorLength: string;
errorPasswordInvalid: string;
};
};
registration: {
buttonLogin: string;
buttonSignUp: string;
@ -71,12 +110,9 @@ export default interface LangFormat {
signUpStepUsername: {
title: string;
description: string;
inputUsername: string;
error: string;
};
signUpStepPhoneNumber: {
title: string;
inputPhoneNumber: string;
};
signUpStepVerifyPhoneNumber: {
title: string;
@ -86,9 +122,6 @@ export default interface LangFormat {
signUpStepPassword: {
title: string;
description: string;
inputPassword: string;
errorLength: string;
errorPasswordInvalid: string;
};
signUpStepAccountName: {
title: string;
@ -96,8 +129,6 @@ export default interface LangFormat {
inputAccountName: string;
buttonGetStarted: string;
signUpError: {[key: number]: string};
errorLength: string;
errorAccountNameInvalid: string;
};
};
profile: {

View File

@ -12,6 +12,28 @@ export const lang: LangFormat = {
join: 'Join',
quit: 'Quit',
},
eventCreateion: {
newEventTitle: 'New Event',
newPublicEventTitle: 'New Public Event',
newPrivateEventTitle: 'New Private Event',
name: 'Name',
uploadImage: 'Upload banner',
description: 'Description',
location: 'Location',
create: 'Create',
website: 'Website',
datetime: 'Date & Time',
openingHoursFlag: 'Opening hours / days',
},
days: {
monday: 'Monday',
tuesday: 'Tuesday',
wednesday: 'Wednesday',
thursday: 'Thursday',
friday: 'Friday',
saturday: 'Saturday',
sunday: 'Sunday',
},
appName: 'Party App',
navigation: {
home: {
@ -41,6 +63,26 @@ export const lang: LangFormat = {
info: 'Info',
error: 'Error',
success: 'Success',
commonInputs: {
username: {
label: 'USERNAME',
errorLength: 'At least ${minLength} characters are required',
errorUsernameInvalid:
'Username can only contain a-z, 0-9, underscores and dots',
},
accountName: {
label: 'ACCOUNT NAME',
errorLength: 'Account name must be at least ${minLength} characters long',
errorAccountNameInvalid:
'Account name can only contain a-z, 0-9, underscores and dots',
},
password: {
label: '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',
},
},
registration: {
buttonLogin: 'Login',
buttonSignUp: 'Sign up',
@ -71,12 +113,9 @@ export const lang: LangFormat = {
signUpStepUsername: {
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',
inputPhoneNumber: 'PHONE NUMBER',
},
signUpStepVerifyPhoneNumber: {
title: 'We sent you a code',
@ -86,10 +125,6 @@ export const lang: LangFormat = {
signUpStepPassword: {
title: "You'll need a password",
description: 'Make sure its ${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',
@ -97,9 +132,6 @@ 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',

View File

@ -2,6 +2,7 @@ import Stack from '@pages/globalStackManager';
import {
NavigatorScreenParams,
getFocusedRouteNameFromRoute,
useNavigation,
} from '@react-navigation/native';
import {
BottomTabBarProps,
@ -16,7 +17,7 @@ import ProfileTab, {
ProfileStackNavigatorParamList,
} from './tabs/main/ProfileTab';
import {FadeInView} from '@helper/animations';
import {Animated, AppState, View} from 'react-native';
import {Animated, View} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import {
RegistrationScreenAnim,
@ -29,8 +30,9 @@ import {RootState, store as reduxStore} from '@redux/store';
import {Text} from '@gluestack-ui/themed';
import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import {useEffect, useRef} from 'react';
import {animated, useSpring} from '@react-spring/native';
import {apiBackendRequest, makeRequest} from '@helper/request';
import {appVarActions} from '@configs/appVarReducer';
import MyUserManager from '@user/MyUserManager';
export type RootStackNavigatorParamList = {
Home: NavigatorScreenParams<HomeStackNavigatorParamList>;
@ -44,9 +46,13 @@ export default function Navigation() {
const currentUser =
reduxStore.getState().appVariables.preferences.selectedAccount;
const rootNavigation = useNavigation<RootScreenNavigationProp>();
useEffect(() => {
console.log('APP NAVIGATION');
if (currentUser === 'none') return;
makeRequest({
path: apiBackendRequest.APP_START,
response: {
@ -65,9 +71,31 @@ export default function Navigation() {
Events: {},
TokenValid: false, */
},
}).then(response => {
console.log(response);
});
})
.then(resp => {
if (resp.response.accountName !== undefined) {
reduxStore.dispatch(
appVarActions.setAccountName({
userId: currentUser,
name: resp.response.accountName,
}),
);
}
if (resp.response.username !== undefined) {
reduxStore.dispatch(
appVarActions.setUsername({
userId: currentUser,
username: resp.response.username,
}),
);
}
})
.catch(err => {
if (err.status === 401) {
MyUserManager.logoutMyUser(rootNavigation);
}
});
}, []);
return (

View File

@ -12,6 +12,10 @@ import {Map} from '@pages/map/map';
import {EventID} from '@components/map/types';
import EventPage from '@pages/event/EventPage';
import CreateEventPage, {
EventCreationNavigatorParamList,
} from '@pages/event/CreateEventNavigation';
import {NavigatorScreenParams} from '@react-navigation/native';
export const MapTabName = 'Map';
@ -19,6 +23,7 @@ export type MapStackNavigatorParamList = {
Overview: undefined;
Event: {eventID: EventID};
EventStore: {eventID: EventID};
CreateEvent: NavigatorScreenParams<EventCreationNavigatorParamList>;
};
const MapStack = createNativeStackNavigator<MapStackNavigatorParamList>();
@ -27,9 +32,7 @@ export type MapScreenNavigationProp =
NativeStackNavigationProp<MapStackNavigatorParamList>;
function MapTab() {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.navigation.home.map,
);
const lang = useSelector((state: RootState) => state.appVariables.lang);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
@ -63,6 +66,13 @@ function MapTab() {
options={{headerShown: true}}
component={EventPage}
/>
<MapStack.Screen
name="CreateEvent"
options={{
headerShown: false,
}}
component={CreateEventPage}
/>
</MapStack.Navigator>
);
}

View File

@ -18,8 +18,6 @@ const events: ArrayLike<any> | null | undefined = [];
for (let i = 1; i <= 100; i++) {
const randomUrlIndex = Math.floor(Math.random() * baseUrls.length);
// hello world
events.push({
id: i,
url: baseUrls[randomUrlIndex],

View File

@ -0,0 +1,57 @@
import {MyIconButton} from '@components/MyButton';
import {MyScreenContainer} from '@components/MyScreenContainer';
import {ButtonText, VStack, HStack} from '@gluestack-ui/themed';
import {RootState} from '@redux/store';
import {View, Text, Button, Pressable} from 'react-native';
import {useSelector} from 'react-redux';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
function Overview() {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.eventCreateion,
);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
return (
<MyScreenContainer>
<VStack space={'sm'} alignItems="center">
<EventType MyIconProps={{name: 'public', size: 28}} />
<EventType MyIconProps={{name: 'public', size: 28}} />
</VStack>
</MyScreenContainer>
);
}
function EventType({
MyIconProps,
}: {
MyIconProps: {name: string; size: number; color?: string};
}) {
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
return (
<Pressable
style={{
width: '100%',
height: 50,
backgroundColor: currentTheme.backgroundDark300,
borderRadius: 10,
}}>
<HStack>
<MaterialIcon
name={MyIconProps.name}
size={MyIconProps.size}
color={MyIconProps.color}
/>
<Text>Event Type</Text>
</HStack>
</Pressable>
);
}
export default Overview;

View File

@ -0,0 +1,53 @@
import {MapStackNavigatorParamList} from '@navigation/tabs/main/MapTab';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from '@react-navigation/native-stack';
import {RootState} from '@redux/store';
import {View, Text} from 'react-native';
import {useSelector} from 'react-redux';
import EventPage from './EventPage';
import Overview from './CreateEvent/Overview';
export type EventCreationNavigatorParamList = {
Overview: undefined;
PublicEvent: undefined;
PrivateEvent: undefined;
};
const EventCreationStack =
createNativeStackNavigator<MapStackNavigatorParamList>();
export type EventCreationScreenNavigationProp =
NativeStackNavigationProp<MapStackNavigatorParamList>;
function CreateEventPage() {
const lang = useSelector((state: RootState) => state.appVariables.lang);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
const headerStyle = {
backgroundColor: currentTheme.backgroundDark400,
};
return (
<EventCreationStack.Navigator initialRouteName="Overview">
<EventCreationStack.Screen
name="Overview"
options={{
animation: 'simple_push',
title: lang.eventCreateion.newEventTitle,
headerShown: true,
headerStyle: headerStyle,
headerShadowVisible: false,
headerTitleAlign: 'center',
}}
component={Overview}
/>
</EventCreationStack.Navigator>
);
}
export default CreateEventPage;

View File

@ -1,4 +1,4 @@
import {StyleSheet, View, Text, Image} from 'react-native';
import {StyleSheet, View, Text, Image, ViewStyle} from 'react-native';
import Mapbox, {MarkerView} from '@rnmapbox/maps';
import React, {useState} from 'react'; // Add useState import
@ -11,14 +11,17 @@ import {RootState, store} from '@redux/store';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {Position} from '@rnmapbox/maps/src/types/Position';
import {Dimensions} from 'react-native';
import {Dimensions, Pressable} from 'react-native';
import {MyIconButton} from '@components/MyButton';
import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import {MyIcon} from '@components/MyIcon';
import {useSelector} from 'react-redux';
import {Button, ButtonIcon} from '@gluestack-ui/themed';
import LinearGradient from 'react-native-linear-gradient';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {MapScreenNavigationProp} from '@navigation/tabs/main/MapTab';
import {useNavigation} from '@react-navigation/native';
Mapbox.setAccessToken(
'pk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGgzZGJxMDAwbHQyaXA2N3BtOWUxbWkifQ.x-f8JJxwQHWmPFI3P6Qn-w',
@ -27,6 +30,11 @@ Mapbox.setAccessToken(
let lastCameraChange = 0;
let isRerenderData = 0;
const IconButtonSize = 28;
const IconButtonCircleSize = IconButtonSize * 1.75;
const IconMarginRight = 15;
export const Map = () => {
const mapRef = React.useRef<Mapbox.MapView | null>(null);
//const [mapMarkers, setMapMarkers] = useState<PA_Point[]>([]); // Add useState for visibleBounds
@ -36,6 +44,8 @@ export const Map = () => {
(state: RootState) => state.nonSaveVariables.theme.colors,
);
const navigation = useNavigation<MapScreenNavigationProp>();
const getVisibleBounds = async () => {
// return when lastDataRerender is 300ms ago
const now = Date.now();
@ -132,16 +142,63 @@ export const Map = () => {
attributionPosition={{top: 10, left: 100}}
logoPosition={{top: 10, left: 10}}
styleURL="mapbox://styles/titaniumbach/clpij5uoo00o301pg2dj23j0m"
projection="globe">
projection="globe"
compassPosition={{top: 10, right: IconMarginRight}}
compassImage={'mycompassimagekey'}>
<DisplayMarkerList />
</Mapbox.MapView>
<MapIconButton
MyIconProps={{
name: 'close',
size: 24,
name: 'group',
size: IconButtonSize,
backgroundColor: colors.backgroundDark300,
}}
onPress={() => {}}
style={{
top: 70,
right: IconMarginRight,
}}
/>
<MapIconButton
MyIconProps={{
name: 'tune',
size: IconButtonSize,
backgroundColor: colors.backgroundDark300,
}}
onPress={() => {}}
style={{
top: 70 + IconButtonCircleSize + 10,
right: IconMarginRight,
}}
/>
<MapIconButton
MyIconProps={{
name: 'add',
size: IconButtonSize,
color: colors.backgroundLight300,
}}
onPress={() => {
navigation.navigate('CreateEvent', {screen: 'Overview'});
}}
style={{
bottom: 90 + 10 + IconButtonCircleSize,
right: IconMarginRight,
}}
type="primary"
/>
<MapIconButton
MyIconProps={{
name: 'search',
size: IconButtonSize,
backgroundColor: colors.backgroundDark300,
}}
onPress={() => {}}
style={{
bottom: 90,
right: IconMarginRight,
}}
/>
</View>
</View>
@ -166,6 +223,8 @@ const styles = StyleSheet.create({
function MapIconButton({
onPress,
MyIconProps,
style,
type,
}: {
onPress: () => void;
MyIconProps: {
@ -174,18 +233,73 @@ function MapIconButton({
size: number;
backgroundColor?: string;
};
style?: ViewStyle;
type?: 'primary' | 'secondary';
}) {
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme,
);
const viewStyle: ViewStyle = {
position: 'absolute',
width: IconButtonCircleSize,
height: IconButtonCircleSize,
borderRadius: MyIconProps.size, // This will make the View circular
justifyContent: 'center', // Center the icon vertically
alignItems: 'center', // Center the icon horizontally
elevation: 5,
overflow: 'hidden',
...style,
};
const pressableStyle: ViewStyle = {
width: '100%',
height: '100%',
justifyContent: 'center', // Center the icon vertically
alignItems: 'center', // Center the icon horizontally
};
if (type === 'primary') {
return (
<LinearGradient
colors={[
currentTheme.colors.secondary200,
currentTheme.colors.primary400,
]}
start={{x: 0, y: 1}}
end={{x: 1, y: 0}}
style={{elevation: 50, ...viewStyle}}>
<Pressable
android_ripple={{color: '#3e00a8'}}
style={pressableStyle}
onPress={onPress}>
<MaterialIcon
name={MyIconProps.name}
size={MyIconProps.size}
color={MyIconProps.color}
/>
</Pressable>
</LinearGradient>
);
}
return (
<View
style={{
position: 'absolute',
top: 60,
right: 10,
width: MyIconProps.size * 2,
height: MyIconProps.size * 2,
backgroundColor: MyIconProps.backgroundColor, // Set the background color
...viewStyle,
}}>
<MaterialIcon.Button name="facebook" backgroundColor="#3b5998" />
<Pressable
android_ripple={{color: '#fff2', foreground: false}}
style={pressableStyle}
onPress={onPress}>
<MaterialIcon
name={MyIconProps.name}
size={MyIconProps.size}
color={MyIconProps.color}
/>
</Pressable>
</View>
);
}

View File

@ -7,7 +7,7 @@ import {MyVerticalDivider} from '@components/MyDivider';
import {MyIcon} from '@components/MyIcon';
import {useNavigation} from '@react-navigation/native';
import {ProfileScreenNavigationProp} from '@navigation/tabs/main/ProfileTab';
import {MyIconInput} from '@components/MyInput';
import {MyIconInput, MyInputError} from '@components/MyInput';
import {useEffect, useState} from 'react';
import {RootScreenNavigationProp} from '@navigation/navigation';
import {useSelector} from 'react-redux';
@ -19,6 +19,12 @@ import MyUserManager from '@user/MyUserManager';
import {apiBackendRequest, makeRequest} from '@helper/request';
import reactStringReplace from 'react-string-replace';
import {passwordOptions, userNameOptions} from '@configs/types';
import {
MyPasswordInput,
MyUsernameInput,
passwordValid,
usernameValid,
} from '@components/MyCommonInputs';
function UserAvatar() {
return (
@ -137,7 +143,9 @@ export function ProfileSettings() {
<SettingsItem
icon="person"
title={lang.accountData.accountName}
value={user.AccountName === undefined ? '' : user.AccountName}
value={
user.AccountName.data === undefined ? '' : user.AccountName.data
}
/>
<SettingsItem
@ -182,12 +190,7 @@ export function ProfileSettings() {
console.log('logout failed, err: ', reason);
});
MyUserManager.logoutMyUser();
rootNavigation.navigate('Registration', {screen: 'LoginPreview'});
rootNavigation.reset({
index: 0,
routes: [{name: 'Registration'}],
});
MyUserManager.logoutMyUser(rootNavigation);
}}
/>
</SettingsItemContainer>
@ -256,9 +259,7 @@ function SettingsItem({icon, title, value, onPress}: SettingsItemProps) {
}
export function UpdateUsername() {
const lang = useSelector(
(state: RootState) => state.appVariables.lang.profile.settings,
);
const lang = useSelector((state: RootState) => state.appVariables.lang);
const currentTheme = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
@ -273,7 +274,7 @@ export function UpdateUsername() {
const [newUsername, setNewUsername] = useState(user.Username.data || '');
const info2Text = reactStringReplace(
lang.changeUsername.info2,
lang.profile.settings.changeUsername.info2,
'${minLength}',
() => userNameOptions.minLength.toString(),
);
@ -283,7 +284,7 @@ export function UpdateUsername() {
navigation.setOptions({
headerRight: () =>
changed ? (
changed && usernameValid(newUsername) ? (
<MyTouchableOpacity onPress={() => navigation.goBack()}>
<MyIcon
name="done"
@ -299,16 +300,14 @@ export function UpdateUsername() {
return (
<MyScreenContainer style={{paddingTop: 20}}>
<MyIconInput
iconName="person"
text={lang.changeUsername.username}
value={newUsername}
onChangeText={text => {
setNewUsername(text);
<MyUsernameInput
username={newUsername}
setUsername={value => {
setNewUsername(value);
}}
/>
<Text color={currentTheme.textLight100} style={{marginTop: 12}}>
{lang.changeUsername.info}
{lang.profile.settings.changeUsername.info}
</Text>
<Text color={currentTheme.textLight100}>{info2Text}</Text>
</MyScreenContainer>
@ -335,9 +334,9 @@ export function UpdatePassword() {
useEffect(() => {
const passwordChanged =
currentPassword.length > 0 &&
newPassword.length > 0 &&
repeatNewPassword.length > 0 &&
passwordValid(currentPassword) &&
passwordValid(newPassword) &&
passwordValid(repeatNewPassword) &&
newPassword === repeatNewPassword;
navigation.setOptions({
@ -363,28 +362,19 @@ export function UpdatePassword() {
backgroundColor: currentTheme.backgroundDark300,
marginTop: 4,
}}>
<MyIconInput
iconName="lock"
text={lang.currentPassword}
secureTextEntry={true}
value={currentPassword}
onChangeText={text => setCurrentPassword(text)}
<MyPasswordInput
password={currentPassword}
setPassword={value => setCurrentPassword(value)}
disableContainer
/>
<MyIconInput
iconName="lock"
text={lang.newPassword}
secureTextEntry={true}
value={newPassword}
onChangeText={text => setNewPassword(text)}
<MyPasswordInput
password={newPassword}
setPassword={value => setNewPassword(value)}
disableContainer
/>
<MyIconInput
iconName="lock"
text={lang.repeatNewPassword}
secureTextEntry={true}
value={repeatNewPassword}
onChangeText={text => setRepeatNewPassword(text)}
<MyPasswordInput
password={repeatNewPassword}
setPassword={value => setRepeatNewPassword(value)}
disableContainer
/>
</View>

View File

@ -1,5 +1,4 @@
import {MyButton} from '@components/MyButton';
import {MyIconInput} from '@components/MyInput';
import {MyScreenContainer} from '@components/MyScreenContainer';
import {MyTitle} from '@components/MyTitle';
import {apiBackendRequest, makeRequest} from '@helper/request';
@ -17,6 +16,12 @@ import {useSelector} from 'react-redux';
import {ToBase64} from '@helper/base64';
import showToast from '@components/MyToast';
import {useToast} from '@gluestack-ui/themed';
import {
MyAccountNameInput,
MyPasswordInput,
accountNameValid,
passwordValid,
} from '@components/MyCommonInputs';
export function Login() {
const lang = useSelector(
@ -29,7 +34,7 @@ export function Login() {
const [accountName, setAccountName] = useState('anna');
const [password, setPassword] = useState('testtesttest1#S');
const loginEnabled = accountName.length > 0 && password.length > 0;
const loginEnabled = accountNameValid(accountName) && passwordValid(password);
return (
<MyScreenContainer
@ -40,18 +45,14 @@ export function Login() {
<MyTitle text={lang.login.title} />
<View style={{gap: 12}}>
<MyIconInput
text={lang.login.inputPhoneNumberOrAccountName}
iconName="person"
value={accountName}
onChangeText={text => setAccountName(text)}
<MyAccountNameInput
accountName={accountName}
setAccountName={value => setAccountName(value)}
/>
<MyIconInput
text={lang.login.inputPassword}
iconName="lock"
secureTextEntry
value={password}
onChangeText={text => setPassword(text)}
<MyPasswordInput
password={password}
setPassword={value => setPassword(value)}
/>
<MyButton
@ -70,6 +71,7 @@ export function Login() {
},
response: {
XAuthorization: '',
UserId: '',
Username: '',
},
})
@ -77,6 +79,7 @@ export function Login() {
console.log('resp', resp);
MyUserManager.createNewMyUser(
resp.response.UserId,
accountName,
resp.response.Username,
resp.response.XAuthorization,

View File

@ -1,16 +1,19 @@
import {MyButton, MyIconButton} from '@components/MyButton';
import {MyIcon} from '@components/MyIcon';
import {MyIconInput, MyInputError} from '@components/MyInput';
import {MyButton} from '@components/MyButton';
import {
AccountNameAvailable,
MyAccountNameInput,
MyPasswordInput,
MyUsernameInput,
accountNameValid,
passwordValid,
usernameValid,
} from '@components/MyCommonInputs';
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 {passwordOptions} from '@configs/types';
import {useToast} from '@gluestack-ui/themed';
import {ToBase64} from '@helper/base64';
import {apiBackendRequest, makeRequest} from '@helper/request';
import {RootScreenNavigationProp} from '@navigation/navigation';
@ -21,7 +24,7 @@ import {
import {useNavigation} from '@react-navigation/native';
import {RootState, store} from '@redux/store';
import MyUserManager from '@user/MyUserManager';
import {useEffect, useState} from 'react';
import {useState} from 'react';
import {Text} from 'react-native';
import {View} from 'react-native';
import {useSelector} from 'react-redux';
@ -47,17 +50,6 @@ 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
@ -71,26 +63,16 @@ export function SignUpStepUsername() {
/>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput
text={lang.signUpStepUsername.inputUsername}
iconName="person"
value={username}
onChangeText={text => {
setUsername(text);
setInputTouched(true);
}}
maxLength={userNameOptions.maxLength}
helper={
inputTouched &&
usernameValid && <MyInputError text={errorText.join('')} />
}
<MyUsernameInput
username={username}
setUsername={value => setUsername(value)}
/>
<MyButton
type="secondary"
text={lang.buttonNext}
style={{marginBottom: 20}}
disabled={username.length < userNameOptions.minLength}
disabled={!usernameValid(username)}
onPress={() => {
let rp = {...registerProcess};
@ -202,23 +184,6 @@ 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,
@ -228,24 +193,6 @@ export function SignUpStepPassword() {
},
);
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
style={{
@ -258,36 +205,16 @@ export function SignUpStepPassword() {
/>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput
text={lang.signUpStepPassword.inputPassword}
iconName="lock"
secureTextEntry={!isPasswordVisible}
value={password}
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()} />
}
<MyPasswordInput
password={password}
setPassword={value => setPassword(value)}
/>
<MyButton
type="secondary"
text={lang.buttonNext}
style={{marginBottom: 2}}
disabled={!passwordValid()}
disabled={!passwordValid(password)}
onPress={() => {
let rp = {...registerProcess};
@ -306,19 +233,10 @@ 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();
@ -329,72 +247,10 @@ 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
@ -409,21 +265,14 @@ export function SignUpStepAccountName() {
/>
<View style={{gap: 12, marginTop: 20}}>
<MyIconInput
text={lang.signUpStepAccountName.inputAccountName}
iconName="person"
value={accountName}
onChangeText={text => {
setAccountName(text);
setInputTouched(true);
setIsAccountNameAvailable(AccountNameAvailable.Loading);
}}
maxLength={accountNameOptions.maxLength}
rightComponent={rightComponent()}
helper={
inputTouched &&
!accountNameValid() && <MyInputError text={errorText()} />
<MyAccountNameInput
accountName={accountName}
setAccountName={value => setAccountName(value)}
isAccountNameAvailable={isAccountNameAvailable}
setIsAccountNameAvailable={value =>
setIsAccountNameAvailable(value)
}
checkAccountNameAvailability
/>
<MyButton
@ -432,7 +281,7 @@ export function SignUpStepAccountName() {
style={{marginBottom: 2}}
isLoading={isLoading}
disabled={
!accountNameValid() ||
!accountNameValid(accountName) ||
isAccountNameAvailable !== AccountNameAvailable.Available
}
onPress={() => {
@ -450,11 +299,13 @@ export function SignUpStepAccountName() {
method: 'POST',
response: {
XAuthorization: '',
UserId: '',
Username: '',
},
})
.then(resp => {
MyUserManager.createNewMyUser(
resp.response.UserId,
accountName,
resp.response.Username,
resp.response.XAuthorization,

View File

@ -1,20 +1,23 @@
import {appVarActions} from '@configs/appVarReducer';
import {AccountName, XAuthorization, Username} from '@configs/types';
import {AccountName, XAuthorization, Username, UserId} from '@configs/types';
import {saveVarChanges} from '@helper/appData';
import {apiBackendRequest, makeRequest} from '@helper/request';
import BigDataManager from '@helper/storage/BigDataManager';
import {RootState, store} from '@redux/store';
import {useSelector} from 'react-redux';
import {MyUserAccount, createUserProp, SourceProp} from './types';
import {RootScreenNavigationProp} from '@navigation/navigation';
function createNewMyUser(
UserId: UserId,
AccountName: AccountName,
Username: Username,
SessionId: XAuthorization,
): Promise<void> {
return new Promise((resolve, reject) => {
let user: MyUserAccount = {
AccountName /*: createUserProp(SourceProp.offline, AccountName)*/,
UserId,
AccountName: createUserProp(SourceProp.offline, AccountName),
Username: createUserProp(SourceProp.offline, Username),
/* Description: createUserProp(SourceProp.online),
FollowersCount: createUserProp(SourceProp.online),
@ -101,7 +104,7 @@ function createNewMyUser(
function createMyUser(user: MyUserAccount) {
store.dispatch(appVarActions.setAccount(user));
store.dispatch(appVarActions.setCurrentAccount(user.AccountName));
store.dispatch(appVarActions.setCurrentAccount(user.UserId));
saveVarChanges();
}
@ -114,9 +117,17 @@ function setMyUser(user: MyUserAccount) {
saveVarChanges();
}
function logoutMyUser() {
function logoutMyUser(rootNavigation?: RootScreenNavigationProp) {
store.dispatch(appVarActions.setCurrentAccount('none'));
saveVarChanges();
if (rootNavigation === undefined) return;
rootNavigation.navigate('Registration', {screen: 'LoginPreview'});
rootNavigation.reset({
index: 0,
routes: [{name: 'Registration'}],
});
}
function getSelectedUserAccount(): AccountName {

View File

@ -1,26 +1,19 @@
import {maxCachedUsers} from '@configs/appNonSaveVar';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {AccountName, XAuthorization} from '@configs/types';
import {AccountName, UserId} 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 {
BasicUserProp,
createUserProp,
ProfilePicture,
ProfilePictureType,
SourceProp,
User,
} from './types';
import {createUserProp, ProfilePicture, SourceProp, User} from './types';
let cachedUserList: AccountName[] = [];
async function getUser(
AccountName: AccountName,
UserId: UserId,
save?: boolean,
): Promise<User | undefined> {
if (AccountName === 'none') {
if (UserId === 'none') {
return undefined;
}
@ -30,18 +23,19 @@ async function getUser(
let userIsInCache = false;
{
const usr = state.nonSaveVariables.cachedUsers[AccountName];
const usr = state.nonSaveVariables.cachedUsers[UserId];
if (usr !== undefined) {
user = usr;
userIsInCache = true;
}
}
if (AccountName === state.appVariables.preferences.selectedAccount) {
const usr = state.appVariables.preferences.accounts[AccountName];
if (UserId === state.appVariables.preferences.selectedAccount) {
const usr = state.appVariables.preferences.accounts[UserId];
if (usr !== undefined) {
user = {
UserId,
AccountName: usr.AccountName,
/*Description: usr.Description,
FollowersCount: usr.FollowersCount,
@ -57,7 +51,7 @@ async function getUser(
if (user === undefined) {
const usrDBKeys = BigDataManager.databases.users.keys;
const usr = await BigDataManager.databases.users.getEntry(AccountName);
const usr = await BigDataManager.databases.users.getEntry(UserId);
if (usr !== undefined && usr !== null) {
let ProfilePicture = {
@ -88,7 +82,11 @@ async function getUser(
};
user = {
AccountName,
UserId,
AccountName: createUserProp(
SourceProp.offline,
usr[usrDBKeys.AccountName],
),
Description: createUserProp(
SourceProp.offline,
usr[usrDBKeys.Description],
@ -114,7 +112,7 @@ async function getUser(
try {
const resp = await makeRequest({
path: apiBackendRequest.GET_USER_PROFILE,
requestGET: {':accountName': AccountName},
requestGET: {':userId': UserId},
response: {
Description: '',
FollowersCount: 0,
@ -127,7 +125,11 @@ async function getUser(
});
user = {
AccountName: AccountName,
UserId,
AccountName: createUserProp(
SourceProp.cached,
resp.response.AccountName,
),
Description: createUserProp(
SourceProp.cached,
resp.response.Description,
@ -162,7 +164,7 @@ async function getUser(
if (userIsInCache === false && user !== undefined) {
console.log('save in cache');
store.dispatch(appNonSaveVarActions.setCachedUser(user));
cachedUserList.push(user.AccountName);
cachedUserList.push(user.UserId);
if (cachedUserList.length > maxCachedUsers) {
let usrId = cachedUserList[0];
@ -181,47 +183,47 @@ enum GetParam {
SAVE,
}
let getUserList: {[key: AccountName]: GetParam} = {};
let getUserList: {[key: UserId]: GetParam} = {};
async function refreshUsers() {
for (let AccountName in getUserList) {
const param = getUserList[AccountName];
delete getUserList[AccountName];
for (let UserId in getUserList) {
const param = getUserList[UserId];
delete getUserList[UserId];
await getUser(AccountName);
await getUser(UserId);
}
}
setInterval(refreshUsers, 500);
function addUserToGetQueue(AccountName: AccountName, param: GetParam) {
if (getUserList[AccountName] === undefined) {
getUserList[AccountName] = param;
} else if (getUserList[AccountName] < param) {
getUserList[AccountName] = param;
function addUserToGetQueue(UserId: UserId, param: GetParam) {
if (getUserList[UserId] === undefined) {
getUserList[UserId] = param;
} else if (getUserList[UserId] < param) {
getUserList[UserId] = param;
}
}
function getUserSelector(AccountName: AccountName) {
addUserToGetQueue(AccountName, GetParam.CACHE);
function getUserSelector(UserId: UserId) {
addUserToGetQueue(UserId, GetParam.CACHE);
const myUser = useSelector(
(state: RootState) => state.nonSaveVariables.cachedUsers[AccountName],
(state: RootState) => state.nonSaveVariables.cachedUsers[UserId],
);
if (myUser === undefined) {
return initUndefinedUser(AccountName);
return initUndefinedUser(UserId);
}
return myUser;
}
function getUserSelectorPicture(AccountName: AccountName): ProfilePicture {
addUserToGetQueue(AccountName, GetParam.CACHE);
function getUserSelectorPicture(UserId: UserId): ProfilePicture {
addUserToGetQueue(UserId, GetParam.CACHE);
const myUser = useSelector(
(state: RootState) =>
state.nonSaveVariables.cachedUsers[AccountName]?.ProfilePicture,
state.nonSaveVariables.cachedUsers[UserId]?.ProfilePicture,
);
if (myUser === undefined) {
@ -251,9 +253,10 @@ function getUserSelectorAccountName(
return myUser;
} */
function initUndefinedUser(AccountName: AccountName): User {
function initUndefinedUser(UserId: UserId): User {
return {
AccountName: AccountName,
UserId,
AccountName: createUserProp(SourceProp.online),
/* Description: createUserProp(SourceProp.online),
FollowersCount: createUserProp(SourceProp.online),
FollowingCount: createUserProp(SourceProp.online),

View File

@ -3,8 +3,8 @@ import {
AccountName,
langCode,
XAuthorization,
timestamp,
Username,
UserId,
} from '@configs/types';
export enum SourceProp {
@ -27,8 +27,8 @@ export interface ProfilePicture {
}
export interface User {
//UserId: UserId;
AccountName: AccountName;
UserId: UserId;
AccountName: BasicUserProp<AccountName>;
/* ProfilePicture: ProfilePicture;
lastUpdateTimestamp: timestamp; */