- 新增自定义挑战的编辑模式,支持修改挑战信息 - 在详情页为创建者添加删除(归档)挑战的功能入口 - 全面完善挑战创建页面的国际化(i18n)文案适配 - 优化个人中心页面的字体样式,统一使用 AliBold/Regular - 更新 Store 逻辑以处理挑战更新、删除及列表数据映射调整
213 lines
5.7 KiB
TypeScript
213 lines
5.7 KiB
TypeScript
import { api } from './api';
|
|
|
|
export type ChallengeStatus = 'upcoming' | 'ongoing' | 'expired';
|
|
|
|
export enum ChallengeSource {
|
|
SYSTEM = 'system',
|
|
CUSTOM = 'custom',
|
|
}
|
|
|
|
export enum ChallengeState {
|
|
DRAFT = 'draft',
|
|
ACTIVE = 'active',
|
|
ARCHIVED = 'archived',
|
|
}
|
|
|
|
export type ChallengeProgressDto = {
|
|
completed: number;
|
|
target: number;
|
|
remaining: number
|
|
checkedInToday: boolean;
|
|
lastProgressAt?: string;
|
|
last_progress_at?: string;
|
|
};
|
|
|
|
export type RankingItemDto = {
|
|
id: string;
|
|
name: string;
|
|
avatar: string | null;
|
|
metric: string;
|
|
badge?: string;
|
|
todayReportedValue?: number;
|
|
todayTargetValue?: number;
|
|
};
|
|
|
|
export enum ChallengeType {
|
|
WATER = 'water',
|
|
EXERCISE = 'exercise',
|
|
DIET = 'diet',
|
|
MOOD = 'mood',
|
|
SLEEP = 'sleep',
|
|
WEIGHT = 'weight',
|
|
}
|
|
|
|
|
|
|
|
export type ChallengeListItemDto = {
|
|
id: string;
|
|
title: string;
|
|
image: string;
|
|
periodLabel?: string;
|
|
durationLabel: string;
|
|
requirementLabel: string;
|
|
unit?: string;
|
|
status?: ChallengeStatus;
|
|
participantsCount: number;
|
|
rankingDescription?: string;
|
|
highlightTitle: string;
|
|
highlightSubtitle: string;
|
|
ctaLabel: string;
|
|
progress?: ChallengeProgressDto;
|
|
isJoined: boolean;
|
|
startAt?: string;
|
|
endAt?: string;
|
|
minimumCheckInDays: number; // 最小打卡天数
|
|
type: ChallengeType;
|
|
shareCode?: string | null;
|
|
source?: ChallengeSource;
|
|
creatorId?: string | null;
|
|
isCreator?: boolean;
|
|
isPublic?: boolean;
|
|
maxParticipants?: number | null;
|
|
challengeState?: ChallengeState;
|
|
summary?: string | null;
|
|
};
|
|
|
|
export type ChallengeDetailDto = ChallengeListItemDto & {
|
|
summary?: string | null;
|
|
rankings?: RankingItemDto[];
|
|
userRank?: number;
|
|
challengeState?: ChallengeState;
|
|
};
|
|
|
|
export type ChallengeRankingsDto = {
|
|
total: number;
|
|
page: number;
|
|
pageSize: number;
|
|
items: RankingItemDto[];
|
|
};
|
|
|
|
export type ChallengeListResponse = {
|
|
items: ChallengeListItemDto[];
|
|
total: number;
|
|
page: number;
|
|
pageSize: number;
|
|
};
|
|
|
|
export type CreateCustomChallengePayload = {
|
|
title: string;
|
|
type: ChallengeType;
|
|
image?: string | null;
|
|
startAt: number;
|
|
endAt: number;
|
|
targetValue: number;
|
|
minimumCheckInDays: number;
|
|
durationLabel: string;
|
|
requirementLabel: string;
|
|
summary?: string | null;
|
|
progressUnit?: string;
|
|
periodLabel?: string | null;
|
|
rankingDescription?: string | null;
|
|
isPublic?: boolean;
|
|
maxParticipants?: number | null;
|
|
};
|
|
|
|
export type UpdateCustomChallengePayload = {
|
|
title?: string;
|
|
image?: string;
|
|
summary?: string;
|
|
isPublic?: boolean;
|
|
maxParticipants?: number;
|
|
highlightTitle?: string;
|
|
highlightSubtitle?: string;
|
|
ctaLabel?: string;
|
|
};
|
|
|
|
export async function listChallenges(): Promise<ChallengeListItemDto[]> {
|
|
return api.get<ChallengeListItemDto[]>('/challenges');
|
|
}
|
|
|
|
export async function getChallengeDetail(id: string): Promise<ChallengeDetailDto> {
|
|
return api.get<ChallengeDetailDto>(`/challenges/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
export async function joinChallenge(id: string): Promise<ChallengeProgressDto> {
|
|
return api.post<ChallengeProgressDto>(`/challenges/${encodeURIComponent(id)}/join`);
|
|
}
|
|
|
|
export async function leaveChallenge(id: string): Promise<boolean> {
|
|
return api.post<boolean>(`/challenges/${encodeURIComponent(id)}/leave`);
|
|
}
|
|
|
|
export async function reportChallengeProgress(id: string, value?: number): Promise<ChallengeProgressDto> {
|
|
const body = value != null ? { value } : undefined;
|
|
return api.post<ChallengeProgressDto>(`/challenges/${encodeURIComponent(id)}/progress`, body);
|
|
}
|
|
|
|
export async function getChallengeRankings(
|
|
id: string,
|
|
params?: { page?: number; pageSize?: number }
|
|
): Promise<ChallengeRankingsDto> {
|
|
const searchParams = new URLSearchParams();
|
|
if (params?.page) {
|
|
searchParams.append('page', String(params.page));
|
|
}
|
|
if (params?.pageSize) {
|
|
searchParams.append('pageSize', String(params.pageSize));
|
|
}
|
|
const query = searchParams.toString();
|
|
const url = `/challenges/${encodeURIComponent(id)}/rankings${query ? `?${query}` : ''}`;
|
|
return api.get<ChallengeRankingsDto>(url);
|
|
}
|
|
|
|
export async function listMyCustomChallenges(
|
|
params?: { page?: number; pageSize?: number; state?: ChallengeState }
|
|
): Promise<ChallengeListResponse> {
|
|
const searchParams = new URLSearchParams();
|
|
if (params?.page) {
|
|
searchParams.append('page', String(params.page));
|
|
}
|
|
if (params?.pageSize) {
|
|
searchParams.append('pageSize', String(params.pageSize));
|
|
}
|
|
if (params?.state) {
|
|
searchParams.append('state', params.state);
|
|
}
|
|
const query = searchParams.toString();
|
|
const url = `/challenges/my/created${query ? `?${query}` : ''}`;
|
|
return api.get<ChallengeListResponse>(url);
|
|
}
|
|
|
|
export async function createCustomChallenge(
|
|
payload: CreateCustomChallengePayload
|
|
): Promise<ChallengeDetailDto> {
|
|
return api.post<ChallengeDetailDto>('/challenges/custom', payload);
|
|
}
|
|
|
|
export async function joinChallengeByCode(shareCode: string): Promise<ChallengeProgressDto> {
|
|
return api.post<ChallengeProgressDto>('/challenges/join-by-code', { shareCode });
|
|
}
|
|
|
|
export async function getChallengeByShareCode(shareCode: string): Promise<ChallengeDetailDto> {
|
|
return api.get<ChallengeDetailDto>(`/challenges/share/${encodeURIComponent(shareCode)}`);
|
|
}
|
|
|
|
export async function regenerateChallengeShareCode(
|
|
id: string
|
|
): Promise<{ shareCode: string }> {
|
|
return api.post<{ shareCode: string }>(
|
|
`/challenges/custom/${encodeURIComponent(id)}/regenerate-code`
|
|
);
|
|
}
|
|
|
|
export async function updateCustomChallenge(
|
|
id: string,
|
|
payload: UpdateCustomChallengePayload
|
|
): Promise<ChallengeDetailDto> {
|
|
return api.put<ChallengeDetailDto>(`/challenges/custom/${encodeURIComponent(id)}`, payload);
|
|
}
|
|
|
|
export async function archiveCustomChallenge(id: string): Promise<boolean> {
|
|
return api.delete<boolean>(`/challenges/custom/${encodeURIComponent(id)}`);
|
|
}
|