This commit is contained in:
2025-09-09 09:34:35 +03:00
parent 6a1d81bfa8
commit 3612ada03a
22 changed files with 285 additions and 54 deletions

View File

@@ -54,7 +54,7 @@ export function useAccountSettings() {
}
},
enabled: !!acct,
staleTime: Infinity,
staleTime: 900_000,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false

View File

@@ -19,25 +19,16 @@ export function useAvatarOutfits(): Outfit[] | false | null {
queryKey: ["avatarOutfits", acct ? acct.id : "acctId"],
enabled: !!acct,
queryFn: async () => {
if (!acct) return false;
if (!acct) return [];
const res = await proxyFetch(
`https://avatar.roblox.com/v2/avatar/users/${acct.id}/outfits?page=1&itemsPerPage=25&isEditable=true`
);
const data = (await res.json()) as { data: Outfit[] };
loadThumbnails(
data.data.map((a) => ({
type: "Outfit",
targetId: a.id,
format: "webp",
size: "420x420"
}))
).catch(() => {});
return data.data;
},
staleTime: 1000 * 60 * 5,
staleTime: 60_000,
refetchOnWindowFocus: false
});

View File

@@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query";
import { proxyFetch } from "@/lib/utils";
import { loadThumbnails } from "@/lib/thumbnailLoader";
import { useCurrentAccount } from "./useCurrentAccount";
import assert from "assert";
export function useBestFriends():
| {
@@ -46,6 +47,7 @@ export function useBestFriends():
})
}
);
assert(friendsAPICall2.ok);
const J2 = (await friendsAPICall2.json()) as {
data: {

View File

@@ -4,17 +4,19 @@ import { useQuery } from "@tanstack/react-query";
import { useCurrentAccount } from "./useCurrentAccount";
import { proxyFetch } from "@/lib/utils";
import { loadThumbnails } from "@/lib/thumbnailLoader";
import { UserProfileDetails } from "@/lib/profile";
import assert from "assert";
export function useFriendsHome() {
export function useFriendsHome( targetId?: string ) {
const acct = useCurrentAccount();
const target = targetId || (acct ? acct.id : "acctId")
const { data: friends } = useQuery({
queryKey: ["friends", acct ? acct.id : "acctId"],
queryKey: ["friends", target],
queryFn: async () => {
if (!acct) return null;
if (target === "acctId") return [];
const friendsAPICall = await proxyFetch(
`https://friends.roblox.com/v1/users/${acct.id}/friends`
`https://friends.roblox.com/v1/users/${target}/friends`
);
assert(friendsAPICall.ok);
const j = (await friendsAPICall.json()) as {
data: { id: number }[];
};
@@ -24,10 +26,11 @@ export function useFriendsHome() {
method: "POST",
body: JSON.stringify({
userIds: j.data.map((a) => a.id),
excludeBannedUsers: false
excludeBannedUsers: true
})
}
);
assert(friendsAPICall2.ok);
const j2 = (await friendsAPICall2.json()) as {
data: {
hasVerifiedBadge: boolean;
@@ -46,13 +49,13 @@ export function useFriendsHome() {
).catch(() => {});
const friendsList = j.data.map((a) => {
const x = j2.data.find((b) => b.id === a.id);
return {
return !!x ? {
id: a.id,
hasVerifiedBadge: x?.hasVerifiedBadge || false,
name: x?.name || "?",
displayName: x?.displayName || "?"
};
});
} : null;
}).filter(a=>!!a).filter(a=>a.id.toString()!=="-1");
return friendsList;
},
enabled: !!acct,

View File

@@ -0,0 +1,83 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { proxyFetch } from "@/lib/utils";
type Creator = {
id: number;
name: string;
type: string;
/** only used for 罗布乐思 (chinese roblox) */
isRNVAccount: boolean;
hasVerifiedBadge: boolean;
};
type PlaceDetails = {
id: number;
rootPlaceId: number;
name: string;
description: string;
creator: Creator;
playing: number;
visits: number;
maxPlayers: number;
created: string;
updated: string;
genre: string;
genre_l1?: string;
genre_l2?: string;
favoritedCount: number;
isFavoritedByUser: boolean;
universeAvatarType: string;
};
export function usePlaceDetails(placeId: number | string | null) {
const { data: placeDetails } = useQuery<PlaceDetails | null | false>({
queryKey: ["place-details", placeId],
queryFn: async () => {
if (!placeId) return null;
try {
// First: get universeId from place
const res1 = await proxyFetch(
`https://apis.roblox.com/universes/v1/places/${placeId}/universe`
);
if (!res1.ok) {
console.error(
`[usePlaceDetails] API Error ${res1.status} ${res1.statusText}`
);
return false;
}
const { universeId } = await res1.json();
if (!universeId) return false;
// Then: get universe details
const res2 = await proxyFetch(
`https://games.roblox.com/v1/games?universeIds=${universeId}`
);
if (!res2.ok) {
console.error(
`[usePlaceDetails] API Error ${res2.status} ${res2.statusText}`
);
return false;
}
const data = await res2.json();
if (!data?.data?.[0]) return false;
return data.data[0] as PlaceDetails;
} catch (err) {
console.error(
"[usePlaceDetails] Failed to fetch place details",
err
);
return false;
}
},
enabled: !!placeId,
staleTime: 300_000, // cache 5 minutes
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false
});
return placeDetails;
}

View File

@@ -3,6 +3,7 @@
import { useQuery } from "@tanstack/react-query";
import { useCurrentAccount } from "./useCurrentAccount";
import { proxyFetch } from "@/lib/utils";
import assert from "assert";
type PresenceData = {
userPresenceType: number;
@@ -47,14 +48,13 @@ export function useFriendsPresence(userIds: number[]) {
}
);
// assert is shit
if (!res.ok) {
console.error(
`[usePresence] API Error ${res.status} ${res.statusText}`
);
throw new Error(`API request failed with status ${res.status}`);
}
throw "wtf?";
};
const json = await res.json();
return (json.userPresences || []) as PresenceData[];
},
enabled: !!acct && sortedUserIds.length > 0,

View File

@@ -4,6 +4,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCurrentAccount } from "./useCurrentAccount";
import { proxyFetch } from "@/lib/utils";
import { useEffect } from "react";
import assert from "assert";
export function useRobuxBalance() {
const acct = useCurrentAccount();
@@ -17,6 +18,7 @@ export function useRobuxBalance() {
const res = await proxyFetch(
`https://economy.roblox.com/v1/users/${acct.id}/currency`
);
assert(res.ok);
const data = await res.json();
return data.robux || 0;
} catch {