diff --git a/apps/cyberstorm-remix/app/p/listingUtils.ts b/apps/cyberstorm-remix/app/p/listingUtils.ts index da17906c2..c3a70a8c4 100644 --- a/apps/cyberstorm-remix/app/p/listingUtils.ts +++ b/apps/cyberstorm-remix/app/p/listingUtils.ts @@ -5,6 +5,7 @@ export interface ListingIdentifiers { communityId: string; namespaceId: string; packageId: string; + packageVersion?: string; } /** @@ -16,12 +17,13 @@ export async function getPublicListing( dapper: DapperTs, ids: ListingIdentifiers ) { - const { communityId, namespaceId, packageId } = ids; + const { communityId, namespaceId, packageId, packageVersion } = ids; try { return await dapper.getPackageListingDetails( communityId, namespaceId, - packageId + packageId, + packageVersion ); } catch (e) { if (isApiError(e) && e.response.status === 404) { @@ -42,13 +44,14 @@ export async function getPrivateListing( dapper: DapperTs, ids: ListingIdentifiers ) { - const { communityId, namespaceId, packageId } = ids; + const { communityId, namespaceId, packageId, packageVersion } = ids; try { return await dapper.getPackageListingDetails( communityId, namespaceId, - packageId + packageId, + packageVersion ); } catch (e) { const is404 = isApiError(e) && e.response?.status === 404; @@ -61,6 +64,7 @@ export async function getPrivateListing( communityId, namespaceId, packageId, + packageVersion, true ); diff --git a/apps/cyberstorm-remix/app/p/packageVersion.tsx b/apps/cyberstorm-remix/app/p/packageVersion.tsx index 2502d75cd..26fdd1bed 100644 --- a/apps/cyberstorm-remix/app/p/packageVersion.tsx +++ b/apps/cyberstorm-remix/app/p/packageVersion.tsx @@ -33,7 +33,6 @@ import { type ReactElement, Suspense, useEffect, - useMemo, useRef, useState, } from "react"; @@ -53,68 +52,71 @@ import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { getTeamDetails } from "@thunderstore/dapper-ts/src/methods/team"; -import { isPromise } from "cyberstorm/utils/typeChecks"; -import { getPackageVersionDetails } from "@thunderstore/dapper-ts/src/methods/packageVersion"; +import { getPrivateListing, getPublicListing } from "./listingUtils"; +import { + type PackageListingDetails, + type TeamDetails, +} from "@thunderstore/dapper/types"; export async function loader({ params }: LoaderFunctionArgs) { - if ( - params.communityId && - params.namespaceId && - params.packageId && - params.packageVersion - ) { - const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); - const dapper = new DapperTs(() => { - return { - apiHost: publicEnvVariables.VITE_API_URL, - sessionId: undefined, - }; - }); + const { communityId, namespaceId, packageId, packageVersion } = params; - return { - communityId: params.communityId, - community: await dapper.getCommunity(params.communityId), - version: await dapper.getPackageVersionDetails( - params.namespaceId, - params.packageId, - params.packageVersion - ), - team: await dapper.getTeamDetails(params.namespaceId), - }; + if (!communityId || !namespaceId || !packageId || !packageVersion) { + throw new Response("Package not found", { status: 404 }); } - throw new Response("Package not found", { status: 404 }); + + const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]); + const dapper = new DapperTs(() => ({ + apiHost: publicEnvVariables.VITE_API_URL, + sessionId: undefined, + })); + + const listing = await getPublicListing(dapper, { + communityId, + namespaceId, + packageId, + packageVersion, + }); + + return { + community: await dapper.getCommunity(communityId), + listing, + packageVersion, + team: await dapper.getTeamDetails(namespaceId), + }; } export async function clientLoader({ params }: LoaderFunctionArgs) { - if ( - params.communityId && - params.namespaceId && - params.packageId && - params.packageVersion - ) { - const tools = getSessionTools(); - const dapper = new DapperTs(() => { - return { - apiHost: tools?.getConfig().apiHost, - sessionId: tools?.getConfig().sessionId, - }; - }); + const { communityId, namespaceId, packageId, packageVersion } = params; - return { - communityId: params.communityId, - community: dapper.getCommunity(params.communityId), - version: dapper.getPackageVersionDetails( - params.namespaceId, - params.packageId, - params.packageVersion - ), - team: dapper.getTeamDetails(params.namespaceId), - }; + if (!communityId || !namespaceId || !packageId || !packageVersion) { + throw new Response("Package not found", { status: 404 }); } - throw new Response("Package not found", { status: 404 }); + + const tools = getSessionTools(); + const config = tools.getConfig(); + const dapper = new DapperTs(() => ({ + apiHost: config.apiHost, + sessionId: config.sessionId, + })); + + const listing = await getPrivateListing(dapper, { + communityId, + namespaceId, + packageId, + packageVersion, + }); + + return { + community: dapper.getCommunity(communityId), + listing, + packageVersion, + team: dapper.getTeamDetails(namespaceId), + }; } +clientLoader.hydrate = true; + export function shouldRevalidate(arg: ShouldRevalidateFunctionArgs) { const oldPath = arg.currentUrl.pathname.split("/"); const newPath = arg.nextUrl.pathname.split("/"); @@ -131,7 +133,7 @@ export function shouldRevalidate(arg: ShouldRevalidateFunctionArgs) { } export default function PackageVersion() { - const { communityId, community, version, team } = useLoaderData< + const { community, listing, packageVersion, team } = useLoaderData< typeof loader | typeof clientLoader >(); @@ -151,48 +153,38 @@ export default function PackageVersion() { // https://react.dev/reference/react/StrictMode // If strict mode is removed from the entry.client.tsx, this should only run once useEffect(() => { - if (!startsHydrated.current && isHydrated) return; - if (isPromise(version)) { - version.then((versionData) => { - setFirstUploaded( - - ); - }); - } else { - setFirstUploaded( - - ); + if (!startsHydrated.current && isHydrated) { + return; } + + if (!listing) { + return; + } + + setFirstUploaded( + + ); }, []); // END: For sidebar meta dates const currentTab = location.pathname.split("/")[8] || "details"; - const versionAndCommunityPromise = useMemo( - () => Promise.all([version, community]), - [] - ); - - const versionAndTeamPromise = useMemo(() => Promise.all([version, team]), []); + if (!listing) { + return
Loading listing...
; + } return ( <> - - {(resolvedValue) => ( + + {(resolvedCommunity) => ( <> - + - + @@ -226,81 +215,59 @@ export default function PackageVersion() {
- - You are viewing a potentially older version of this package. - - } - > - - {(resolvedValue) => ( - - You are viewing a potentially older version of this - package.{" "} + + You are viewing a potentially older version of this package.{" "} + + View Latest Version + + + + + + + + + {listing.namespace} + + {listing.website_url ? ( - View Latest Version + {listing.website_url} + + + - - )} - - - + ) : null} + } > - - {(resolvedValue) => ( - - - - - - {resolvedValue.namespace} - - {resolvedValue.website_url ? ( - - {resolvedValue.website_url} - - - - - ) : null} - - } - > - {formatToDisplayName(resolvedValue.name)} - - )} - - + {formatToDisplayName(listing.name)} +
- - } - > - - {(resolvedValue) => ( - <> - - - Details - - - Required ({resolvedValue.dependency_count}) - - - Versions - - - - )} - - + + + + Details + + + Required ({listing.dependency_count}) + + + Versions + + +
@@ -470,15 +390,15 @@ export default function PackageVersion() { } const Actions = memo(function Actions(props: { - team: Awaited>; - version: Awaited>; + listing: PackageListingDetails; + team: TeamDetails; }) { - const { team, version } = props; + const { listing, team } = props; return (
@@ -506,7 +426,7 @@ const Actions = memo(function Actions(props: { function packageMeta( firstUploaded: ReactElement | undefined, - version: Awaited> + listing: PackageListingDetails ) { return (
@@ -517,13 +437,13 @@ function packageMeta(
Downloads
- {formatInteger(version.download_count)} + {formatInteger(listing.download_count)}
Size
- {formatFileSize(version.size)} + {formatFileSize(listing.size)}
@@ -531,12 +451,12 @@ function packageMeta(
- {version.full_version_name} + {listing.full_version_name} - +
diff --git a/packages/dapper-fake/src/fakers/package.ts b/packages/dapper-fake/src/fakers/package.ts index 9ed6585fb..2dffbac05 100644 --- a/packages/dapper-fake/src/fakers/package.ts +++ b/packages/dapper-fake/src/fakers/package.ts @@ -127,12 +127,13 @@ export const getFakePackagePermissions = async ( export const getFakePackageListingDetails = async ( community: string, namespace: string, - name: string + name: string, + version?: string ) => { const seed = `${community}-${namespace}-${name}`; setSeed(seed); - const version = getVersionNumber(); + const ver = version ?? getVersionNumber(); const dependencies = await getFakeDependencies(community, namespace, name); return { @@ -143,10 +144,10 @@ export const getFakePackageListingDetails = async ( dependant_count: faker.number.int({ min: 0, max: 2000 }), dependencies, dependency_count: dependencies.length, - download_url: `https://thunderstore.io/package/download/${namespace}/${name}/${version}/`, - full_version_name: `${namespace}-${name}-${version}}`, + download_url: `https://thunderstore.io/package/download/${namespace}/${name}/${ver}/`, + full_version_name: `${namespace}-${name}-${ver}`, has_changelog: true, - install_url: `ror2mm://v1/install/thunderstore.io/${namespace}/${name}/${version}/`, + install_url: `ror2mm://v1/install/thunderstore.io/${namespace}/${name}/${ver}/`, latest_version_number: getVersionNumber(), team: { name: faker.word.words(3), diff --git a/packages/dapper-ts/src/methods/packageListings.ts b/packages/dapper-ts/src/methods/packageListings.ts index 3894a91f6..604e21ab5 100644 --- a/packages/dapper-ts/src/methods/packageListings.ts +++ b/packages/dapper-ts/src/methods/packageListings.ts @@ -123,6 +123,7 @@ export async function getPackageListingDetails( communityId: string, namespaceId: string, packageName: string, + version: string | undefined = undefined, useSession = false ) { const data = await fetchPackageListingDetails({ @@ -131,6 +132,7 @@ export async function getPackageListingDetails( community_id: communityId, namespace_id: namespaceId, package_name: packageName, + version_number: version, }, data: {}, queryParams: {}, diff --git a/packages/dapper/src/types/methods.ts b/packages/dapper/src/types/methods.ts index d27a4ce84..62ab6443c 100644 --- a/packages/dapper/src/types/methods.ts +++ b/packages/dapper/src/types/methods.ts @@ -44,7 +44,8 @@ export type GetPackageChangelog = ( export type GetPackageListingDetails = ( community: string, namespace: string, - name: string + name: string, + version?: string ) => Promise; export type GetPackageListings = ( diff --git a/packages/thunderstore-api/src/get/packageListingDetails.ts b/packages/thunderstore-api/src/get/packageListingDetails.ts index 189900891..269091faf 100644 --- a/packages/thunderstore-api/src/get/packageListingDetails.ts +++ b/packages/thunderstore-api/src/get/packageListingDetails.ts @@ -16,7 +16,11 @@ export async function fetchPackageListingDetails( props: ApiEndpointProps ): Promise { const { config, params, useSession } = props; - const path = `${BASE_LISTING_PATH}${params.community_id}/${params.namespace_id}/${params.package_name}/`; + let path = `${BASE_LISTING_PATH}${params.community_id}/${params.namespace_id}/${params.package_name}/`; + + if (params.version_number) { + path = `${path}v/${params.version_number}/`; + } return await apiFetch({ args: { diff --git a/packages/thunderstore-api/src/schemas/requestSchemas.ts b/packages/thunderstore-api/src/schemas/requestSchemas.ts index 491b4fdce..e4ddb568e 100644 --- a/packages/thunderstore-api/src/schemas/requestSchemas.ts +++ b/packages/thunderstore-api/src/schemas/requestSchemas.ts @@ -170,6 +170,7 @@ export const packageListingDetailsRequestParamsSchema = z.object({ community_id: z.string(), namespace_id: z.string(), package_name: z.string(), + version_number: z.string().optional(), }); export type PackageListingDetailsRequestParams = z.infer<