diff --git a/package-lock.json b/package-lock.json index 5cf16ef..99e23da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46961,7 +46961,7 @@ "@ldo/solid": { "version": "file:packages/solid", "requires": { - "@inrupt/solid-client": "*", + "@inrupt/solid-client": "^1.30.0", "@inrupt/solid-client-authn-core": "^1.17.1", "@ldo/cli": "^0.0.0", "@ldo/dataset": "^0.0.0", diff --git a/packages/demo-react/src/Layout.tsx b/packages/demo-react/src/Layout.tsx index 109bddb..fd811a4 100644 --- a/packages/demo-react/src/Layout.tsx +++ b/packages/demo-react/src/Layout.tsx @@ -3,8 +3,8 @@ import React, { useState } from "react"; import type { FunctionComponent } from "react"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { Dashboard } from "./dashboard/Dashboard"; -import { Media } from "./media/Media"; import { BuildMainContainer } from "./dashboard/BuildMainContainer"; +import { MediaPage } from "./media/MediaPage"; const router = createBrowserRouter([ { @@ -13,7 +13,7 @@ const router = createBrowserRouter([ }, { path: "/media/:uri", - element: , + element: , }, ]); diff --git a/packages/demo-react/src/dashboard/BuildMainContainer.tsx b/packages/demo-react/src/dashboard/BuildMainContainer.tsx index d9c29de..2429325 100644 --- a/packages/demo-react/src/dashboard/BuildMainContainer.tsx +++ b/packages/demo-react/src/dashboard/BuildMainContainer.tsx @@ -1,10 +1,10 @@ import React, { useState, useEffect } from "react"; import type { FunctionComponent } from "react"; -import type { Container, LeafUri } from "@ldo/solid"; +import type { Container, ContainerUri, LeafUri } from "@ldo/solid"; import { useSolidAuth, useLdo } from "@ldo/solid-react"; export interface BuildMainContainerChildProps { - mainContainer: Container; + mainContainerUri: ContainerUri; } export const BuildMainContainer: FunctionComponent<{ @@ -23,29 +23,28 @@ export const BuildMainContainer: FunctionComponent<{ alert(rootContainer.message); return; } - const mainContainer = - await rootContainer.createChildIfAbsent("demo-react/"); - if (mainContainer.type === "error") { - alert(mainContainer.message); - return; - } + const mainContainer = getResource(`${rootContainer.uri}demo-react/`); setMainContainer(mainContainer); - mainContainer.setAccessRules({ - public: { - read: true, - write: false, - append: false, - control: false, - }, - agent: { - [session.webId!]: { + await mainContainer.read(); + if (mainContainer.isAbsent()) { + await mainContainer.createIfAbsent(); + await mainContainer.setAccessRules({ + public: { read: true, - write: true, - append: true, - control: true, + write: false, + append: false, + control: false, }, - }, - }); + agent: { + [session.webId!]: { + read: true, + write: true, + append: true, + control: true, + }, + }, + }); + } }); } }, [session.webId]); @@ -55,5 +54,5 @@ export const BuildMainContainer: FunctionComponent<{ return

Loading

; } - return ; + return ; }; diff --git a/packages/demo-react/src/dashboard/Dashboard.tsx b/packages/demo-react/src/dashboard/Dashboard.tsx index fe9f6af..4eb5c37 100644 --- a/packages/demo-react/src/dashboard/Dashboard.tsx +++ b/packages/demo-react/src/dashboard/Dashboard.tsx @@ -1,18 +1,27 @@ import React from "react"; import type { FunctionComponent } from "react"; import type { BuildMainContainerChildProps } from "./BuildMainContainer"; +import { useResource } from "@ldo/solid-react"; +import { MediaPost } from "../media/MediaPost"; +import { UploadButton } from "./UploadButton"; export const Dashboard: FunctionComponent = ({ - mainContainer, + mainContainerUri, }) => { - return

{mainContainer.uri}

; - // return ( - //
- //
- // - //
- //
- //
{mainContainer.isLoading ? "Loading" : "Not Loading"}
- //
- // ); + const mainContainer = useResource(mainContainerUri); + if (mainContainer.isLoading()) { + return

Loading Main Container

; + } + + return ( +
+
+ +
+
+ {mainContainer.children().map((child) => ( + + ))} +
+ ); }; diff --git a/packages/demo-react/src/media/MediaPage.tsx b/packages/demo-react/src/media/MediaPage.tsx new file mode 100644 index 0000000..fc113f1 --- /dev/null +++ b/packages/demo-react/src/media/MediaPage.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import type { FunctionComponent } from "react"; +import { useParams } from "react-router-dom"; +import { MediaPost } from "./MediaPost"; + +export const MediaPage: FunctionComponent = () => { + const { uri } = useParams(); + if (!uri) { + return

No URI present

; + } + return ; +}; diff --git a/packages/demo-react/src/media/Media.tsx b/packages/demo-react/src/media/MediaPost.tsx similarity index 53% rename from packages/demo-react/src/media/Media.tsx rename to packages/demo-react/src/media/MediaPost.tsx index b39949e..32fb4ab 100644 --- a/packages/demo-react/src/media/Media.tsx +++ b/packages/demo-react/src/media/MediaPost.tsx @@ -1,12 +1,11 @@ import React from "react"; import type { FunctionComponent } from "react"; -import { useParams } from "react-router-dom"; -export const Media: FunctionComponent = () => { - const { uri } = useParams(); +export const MediaPost: FunctionComponent<{ uri: string }> = ({ uri }) => { return (

Media: {uri}

+
); }; diff --git a/packages/solid-react/src/useResource.ts b/packages/solid-react/src/useResource.ts index bfe048a..8a8d48a 100644 --- a/packages/solid-react/src/useResource.ts +++ b/packages/solid-react/src/useResource.ts @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, useEffect, useRef } from "react"; import type { Container, ContainerUri, @@ -7,11 +7,25 @@ import type { Leaf, } from "@ldo/solid"; import { useLdo } from "./SolidLdoProvider"; +import { useForceReload } from "./util/useForceReload"; export function useResource(uri: ContainerUri): Container; export function useResource(uri: LeafUri): Leaf; -export function useResource(uri: string): Resource; -export function useResource(uri: string): Resource { +export function useResource(uri: string): Leaf | Container; +export function useResource(uri: string): Leaf | Container { const { getResource } = useLdo(); - return useMemo(() => getResource(uri), [getResource, uri]); + const resource = useMemo(() => getResource(uri), [getResource, uri]); + const pastResource = useRef(); + const forceReload = useForceReload(); + useEffect(() => { + if (pastResource.current) { + pastResource.current.off("update", forceReload); + } + pastResource.current = resource; + resource.on("update", forceReload); + return () => { + resource.off("update", forceReload); + }; + }, [resource]); + return resource; } diff --git a/packages/solid-react/src/util/useForceReload.ts b/packages/solid-react/src/util/useForceReload.ts index 7535f7a..6082c84 100644 --- a/packages/solid-react/src/util/useForceReload.ts +++ b/packages/solid-react/src/util/useForceReload.ts @@ -1,6 +1,6 @@ -import { useState } from "react"; +import { useState, useCallback } from "react"; -export function useForceUpdate() { +export function useForceReload() { const [, setValue] = useState(0); - return () => setValue((value) => value + 1); + return useCallback(() => setValue((value) => value + 1), []); } diff --git a/packages/solid/src/SolidLdoDataset.ts b/packages/solid/src/SolidLdoDataset.ts index b3f7f29..75c5003 100644 --- a/packages/solid/src/SolidLdoDataset.ts +++ b/packages/solid/src/SolidLdoDataset.ts @@ -21,8 +21,8 @@ export class SolidLdoDataset extends LdoDataset { getResource(uri: ContainerUri, options?: ResourceGetterOptions): Container; getResource(uri: LeafUri, options?: ResourceGetterOptions): Leaf; - getResource(uri: string, options?: ResourceGetterOptions): Resource; - getResource(uri: string, options?: ResourceGetterOptions): Resource { + getResource(uri: string, options?: ResourceGetterOptions): Leaf | Container; + getResource(uri: string, options?: ResourceGetterOptions): Leaf | Container { return this.context.resourceStore.get(uri, options); } } diff --git a/packages/solid/src/resource/Container.ts b/packages/solid/src/resource/Container.ts index 0088214..2591eeb 100644 --- a/packages/solid/src/resource/Container.ts +++ b/packages/solid/src/resource/Container.ts @@ -18,12 +18,14 @@ import type { Leaf } from "./Leaf"; import { Resource } from "./Resource"; export class Container extends Resource { + readonly uri: ContainerUri; protected requester: ContainerRequester; protected rootContainer: boolean | undefined; readonly type = "container" as const; constructor(uri: ContainerUri, context: SolidLdoDatasetContext) { - super(uri, context); + super(context); + this.uri = uri; this.requester = new ContainerRequester(uri, context); } diff --git a/packages/solid/src/resource/Leaf.ts b/packages/solid/src/resource/Leaf.ts index d9c062b..b9faa97 100644 --- a/packages/solid/src/resource/Leaf.ts +++ b/packages/solid/src/resource/Leaf.ts @@ -19,13 +19,15 @@ import type { Container } from "./Container"; import { Resource } from "./Resource"; export class Leaf extends Resource { + readonly uri: LeafUri; protected requester: Requester; readonly type = "leaf" as const; protected binaryData: { data: Blob; mimeType: string } | undefined; constructor(uri: LeafUri, context: SolidLdoDatasetContext) { - super(uri, context); + super(context); + this.uri = uri; this.requester = new LeafRequester(uri, context); } diff --git a/packages/solid/src/resource/Resource.ts b/packages/solid/src/resource/Resource.ts index 9b72b1a..64e4b11 100644 --- a/packages/solid/src/resource/Resource.ts +++ b/packages/solid/src/resource/Resource.ts @@ -19,18 +19,22 @@ import type { } from "../requester/requestResults/AccessRule"; import { getAccessRules } from "../requester/requests/getAccessRules"; import { setAccessRules } from "../requester/requests/setAccessRules"; +import type TypedEmitter from "typed-emitter"; +import EventEmitter from "events"; -export abstract class Resource { +export abstract class Resource extends (EventEmitter as new () => TypedEmitter<{ + update: () => void; +}>) { // All intance variables protected readonly context: SolidLdoDatasetContext; - readonly uri: string; + abstract readonly uri: string; abstract readonly type: string; protected abstract readonly requester: Requester; protected didInitialFetch: boolean = false; protected absent: boolean | undefined; - constructor(uri: string, context: SolidLdoDatasetContext) { - this.uri = uri; + constructor(context: SolidLdoDatasetContext) { + super(); this.context = context; } @@ -74,18 +78,26 @@ export abstract class Resource { protected parseResult( result: AbsentResult | BinaryResult | DataResult | PossibleErrors, ): this | PossibleErrors { + let toReturn: this | PossibleErrors; switch (result.type) { case "error": - return result; + toReturn = result; + break; case "absent": this.didInitialFetch = true; this.absent = true; - return this; + // eslint-disable-next-line @typescript-eslint/no-this-alias + toReturn = this; + break; default: this.didInitialFetch = true; this.absent = false; - return this; + // eslint-disable-next-line @typescript-eslint/no-this-alias + toReturn = this; + break; } + this.emit("update"); + return toReturn; } // Read Methods diff --git a/packages/solid/src/util/RequestBatcher.ts b/packages/solid/src/util/RequestBatcher.ts index 084b47b..3f0a287 100644 --- a/packages/solid/src/util/RequestBatcher.ts +++ b/packages/solid/src/util/RequestBatcher.ts @@ -141,6 +141,7 @@ export class RequestBatcher { waitingProcess as unknown as WaitingProcess, ); this.loadingMap[waitingProcess.name] = true; + this.loadingMap[ANY_KEY] = true; this.triggerOrWaitProcess(); }); }