diff --git a/frontend/src/components/Scratch/LibraryPanel.tsx b/frontend/src/components/Scratch/LibraryPanel.tsx new file mode 100644 index 00000000..98c338b7 --- /dev/null +++ b/frontend/src/components/Scratch/LibraryPanel.tsx @@ -0,0 +1,73 @@ +import { useLibraries } from "@/lib/api" +import { Library, TerseScratch } from "@/lib/api/types" + +type LibrariesT = { + libraries: Library[] +} + +type Props = { + scratch: TerseScratch + onChange: (value: LibrariesT) => void +} + +export default function LibraryPanel({ scratch, onChange }: Props) { + const libraries = useLibraries() + + const hasLibrary = libName => scratch.libraries.some(lib => lib.name == libName) + const libraryVersion = lib => { + const scratchlib = scratch.libraries.find(scratchlib => scratchlib.name == lib.name) + if (scratchlib != null) { + return scratchlib.version + } else { + return lib.supported_versions[0] + } + } + + const setLibraryVersion = (libName, ver) => { + // clone the libraries + const libs = JSON.parse(JSON.stringify(scratch.libraries)) + // Check if the library is already enabled, if so return it + const scratchlib = scratch.libraries.find(scratchlib => scratchlib.name == libName) + if (scratchlib != null) { + // If it is, set the version + scratchlib.version = ver + } else { + // If it isn't, add the library to the list + libs.push({ name: libName, version: ver }) + } + onChange({ + libraries: libs, + }) + } + const unsetLibrary = libName => { + // clone the libraries + let libs = JSON.parse(JSON.stringify(scratch.libraries)) + // Only keep the libs whose name are not libName + libs = libs.filter(lib => lib.name != libName) + onChange({ + libraries: libs, + }) + } + const toggleLibrary = lib => { + if (hasLibrary(lib.name)) { + unsetLibrary(lib.name) + } else { + setLibraryVersion(lib.name, lib.supported_versions[0]) + } + } + + const librariesElements = libraries.map(lib =>
+ toggleLibrary(lib)} /> + + +
) + + return
+
+

Libraries

+ {librariesElements} +
+
+} diff --git a/frontend/src/components/Scratch/Scratch.tsx b/frontend/src/components/Scratch/Scratch.tsx index 63b8cc0d..b512f12c 100644 --- a/frontend/src/components/Scratch/Scratch.tsx +++ b/frontend/src/components/Scratch/Scratch.tsx @@ -21,6 +21,7 @@ import AboutScratch from "./AboutScratch" import DecompilationPanel from "./DecompilePanel" import FamilyPanel from "./FamilyPanel" import useLanguageServer from "./hooks/useLanguageServer" +import LibraryPanel from "./LibraryPanel" import styles from "./Scratch.module.scss" import ScratchMatchBanner from "./ScratchMatchBanner" import ScratchToolbar from "./ScratchToolbar" @@ -33,6 +34,7 @@ enum TabId { DIFF = "scratch_diff", DECOMPILATION = "scratch_decompilation", FAMILY = "scratch_family", + LIBRARIES = "libraries", } const DEFAULT_LAYOUTS: Record<"desktop_2col" | "mobile_2row", Layout> = { @@ -52,6 +54,7 @@ const DEFAULT_LAYOUTS: Record<"desktop_2col" | "mobile_2row", Layout> = { TabId.SOURCE_CODE, TabId.CONTEXT, TabId.OPTIONS, + TabId.LIBRARIES, ], }, { @@ -92,6 +95,7 @@ const DEFAULT_LAYOUTS: Record<"desktop_2col" | "mobile_2row", Layout> = { TabId.SOURCE_CODE, TabId.CONTEXT, TabId.OPTIONS, + TabId.LIBRARIES, ], }, ], @@ -274,6 +278,10 @@ export default function Scratch({ return {() => } + case TabId.LIBRARIES: + return + + default: return } diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 6c782ab8..dac9b952 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -6,7 +6,7 @@ import useSWR, { Revalidator, RevalidatorOptions, mutate } from "swr" import { useDebouncedCallback } from "use-debounce" import { ResponseError, get, post, patch, delete_ } from "./api/request" -import { AnonymousUser, User, Scratch, TerseScratch, Compilation, Page, Compiler, Platform, Project, ProjectMember } from "./api/types" +import { AnonymousUser, User, Scratch, TerseScratch, Compilation, Page, Compiler, LibraryVersions, Platform, Project, ProjectMember } from "./api/types" import { ignoreNextWarnBeforeUnload } from "./hooks" function onErrorRetry(error: ResponseError, key: string, config: C, revalidate: Revalidator, { retryCount }: RevalidatorOptions) { @@ -82,6 +82,7 @@ export function useSaveScratch(localScratch: Scratch): () => Promise { name: undefinedIfUnchanged(savedScratch, localScratch, "name"), description: undefinedIfUnchanged(savedScratch, localScratch, "description"), match_override: undefinedIfUnchanged(savedScratch, localScratch, "match_override"), + libraries: undefinedIfUnchanged(savedScratch, localScratch, "libraries"), }) await mutate(localScratch.url, updatedScratch, false) @@ -166,6 +167,7 @@ export function useCompilation(scratch: Scratch | null, autoRecompile = true, au compiler_flags: scratch.compiler_flags, diff_flags: scratch.diff_flags, diff_label: scratch.diff_label, + libraries: scratch.libraries, source_code: scratch.source_code, context: savedScratch ? undefinedIfUnchanged(savedScratch, scratch, "context") : scratch.context, }).then((compilation: Compilation) => { @@ -251,6 +253,16 @@ export function useCompilers(): Record { return data.compilers } +export function useLibraries(): LibraryVersions[] { + const { data } = useSWR("/libraries", get, { + refreshInterval: 0, + suspense: true, // TODO: remove + onErrorRetry, + }) + + return data.libraries +} + export function usePaginated(url: string, firstPage?: Page): { results: T[] hasNext: boolean diff --git a/frontend/src/lib/api/types.ts b/frontend/src/lib/api/types.ts index 2230a230..a61525de 100644 --- a/frontend/src/lib/api/types.ts +++ b/frontend/src/lib/api/types.ts @@ -48,6 +48,7 @@ export interface TerseScratch { match_override: boolean project: string project_function: string + libraries: Library[] } export interface Scratch extends TerseScratch { @@ -165,6 +166,16 @@ export type Compiler = { diff_flags: Flag[] } +export type Library = { + name: string + version: string +} + +export type LibraryVersions = { + name: string + supported_versions: string[] +} + export type Platform = { name: string description: string