fix: 刷新页面后恢复滑动进度,防止重复 swipe
服务端 GET room 返回 swipeCounts,前端据此恢复 currentIndex、 swipeHistory 和引导状态;swipe API 增加幂等性检查,跳过已滑过的卡片。
This commit is contained in:
@@ -51,6 +51,7 @@ export async function GET(
|
|||||||
matchType,
|
matchType,
|
||||||
matchLikes,
|
matchLikes,
|
||||||
likeCounts,
|
likeCounts,
|
||||||
|
swipeCounts: data.swipeCounts,
|
||||||
restaurants: data.restaurants,
|
restaurants: data.restaurants,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ export async function POST(
|
|||||||
const rid = String(restaurantId);
|
const rid = String(restaurantId);
|
||||||
|
|
||||||
const updated = await atomicUpdateRoom(id, (data) => {
|
const updated = await atomicUpdateRoom(id, (data) => {
|
||||||
|
const restaurantIndex = data.restaurants.findIndex((r) => r.id === rid);
|
||||||
|
const alreadySwiped =
|
||||||
|
restaurantIndex >= 0 &&
|
||||||
|
restaurantIndex < (data.swipeCounts[userId] ?? 0);
|
||||||
|
|
||||||
|
if (alreadySwiped) return data;
|
||||||
|
|
||||||
if (action === "like") {
|
if (action === "like") {
|
||||||
if (!data.likes[rid]) {
|
if (!data.likes[rid]) {
|
||||||
data.likes[rid] = [];
|
data.likes[rid] = [];
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function RoomPage() {
|
|||||||
const [joined, setJoined] = useState(false);
|
const [joined, setJoined] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
userCount, match, matchType, matchLikes, likeCounts, restaurants, mutate,
|
userCount, match, matchType, matchLikes, likeCounts, swipeCounts, restaurants, mutate,
|
||||||
} = useRoomPolling(roomId);
|
} = useRoomPolling(roomId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -34,6 +34,7 @@ export default function RoomPage() {
|
|||||||
await mutate();
|
await mutate();
|
||||||
}, [roomId, mutate]);
|
}, [roomId, mutate]);
|
||||||
|
|
||||||
|
const initialIndex = swipeCounts[userId] ?? 0;
|
||||||
const ready = joined && userId && restaurants.length > 0;
|
const ready = joined && userId && restaurants.length > 0;
|
||||||
|
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
@@ -52,6 +53,7 @@ export default function RoomPage() {
|
|||||||
restaurants={restaurants}
|
restaurants={restaurants}
|
||||||
roomId={roomId}
|
roomId={roomId}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
|
initialIndex={initialIndex}
|
||||||
matchedRestaurantId={match}
|
matchedRestaurantId={match}
|
||||||
matchType={matchType}
|
matchType={matchType}
|
||||||
matchLikes={matchLikes}
|
matchLikes={matchLikes}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface SwipeDeckProps {
|
|||||||
restaurants: Restaurant[];
|
restaurants: Restaurant[];
|
||||||
roomId: string;
|
roomId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
initialIndex: number;
|
||||||
matchedRestaurantId: string | null;
|
matchedRestaurantId: string | null;
|
||||||
matchType: MatchType;
|
matchType: MatchType;
|
||||||
matchLikes: number;
|
matchLikes: number;
|
||||||
@@ -25,6 +26,7 @@ export default function SwipeDeck({
|
|||||||
restaurants,
|
restaurants,
|
||||||
roomId,
|
roomId,
|
||||||
userId,
|
userId,
|
||||||
|
initialIndex,
|
||||||
matchedRestaurantId,
|
matchedRestaurantId,
|
||||||
matchType,
|
matchType,
|
||||||
matchLikes,
|
matchLikes,
|
||||||
@@ -32,13 +34,15 @@ export default function SwipeDeck({
|
|||||||
userCount,
|
userCount,
|
||||||
onReset,
|
onReset,
|
||||||
}: SwipeDeckProps) {
|
}: SwipeDeckProps) {
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
||||||
const [showMatch, setShowMatch] = useState(false);
|
const [showMatch, setShowMatch] = useState(false);
|
||||||
const [localMatchId, setLocalMatchId] = useState<string | null>(null);
|
const [localMatchId, setLocalMatchId] = useState<string | null>(null);
|
||||||
const [resetting, setResetting] = useState(false);
|
const [resetting, setResetting] = useState(false);
|
||||||
const [bubble, setBubble] = useState("");
|
const [bubble, setBubble] = useState("");
|
||||||
const [guideVisible, setGuideVisible] = useState(true);
|
const [guideVisible, setGuideVisible] = useState(initialIndex === 0);
|
||||||
const [swipeHistory, setSwipeHistory] = useState<string[]>([]);
|
const [swipeHistory, setSwipeHistory] = useState<string[]>(
|
||||||
|
() => restaurants.slice(0, initialIndex).map((r) => r.id),
|
||||||
|
);
|
||||||
const swipeFnRef = useRef<((direction: SwipeDirection) => void) | null>(null);
|
const swipeFnRef = useRef<((direction: SwipeDirection) => void) | null>(null);
|
||||||
const swipingRef = useRef(false);
|
const swipingRef = useRef(false);
|
||||||
const prevLikeCounts = useRef<Record<string, number>>({});
|
const prevLikeCounts = useRef<Record<string, number>>({});
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export function useRoomPolling(roomId: string) {
|
|||||||
matchType: data?.matchType ?? null,
|
matchType: data?.matchType ?? null,
|
||||||
matchLikes: data?.matchLikes ?? 0,
|
matchLikes: data?.matchLikes ?? 0,
|
||||||
likeCounts: data?.likeCounts ?? {},
|
likeCounts: data?.likeCounts ?? {},
|
||||||
|
swipeCounts: data?.swipeCounts ?? {},
|
||||||
restaurants: data?.restaurants ?? [],
|
restaurants: data?.restaurants ?? [],
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -24,5 +24,6 @@ export interface RoomStatus {
|
|||||||
matchType: MatchType;
|
matchType: MatchType;
|
||||||
matchLikes: number;
|
matchLikes: number;
|
||||||
likeCounts: Record<string, number>;
|
likeCounts: Record<string, number>;
|
||||||
|
swipeCounts: Record<string, number>;
|
||||||
restaurants: Restaurant[];
|
restaurants: Restaurant[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user