feat: 更新个人信息和打卡功能
- 在个人信息页面中修改用户姓名字段为“name”,并添加注销帐号功能,支持用户删除帐号及相关数据 - 在打卡页面中集成从后端获取当天打卡列表的功能,确保用户数据的实时同步 - 更新 Redux 状态管理,支持打卡记录的同步和更新 - 新增打卡服务,提供创建、更新和删除打卡记录的 API 接口 - 优化样式以适应新功能的展示和交互
This commit is contained in:
@@ -2,7 +2,8 @@ import { Colors } from '@/constants/Colors';
|
|||||||
import { getTabBarBottomPadding } from '@/constants/TabBar';
|
import { getTabBarBottomPadding } from '@/constants/TabBar';
|
||||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
import { DEFAULT_MEMBER_NAME, fetchMyProfile } from '@/store/userSlice';
|
import { api } from '@/services/api';
|
||||||
|
import { DEFAULT_MEMBER_NAME, fetchMyProfile, logout } from '@/store/userSlice';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
|
||||||
@@ -29,7 +30,7 @@ export default function PersonalScreen() {
|
|||||||
const DEFAULT_AVATAR_URL = 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/avatar/avatarGirl01.jpeg';
|
const DEFAULT_AVATAR_URL = 'https://plates-1251306435.cos.ap-guangzhou.myqcloud.com/images/avatar/avatarGirl01.jpeg';
|
||||||
|
|
||||||
type UserProfile = {
|
type UserProfile = {
|
||||||
fullName?: string;
|
name?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
gender?: 'male' | 'female' | '';
|
gender?: 'male' | 'female' | '';
|
||||||
age?: string;
|
age?: string;
|
||||||
@@ -125,7 +126,34 @@ export default function PersonalScreen() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const displayName = (profile.fullName && profile.fullName.trim()) ? profile.fullName : DEFAULT_MEMBER_NAME;
|
const displayName = (profile.name && profile.name.trim()) ? profile.name : DEFAULT_MEMBER_NAME;
|
||||||
|
|
||||||
|
const handleDeleteAccount = () => {
|
||||||
|
Alert.alert(
|
||||||
|
'确认注销帐号',
|
||||||
|
'此操作不可恢复,将删除您的帐号及相关数据。确定继续吗?',
|
||||||
|
[
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{
|
||||||
|
text: '确认注销',
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: async () => {
|
||||||
|
try {
|
||||||
|
await api.delete('/api/users/delete-account');
|
||||||
|
await AsyncStorage.multiRemove(['@user_personal_info']);
|
||||||
|
await dispatch(logout()).unwrap();
|
||||||
|
Alert.alert('帐号已注销', '您的帐号已成功注销');
|
||||||
|
router.replace('/auth/login');
|
||||||
|
} catch (err: any) {
|
||||||
|
const message = err?.message || '注销失败,请稍后重试';
|
||||||
|
Alert.alert('注销失败', message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{ cancelable: true }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const UserInfoSection = () => (
|
const UserInfoSection = () => (
|
||||||
<View style={[styles.userInfoCard, { backgroundColor: colorTokens.card }]}>
|
<View style={[styles.userInfoCard, { backgroundColor: colorTokens.card }]}>
|
||||||
@@ -282,6 +310,16 @@ export default function PersonalScreen() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const securityItems = [
|
||||||
|
{
|
||||||
|
icon: 'trash-outline',
|
||||||
|
iconBg: '#FFE8E8',
|
||||||
|
iconColor: '#FF4444',
|
||||||
|
title: '注销帐号',
|
||||||
|
onPress: handleDeleteAccount,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const developerItems = [
|
const developerItems = [
|
||||||
{
|
{
|
||||||
icon: 'refresh-outline',
|
icon: 'refresh-outline',
|
||||||
@@ -306,6 +344,7 @@ export default function PersonalScreen() {
|
|||||||
<MenuSection title="账户" items={accountItems} />
|
<MenuSection title="账户" items={accountItems} />
|
||||||
<MenuSection title="通知" items={notificationItems} />
|
<MenuSection title="通知" items={notificationItems} />
|
||||||
<MenuSection title="其他" items={otherItems} />
|
<MenuSection title="其他" items={otherItems} />
|
||||||
|
<MenuSection title="账号与安全" items={securityItems} />
|
||||||
<MenuSection title="开发者" items={developerItems} />
|
<MenuSection title="开发者" items={developerItems} />
|
||||||
|
|
||||||
{/* 底部浮动按钮 */}
|
{/* 底部浮动按钮 */}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { HeaderBar } from '@/components/ui/HeaderBar';
|
|||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
import { removeExercise, setCurrentDate, toggleExerciseCompleted } from '@/store/checkinSlice';
|
import type { CheckinExercise } from '@/store/checkinSlice';
|
||||||
|
import { getDailyCheckins, removeExercise, setCurrentDate, syncCheckin, toggleExerciseCompleted } from '@/store/checkinSlice';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
|
import { useFocusEffect } from '@react-navigation/native';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { Alert, FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Alert, FlatList, SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
@@ -26,8 +28,22 @@ export default function CheckinHome() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setCurrentDate(today));
|
dispatch(setCurrentDate(today));
|
||||||
|
// 进入页面立即从后端获取当天打卡列表,回填本地
|
||||||
|
dispatch(getDailyCheckins(today)).unwrap().catch((err: any) => {
|
||||||
|
Alert.alert('获取打卡失败', err?.message || '请稍后重试');
|
||||||
|
});
|
||||||
}, [dispatch, today]);
|
}, [dispatch, today]);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
React.useCallback(() => {
|
||||||
|
// 返回本页时确保与后端同步(若本地有内容则上报,后台 upsert)
|
||||||
|
if (record?.items && Array.isArray(record.items)) {
|
||||||
|
dispatch(syncCheckin({ date: today, items: record.items as CheckinExercise[], note: record?.note }));
|
||||||
|
}
|
||||||
|
return () => { };
|
||||||
|
}, [dispatch, today, record?.items])
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
<SafeAreaView style={[styles.safeArea, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
||||||
<View style={[styles.container, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
<View style={[styles.container, { backgroundColor: theme === 'light' ? colorTokens.pageBackgroundEmphasis : colorTokens.background }]}>
|
||||||
@@ -68,7 +84,13 @@ export default function CheckinHome() {
|
|||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={item.completed ? '已完成,点击取消完成' : '未完成,点击标记完成'}
|
accessibilityLabel={item.completed ? '已完成,点击取消完成' : '未完成,点击标记完成'}
|
||||||
style={styles.doneIconBtn}
|
style={styles.doneIconBtn}
|
||||||
onPress={() => dispatch(toggleExerciseCompleted({ date: today, key: item.key }))}
|
onPress={() => {
|
||||||
|
dispatch(toggleExerciseCompleted({ date: today, key: item.key }));
|
||||||
|
const nextItems: CheckinExercise[] = (record?.items || []).map((it: CheckinExercise) =>
|
||||||
|
it.key === item.key ? { ...it, completed: !it.completed } : it
|
||||||
|
);
|
||||||
|
dispatch(syncCheckin({ date: today, items: nextItems, note: record?.note }));
|
||||||
|
}}
|
||||||
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }}
|
||||||
>
|
>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
@@ -85,7 +107,11 @@ export default function CheckinHome() {
|
|||||||
{
|
{
|
||||||
text: '移除',
|
text: '移除',
|
||||||
style: 'destructive',
|
style: 'destructive',
|
||||||
onPress: () => dispatch(removeExercise({ date: today, key: item.key })),
|
onPress: () => {
|
||||||
|
dispatch(removeExercise({ date: today, key: item.key }));
|
||||||
|
const nextItems: CheckinExercise[] = (record?.items || []).filter((it: CheckinExercise) => it.key !== item.key);
|
||||||
|
dispatch(syncCheckin({ date: today, items: nextItems, note: record?.note }));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { HeaderBar } from '@/components/ui/HeaderBar';
|
|||||||
import { Colors } from '@/constants/Colors';
|
import { Colors } from '@/constants/Colors';
|
||||||
import { useAppDispatch } from '@/hooks/redux';
|
import { useAppDispatch } from '@/hooks/redux';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
import { addExercise } from '@/store/checkinSlice';
|
import { addExercise, syncCheckin } from '@/store/checkinSlice';
|
||||||
import { EXERCISE_LIBRARY, getCategories, searchExercises } from '@/utils/exerciseLibrary';
|
import { EXERCISE_LIBRARY, getCategories, searchExercises } from '@/utils/exerciseLibrary';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
@@ -79,6 +79,21 @@ export default function SelectExerciseScreen() {
|
|||||||
reps: reps && reps > 0 ? reps : undefined,
|
reps: reps && reps > 0 ? reps : undefined,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
console.log('addExercise', today, selected.key, sets, reps);
|
||||||
|
// 同步到后端(读取最新 store 需要在返回后由首页触发 load,或此处直接上报)
|
||||||
|
// 简单做法:直接上报新增项(其余项由后端合并/覆盖)
|
||||||
|
dispatch(syncCheckin({
|
||||||
|
date: today,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: selected.key,
|
||||||
|
name: selected.name,
|
||||||
|
category: selected.category,
|
||||||
|
sets: Math.max(1, sets),
|
||||||
|
reps: reps && reps > 0 ? reps : undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
router.back();
|
router.back();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type WeightUnit = 'kg' | 'lb';
|
|||||||
type HeightUnit = 'cm' | 'ft';
|
type HeightUnit = 'cm' | 'ft';
|
||||||
|
|
||||||
interface UserProfile {
|
interface UserProfile {
|
||||||
fullName?: string;
|
name?: string;
|
||||||
gender?: 'male' | 'female' | '';
|
gender?: 'male' | 'female' | '';
|
||||||
age?: string; // 存储为字符串,方便非必填
|
age?: string; // 存储为字符串,方便非必填
|
||||||
// 以公制为基准存储
|
// 以公制为基准存储
|
||||||
@@ -43,7 +43,7 @@ export default function EditProfileScreen() {
|
|||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const [profile, setProfile] = useState<UserProfile>({
|
const [profile, setProfile] = useState<UserProfile>({
|
||||||
fullName: '',
|
name: '',
|
||||||
gender: '',
|
gender: '',
|
||||||
age: '',
|
age: '',
|
||||||
weightKg: undefined,
|
weightKg: undefined,
|
||||||
@@ -66,7 +66,7 @@ export default function EditProfileScreen() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let next: UserProfile = {
|
let next: UserProfile = {
|
||||||
fullName: '',
|
name: '',
|
||||||
gender: '',
|
gender: '',
|
||||||
age: '',
|
age: '',
|
||||||
weightKg: undefined,
|
weightKg: undefined,
|
||||||
@@ -179,11 +179,11 @@ export default function EditProfileScreen() {
|
|||||||
style={[styles.textInput, { color: textColor }]}
|
style={[styles.textInput, { color: textColor }]}
|
||||||
placeholder="填写姓名(可选)"
|
placeholder="填写姓名(可选)"
|
||||||
placeholderTextColor={placeholderColor}
|
placeholderTextColor={placeholderColor}
|
||||||
value={profile.fullName}
|
value={profile.name}
|
||||||
onChangeText={(t) => setProfile((p) => ({ ...p, fullName: t }))}
|
onChangeText={(t) => setProfile((p) => ({ ...p, name: t }))}
|
||||||
/>
|
/>
|
||||||
{/* 校验勾无需强制,仅装饰 */}
|
{/* 校验勾无需强制,仅装饰 */}
|
||||||
{!!profile.fullName && <Text style={{ color: '#C4C4C4' }}>✓</Text>}
|
{!!profile.name && <Text style={{ color: '#C4C4C4' }}>✓</Text>}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 体重(kg) */}
|
{/* 体重(kg) */}
|
||||||
|
|||||||
58
services/checkins.ts
Normal file
58
services/checkins.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { api } from '@/services/api';
|
||||||
|
|
||||||
|
export type CheckinMetrics = Record<string, any>;
|
||||||
|
|
||||||
|
export type CreateCheckinDto = {
|
||||||
|
workoutId?: string;
|
||||||
|
planId?: string;
|
||||||
|
title?: string;
|
||||||
|
checkinDate?: string; // YYYY-MM-DD
|
||||||
|
startedAt?: string; // ISO
|
||||||
|
notes?: string;
|
||||||
|
metrics?: CheckinMetrics;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateCheckinDto = Partial<CreateCheckinDto> & {
|
||||||
|
id: string;
|
||||||
|
status?: string; // 由后端验证为 CheckinStatus
|
||||||
|
completedAt?: string; // ISO
|
||||||
|
durationSeconds?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CompleteCheckinDto = {
|
||||||
|
id: string;
|
||||||
|
completedAt?: string; // ISO
|
||||||
|
durationSeconds?: number;
|
||||||
|
notes?: string;
|
||||||
|
metrics?: CheckinMetrics;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RemoveCheckinDto = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function createCheckin(dto: CreateCheckinDto): Promise<{ id: string } & Record<string, any>> {
|
||||||
|
return await api.post('/api/checkins/create', dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCheckin(dto: UpdateCheckinDto): Promise<{ id: string } & Record<string, any>> {
|
||||||
|
return await api.put('/api/checkins/update', dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function completeCheckin(dto: CompleteCheckinDto): Promise<{ id: string } & Record<string, any>> {
|
||||||
|
return await api.post('/api/checkins/complete', dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeCheckin(dto: RemoveCheckinDto): Promise<boolean | Record<string, any>> {
|
||||||
|
return await api.delete('/api/checkins/remove', { body: dto });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按天获取打卡列表(后端默认今天,不传 date 也可)
|
||||||
|
export async function fetchDailyCheckins(date?: string): Promise<any[]> {
|
||||||
|
const path = date ? `/api/checkins/daily?date=${encodeURIComponent(date)}` : '/api/checkins/daily';
|
||||||
|
const data = await api.get<any[]>(path);
|
||||||
|
// 返回值兼容 { data: T } 或直接 T 已由 api 封装处理
|
||||||
|
return Array.isArray(data) ? data : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createCheckin, fetchDailyCheckins, updateCheckin } from '@/services/checkins';
|
||||||
|
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
export type CheckinExercise = {
|
export type CheckinExercise = {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -70,9 +71,73 @@ const checkinSlice = createSlice({
|
|||||||
delete state.byDate[action.payload];
|
delete state.byDate[action.payload];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(syncCheckin.fulfilled, (state, action) => {
|
||||||
|
if (!action.payload) return;
|
||||||
|
const { date, items, note, id } = action.payload;
|
||||||
|
state.byDate[date] = {
|
||||||
|
id: id || state.byDate[date]?.id || `rec_${date}`,
|
||||||
|
date,
|
||||||
|
items: items || [],
|
||||||
|
note,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.addCase(getDailyCheckins.fulfilled, (state, action) => {
|
||||||
|
const date = action.payload.date as string | undefined;
|
||||||
|
const list = action.payload.list || [];
|
||||||
|
if (!date) return;
|
||||||
|
let mergedItems: CheckinExercise[] = [];
|
||||||
|
let note: string | undefined = undefined;
|
||||||
|
let id: string | undefined = state.byDate[date]?.id;
|
||||||
|
for (const rec of list) {
|
||||||
|
if (!rec) continue;
|
||||||
|
if (rec.id && !id) id = String(rec.id);
|
||||||
|
const itemsFromMetrics = rec?.metrics?.items ?? rec?.items;
|
||||||
|
if (Array.isArray(itemsFromMetrics)) {
|
||||||
|
mergedItems = itemsFromMetrics as CheckinExercise[];
|
||||||
|
}
|
||||||
|
if (typeof rec.notes === 'string') note = rec.notes as string;
|
||||||
|
}
|
||||||
|
state.byDate[date] = {
|
||||||
|
id: id || state.byDate[date]?.id || `rec_${date}`,
|
||||||
|
date,
|
||||||
|
items: mergedItems,
|
||||||
|
note,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setCurrentDate, addExercise, removeExercise, toggleExerciseCompleted, setNote, resetDate } = checkinSlice.actions;
|
export const { setCurrentDate, addExercise, removeExercise, toggleExerciseCompleted, setNote, resetDate } = checkinSlice.actions;
|
||||||
export default checkinSlice.reducer;
|
export default checkinSlice.reducer;
|
||||||
|
|
||||||
|
// Thunks
|
||||||
|
export const syncCheckin = createAsyncThunk('checkin/sync', async (record: { date: string; items: CheckinExercise[]; note?: string; id?: string }, { getState }) => {
|
||||||
|
const state = getState() as any;
|
||||||
|
const existingId: string | undefined = record.id || state?.checkin?.byDate?.[record.date]?.id;
|
||||||
|
const metrics = { items: record.items } as any;
|
||||||
|
if (!existingId || existingId.startsWith('rec_')) {
|
||||||
|
const created = await createCheckin({
|
||||||
|
title: '每日训练打卡',
|
||||||
|
checkinDate: record.date,
|
||||||
|
notes: record.note,
|
||||||
|
metrics,
|
||||||
|
startedAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
const newId = (created as any)?.id;
|
||||||
|
return { id: newId, date: record.date, items: record.items, note: record.note };
|
||||||
|
}
|
||||||
|
const updated = await updateCheckin({ id: existingId, notes: record.note, metrics });
|
||||||
|
const newId = (updated as any)?.id ?? existingId;
|
||||||
|
return { id: newId, date: record.date, items: record.items, note: record.note };
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取当天打卡列表(用于进入页面时拉取最新云端数据)
|
||||||
|
export const getDailyCheckins = createAsyncThunk('checkin/getDaily', async (date?: string) => {
|
||||||
|
const list = await fetchDailyCheckins(date);
|
||||||
|
|
||||||
|
return { date, list } as { date?: string; list: any[] };
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|||||||
export type Gender = 'male' | 'female' | '';
|
export type Gender = 'male' | 'female' | '';
|
||||||
|
|
||||||
export type UserProfile = {
|
export type UserProfile = {
|
||||||
fullName?: string;
|
name?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
gender?: Gender;
|
gender?: Gender;
|
||||||
age?: string; // 个人中心是字符串展示
|
age?: string; // 个人中心是字符串展示
|
||||||
@@ -29,7 +29,7 @@ export const DEFAULT_MEMBER_NAME = '普拉提星球学员';
|
|||||||
const initialState: UserState = {
|
const initialState: UserState = {
|
||||||
token: null,
|
token: null,
|
||||||
profile: {
|
profile: {
|
||||||
fullName: DEFAULT_MEMBER_NAME,
|
name: DEFAULT_MEMBER_NAME,
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
@@ -164,8 +164,8 @@ const userSlice = createSlice({
|
|||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.token = action.payload.token;
|
state.token = action.payload.token;
|
||||||
state.profile = action.payload.profile;
|
state.profile = action.payload.profile;
|
||||||
if (!state.profile?.fullName || !state.profile.fullName.trim()) {
|
if (!state.profile?.name || !state.profile.name.trim()) {
|
||||||
state.profile.fullName = DEFAULT_MEMBER_NAME;
|
state.profile.name = DEFAULT_MEMBER_NAME;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addCase(login.rejected, (state, action) => {
|
.addCase(login.rejected, (state, action) => {
|
||||||
@@ -175,14 +175,14 @@ const userSlice = createSlice({
|
|||||||
.addCase(rehydrateUser.fulfilled, (state, action) => {
|
.addCase(rehydrateUser.fulfilled, (state, action) => {
|
||||||
state.token = action.payload.token;
|
state.token = action.payload.token;
|
||||||
state.profile = action.payload.profile;
|
state.profile = action.payload.profile;
|
||||||
if (!state.profile?.fullName || !state.profile.fullName.trim()) {
|
if (!state.profile?.name || !state.profile.name.trim()) {
|
||||||
state.profile.fullName = DEFAULT_MEMBER_NAME;
|
state.profile.name = DEFAULT_MEMBER_NAME;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addCase(fetchMyProfile.fulfilled, (state, action) => {
|
.addCase(fetchMyProfile.fulfilled, (state, action) => {
|
||||||
state.profile = action.payload || {};
|
state.profile = action.payload || {};
|
||||||
if (!state.profile?.fullName || !state.profile.fullName.trim()) {
|
if (!state.profile?.name || !state.profile.name.trim()) {
|
||||||
state.profile.fullName = DEFAULT_MEMBER_NAME;
|
state.profile.name = DEFAULT_MEMBER_NAME;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addCase(fetchMyProfile.rejected, (state, action) => {
|
.addCase(fetchMyProfile.rejected, (state, action) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user