feat(challenges): 实现自定义挑战的编辑与删除功能并完善多语言支持
- 新增自定义挑战的编辑模式,支持修改挑战信息 - 在详情页为创建者添加删除(归档)挑战的功能入口 - 全面完善挑战创建页面的国际化(i18n)文案适配 - 优化个人中心页面的字体样式,统一使用 AliBold/Regular - 更新 Store 逻辑以处理挑战更新、删除及列表数据映射调整
This commit is contained in:
@@ -10,6 +10,8 @@ import {
|
||||
type ChallengeStatus,
|
||||
type CreateCustomChallengePayload,
|
||||
type RankingItemDto,
|
||||
type UpdateCustomChallengePayload,
|
||||
archiveCustomChallenge,
|
||||
createCustomChallenge,
|
||||
getChallengeByShareCode,
|
||||
getChallengeDetail,
|
||||
@@ -19,6 +21,7 @@ import {
|
||||
leaveChallenge as leaveChallengeApi,
|
||||
listChallenges,
|
||||
reportChallengeProgress as reportChallengeProgressApi,
|
||||
updateCustomChallenge,
|
||||
} from '@/services/challengesApi';
|
||||
import { createAsyncThunk, createSelector, createSlice } from '@reduxjs/toolkit';
|
||||
import type { RootState } from './index';
|
||||
@@ -63,6 +66,10 @@ type ChallengesState = {
|
||||
rankingError: Record<string, string | undefined>;
|
||||
createStatus: AsyncStatus;
|
||||
createError?: string;
|
||||
updateStatus: AsyncStatus;
|
||||
updateError?: string;
|
||||
archiveStatus: Record<string, AsyncStatus>;
|
||||
archiveError: Record<string, string | undefined>;
|
||||
joinByCodeStatus: AsyncStatus;
|
||||
joinByCodeError?: string;
|
||||
};
|
||||
@@ -86,6 +93,10 @@ const initialState: ChallengesState = {
|
||||
rankingError: {},
|
||||
createStatus: 'idle',
|
||||
createError: undefined,
|
||||
updateStatus: 'idle',
|
||||
updateError: undefined,
|
||||
archiveStatus: {},
|
||||
archiveError: {},
|
||||
joinByCodeStatus: 'idle',
|
||||
joinByCodeError: undefined,
|
||||
};
|
||||
@@ -210,6 +221,31 @@ export const joinChallengeByCode = createAsyncThunk<
|
||||
}
|
||||
});
|
||||
|
||||
export const updateCustomChallengeThunk = createAsyncThunk<
|
||||
ChallengeDetail,
|
||||
{ id: string; payload: UpdateCustomChallengePayload },
|
||||
{ rejectValue: string }
|
||||
>('challenges/updateCustom', async ({ id, payload }, { rejectWithValue }) => {
|
||||
try {
|
||||
return await updateCustomChallenge(id, payload);
|
||||
} catch (error) {
|
||||
return rejectWithValue(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
|
||||
export const archiveCustomChallengeThunk = createAsyncThunk<
|
||||
{ id: string },
|
||||
string,
|
||||
{ rejectValue: string }
|
||||
>('challenges/archiveCustom', async (id, { rejectWithValue }) => {
|
||||
try {
|
||||
await archiveCustomChallenge(id);
|
||||
return { id };
|
||||
} catch (error) {
|
||||
return rejectWithValue(toErrorMessage(error));
|
||||
}
|
||||
});
|
||||
|
||||
const challengesSlice = createSlice({
|
||||
name: 'challenges',
|
||||
initialState,
|
||||
@@ -394,6 +430,55 @@ const challengesSlice = createSlice({
|
||||
state.createError = action.payload ?? toErrorMessage(action.error);
|
||||
});
|
||||
|
||||
builder
|
||||
.addCase(updateCustomChallengeThunk.pending, (state) => {
|
||||
state.updateStatus = 'loading';
|
||||
state.updateError = undefined;
|
||||
})
|
||||
.addCase(updateCustomChallengeThunk.fulfilled, (state, action) => {
|
||||
state.updateStatus = 'succeeded';
|
||||
state.updateError = undefined;
|
||||
const challenge = action.payload;
|
||||
const existing = state.entities[challenge.id];
|
||||
const source = ChallengeSource.CUSTOM;
|
||||
state.entities[challenge.id] = { ...(existing ?? {}), ...challenge, source };
|
||||
})
|
||||
.addCase(updateCustomChallengeThunk.rejected, (state, action) => {
|
||||
state.updateStatus = 'failed';
|
||||
state.updateError = action.payload ?? toErrorMessage(action.error);
|
||||
});
|
||||
|
||||
builder
|
||||
.addCase(archiveCustomChallengeThunk.pending, (state, action) => {
|
||||
const id = action.meta.arg;
|
||||
state.archiveStatus[id] = 'loading';
|
||||
state.archiveError[id] = undefined;
|
||||
})
|
||||
.addCase(archiveCustomChallengeThunk.fulfilled, (state, action) => {
|
||||
const { id } = action.payload;
|
||||
state.archiveStatus[id] = 'succeeded';
|
||||
state.archiveError[id] = undefined;
|
||||
delete state.entities[id];
|
||||
state.orderedIds = state.orderedIds.filter((itemId) => itemId !== id);
|
||||
delete state.detailStatus[id];
|
||||
delete state.detailError[id];
|
||||
delete state.joinStatus[id];
|
||||
delete state.joinError[id];
|
||||
delete state.leaveStatus[id];
|
||||
delete state.leaveError[id];
|
||||
delete state.progressStatus[id];
|
||||
delete state.progressError[id];
|
||||
delete state.rankingList[id];
|
||||
delete state.rankingStatus[id];
|
||||
delete state.rankingLoadMoreStatus[id];
|
||||
delete state.rankingError[id];
|
||||
})
|
||||
.addCase(archiveCustomChallengeThunk.rejected, (state, action) => {
|
||||
const id = action.meta.arg;
|
||||
state.archiveStatus[id] = 'failed';
|
||||
state.archiveError[id] = action.payload ?? toErrorMessage(action.error);
|
||||
});
|
||||
|
||||
builder
|
||||
.addCase(joinChallengeByCode.pending, (state) => {
|
||||
state.joinByCodeStatus = 'loading';
|
||||
@@ -545,8 +630,8 @@ export const selectChallengeCards = createSelector([selectChallengeList], (chall
|
||||
source: challenge.source,
|
||||
shareCode: challenge.shareCode ?? null,
|
||||
challengeState: challenge.challengeState,
|
||||
progressUnit: challenge.progressUnit,
|
||||
targetValue: challenge.targetValue,
|
||||
progressUnit: challenge.unit,
|
||||
targetValue: challenge.progress?.target,
|
||||
isCreator: challenge.isCreator,
|
||||
};
|
||||
})
|
||||
@@ -578,8 +663,8 @@ export const selectCustomChallengeCards = createSelector(
|
||||
source: challenge.source ?? ChallengeSource.CUSTOM,
|
||||
shareCode: challenge.shareCode ?? null,
|
||||
challengeState: challenge.challengeState,
|
||||
progressUnit: challenge.progressUnit,
|
||||
targetValue: challenge.targetValue,
|
||||
progressUnit: challenge.unit,
|
||||
targetValue: challenge.progress?.target,
|
||||
isCreator: challenge.isCreator,
|
||||
};
|
||||
})
|
||||
@@ -611,8 +696,8 @@ export const selectOfficialChallengeCards = createSelector(
|
||||
source: challenge.source ?? ChallengeSource.SYSTEM,
|
||||
shareCode: challenge.shareCode ?? null,
|
||||
challengeState: challenge.challengeState,
|
||||
progressUnit: challenge.progressUnit,
|
||||
targetValue: challenge.targetValue,
|
||||
progressUnit: challenge.unit,
|
||||
targetValue: challenge.progress?.target,
|
||||
isCreator: challenge.isCreator,
|
||||
};
|
||||
})
|
||||
@@ -676,3 +761,19 @@ export const selectJoinByCodeError = createSelector(
|
||||
[selectChallengesState],
|
||||
(state) => state.joinByCodeError
|
||||
);
|
||||
|
||||
export const selectUpdateChallengeStatus = createSelector(
|
||||
[selectChallengesState],
|
||||
(state) => state.updateStatus
|
||||
);
|
||||
|
||||
export const selectUpdateChallengeError = createSelector(
|
||||
[selectChallengesState],
|
||||
(state) => state.updateError
|
||||
);
|
||||
|
||||
export const selectArchiveStatus = (id: string) =>
|
||||
createSelector([selectChallengesState], (state) => state.archiveStatus[id] ?? 'idle');
|
||||
|
||||
export const selectArchiveError = (id: string) =>
|
||||
createSelector([selectChallengesState], (state) => state.archiveError[id]);
|
||||
|
||||
Reference in New Issue
Block a user