Files
gitlab-instance-0a899031_cn…/app/[locale]/dating/utils.ts
2026-06-08 03:06:12 -05:00

134 lines
5.0 KiB
TypeScript

import { apiFetch } from "@/app/lib/api-client";
import { avatarForName } from "@/app/data/avatars";
import { FALLBACK_PROFILES, PROFILE_GRADIENTS } from "./constants";
import type { DatingProfile, MatchIntent, ProfileRecord, SwipeResult } from "./types";
export function mapProfile(record: ProfileRecord, index: number): DatingProfile {
const name = record.name || "数字游民";
const initial = name.slice(0, 1);
return {
id: record.id,
name,
age: Number(record.age || 26 + (index % 8)),
location: record.location || "远程",
citySlug: record.citySlug || "",
gender: record.gender || "",
single: record.single || "",
tags: Array.isArray(record.tags) ? record.tags : ["数字游民", "远程工作者"],
bio: record.bio || "正在完善个人资料,期待结识同城和同路的远程工作伙伴。",
lookingFor: Array.isArray(record.lookingFor) ? record.lookingFor : ["friends", "explore"],
gradient: PROFILE_GRADIENTS[index % PROFILE_GRADIENTS.length],
initial,
avatarColor: "bg-[#ff4d4f]",
photo: record.photo || avatarForName(name),
likedAt: record.likedAt,
matchedAt: record.matchedAt,
intent: record.intent,
connectionId: record.connectionId,
conversationId: record.conversationId,
};
}
export function filterFallbackProfiles(
intent: MatchIntent,
city: string,
advanced?: { gender: string; single: string }
): DatingProfile[] {
const cityQuery = city.trim().toLowerCase();
return FALLBACK_PROFILES.filter((profile) => {
if (!profile.lookingFor.includes(intent)) return false;
if (!cityQuery || cityQuery === "all") {
// continue
} else if (
profile.citySlug.toLowerCase() !== cityQuery &&
!profile.location.toLowerCase().includes(cityQuery)
) {
return false;
}
if (advanced?.gender && advanced.gender !== "all" && profile.gender !== advanced.gender) {
return false;
}
if (advanced?.single && advanced.single !== "all" && profile.single !== advanced.single) {
return false;
}
return true;
});
}
export function formatTimeAgo(value?: string, locale = "zh"): string {
if (!value) return locale === "zh" ? "刚刚活跃" : "Active now";
const date = new Date(value);
if (Number.isNaN(date.getTime())) return locale === "zh" ? "刚刚活跃" : "Active now";
const diff = Date.now() - date.getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 1) return locale === "zh" ? "刚刚" : "Just now";
if (minutes < 60) return locale === "zh" ? `${minutes} 分钟前` : `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return locale === "zh" ? `${hours} 小时前` : `${hours}h ago`;
const days = Math.floor(hours / 24);
if (days < 7) return locale === "zh" ? `${days} 天前` : `${days}d ago`;
const weeks = Math.floor(days / 7);
if (weeks < 5) return locale === "zh" ? `${weeks} 周前` : `${weeks}w ago`;
const months = Math.floor(days / 30);
return locale === "zh" ? `${Math.max(months, 1)} 月前` : `${Math.max(months, 1)}mo ago`;
}
export interface MatchQuota {
vip: boolean;
limit: number;
used: number;
remaining: number;
}
export async function fetchCandidates(
intent: MatchIntent,
city: string,
advanced?: { gender: string; single: string }
): Promise<DatingProfile[]> {
const params = new URLSearchParams({ intent });
if (city && city !== "all") params.set("city", city);
if (advanced?.gender && advanced.gender !== "all") params.set("gender", advanced.gender);
if (advanced?.single && advanced.single !== "all") params.set("single", advanced.single);
params.set("exclude_swiped", "true");
const data = await apiFetch<{ items: ProfileRecord[] }>(`/api/matches/candidates?${params}`);
return (data.items || []).map((item, index) => mapProfile(item, index));
}
export async function fetchQuota(): Promise<MatchQuota> {
const data = await apiFetch<MatchQuota & { ok?: boolean }>("/api/matches/quota");
return {
vip: Boolean(data.vip),
limit: Number(data.limit || 25),
used: Number(data.used || 0),
remaining: Number(data.remaining || 0),
};
}
export async function undoSwipe(): Promise<{ profileId?: string }> {
return apiFetch<{ ok: boolean; profileId?: string }>("/api/matches/swipes/undo", {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchLikes(): Promise<DatingProfile[]> {
const data = await apiFetch<{ items: ProfileRecord[] }>("/api/matches/likes");
return (data.items || []).map((item, index) => mapProfile(item, index));
}
export async function fetchMutualMatches(): Promise<DatingProfile[]> {
const data = await apiFetch<{ items: ProfileRecord[] }>("/api/matches/mutual");
return (data.items || []).map((item, index) => mapProfile(item, index));
}
export async function saveSwipe(
profileId: string,
action: "like" | "dislike",
intent: MatchIntent
): Promise<SwipeResult> {
return apiFetch<SwipeResult>("/api/matches/swipes", {
method: "POST",
body: JSON.stringify({ profileId, action, intent }),
});
}