diff --git a/app/api/BucketConfig.ts b/app/api/BucketConfig.ts new file mode 100644 index 0000000000..74b821c809 --- /dev/null +++ b/app/api/BucketConfig.ts @@ -0,0 +1,56 @@ +class BucketConfig { + isLocalDev: boolean; + + constructor() { + this.isLocalDev = + document.location.host.includes("localhost") || + document.location.host.includes("jungle.horse") || + document.location.host.startsWith("dev."); + } + + // TODO: Prevent callers with undefined/null paths + getGcsUrl(bucketRelativePath: string | undefined | null): string { + let bucket = this.getBucket(); + let path = bucketRelativePath; + if (path !== undefined && path !== null && !path.startsWith("/")) { + path = "/" + path; + } + return `https://storage.googleapis.com/${bucket}${path}`; + } + + private getBucket(): string { + return this.isLocalDev ? "dev-vocodes-public" : "vocodes-public"; + } + + getCdnUrl( + bucketRelativePath: string, + width?: number, + quality?: number + ): string { + // const basePath = this.isLocalDev + // ? "https://dev-cdn.fakeyou.com" + // : "https://cdn.fakeyou.com"; + const basePath = "https://cdn.fakeyou.com"; + let path = bucketRelativePath?.startsWith("/") + ? bucketRelativePath + : "/" + bucketRelativePath; + let resizeParams = ""; + if (width || quality) { + resizeParams = "cdn-cgi/image/"; + if (width) { + resizeParams += `width=${width},`; + } + if (quality) { + resizeParams += `quality=${quality},`; + } + resizeParams = resizeParams.slice(0, -1); + } + if (resizeParams) { + return `${basePath}/${resizeParams}${path}`; + } else { + return `${basePath}${path}`; + } + } +} + +export { BucketConfig }; diff --git a/app/api/GetApiHost.ts b/app/api/GetApiHost.ts new file mode 100644 index 0000000000..e2a7547b83 --- /dev/null +++ b/app/api/GetApiHost.ts @@ -0,0 +1,34 @@ +// interface ApiDetails { hostname: string, useSsl: boolean, } + +const domainWithoutPort = document.location.host.split(":")[0]; + +const hostConfig = (host: string, useSsl = true) => ({ + formatUrl: (endpoint = "") => `${ useSsl ? "https" : "http" }://${ host + endpoint }`, + host, + useSsl, +}); + +export default function GetApiHost() { + switch (domainWithoutPort) { + case "fakeyou.com": + case "staging.fakeyou.com": + return hostConfig("api.fakeyou.com"); + case "storyteller.ai": + case "staging.storyteller.ai": + //return hostConfig("api.storyteller.ai"); + return hostConfig("api.fakeyou.com"); + case "storyteller.stream": // Storyteller.stream is deprecated and will be decommissioned in the future. + return hostConfig("api.storyteller.stream"); + case "devproxy.fakeyou.com": + return hostConfig("api.fakeyou.com"); + case "devproxy.storyteller.ai": + //return hostConfig("api.storyteller.ai"); + return hostConfig("api.fakeyou.com"); + case "dev.fakeyou.com": + return hostConfig("api.dev.fakeyou.com:12345",false); // false disables SSL + default: + return document.location.host.includes("localhost") ? + hostConfig("localhost:12345",document.location.protocol === "https:") : + hostConfig("api.fakeyou.com"); // default + } +}; \ No newline at end of file diff --git a/app/api/MakeRequest.ts b/app/api/MakeRequest.ts new file mode 100644 index 0000000000..b287051b39 --- /dev/null +++ b/app/api/MakeRequest.ts @@ -0,0 +1,59 @@ +import GetApiHost from "./GetApiHost"; + +// const { formatUrl } = GetApiHost(); + +type UrlRoutingFunction = (urlRouteArgs: UrlRouteArgs) => string; + +interface RequestHeaders { + [name: string]: string; +} + +interface RouteSetup { + method: string; + multipart?: boolean; + routingFunction: UrlRoutingFunction; +} + +const METHOD_OMITS_BODY: { [key: string]: boolean } = { + DELETE: false, + GET: true, + OPTIONS: true, + PATCH: false, + POST: false, + PUT: false, +}; + +const MakeRequest = (routeSetup: RouteSetup) : (urlRouteArgs: UrlRouteArgs, request: Request, queries?: UrlParams) => Promise => { + return async function(urlRouteArgs: UrlRouteArgs, request: Request, queries?: any) : Promise { + const newQueries = queries ? Object.keys(queries).map((key, i) => { + return `${ i ? "&" : "" }${ key }=${ Array.isArray(queries[key]) ? queries[key].join(`&${ key }=`) : queries[key] }` + }).join("") : null; + + const endpoint = `${ routeSetup.routingFunction(urlRouteArgs) }${ newQueries ? "?" + newQueries : "" }`; + const method = routeSetup.method; + const methodOmitsBody = METHOD_OMITS_BODY[method] || false; + + // return fetch(formatUrl(endpoint), { + return fetch(`https://api.fakeyou.com${ endpoint }`, { + method, + headers: { + "Accept": "application/json", + ...methodOmitsBody? {} : { "Content-Type": "application/json" } + }, + credentials: 'include', + ...methodOmitsBody ? {} : { body: JSON.stringify(request) }, + }) + .then(res => res.json()) + .then(res => { + if (!res.success && res && (res.voice_token || res.dataset_token)) { + return res; + } + else if (res && "success" in res) { + return res; + } else Promise.reject(); + }) + .catch(e => ({ success : false })); + } +}; + +export default MakeRequest; \ No newline at end of file diff --git a/app/api/media_files/GetMediaByUser.ts b/app/api/media_files/GetMediaByUser.ts new file mode 100644 index 0000000000..4ea5c95f6d --- /dev/null +++ b/app/api/media_files/GetMediaByUser.ts @@ -0,0 +1,26 @@ +import MakeRequest from "../MakeRequest"; +import { MediaFile } from "./GetMedia"; +import { Pagination } from "../_common/SharedFetchTypes"; + +export interface GetMediaRequest {} + +export interface GetMediaListResponse { + pagination: Pagination; + success: boolean; + results: MediaFile[]; +} + +export interface GetMediaParams { + page_index: number; +} + +export const GetMediaByUser = MakeRequest< + string, + GetMediaRequest, + GetMediaListResponse, + GetMediaParams +>({ + method: "GET", + routingFunction: (userToken: string) => + `/v1/media_files/list/user/${userToken}`, +}); \ No newline at end of file diff --git a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx index 2dbb3bd88b..8fba5aaf57 100644 --- a/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx +++ b/app/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ObjectsTab.tsx @@ -1,3 +1,4 @@ +import { useState, useEffect } from "react"; import { AssetFilterOption, AssetType } from "~/pages/PageEnigma/models"; import { useSignals } from "@preact/signals-react/runtime"; import { @@ -11,17 +12,67 @@ import { Button } from "~/components"; import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons"; import { twMerge } from "tailwind-merge"; import { shapeItems } from "~/pages/PageEnigma/store"; +import { GetMediaByUser, GetMediaListResponse } from "~/api/media_files/GetMediaByUser"; +import { AssetFilterOption, MediaItem } from "~/pages/PageEnigma/models"; +import { BucketConfig } from "~/api/BucketConfig"; interface Props { type: AssetType; } +export enum FetchStatus { + paused, + ready, + in_progress, + success, + error +} + export const ObjectsTab = ({ type }: Props) => { useSignals(); + const [objects,objectsSet] = useState<{ value: MediaItem[] }>({ + value:[{ + media_id: "", + name: "", + type: "", + version: 1, + }] + }); + + const [status,statusSet] = useState(FetchStatus.ready); + + useEffect(() => { + if (status === FetchStatus.ready && type !== AssetType.CHARACTER) { + statusSet(FetchStatus.in_progress); + GetMediaByUser("echelon",{},{ + filter_media_type: "glb" + }) + .then((res: GetMediaListResponse) => { + if (res.success && res.results) { + statusSet(FetchStatus.success); + objectsSet({ + value: res.results.map((item,i) => { + let bucketConfig = new BucketConfig(); + let itemThumb = bucketConfig.getCdnUrl(item.cover_image.maybe_cover_image_public_bucket_path,600,100); + return { + media_id: item.token, + name: item.maybe_title, + type: item.media_type, + version: 1, + ...item.cover_image.maybe_cover_image_public_bucket_path ? { + thumbnail: itemThumb + } : {} + } + }) + }); + } + }); + } + },[status,type]); const assetFilter = type === AssetType.CHARACTER ? characterFilter : objectFilter; - const items = type === AssetType.CHARACTER ? characterItems : objectItems; + const items = type === AssetType.CHARACTER ? characterItems : objects; return ( <> @@ -77,4 +128,4 @@ export const ObjectsTab = ({ type }: Props) => { ); -}; +}; \ No newline at end of file