feat: add party user metadata storage and display support, including storage API, Redux actions, state management, and UI updates for profile page

This commit is contained in:
louiscklaw
2025-06-18 01:14:37 +08:00
parent c93b31b2f6
commit 215476cfaa
6 changed files with 55 additions and 14 deletions

View File

@@ -16,6 +16,8 @@ const USERNAME = 'username';
const ACCESS_TOKEN = 'a_token'; const ACCESS_TOKEN = 'a_token';
const ACTIVE_SESSION = 'a_session'; const ACTIVE_SESSION = 'a_session';
const PARTY_USER_META = 'party_user_meta';
export const getConfData = async () => { export const getConfData = async () => {
console.log({ t: constants.API_ENDPOINT }); console.log({ t: constants.API_ENDPOINT });
@@ -89,14 +91,21 @@ export const getUserData = async () => {
Storage.get({ key: HAS_LOGGED_IN }), Storage.get({ key: HAS_LOGGED_IN }),
Storage.get({ key: HAS_SEEN_TUTORIAL }), Storage.get({ key: HAS_SEEN_TUTORIAL }),
Storage.get({ key: USERNAME }), Storage.get({ key: USERNAME }),
Storage.get({ key: PARTY_USER_META }),
]); ]);
const isLoggedin = (await response[0].value) === 'true'; const isLoggedin = (await response[0].value) === 'true';
const hasSeenTutorial = (await response[1].value) === 'true'; const hasSeenTutorial = (await response[1].value) === 'true';
const username = (await response[2].value) || undefined; const username = (await response[2].value) || undefined;
let result = (await response[3].value) || undefined;
const meta = result ? JSON.parse(result) : undefined;
const data = { const data = {
isLoggedin, isLoggedin,
hasSeenTutorial, hasSeenTutorial,
username, username,
meta,
}; };
return data; return data;
}; };
@@ -120,6 +129,14 @@ export const setUsernameData = async (username?: string) => {
} }
}; };
export const setPartyUserMetaData = async (party_user?: Record<string, any>) => {
if (!party_user) {
await Storage.remove({ key: PARTY_USER_META });
} else {
await Storage.set({ key: PARTY_USER_META, value: JSON.stringify(party_user) });
}
};
export const setAccessTokenData = async (accessToken?: string) => { export const setAccessTokenData = async (accessToken?: string) => {
if (!accessToken) { if (!accessToken) {
await Storage.remove({ key: ACCESS_TOKEN }); await Storage.remove({ key: ACCESS_TOKEN });

View File

@@ -6,6 +6,7 @@ import {
setAccessTokenData, setAccessTokenData,
getAccessTokenData, getAccessTokenData,
setActiveSessionData, setActiveSessionData,
setPartyUserMetaData,
} from '../dataApi'; } from '../dataApi';
import { ActionType } from '../../util/types'; import { ActionType } from '../../util/types';
import { UserState } from './user.state'; import { UserState } from './user.state';
@@ -34,6 +35,14 @@ export const setData = (data: Partial<UserState>) =>
data, data,
}) as const; }) as const;
export const setPartyUserMeta = async (partyUserMeta: Record<string, any>) => {
await setPartyUserMetaData(partyUserMeta);
return {
type: 'set-party-user-meta',
partyUserMeta,
} as const;
};
export const logoutUser = () => async (dispatch: React.Dispatch<any>) => { export const logoutUser = () => async (dispatch: React.Dispatch<any>) => {
// //
await setIsLoggedInData(false); await setIsLoggedInData(false);
@@ -124,6 +133,7 @@ export const setDarkMode = (darkMode: boolean) =>
export type UserActions = export type UserActions =
| ActionType<typeof setLoading> | ActionType<typeof setLoading>
| ActionType<typeof setData> | ActionType<typeof setData>
| ActionType<typeof setPartyUserMeta>
| ActionType<typeof setIsLoggedIn> | ActionType<typeof setIsLoggedIn>
| ActionType<typeof setUsername> | ActionType<typeof setUsername>
| ActionType<typeof setHasSeenTutorial> | ActionType<typeof setHasSeenTutorial>

View File

@@ -19,6 +19,9 @@ export function userReducer(state: UserState, action: UserActions): UserState {
return { ...state, token: action.token }; return { ...state, token: action.token };
case 'check-user-session': case 'check-user-session':
return { ...state, isSessionValid: action.sessionValid }; return { ...state, isSessionValid: action.sessionValid };
case 'set-party-user-meta':
return { ...state, meta: action.partyUserMeta };
default: default:
return { ...state }; return { ...state };
} }

View File

@@ -9,14 +9,16 @@ export interface UserState {
token?: string; token?: string;
// //
name?: string; meta?: {
email?: string; name?: string;
avatarUrl?: string; email?: string;
phoneNumber?: string; avatarUrl?: string;
company?: string; phoneNumber?: string;
role?: string; company?: string;
rank?: string; role?: string;
isVerified?: Boolean; rank?: string;
isVerified?: Boolean;
};
// //
accessToken?: string; accessToken?: string;

View File

@@ -151,7 +151,7 @@ const MyProfilePage: React.FC<PageProps> = ({
<IonAvatar> <IonAvatar>
<img <img
alt="Silhouette of a person's head" alt="Silhouette of a person's head"
src={partyUserState.avatarUrl ? partyUserState.avatarUrl : ''} src={partyUserState?.meta?.avatarUrl ? partyUserState.meta.avatarUrl : ''}
/> />
</IonAvatar> </IonAvatar>
<div style={{ flexGrow: 1 }}> <div style={{ flexGrow: 1 }}>
@@ -163,11 +163,11 @@ const MyProfilePage: React.FC<PageProps> = ({
}} }}
> >
<div style={{ fontSize: '1.2rem', fontWeight: 'bold' }}> <div style={{ fontSize: '1.2rem', fontWeight: 'bold' }}>
{partyUserState.name} {partyUserState.meta?.name}
</div> </div>
<div style={{ fontSize: '0.8rem' }}>{partyUserState.rank}</div> <div style={{ fontSize: '0.8rem' }}>{partyUserState.meta?.rank}</div>
<div style={{ fontSize: '0.8rem' }}> <div style={{ fontSize: '0.8rem' }}>
{partyUserState.isVerified ? 'verified' : 'no'} {partyUserState.meta?.isVerified ? 'verified' : 'no'}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -19,7 +19,12 @@ import {
IonToast, IonToast,
} from '@ionic/react'; } from '@ionic/react';
import './Login.scss'; import './Login.scss';
import { setIsLoggedIn, setUsername, setData } from '../../data/user/user.actions'; import {
setIsLoggedIn,
setUsername,
setData,
setPartyUserMeta,
} from '../../data/user/user.actions';
import { connect } from '../../data/connect'; import { connect } from '../../data/connect';
import { RouteComponentProps } from 'react-router'; import { RouteComponentProps } from 'react-router';
import { chevronBackOutline } from 'ionicons/icons'; import { chevronBackOutline } from 'ionicons/icons';
@@ -39,6 +44,7 @@ interface DispatchProps {
setIsLoggedIn: typeof setIsLoggedIn; setIsLoggedIn: typeof setIsLoggedIn;
setUsername: typeof setUsername; setUsername: typeof setUsername;
setData: typeof setData; setData: typeof setData;
setPartyUserMeta: typeof setPartyUserMeta;
} }
interface LoginProps extends OwnProps, DispatchProps {} interface LoginProps extends OwnProps, DispatchProps {}
@@ -48,6 +54,7 @@ const Login: React.FC<LoginProps> = ({
history, history,
setUsername: setUsernameAction, setUsername: setUsernameAction,
setData, setData,
setPartyUserMeta,
}) => { }) => {
const [username, setUsername] = useState('demo@minimals.cc'); const [username, setUsername] = useState('demo@minimals.cc');
const [email, setEmail] = useState('demo@minimals.cc'); const [email, setEmail] = useState('demo@minimals.cc');
@@ -76,7 +83,8 @@ const Login: React.FC<LoginProps> = ({
if (status == 200) { if (status == 200) {
// if username and password ok // if username and password ok
setData({ isLoggedin: true, accessToken, ...user }); setData({ isLoggedin: true, accessToken });
setPartyUserMeta(user);
await setIsLoggedIn(true); await setIsLoggedIn(true);
await setUsernameAction(username); await setUsernameAction(username);
@@ -192,6 +200,7 @@ export default connect<OwnProps, StateProps, DispatchProps>({
setIsLoggedIn, setIsLoggedIn,
setUsername, setUsername,
setData, setData,
setPartyUserMeta,
}, },
mapStateToProps: (state) => ({ mapStateToProps: (state) => ({
partyUserState: selectors.getPartyUserState(state), partyUserState: selectors.getPartyUserState(state),