appstart, create event, common inputs
parent
00045692ad
commit
cc569a8187
|
@ -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()} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 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',
|
||||
|
@ -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',
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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; */
|
||||
|
|
Reference in New Issue