From e3e4b9fc341a34523a91f25837cb1013248c1e0e Mon Sep 17 00:00:00 2001 From: PepperLola Date: Mon, 1 Jul 2024 11:38:41 -0700 Subject: [PATCH 01/20] show cached robots and fields in import mira modal --- fission/src/aps/APSDataManagement.ts | 52 ++++++- .../ui/modals/mirabuf/ImportMirabufModal.tsx | 133 +++++++++++------- 2 files changed, 135 insertions(+), 50 deletions(-) diff --git a/fission/src/aps/APSDataManagement.ts b/fission/src/aps/APSDataManagement.ts index 0c6c4a6cec..e687429e58 100644 --- a/fission/src/aps/APSDataManagement.ts +++ b/fission/src/aps/APSDataManagement.ts @@ -1,9 +1,35 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { MainHUD_AddToast } from "@/ui/components/MainHUD"; import APS from "./APS" export const FOLDER_DATA_TYPE = "folders" export const ITEM_DATA_TYPE = "items" +export class APSDataError extends Error { + error_code: string; + title: string; + detail: string; + + constructor(error_code: string, title: string, detail: string) { + super(title); + this.name = "APSDataError"; + this.error_code = error_code; + this.title = title; + this.detail = detail; + } +} + +export type APSHubError = { + Id: string | null + HttpStatusCode: string + ErrorCode: string + Title: string + Detail: string + AboutLink: string | null + Source: string | null + meta: object | null +} + export interface Hub { id: string name: string @@ -59,7 +85,7 @@ export class Item extends Data { } export async function getHubs(): Promise { - const auth = APS.auth + const auth = await APS.getAuth() if (!auth) { return undefined } @@ -73,6 +99,12 @@ export async function getHubs(): Promise { }) .then(x => x.json()) .then(x => { + if (x.meta.warnings.length > 0) { + const warnings: APSHubError[] = x.meta.warnings; + warnings.forEach(w => console.error(w.Title)) + const first = warnings[0] + throw new APSDataError(first.ErrorCode, first.Title, first.Detail) + } if ((x.data as any[]).length > 0) { return (x.data as any[]).map(y => { return { id: y.id, name: y.attributes.name } @@ -83,12 +115,20 @@ export async function getHubs(): Promise { }) } catch (e) { console.error("Failed to get hubs") + console.error(e) + console.log(auth) + console.log(APS.userInfo) + if (e instanceof APSDataError) { + MainHUD_AddToast('error', e.title, e.detail); + } else if (e instanceof Error) { + MainHUD_AddToast('error', 'Failed to get hubs.', e.message); + } return undefined } } export async function getProjects(hub: Hub): Promise { - const auth = APS.auth + const auth = await APS.getAuth() if (!auth) { return undefined } @@ -116,12 +156,15 @@ export async function getProjects(hub: Hub): Promise { }) } catch (e) { console.error("Failed to get hubs") + if (e instanceof Error) { + MainHUD_AddToast('error', 'Failed to get hubs.', e.message); + } return undefined } } export async function getFolderData(project: Project, folder: Folder): Promise { - const auth = APS.auth + const auth = await APS.getAuth() if (!auth) { return undefined } @@ -157,6 +200,9 @@ export async function getFolderData(project: Project, folder: Folder): Promise = ({ modalId }) => { const [selectedHub, setSelectedHub] = useState(undefined) const [selectedProject, setSelectedProject] = useState(undefined) const [selectedFolder, setSelectedFolder] = useState(undefined) + const cachedRobots = GetMap(MiraType.ROBOT) + const cachedFields = GetMap(MiraType.FIELD) + const robotPaths = cachedRobots ? Object.keys(cachedRobots) : [] + const fieldPaths = cachedFields ? Object.keys(cachedFields) : [] + console.log(cachedRobots, cachedFields) const [hubs, setHubs] = useState(undefined) useEffect(() => { - ;(async () => { + (async () => { setHubs(await getHubs()) })() }, []) const [projects, setProjects] = useState(undefined) useEffect(() => { - ;(async () => { + (async () => { if (selectedHub) { setProjects(await getProjects(selectedHub)) } @@ -50,7 +56,7 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { const [folderData, setFolderData] = useState(undefined) useEffect(() => { - ;(async () => { + (async () => { if (selectedProject) { console.log("Project has been selected") if (selectedFolder) { @@ -75,6 +81,81 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { } }, [folderData]) + let cachedElements; + if (robotPaths.length > 0) { + cachedElements = robotPaths.map(robotPath => + ItemCard({ + name: robotPath, + id: robotPath, + buttonText: "import", + onClick: () => console.log(`Selecting cached robot: ${robotPath}`) + }) + ) + } + if (fieldPaths.length > 0) { + const fieldElements = fieldPaths.map(fieldPath => + ItemCard({ + name: fieldPath, + id: fieldPath, + buttonText: "import", + onClick: () => console.log(`Selecting cached field: ${fieldPath}`) + }) + ); + cachedElements = cachedElements ? cachedElements.concat(fieldElements) : fieldElements + } + + let hubElements; + + if (!selectedHub) { + hubElements = hubs?.map(x => + ItemCard({ + name: x.name, + id: x.id, + buttonText: ">", + onClick: () => setSelectedHub(x), + }) + ) + } else { + if (!selectedProject) { + hubElements = projects?.map(x => + ItemCard({ + name: x.name, + id: x.id, + buttonText: ">", + onClick: () => setSelectedProject(x), + }) + ) + } else { + hubElements = folderData?.map(x => + x instanceof Folder + ? ItemCard({ + name: `DIR: ${x.displayName}`, + id: x.id, + buttonText: ">", + onClick: () => setSelectedFolder(x), + }) + : x instanceof Item + ? ItemCard({ + name: `${x.displayName}`, + id: x.id, + buttonText: "import", + onClick: () => { + console.log(`Selecting ${x.displayName} (${x.id})`) + }, + }) + : ItemCard({ + name: `${x.type}: ${x.id}`, + id: x.id, + buttonText: "---", + onClick: () => { + console.log(`Selecting (${x.id})`) + }, + }) + ) + } + } + console.log('HUB ELEMENTS', hubElements) + const displayElements = (cachedElements || []).concat(hubElements) return ( = ({ modalId }) => { )}
- {!selectedHub - ? hubs?.map(x => - ItemCard({ - name: x.name, - id: x.id, - buttonText: ">", - onClick: () => setSelectedHub(x), - }) - ) - : !selectedProject - ? projects?.map(x => - ItemCard({ - name: x.name, - id: x.id, - buttonText: ">", - onClick: () => setSelectedProject(x), - }) - ) - : folderData?.map(x => - x instanceof Folder - ? ItemCard({ - name: `DIR: ${x.displayName}`, - id: x.id, - buttonText: ">", - onClick: () => setSelectedFolder(x), - }) - : x instanceof Item - ? ItemCard({ - name: `${x.displayName}`, - id: x.id, - buttonText: "import", - onClick: () => { - console.log(`Selecting ${x.displayName} (${x.id})`) - }, - }) - : ItemCard({ - name: `${x.type}: ${x.id}`, - id: x.id, - buttonText: "---", - onClick: () => { - console.log(`Selecting (${x.id})`) - }, - }) - )} + {displayElements && displayElements.length > 0 ? + displayElements :

Nothing to display.

}
) From 47048780582978ebb4276c1c6b87c6e075bc92a4 Mon Sep 17 00:00:00 2001 From: PepperLola Date: Mon, 1 Jul 2024 14:07:55 -0700 Subject: [PATCH 02/20] show default models from assetpack and delete button for cache --- fission/src/mirabuf/MirabufLoader.ts | 17 +++- .../ui/modals/mirabuf/ImportMirabufModal.tsx | 77 ++++++++++++++----- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/fission/src/mirabuf/MirabufLoader.ts b/fission/src/mirabuf/MirabufLoader.ts index 6144874fa6..d01afaec88 100644 --- a/fission/src/mirabuf/MirabufLoader.ts +++ b/fission/src/mirabuf/MirabufLoader.ts @@ -7,6 +7,8 @@ const root = await navigator.storage.getDirectory() const robotFolderHandle = await root.getDirectoryHandle(robots, { create: true }) const fieldFolderHandle = await root.getDirectoryHandle(fields, { create: true }) +export type MiraCache = { [key: string]: string } + export function UnzipMira(buff: Uint8Array): Uint8Array { // Check if file is gzipped via magic gzip numbers 31 139 if (buff[0] == 31 && buff[1] == 139) { @@ -45,7 +47,7 @@ export async function ClearMira() { window.localStorage.removeItem(fields) } -export function GetMap(type: MiraType): any { +export function GetMap(type: MiraType): MiraCache | undefined { const miraJSON = window.localStorage.getItem(type == MiraType.ROBOT ? robots : fields) if (miraJSON != undefined) { @@ -57,6 +59,15 @@ export function GetMap(type: MiraType): any { } } +export function DeleteCached(type: MiraType, id: string) { + // window.localStorage.getItem(type == MiraType.ROBOT ? robots : fields) + if (type == MiraType.ROBOT) { + robotFolderHandle.removeEntry(id) + } else { + fieldFolderHandle.removeEntry(id) + } +} + async function LoadAndCacheMira(fetchLocation: string, type: MiraType): Promise { try { const backupID = Date.now().toString() @@ -78,7 +89,7 @@ async function LoadAndCacheMira(fetchLocation: string, type: MiraType): Promise< await writable.close() // Local cache map - let map: { [k: string]: string } = GetMap(type) ?? {} + const map: MiraCache = GetMap(type) ?? {} map[fetchLocation] = backupID window.localStorage.setItem(type == MiraType.ROBOT ? robots : fields, JSON.stringify(map)) @@ -93,7 +104,7 @@ async function LoadMirabufCache( fetchLocation: string, targetID: string, type: MiraType, - map: { [k: string]: string } + map: MiraCache ): Promise { try { const fileHandle = diff --git a/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx b/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx index e00a144166..95148dc70d 100644 --- a/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx +++ b/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx @@ -4,16 +4,18 @@ import { FaPlus } from "react-icons/fa6" import Button from "@/components/Button" import Label, { LabelSize } from "@/components/Label" import { Data, Folder, Hub, Item, Project, getFolderData, getHubs, getProjects } from "@/aps/APSDataManagement" -import { GetMap, MiraType } from "@/mirabuf/MirabufLoader" +import { DeleteCached, GetMap, MiraType } from "@/mirabuf/MirabufLoader" interface ItemCardProps { id: string name: string buttonText: string + secondaryButtonText?: string onClick: () => void + secondaryOnClick?: () => void } -const ItemCard: React.FC = ({ id, name, buttonText, onClick }) => { +const ItemCard: React.FC = ({ id, name, buttonText, secondaryButtonText, onClick, secondaryOnClick }) => { return (
= ({ id, name, buttonText, onClick }) => >
) } +export type MiraManifest = { + robots: string[] + fields: string[] +} + const ImportMirabufModal: React.FC = ({ modalId }) => { // update tooltip based on type of drivetrain, receive message from Synthesis // const { showTooltip } = useTooltipControlContext() const [selectedHub, setSelectedHub] = useState(undefined) - const [selectedProject, setSelectedProject] = useState(undefined) - const [selectedFolder, setSelectedFolder] = useState(undefined) - const cachedRobots = GetMap(MiraType.ROBOT) - const cachedFields = GetMap(MiraType.FIELD) - const robotPaths = cachedRobots ? Object.keys(cachedRobots) : [] - const fieldPaths = cachedFields ? Object.keys(cachedFields) : [] + const [selectedProject, setSelectedProject] = useState() + const [selectedFolder, setSelectedFolder] = useState() + const [manifest, setManifest] = useState() + const cachedRobots = Object.entries(GetMap(MiraType.ROBOT) || {}) + const cachedFields = Object.entries(GetMap(MiraType.FIELD) || {}) console.log(cachedRobots, cachedFields) + useEffect(() => { + fetch('/api/mira/manifest.json').then(x => x.json()).then(data => { + setManifest(data) + }) + }, []) + const [hubs, setHubs] = useState(undefined) useEffect(() => { (async () => { @@ -82,28 +95,54 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { }, [folderData]) let cachedElements; - if (robotPaths.length > 0) { - cachedElements = robotPaths.map(robotPath => + if (cachedRobots.length > 0) { + cachedElements = cachedRobots.map(([key, value]) => ItemCard({ - name: robotPath, - id: robotPath, + name: key, + id: value, buttonText: "import", - onClick: () => console.log(`Selecting cached robot: ${robotPath}`) + onClick: () => console.log(`Selecting cached robot: ${key}`), + secondaryButtonText: "delete", + secondaryOnClick: () => { + console.log(`Deleting cache of: ${key}, ${value}`) + DeleteCached(MiraType.ROBOT, value); + } }) ) } - if (fieldPaths.length > 0) { - const fieldElements = fieldPaths.map(fieldPath => + if (cachedFields.length > 0) { + const fieldElements = cachedFields.map(([key, value]) => ItemCard({ - name: fieldPath, - id: fieldPath, + name: key, + id: value, buttonText: "import", - onClick: () => console.log(`Selecting cached field: ${fieldPath}`) + onClick: () => console.log(`Selecting cached field: ${key}`), + secondaryButtonText: "delete", + secondaryOnClick: () => { + console.log(`Deleting cache of: ${key}`) + DeleteCached(MiraType.FIELD, value); + } }) ); cachedElements = cachedElements ? cachedElements.concat(fieldElements) : fieldElements } + let remotePaths = (manifest ? manifest.robots.concat(manifest.fields) : []); + if (cachedRobots.length > 0) { + remotePaths = remotePaths.filter(path => !cachedRobots.some(([key, _value]) => key.includes(path))) + } + if (cachedFields.length > 0) { + remotePaths = remotePaths.filter(path => !cachedFields.some(([key, _value]) => key.includes(path))) + } + const remoteElements = remotePaths.map(path => + ItemCard({ + name: path, + id: path, + buttonText: "import", + onClick: () => console.log(`Selecting remote: ${path}`) + }) + ); + let hubElements; if (!selectedHub) { @@ -155,7 +194,7 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { } } console.log('HUB ELEMENTS', hubElements) - const displayElements = (cachedElements || []).concat(hubElements) + const displayElements = (cachedElements || []).concat(hubElements, remoteElements) return ( Date: Mon, 1 Jul 2024 15:21:50 -0700 Subject: [PATCH 03/20] add sign-in button and don't try requests when not signed in --- fission/src/aps/APS.ts | 4 +-- .../ui/modals/mirabuf/ImportMirabufModal.tsx | 27 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/fission/src/aps/APS.ts b/fission/src/aps/APS.ts index 78997d2bd4..61b5197e1b 100644 --- a/fission/src/aps/APS.ts +++ b/fission/src/aps/APS.ts @@ -8,7 +8,7 @@ export const APS_USER_INFO_UPDATE_EVENT = "aps_user_info_update" const CLIENT_ID = "GCxaewcLjsYlK8ud7Ka9AKf9dPwMR3e4GlybyfhAK2zvl3tU" -interface APSAuth { +export interface APSAuth { access_token: string refresh_token: string expires_in: number @@ -16,7 +16,7 @@ interface APSAuth { token_type: number } -interface APSUserInfo { +export interface APSUserInfo { name: string picture: string givenName: string diff --git a/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx b/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx index 95148dc70d..e91a62f33a 100644 --- a/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx +++ b/fission/src/ui/modals/mirabuf/ImportMirabufModal.tsx @@ -5,6 +5,7 @@ import Button from "@/components/Button" import Label, { LabelSize } from "@/components/Label" import { Data, Folder, Hub, Item, Project, getFolderData, getHubs, getProjects } from "@/aps/APSDataManagement" import { DeleteCached, GetMap, MiraType } from "@/mirabuf/MirabufLoader" +import APS, { APSAuth } from "@/aps/APS" interface ItemCardProps { id: string @@ -41,6 +42,12 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { const [selectedProject, setSelectedProject] = useState() const [selectedFolder, setSelectedFolder] = useState() const [manifest, setManifest] = useState() + const [auth, setAuth] = useState() + + useEffect(() => { + APS.getAuth().then(setAuth) + }, []) + const cachedRobots = Object.entries(GetMap(MiraType.ROBOT) || {}) const cachedFields = Object.entries(GetMap(MiraType.FIELD) || {}) console.log(cachedRobots, cachedFields) @@ -54,23 +61,23 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { const [hubs, setHubs] = useState(undefined) useEffect(() => { (async () => { - setHubs(await getHubs()) + if (auth) setHubs(await getHubs()) })() - }, []) + }, [auth]) const [projects, setProjects] = useState(undefined) useEffect(() => { (async () => { - if (selectedHub) { + if (auth && selectedHub) { setProjects(await getProjects(selectedHub)) } })() - }, [selectedHub]) + }, [auth, selectedHub]) const [folderData, setFolderData] = useState(undefined) useEffect(() => { (async () => { - if (selectedProject) { + if (auth && selectedProject) { console.log("Project has been selected") if (selectedFolder) { console.log(`Selecting folder '${selectedFolder.displayName}'`) @@ -84,15 +91,15 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { } } })() - }, [selectedProject, selectedFolder]) + }, [auth, selectedProject, selectedFolder]) useEffect(() => { - if (folderData) { + if (auth && folderData) { console.log(`${folderData.length} items in folder data`) } else { console.log("No folder data") } - }, [folderData]) + }, [auth, folderData]) let cachedElements; if (cachedRobots.length > 0) { @@ -226,8 +233,10 @@ const ImportMirabufModal: React.FC = ({ modalId }) => { + ) +} + +const ButtonSecondary: React.FC = ({ value, onClick }) => { + return ( + + ) +} + +interface ItemCardProps { + id: string + name: string + primaryButtonNode: ReactNode + secondaryButtonNode?: ReactNode + primaryOnClick: () => void + secondaryOnClick?: () => void +} + +const ItemCard: React.FC = ({ + id, + name, + primaryButtonNode, + secondaryButtonNode, + primaryOnClick, + secondaryOnClick, +}) => { + return ( + + {name.replace(/.mira$/, '')} + + + {secondaryButtonNode && secondaryOnClick && ( + + )} + + + + ) +} + +export type MiraManifest = { + robots: MirabufRemoteInfo[] + fields: MirabufRemoteInfo[] +} + +function GetCacheInfo(miraType: MiraType): MirabufCacheInfo[] { + return Object.values(MirabufCachingService.GetCacheMap(miraType)) +} + +function SpawnCachedMira(info: MirabufCacheInfo, type: MiraType) { + MirabufCachingService.Get(info.id, type).then(assembly => { + if (assembly) { + CreateMirabuf(assembly).then(x => { + if (x) { + World.SceneRenderer.RegisterSceneObject(x) + } + }) + + if (!info.name) + MirabufCachingService.CacheInfo(info.cacheKey, type, assembly.info?.name ?? undefined) + } else { + console.error("Failed to spawn robot") + } + }) +} + +const ImportMirabufPanel: React.FC = ({ panelId }) => { + const { showTooltip } = useTooltipControlContext() + const { closePanel } = usePanelControlContext() + + const [cachedRobots, setCachedRobots] = useState(GetCacheInfo(MiraType.ROBOT)) + const [cachedFields, setCachedFields] = useState(GetCacheInfo(MiraType.FIELD)) + + + const [manifest, setManifest] = useState() + const [files, setFiles] = useState() + const [apsFilesState, setApsFilesState] = useState({ isDone: false, message: 'Retrieving APS Hubs...' }) + const auth = useMemo(async () => await APS.getAuth(), []) + + const [viewType, setViewType] = useState(MiraType.ROBOT) + + // Get APS Mirabuf Data, Load into files. + useEffect(() => { + (async () => { + if (await auth) { + getHubs().then(async hubs => { + if (!hubs) { + setApsFilesState({ isDone: true, message: 'Failed to load APS Hubs' }) + return + } + const fileData: Data[] = [] + for (const hub of hubs) { + const projects = await getProjects(hub) + if (!projects) continue + for (const project of projects) { + setApsFilesState({ isDone: false, message: `Searching Project '${project.name}'` }) + const data = await searchRootForMira(project) + if (data) fileData.push(...data) + } + } + setApsFilesState({ isDone: true, message: `Found ${fileData.length} file${fileData.length == 1 ? '' : 's'}` }) + setFiles(fileData) + }) + } + })() + }, [auth]) + + // Get Default Mirabuf Data, Load into manifest. + useEffect(() => { + (async () => { + fetch(`/api/mira/manifest.json`) + .then(x => x.json()) + .then(x => { + const map = MirabufCachingService.GetCacheMap(MiraType.ROBOT) + const robots: MirabufRemoteInfo[] = [] + for (const src of x["robots"]) { + if (typeof src == "string") { + const str = `/api/mira/Robots/${src}` + if (!map[str]) robots.push({ displayName: src, src: str }) + } else { + if (!map[src["src"]]) robots.push({ displayName: src["displayName"], src: src["src"] }) + } + } + const fields: MirabufRemoteInfo[] = [] + for (const src of x["fields"]) { + if (typeof src == "string") { + const str = `/api/mira/Fields/${src}` + if (!map[str]) fields.push({ displayName: src, src: str }) + } else { + if (!map[src["src"]]) fields.push({ displayName: src["displayName"], src: src["src"] }) + } + } + setManifest({ + robots, + fields + }) + }) + })() + }, []) + + // Select a mirabuf assembly from the cache. + const selectCache = useCallback((info: MirabufCacheInfo, type: MiraType) => { + SpawnCachedMira(info, type) + + showTooltip("controls", [ + { control: "WASD", description: "Drive" }, + { control: "E", description: "Intake" }, + { control: "Q", description: "Dispense" }, + ]) + + closePanel(panelId) + }, [showTooltip, closePanel, panelId]) + + // Cache a selected remote mirabuf assembly, load from cache. + const selectRemote = useCallback((info: MirabufRemoteInfo, type: MiraType) => { + MirabufCachingService.CacheRemote(info.src, type).then(cacheInfo => { + cacheInfo && SpawnCachedMira(cacheInfo, type) + }) + + closePanel(panelId) + }, [closePanel, panelId]) + + // Generate Item cards for cached robots. + const cachedRobotElements = useMemo(() => cachedRobots.map(info => + ItemCard({ + name: info.name || info.cacheKey || "Unnamed Robot", + id: info.id, + primaryButtonNode: AddIcon, + primaryOnClick: () => { + console.log(`Selecting cached robot: ${info.cacheKey}`) + selectCache(info, MiraType.ROBOT) + }, + secondaryButtonNode: DeleteIcon, + secondaryOnClick: () => { + console.log(`Deleting cache of: ${info.cacheKey}`) + MirabufCachingService.Remove(info.cacheKey, info.id, MiraType.ROBOT) + + setCachedRobots(GetCacheInfo(MiraType.ROBOT)) + }, + }) + ), [cachedRobots, selectCache, setCachedRobots]) + + // Generate Item cards for cached fields. + const cachedFieldElements = useMemo(() => cachedFields.map(info => + ItemCard({ + name: info.name || info.cacheKey || "Unnamed Field", + id: info.id, + primaryButtonNode: AddIcon, + primaryOnClick: () => { + console.log(`Selecting cached field: ${info.cacheKey}`) + selectCache(info, MiraType.FIELD) + }, + secondaryButtonNode: DeleteIcon, + secondaryOnClick: () => { + console.log(`Deleting cache of: ${info.cacheKey}`) + MirabufCachingService.Remove(info.cacheKey, info.id, MiraType.FIELD) + + setCachedFields(GetCacheInfo(MiraType.FIELD)) + }, + }) + ), [cachedFields, selectCache, setCachedFields]) + + // Generate Item cards for remote robots. + const remoteRobotElements = useMemo(() => { + const remoteRobots = manifest?.robots.filter(path => !cachedRobots.some(info => info.cacheKey.includes(path.src))) + return remoteRobots?.map(path => + ItemCard({ + name: path.displayName, + id: path.src, + primaryButtonNode: DownloadIcon, + primaryOnClick: () => { + console.log(`Selecting remote: ${path}`) + selectRemote(path, MiraType.ROBOT) + }, + }) + ) + }, [manifest?.robots, cachedRobots, selectRemote]) + + // Generate Item cards for remote fields. + const remoteFieldElements = useMemo(() => { + const remoteFields = manifest?.fields.filter(path => !cachedFields.some(info => info.cacheKey.includes(path.src))) + return remoteFields?.map(path => + ItemCard({ + name: path.displayName, + id: path.src, + primaryButtonNode: DownloadIcon, + primaryOnClick: () => { + console.log(`Selecting remote: ${path}`) + selectRemote(path, MiraType.FIELD) + }, + }) + ) + }, [manifest?.fields, cachedFields, selectRemote]) + + // Generate Item cards for APS robots and fields. + const hubElements = useMemo(() => files?.map(file => + ItemCard({ + name: file.attributes.displayName!, + id: file.id, + primaryButtonNode: DownloadIcon, + primaryOnClick: () => { + if (file.href) { + selectRemote({ src: file.href, displayName: file.attributes.displayName ?? file.id }, viewType) + } else { + console.error('No href for file') + console.debug(file.raw) + } + } + }) + ), [files, selectRemote, viewType]) + + return ( + + {/*
+ {selectedHub ? ( + selectedFolder ? ( + <> + +
*/} +
+ v != null && setViewType(v)} + sx={{ + alignSelf: "center" + }} + > + Robots + Fields + + { + viewType == MiraType.ROBOT + ? + (<> + + {cachedRobotElements + ? `${cachedRobotElements.length} Saved Robot${cachedRobotElements.length == 1 ? "" : "s"}` + : "Loading Saved Robots"} + + {cachedRobotElements} + ) + : + (<> + + {cachedFieldElements + ? `${cachedFieldElements.length} Saved Field${cachedFieldElements.length == 1 ? "" : "s"}` + : "Loading Saved Fields"} + + {cachedFieldElements} + ) + } + + {hubElements + ? `${hubElements.length} Remote Asset${hubElements.length == 1 ? "" : "s"}` + : apsFilesState.message} + + {hubElements} + { + viewType == MiraType.ROBOT + ? + (<> + + {remoteRobotElements + ? `${remoteRobotElements.length} Default Robot${remoteRobotElements.length == 1 ? "" : "s"}` + : "Loading Default Robots"} + + {remoteRobotElements} + ) + : + (<> + + {remoteFieldElements + ? `${remoteFieldElements.length} Default Field${remoteFieldElements.length == 1 ? "" : "s"}` + : "Loading Default Fields"} + + {remoteFieldElements} + ) + } +
+
+ ) +} + +export default ImportMirabufPanel diff --git a/fission/src/vite-env.d.ts b/fission/src/vite-env.d.ts index 007c4f0852..11f02fe2a0 100644 --- a/fission/src/vite-env.d.ts +++ b/fission/src/vite-env.d.ts @@ -1,10 +1 @@ /// - -interface ImportMetaEnv { - readonly VITE_SYNTHESIS_SERVER_PATH: string - // more env variables... - } - - interface ImportMeta { - readonly env: ImportMetaEnv - } \ No newline at end of file diff --git a/fission/vite.config.ts b/fission/vite.config.ts index ee38e0393c..1f052ab08a 100644 --- a/fission/vite.config.ts +++ b/fission/vite.config.ts @@ -7,6 +7,8 @@ const basePath = '/fission/' const serverPort = 3000 const dockerServerPort = 80 +const useLocal = false + // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), /* viteSingleFile() */ glsl({ @@ -46,7 +48,7 @@ export default defineConfig({ // this sets a default port to 3000 port: serverPort, cors: false, - proxy: { + proxy: useLocal ? { '/api/mira': { target: `http://localhost:${serverPort}${basePath}`, changeOrigin: true, @@ -58,6 +60,12 @@ export default defineConfig({ changeOrigin: true, secure: false, }, + } : { + '/api': { + target: `https://synthesis.autodesk.com/`, + changeOrigin: true, + secure: true, + }, }, }, build: { From 1b48c0a14e0fee701cd957560b7f59eab8b61728 Mon Sep 17 00:00:00 2001 From: KyroVibe Date: Fri, 12 Jul 2024 16:27:33 -0600 Subject: [PATCH 13/20] Finally done I think --- fission/src/aps/APSDataManagement.ts | 90 ++++++++++ fission/src/mirabuf/MirabufLoader.ts | 23 +++ fission/src/ui/components/Button.tsx | 33 ++-- .../src/ui/components/ToggleButtonGroup.tsx | 4 + .../ui/panels/mirabuf/ImportMirabufPanel.tsx | 170 ++++++++++-------- fission/src/util/TaskStatus.ts | 6 + 6 files changed, 232 insertions(+), 94 deletions(-) create mode 100644 fission/src/util/TaskStatus.ts diff --git a/fission/src/aps/APSDataManagement.ts b/fission/src/aps/APSDataManagement.ts index ad2a51f3c5..93881b2b43 100644 --- a/fission/src/aps/APSDataManagement.ts +++ b/fission/src/aps/APSDataManagement.ts @@ -1,10 +1,15 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { MainHUD_AddToast } from "@/ui/components/MainHUD"; import APS from "./APS" +import TaskStatus from "@/util/TaskStatus"; +import { Mutex } from "async-mutex"; export const FOLDER_DATA_TYPE = "folders" export const ITEM_DATA_TYPE = "items" +let mirabufFiles: Data[] | undefined; +const mirabufFilesMutex: Mutex = new Mutex(); + export class APSDataError extends Error { error_code: string; title: string; @@ -251,3 +256,88 @@ export async function searchFolder(project: Project, folder: Folder, filters?: F export async function searchRootForMira(project: Project): Promise { return searchFolder(project, project.folder, [{ fieldName: 'fileType', matchValue: 'mira' }]) } + +export async function downloadData(data: Data): Promise { + if (!data.href) { + return undefined + } + + const auth = await APS.getAuth() + if (!auth) { + return undefined + } + + return await fetch(data.href, { + method: "GET", + headers: { + Authorization: `Bearer ${auth.access_token}`, + }, + }).then(x => x.arrayBuffer()) +} + +export function HasMirabufFiles(): boolean { + return mirabufFiles != undefined +} + +export async function RequestMirabufFiles() { + console.log('Request') + + if (mirabufFilesMutex.isLocked()) { + return + } + + mirabufFilesMutex.runExclusive(async () => { + const auth = await APS.getAuth() + if (auth) { + getHubs().then(async hubs => { + if (!hubs) { + window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: true, message: 'Failed to get Hubs' })) + return + } + const fileData: Data[] = [] + for (const hub of hubs) { + const projects = await getProjects(hub) + if (!projects) continue + for (const project of projects) { + window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: false, message: `Searching Project '${project.name}'` })) + const data = await searchRootForMira(project) + if (data) fileData.push(...data) + } + } + window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: true, message: `Found ${fileData.length} file${fileData.length == 1 ? '' : 's'}` })) + mirabufFiles = fileData + window.dispatchEvent(new MirabufFilesUpdateEvent(mirabufFiles)) + }) + } + }) +} + +export function GetMirabufFiles(): Data[] | undefined { + return mirabufFiles +} + +export class MirabufFilesUpdateEvent extends Event { + + public static readonly EVENT_KEY: string = 'MirabufFilesUpdateEvent' + + public data: Data[]; + + public constructor(data: Data[]) { + super(MirabufFilesUpdateEvent.EVENT_KEY) + + this.data = data + } +} + +export class MirabufFilesStatusUpdateEvent extends Event { + + public static readonly EVENT_KEY: string = 'MirabufFilesStatusUpdateEvent' + + public status: TaskStatus + + public constructor(status: TaskStatus) { + super(MirabufFilesStatusUpdateEvent.EVENT_KEY) + + this.status = status + } +} diff --git a/fission/src/mirabuf/MirabufLoader.ts b/fission/src/mirabuf/MirabufLoader.ts index 4a15b4773b..502b94fb05 100644 --- a/fission/src/mirabuf/MirabufLoader.ts +++ b/fission/src/mirabuf/MirabufLoader.ts @@ -1,3 +1,4 @@ +import { Data, downloadData } from "@/aps/APSDataManagement" import { mirabuf } from "@/proto/mirabuf" import Pako from "pako" @@ -90,6 +91,28 @@ class MirabufCachingService { return await MirabufCachingService.StoreInCache(fetchLocation, miraBuff, miraType) } + public static async CacheAPS(data: Data, miraType: MiraType): Promise { + if (!data.href) { + console.error('Data has no href') + return undefined + } + + const map = MirabufCachingService.GetCacheMap(miraType) + const target = map[data.id] + + if (target) { + return target; + } + + const miraBuff = await downloadData(data) + if (!miraBuff) { + console.error('Failed to download file') + return undefined + } + + return await MirabufCachingService.StoreInCache(data.id, miraBuff, miraType) + } + /** * Cache local Mirabuf file * diff --git a/fission/src/ui/components/Button.tsx b/fission/src/ui/components/Button.tsx index 8424edb7e8..3f6f5ada7e 100644 --- a/fission/src/ui/components/Button.tsx +++ b/fission/src/ui/components/Button.tsx @@ -11,29 +11,32 @@ export enum ButtonSize { export type ButtonProps = { value: ReactNode colorOverrideClass?: string + sizeOverrideClass?: string size?: ButtonSize onClick?: () => void className?: string } -const Button: React.FC = ({ value, colorOverrideClass, size, onClick, className }) => { - let sizeClassNames +const Button: React.FC = ({ value, colorOverrideClass, sizeOverrideClass, size, onClick, className }) => { + let sizeClassNames = sizeOverrideClass if (!size) size = ButtonSize.Medium as ButtonSize - switch (size) { - case ButtonSize.Small: - sizeClassNames = "px-4 py-1" - break - case ButtonSize.Medium: - sizeClassNames = "px-6 py-1.5" - break - case ButtonSize.Large: - sizeClassNames = "px-8 py-2" - break - case ButtonSize.XL: - sizeClassNames = "px-10 py-2" - break + if (!sizeClassNames) { + switch (size) { + case ButtonSize.Small: + sizeClassNames = "px-4 py-1" + break + case ButtonSize.Medium: + sizeClassNames = "px-6 py-1.5" + break + case ButtonSize.Large: + sizeClassNames = "px-8 py-2" + break + case ButtonSize.XL: + sizeClassNames = "px-10 py-2" + break + } } return ( diff --git a/fission/src/ui/components/ToggleButtonGroup.tsx b/fission/src/ui/components/ToggleButtonGroup.tsx index 215b4537c3..86eb065e2e 100644 --- a/fission/src/ui/components/ToggleButtonGroup.tsx +++ b/fission/src/ui/components/ToggleButtonGroup.tsx @@ -8,6 +8,8 @@ import { colorNameToVar } from "../ThemeContext"; export const ToggleButton = styled(ToggleButtonMUI)({ // backgroundColor: "white" borderColor: "none", + fontFamily: "Artifakt", + fontWeight: 700, color: "white", "&.Mui-selected": { color: "white", @@ -17,5 +19,7 @@ export const ToggleButton = styled(ToggleButtonMUI)({ export const ToggleButtonGroup = styled(ToggleButtonGroupMUI)({ backgroundColor: colorNameToVar("Background"), + fontFamily: "Artifakt", + fontWeight: 700, width: "fit-content" }) \ No newline at end of file diff --git a/fission/src/ui/panels/mirabuf/ImportMirabufPanel.tsx b/fission/src/ui/panels/mirabuf/ImportMirabufPanel.tsx index ccbdbd477d..8df53b04b1 100644 --- a/fission/src/ui/panels/mirabuf/ImportMirabufPanel.tsx +++ b/fission/src/ui/panels/mirabuf/ImportMirabufPanel.tsx @@ -1,13 +1,11 @@ import React, { ReactNode, useCallback, useEffect, useMemo, useState } from "react" import Label, { LabelSize } from "@/components/Label" -import { Data, getHubs, getProjects, searchRootForMira } from "@/aps/APSDataManagement" +import { Data, GetMirabufFiles, HasMirabufFiles, MirabufFilesStatusUpdateEvent, MirabufFilesUpdateEvent, RequestMirabufFiles } from "@/aps/APSDataManagement" import MirabufCachingService, { MirabufCacheInfo, MirabufRemoteInfo, MiraType } from "@/mirabuf/MirabufLoader" -import APS from "@/aps/APS" import World from "@/systems/World" -import { useModalControlContext } from "@/ui/ModalContext" import { useTooltipControlContext } from "@/ui/TooltipContext" import { CreateMirabuf } from "@/mirabuf/MirabufSceneObject" -import { Box, styled } from "@mui/material" +import { Box, Divider, styled } from "@mui/material" import { HiDownload } from "react-icons/hi" import Button, { ButtonProps, ButtonSize } from "@/ui/components/Button" import { ToggleButton, ToggleButtonGroup } from "@/ui/components/ToggleButtonGroup" @@ -15,18 +13,21 @@ import { IoTrashBin } from "react-icons/io5" import { AiOutlinePlus } from "react-icons/ai" import Panel, { PanelPropsImpl } from "@/ui/components/Panel" import { usePanelControlContext } from "@/ui/PanelContext" +import TaskStatus from "@/util/TaskStatus" +import { BiRefresh } from "react-icons/bi" const DownloadIcon = () const AddIcon = () const DeleteIcon = () - -interface TaskStatus { - isDone: boolean, - message: string, -} +const RefreshIcon = () const LabelStyled = styled(Label)({ - fontWeight: 700 + fontWeight: 700, + margin: "0pt", +}) + +const DividerStyled = styled(Divider)({ + borderColor: "white" }) const ButtonPrimary: React.FC = ({ value, onClick }) => { @@ -51,6 +52,17 @@ const ButtonSecondary: React.FC = ({ value, onClick }) => { ) } +const ButtonIcon: React.FC = ({ value, onClick }) => { + return ( + + ) +} + interface ItemCardProps { id: string name: string @@ -130,39 +142,37 @@ const ImportMirabufPanel: React.FC = ({ panelId }) => { const [cachedRobots, setCachedRobots] = useState(GetCacheInfo(MiraType.ROBOT)) const [cachedFields, setCachedFields] = useState(GetCacheInfo(MiraType.FIELD)) - const [manifest, setManifest] = useState() - const [files, setFiles] = useState() - const [apsFilesState, setApsFilesState] = useState({ isDone: false, message: 'Retrieving APS Hubs...' }) - const auth = useMemo(async () => await APS.getAuth(), []) - const [viewType, setViewType] = useState(MiraType.ROBOT) - // Get APS Mirabuf Data, Load into files. + const [filesStatus, setFilesStatus] = useState({ isDone: false, message: "Waiting on APS..." }) + const [files, setFiles] = useState(undefined) + useEffect(() => { - (async () => { - if (await auth) { - getHubs().then(async hubs => { - if (!hubs) { - setApsFilesState({ isDone: true, message: 'Failed to load APS Hubs' }) - return - } - const fileData: Data[] = [] - for (const hub of hubs) { - const projects = await getProjects(hub) - if (!projects) continue - for (const project of projects) { - setApsFilesState({ isDone: false, message: `Searching Project '${project.name}'` }) - const data = await searchRootForMira(project) - if (data) fileData.push(...data) - } - } - setApsFilesState({ isDone: true, message: `Found ${fileData.length} file${fileData.length == 1 ? '' : 's'}` }) - setFiles(fileData) - }) - } - })() - }, [auth]) + const updateFilesStatus = (e: Event) => { + setFilesStatus((e as MirabufFilesStatusUpdateEvent).status) + } + + const updateFiles = (e: Event) => { + setFiles((e as MirabufFilesUpdateEvent).data) + } + + window.addEventListener(MirabufFilesStatusUpdateEvent.EVENT_KEY, updateFilesStatus) + window.addEventListener(MirabufFilesUpdateEvent.EVENT_KEY, updateFiles) + + return () => { + window.removeEventListener(MirabufFilesStatusUpdateEvent.EVENT_KEY, updateFilesStatus) + window.removeEventListener(MirabufFilesUpdateEvent.EVENT_KEY, updateFiles) + } + }) + + useEffect(() => { + if (!HasMirabufFiles()) { + RequestMirabufFiles() + } else { + setFiles(GetMirabufFiles()) + } + }, []) // Get Default Mirabuf Data, Load into manifest. useEffect(() => { @@ -219,6 +229,14 @@ const ImportMirabufPanel: React.FC = ({ panelId }) => { closePanel(panelId) }, [closePanel, panelId]) + const selectAPS = useCallback((data: Data, type: MiraType) => { + MirabufCachingService.CacheAPS(data, type).then(cacheInfo => { + cacheInfo && SpawnCachedMira(cacheInfo, type) + }) + + closePanel(panelId) + }, [closePanel, panelId]) + // Generate Item cards for cached robots. const cachedRobotElements = useMemo(() => cachedRobots.map(info => ItemCard({ @@ -298,15 +316,11 @@ const ImportMirabufPanel: React.FC = ({ panelId }) => { id: file.id, primaryButtonNode: DownloadIcon, primaryOnClick: () => { - if (file.href) { - selectRemote({ src: file.href, displayName: file.attributes.displayName ?? file.id }, viewType) - } else { - console.error('No href for file') - console.debug(file.raw) - } + console.debug(file.raw) + selectAPS(file, viewType) } }) - ), [files, selectRemote, viewType]) + ), [files, selectAPS, viewType]) return ( = ({ panelId }) => { cancelName="Back" openLocation="right" > - {/*
- {selectedHub ? ( - selectedFolder ? ( - <> - -
*/}
= ({ panelId }) => { viewType == MiraType.ROBOT ? (<> - + {cachedRobotElements ? `${cachedRobotElements.length} Saved Robot${cachedRobotElements.length == 1 ? "" : "s"}` : "Loading Saved Robots"} + {cachedRobotElements} ) : (<> - + {cachedFieldElements ? `${cachedFieldElements.length} Saved Field${cachedFieldElements.length == 1 ? "" : "s"}` : "Loading Saved Fields"} + {cachedFieldElements} ) } - - {hubElements - ? `${hubElements.length} Remote Asset${hubElements.length == 1 ? "" : "s"}` - : apsFilesState.message} - + + + {hubElements + ? `${hubElements.length} Remote Asset${hubElements.length == 1 ? "" : "s"}` + : filesStatus.message} + + { + hubElements && filesStatus.isDone + ? + ( RequestMirabufFiles()} />) + : + (<>) + } + + {hubElements} { viewType == MiraType.ROBOT ? (<> - + {remoteRobotElements ? `${remoteRobotElements.length} Default Robot${remoteRobotElements.length == 1 ? "" : "s"}` : "Loading Default Robots"} + {remoteRobotElements} ) : (<> - + {remoteFieldElements ? `${remoteFieldElements.length} Default Field${remoteFieldElements.length == 1 ? "" : "s"}` : "Loading Default Fields"} + {remoteFieldElements} ) } diff --git a/fission/src/util/TaskStatus.ts b/fission/src/util/TaskStatus.ts new file mode 100644 index 0000000000..c41d0cfc5b --- /dev/null +++ b/fission/src/util/TaskStatus.ts @@ -0,0 +1,6 @@ +interface TaskStatus { + isDone: boolean, + message: string, +} + +export default TaskStatus \ No newline at end of file From 5b447bc6031b468b850f6aeb2cf713a27a367132 Mon Sep 17 00:00:00 2001 From: KyroVibe Date: Fri, 12 Jul 2024 16:32:51 -0600 Subject: [PATCH 14/20] Formatted --- fission/src/aps/APS.ts | 18 +- fission/src/aps/APSDataManagement.ts | 78 +++-- fission/src/mirabuf/MirabufLoader.ts | 17 +- fission/src/ui/components/MainHUD.tsx | 4 +- fission/src/ui/components/Panel.tsx | 5 +- .../src/ui/components/ToggleButtonGroup.tsx | 25 +- .../ui/panels/mirabuf/ImportMirabufPanel.tsx | 300 ++++++++++-------- fission/src/util/TaskStatus.ts | 6 +- 8 files changed, 246 insertions(+), 207 deletions(-) diff --git a/fission/src/aps/APS.ts b/fission/src/aps/APS.ts index 91093b41c9..4ac006addf 100644 --- a/fission/src/aps/APS.ts +++ b/fission/src/aps/APS.ts @@ -78,7 +78,7 @@ class APS { if (!auth) return undefined if (Date.now() > auth.expires_at) { - console.debug('Expired. Refreshing...') + console.debug("Expired. Refreshing...") await this.refreshAuthToken(auth.refresh_token, false) } return this.auth @@ -166,12 +166,12 @@ class APS { } /** - * Refreshes the access token using our refresh token. - * @param {string} refresh_token - The refresh token from our auth data - * - * @returns If the promise returns true, that means the auth token is currently available. If not, it means it - * is not readily available, although one may be in the works - */ + * Refreshes the access token using our refresh token. + * @param {string} refresh_token - The refresh token from our auth data + * + * @returns If the promise returns true, that means the auth token is currently available. If not, it means it + * is not readily available, although one may be in the works + */ static async refreshAuthToken(refresh_token: string, shouldRelog: boolean): Promise { return this.requestMutex.runExclusive(async () => { try { @@ -198,7 +198,7 @@ class APS { return false } } - json.expires_at = (json.expires_in * 1000) + Date.now() + json.expires_at = json.expires_in * 1000 + Date.now() this.auth = json as APSAuth if (this.auth) { await this.loadUserInfo(this.auth) @@ -231,7 +231,7 @@ class APS { return } const auth_res = json.response as APSAuth - auth_res.expires_at = (auth_res.expires_in * 1000) + Date.now() + auth_res.expires_at = auth_res.expires_in * 1000 + Date.now() this.auth = auth_res console.log("Preloading user info") const auth = await this.getAuth() diff --git a/fission/src/aps/APSDataManagement.ts b/fission/src/aps/APSDataManagement.ts index 93881b2b43..6eac626c53 100644 --- a/fission/src/aps/APSDataManagement.ts +++ b/fission/src/aps/APSDataManagement.ts @@ -1,26 +1,26 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { MainHUD_AddToast } from "@/ui/components/MainHUD"; +import { MainHUD_AddToast } from "@/ui/components/MainHUD" import APS from "./APS" -import TaskStatus from "@/util/TaskStatus"; -import { Mutex } from "async-mutex"; +import TaskStatus from "@/util/TaskStatus" +import { Mutex } from "async-mutex" export const FOLDER_DATA_TYPE = "folders" export const ITEM_DATA_TYPE = "items" -let mirabufFiles: Data[] | undefined; -const mirabufFilesMutex: Mutex = new Mutex(); +let mirabufFiles: Data[] | undefined +const mirabufFilesMutex: Mutex = new Mutex() export class APSDataError extends Error { - error_code: string; - title: string; - detail: string; + error_code: string + title: string + detail: string constructor(error_code: string, title: string, detail: string) { - super(title); - this.name = "APSDataError"; - this.error_code = error_code; - this.title = title; - this.detail = detail; + super(title) + this.name = "APSDataError" + this.error_code = error_code + this.title = title + this.detail = detail } } @@ -69,7 +69,7 @@ export class Data { constructor(x: any) { this.id = x.id this.type = x.type - this.attributes = x.attributes; + this.attributes = x.attributes this.raw = x @@ -139,9 +139,9 @@ export async function getHubs(): Promise { console.log(auth) console.log(APS.userInfo) if (e instanceof APSDataError) { - MainHUD_AddToast('error', e.title, e.detail); + MainHUD_AddToast("error", e.title, e.detail) } else if (e instanceof Error) { - MainHUD_AddToast('error', 'Failed to get hubs.', e.message); + MainHUD_AddToast("error", "Failed to get hubs.", e.message) } return undefined } @@ -177,7 +177,7 @@ export async function getProjects(hub: Hub): Promise { } catch (e) { console.error("Failed to get hubs") if (e instanceof Error) { - MainHUD_AddToast('error', 'Failed to get hubs.', e.message); + MainHUD_AddToast("error", "Failed to get hubs.", e.message) } return undefined } @@ -221,14 +221,14 @@ export async function getFolderData(project: Project, folder: Folder): Promise encodeURIComponent(`filter[${filter.fieldName}]`) + `=${filter.matchValue}`).join('&') + return filters.map(filter => encodeURIComponent(`filter[${filter.fieldName}]`) + `=${filter.matchValue}`).join("&") } export async function searchFolder(project: Project, folder: Folder, filters?: Filter[]): Promise { @@ -246,15 +246,15 @@ export async function searchFolder(project: Project, folder: Folder, filters?: F }, }) if (!res.ok) { - MainHUD_AddToast('error', 'Error getting cloud files.', 'Please sign in again.'); - return []; + MainHUD_AddToast("error", "Error getting cloud files.", "Please sign in again.") + return [] } const json = await res.json() - return json.data.map((data: any) => new Data(data)); + return json.data.map((data: any) => new Data(data)) } export async function searchRootForMira(project: Project): Promise { - return searchFolder(project, project.folder, [{ fieldName: 'fileType', matchValue: 'mira' }]) + return searchFolder(project, project.folder, [{ fieldName: "fileType", matchValue: "mira" }]) } export async function downloadData(data: Data): Promise { @@ -280,7 +280,7 @@ export function HasMirabufFiles(): boolean { } export async function RequestMirabufFiles() { - console.log('Request') + console.log("Request") if (mirabufFilesMutex.isLocked()) { return @@ -291,7 +291,9 @@ export async function RequestMirabufFiles() { if (auth) { getHubs().then(async hubs => { if (!hubs) { - window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: true, message: 'Failed to get Hubs' })) + window.dispatchEvent( + new MirabufFilesStatusUpdateEvent({ isDone: true, message: "Failed to get Hubs" }) + ) return } const fileData: Data[] = [] @@ -299,12 +301,22 @@ export async function RequestMirabufFiles() { const projects = await getProjects(hub) if (!projects) continue for (const project of projects) { - window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: false, message: `Searching Project '${project.name}'` })) + window.dispatchEvent( + new MirabufFilesStatusUpdateEvent({ + isDone: false, + message: `Searching Project '${project.name}'`, + }) + ) const data = await searchRootForMira(project) if (data) fileData.push(...data) } } - window.dispatchEvent(new MirabufFilesStatusUpdateEvent({ isDone: true, message: `Found ${fileData.length} file${fileData.length == 1 ? '' : 's'}` })) + window.dispatchEvent( + new MirabufFilesStatusUpdateEvent({ + isDone: true, + message: `Found ${fileData.length} file${fileData.length == 1 ? "" : "s"}`, + }) + ) mirabufFiles = fileData window.dispatchEvent(new MirabufFilesUpdateEvent(mirabufFiles)) }) @@ -317,27 +329,25 @@ export function GetMirabufFiles(): Data[] | undefined { } export class MirabufFilesUpdateEvent extends Event { + public static readonly EVENT_KEY: string = "MirabufFilesUpdateEvent" - public static readonly EVENT_KEY: string = 'MirabufFilesUpdateEvent' - - public data: Data[]; + public data: Data[] public constructor(data: Data[]) { super(MirabufFilesUpdateEvent.EVENT_KEY) - + this.data = data } } export class MirabufFilesStatusUpdateEvent extends Event { - - public static readonly EVENT_KEY: string = 'MirabufFilesStatusUpdateEvent' + public static readonly EVENT_KEY: string = "MirabufFilesStatusUpdateEvent" public status: TaskStatus public constructor(status: TaskStatus) { super(MirabufFilesStatusUpdateEvent.EVENT_KEY) - + this.status = status } } diff --git a/fission/src/mirabuf/MirabufLoader.ts b/fission/src/mirabuf/MirabufLoader.ts index 502b94fb05..ce438ec140 100644 --- a/fission/src/mirabuf/MirabufLoader.ts +++ b/fission/src/mirabuf/MirabufLoader.ts @@ -93,20 +93,20 @@ class MirabufCachingService { public static async CacheAPS(data: Data, miraType: MiraType): Promise { if (!data.href) { - console.error('Data has no href') + console.error("Data has no href") return undefined } - + const map = MirabufCachingService.GetCacheMap(miraType) const target = map[data.id] if (target) { - return target; + return target } const miraBuff = await downloadData(data) if (!miraBuff) { - console.error('Failed to download file') + console.error("Failed to download file") return undefined } @@ -238,8 +238,11 @@ class MirabufCachingService { try { const map = this.GetCacheMap(miraType) if (map) { - delete map[key]; - window.localStorage.setItem(miraType == MiraType.ROBOT ? robotsDirName : fieldsDirName, JSON.stringify(map)) + delete map[key] + window.localStorage.setItem( + miraType == MiraType.ROBOT ? robotsDirName : fieldsDirName, + JSON.stringify(map) + ) } const dir = miraType == MiraType.ROBOT ? robotFolderHandle : fieldFolderHandle @@ -278,7 +281,7 @@ class MirabufCachingService { const backupID = Date.now().toString() try { if (!miraType) { - console.log('Double loading') + console.log("Double loading") miraType = this.AssemblyFromBuffer(miraBuff).dynamic ? MiraType.ROBOT : MiraType.FIELD } diff --git a/fission/src/ui/components/MainHUD.tsx b/fission/src/ui/components/MainHUD.tsx index 67dfc8ba83..2b76d19df5 100644 --- a/fission/src/ui/components/MainHUD.tsx +++ b/fission/src/ui/components/MainHUD.tsx @@ -130,7 +130,9 @@ const MainHUD: React.FC = () => { } - onClick={async () => APS.isSignedIn() && APS.refreshAuthToken((await APS.getAuth())!.refresh_token, true)} + onClick={async () => + APS.isSignedIn() && APS.refreshAuthToken((await APS.getAuth())!.refresh_token, true) + } /> = ({ {children}
{(cancelEnabled || middleEnabled || acceptEnabled) && ( -