diff --git a/package-lock.json b/package-lock.json index 51f2caa..f84cb12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6706,6 +6706,14 @@ "react-native": "*" } }, + "node_modules/@remix-run/router": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", + "integrity": "sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -12393,11 +12401,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==" }, - "node_modules/emitter-component": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", - "integrity": "sha512-G+mpdiAySMuB7kesVRLuyvYRqDmshB7ReKEVuyBPkzQlmiDiLrt7hHHIy4Aff552bgknVN7B2/d3lzhGO5dvpQ==" - }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -25330,6 +25333,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz", + "integrity": "sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg==", + "dependencies": { + "@remix-run/router": "1.8.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz", + "integrity": "sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==", + "dependencies": { + "@remix-run/router": "1.8.0", + "react-router": "6.15.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", @@ -27155,14 +27188,6 @@ "node": ">= 0.6" } }, - "node_modules/stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "dependencies": { - "emitter-component": "^1.1.1" - } - }, "node_modules/stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", @@ -30664,6 +30689,7 @@ "@ldo/solid-react": "^0.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.15.0", "react-scripts": "5.0.1", "solid-authn-react-native": "^2.0.3" }, @@ -31275,29 +31301,39 @@ "license": "MIT", "dependencies": { "@inrupt/solid-client": "^1.29.0", + "@inrupt/solid-client-authn-browser": "^1.17.1", "@ldo/dataset": "^0.0.0", "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", "@ldo/solid": "^0.0.0", "@ldo/subscribable-dataset": "^0.0.0", "@rdfjs/data-model": "^1.2.0", - "cross-fetch": "^3.1.6", - "solid-authn-react-native": "^2.0.3", - "stream": "^0.0.2" + "cross-fetch": "^3.1.6" }, "devDependencies": { "@babel/preset-env": "^7.22.10", "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "^7.22.11", + "@inrupt/solid-client-authn-core": "^1.17.1", "@ldo/rdf-utils": "^0.0.0", "@rdfjs/types": "^1.0.1", "@types/jest": "^29.0.3", - "@types/jsonld": "^1.5.8", - "@types/n3": "^1.10.4", - "@types/shexj": "2.1.4", "ts-jest": "^29.0.2" } }, + "packages/solid-react/node_modules/@inrupt/solid-client-authn-browser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-browser/-/solid-client-authn-browser-1.17.1.tgz", + "integrity": "sha512-ApTK9H+v6YOp099opLdyLq9qM5j8adpYkK4KMOQKsK9ndDLVNiduq4EgPq4/jtwPGe0hG0YWuFuB8mJpLzJftA==", + "dependencies": { + "@inrupt/oidc-client-ext": "^1.17.1", + "@inrupt/solid-client-authn-core": "^1.17.1", + "@inrupt/universal-fetch": "^1.0.2", + "events": "^3.3.0", + "jose": "^4.3.7", + "uuid": "^9.0.0" + } + }, "packages/solid-react/node_modules/@jest/console": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", @@ -38878,6 +38914,7 @@ "@types/shexj": "^2.1.4", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "*", "react-scripts": "5.0.1", "solid-authn-react-native": "^2.0.3", "tsconfig-paths-webpack-plugin": "^4.1.0" @@ -39245,7 +39282,7 @@ "@types/jest": "^29.0.3", "cross-fetch": "^3.1.6", "ts-jest": "^29.0.2", - "typed-emitter": "*" + "typed-emitter": "^2.1.0" }, "dependencies": { "@jest/console": { @@ -40165,6 +40202,8 @@ "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "^7.22.11", "@inrupt/solid-client": "^1.29.0", + "@inrupt/solid-client-authn-browser": "^1.17.1", + "@inrupt/solid-client-authn-core": "^1.17.1", "@ldo/dataset": "^0.0.0", "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", @@ -40174,15 +40213,23 @@ "@rdfjs/data-model": "^1.2.0", "@rdfjs/types": "^1.0.1", "@types/jest": "^29.0.3", - "@types/jsonld": "^1.5.8", - "@types/n3": "^1.10.4", - "@types/shexj": "2.1.4", "cross-fetch": "^3.1.6", - "solid-authn-react-native": "^2.0.3", - "stream": "^0.0.2", "ts-jest": "^29.0.2" }, "dependencies": { + "@inrupt/solid-client-authn-browser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@inrupt/solid-client-authn-browser/-/solid-client-authn-browser-1.17.1.tgz", + "integrity": "sha512-ApTK9H+v6YOp099opLdyLq9qM5j8adpYkK4KMOQKsK9ndDLVNiduq4EgPq4/jtwPGe0hG0YWuFuB8mJpLzJftA==", + "requires": { + "@inrupt/oidc-client-ext": "^1.17.1", + "@inrupt/solid-client-authn-core": "^1.17.1", + "@inrupt/universal-fetch": "^1.0.2", + "events": "^3.3.0", + "jose": "^4.3.7", + "uuid": "^9.0.0" + } + }, "@jest/console": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", @@ -43230,6 +43277,11 @@ "nullthrows": "^1.1.1" } }, + "@remix-run/router": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", + "integrity": "sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==" + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -47500,11 +47552,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==" }, - "emitter-component": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", - "integrity": "sha512-G+mpdiAySMuB7kesVRLuyvYRqDmshB7ReKEVuyBPkzQlmiDiLrt7hHHIy4Aff552bgknVN7B2/d3lzhGO5dvpQ==" - }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -57057,6 +57104,23 @@ "integrity": "sha512-Hwln1VNuGl/6bVwnd0Xdn1e84gT/8T9aYNL+HAKDArLCS7LWjwr7StE30IEYbIkx0Vi3vs+coQxe+SQDbGbbpA==", "peer": true }, + "react-router": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz", + "integrity": "sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg==", + "requires": { + "@remix-run/router": "1.8.0" + } + }, + "react-router-dom": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz", + "integrity": "sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==", + "requires": { + "@remix-run/router": "1.8.0", + "react-router": "6.15.0" + } + }, "react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", @@ -58472,14 +58536,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" }, - "stream": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", - "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", - "requires": { - "emitter-component": "^1.1.1" - } - }, "stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", diff --git a/packages/demo-react/src/BlurTextInput.tsx b/packages/demo-react/old/BlurTextInput.tsx similarity index 100% rename from packages/demo-react/src/BlurTextInput.tsx rename to packages/demo-react/old/BlurTextInput.tsx diff --git a/packages/demo-react/old/Layout.tsx b/packages/demo-react/old/Layout.tsx new file mode 100644 index 0000000..60885e6 --- /dev/null +++ b/packages/demo-react/old/Layout.tsx @@ -0,0 +1,37 @@ +import type { FunctionComponent, PropsWithChildren } from "react"; +import React, { useCallback } from "react"; +import { useSolidAuth } from "@ldo/solid-react"; + +const Layout: FunctionComponent = ({ children }) => { + const { login, session, logout } = useSolidAuth(); + + const loginCb = useCallback(async () => { + const issuer = prompt( + "Enter your Pod Provider", + "https://solidcommunity.net", + ); + if (issuer) { + await login(issuer); + } + }, []); + + return ( +
+

LDO Solid React Test

+ {session.isLoggedIn ? ( +
+

+ Logged in as {session.webId}{" "} + +

+
+ {children} +
+ ) : ( + + )} +
+ ); +}; + +export default Layout; diff --git a/packages/demo-react/src/Profile.tsx b/packages/demo-react/old/Profile.tsx similarity index 100% rename from packages/demo-react/src/Profile.tsx rename to packages/demo-react/old/Profile.tsx diff --git a/packages/demo-react/package.json b/packages/demo-react/package.json index 11a516d..a7a17ed 100644 --- a/packages/demo-react/package.json +++ b/packages/demo-react/package.json @@ -6,6 +6,7 @@ "@ldo/solid-react": "^0.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.15.0", "react-scripts": "5.0.1", "solid-authn-react-native": "^2.0.3" }, diff --git a/packages/demo-react/src/App.tsx b/packages/demo-react/src/App.tsx index ba36923..8cc2ffd 100644 --- a/packages/demo-react/src/App.tsx +++ b/packages/demo-react/src/App.tsx @@ -1,19 +1,13 @@ import type { FunctionComponent } from "react"; import React from "react"; -import Profile from "./Profile"; -import { SolidAuthProvider, LdoProvider } from "@ldo/solid-react"; -import { fetch } from "solid-authn-react-native"; -import Layout from "./Layout"; +import { Layout } from "./Layout"; +import { BrowserSolidLdoProvider } from "@ldo/solid-react"; const ProfileApp: FunctionComponent = () => { return ( - - - - - - - + + + ); }; export default ProfileApp; diff --git a/packages/demo-react/src/Layout.tsx b/packages/demo-react/src/Layout.tsx index 60885e6..46a5965 100644 --- a/packages/demo-react/src/Layout.tsx +++ b/packages/demo-react/src/Layout.tsx @@ -1,37 +1,60 @@ -import type { FunctionComponent, PropsWithChildren } from "react"; -import React, { useCallback } from "react"; import { useSolidAuth } from "@ldo/solid-react"; +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"; -const Layout: FunctionComponent = ({ children }) => { - const { login, session, logout } = useSolidAuth(); +const router = createBrowserRouter([ + { + path: "/", + element: , + }, + { + path: "/media/:uri", + element: , + }, +]); - const loginCb = useCallback(async () => { - const issuer = prompt( - "Enter your Pod Provider", - "https://solidcommunity.net", - ); - if (issuer) { - await login(issuer); - } - }, []); +const DEFAULT_ISSUER = "https://pod.lightover.com"; +export const Layout: FunctionComponent = () => { + const { login, logout, signUp, session, ranInitialAuthCheck } = + useSolidAuth(); + const [issuer, setIssuer] = useState(DEFAULT_ISSUER); + console.log(ranInitialAuthCheck); + if (!ranInitialAuthCheck) { + return

Loading

; + } return (
-

LDO Solid React Test

- {session.isLoggedIn ? ( -
-

- Logged in as {session.webId}{" "} +

+ {session.isLoggedIn ? ( + <> +

Logged in as {session.webId}

-

-
- {children} -
- ) : ( - - )} + + ) : ( + <> + setIssuer(e.target.value)} + /> + + + + )} + + {session.isLoggedIn ? : undefined}
); }; - -export default Layout; diff --git a/packages/demo-react/src/dashboard/Dashboard.tsx b/packages/demo-react/src/dashboard/Dashboard.tsx new file mode 100644 index 0000000..d4c2944 --- /dev/null +++ b/packages/demo-react/src/dashboard/Dashboard.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import type { FunctionComponent } from "react"; + +export const Dashboard: FunctionComponent = () => { + return ( +
+

Dashboard

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

Media: {uri}

+
+ ); +}; diff --git a/packages/solid-react/.babelrc b/packages/solid-react/.babelrc deleted file mode 100644 index b5cf683..0000000 --- a/packages/solid-react/.babelrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "sourceType": "unambiguous", - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "chrome": 100 - } - } - ], - "@babel/preset-typescript", - "@babel/preset-react" - ], - "plugins": [] -} \ No newline at end of file diff --git a/packages/solid-react/package.json b/packages/solid-react/package.json index 9169de1..8545138 100644 --- a/packages/solid-react/package.json +++ b/packages/solid-react/package.json @@ -23,27 +23,21 @@ }, "homepage": "https://github.com/o-development/devtool-boilerplate#readme", "devDependencies": { - "@babel/preset-env": "^7.22.10", - "@babel/preset-react": "^7.22.5", - "@babel/preset-typescript": "^7.22.11", + "@inrupt/solid-client-authn-core": "^1.17.1", "@ldo/rdf-utils": "^0.0.0", "@rdfjs/types": "^1.0.1", "@types/jest": "^29.0.3", - "@types/jsonld": "^1.5.8", - "@types/n3": "^1.10.4", - "@types/shexj": "2.1.4", "ts-jest": "^29.0.2" }, "dependencies": { "@inrupt/solid-client": "^1.29.0", + "@inrupt/solid-client-authn-browser": "^1.17.1", "@ldo/dataset": "^0.0.0", "@ldo/jsonld-dataset-proxy": "^0.0.0", "@ldo/ldo": "^0.0.0", "@ldo/solid": "^0.0.0", "@ldo/subscribable-dataset": "^0.0.0", "@rdfjs/data-model": "^1.2.0", - "cross-fetch": "^3.1.6", - "solid-authn-react-native": "^2.0.3", - "stream": "^0.0.2" + "cross-fetch": "^3.1.6" } -} \ No newline at end of file +} diff --git a/packages/solid-react/src/BrowserSolidLdoProvider.tsx b/packages/solid-react/src/BrowserSolidLdoProvider.tsx new file mode 100644 index 0000000..4c117e9 --- /dev/null +++ b/packages/solid-react/src/BrowserSolidLdoProvider.tsx @@ -0,0 +1,96 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import type { FunctionComponent, PropsWithChildren } from "react"; +import type { LoginOptions, SessionInfo } from "./SolidAuthContext"; +import { SolidAuthContext } from "./SolidAuthContext"; +import { + getDefaultSession, + handleIncomingRedirect, + login as libraryLogin, + logout as libraryLogout, + fetch as libraryFetch, +} from "@inrupt/solid-client-authn-browser"; + +const PRE_REDIRECT_URI = "PRE_REDIRECT_URI"; + +export const BrowserSolidLdoProvider: FunctionComponent = ({ + children, +}) => { + const [session, setSession] = useState(getDefaultSession().info); + const [ranInitialAuthCheck, setRanInitialAuthCheck] = useState(false); + + const runInitialAuthCheck = useCallback(async () => { + if (!window.localStorage.getItem(PRE_REDIRECT_URI)) { + window.localStorage.setItem(PRE_REDIRECT_URI, window.location.href); + } + + await handleIncomingRedirect({ + restorePreviousSession: true, + }); + setSession({ ...getDefaultSession().info }); + + window.history.replaceState( + {}, + "", + window.localStorage.getItem(PRE_REDIRECT_URI), + ); + window.localStorage.removeItem(PRE_REDIRECT_URI); + + setRanInitialAuthCheck(true); + }, []); + + const login = useCallback(async (issuer: string, options?: LoginOptions) => { + console.log("Before full options"); + const fullOptions = { + redirectUrl: window.location.href, + clientName: "Solid App", + oidcIssuer: issuer, + ...options, + }; + console.log("After full options"); + window.localStorage.setItem(PRE_REDIRECT_URI, fullOptions.redirectUrl); + console.log("Set Item"); + console.log(fullOptions); + await libraryLogin(fullOptions); + console.log("After login"); + setSession({ ...getDefaultSession().info }); + }, []); + + const logout = useCallback(async () => { + await libraryLogout(); + setSession({ ...getDefaultSession().info }); + }, []); + + const signUp = useCallback( + async (issuer: string, options?: LoginOptions) => { + // The typings on @inrupt/solid-client-authn-core have not yet been updated + // TODO: remove this ts-ignore when they are updated. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return login(issuer, { ...options, prompt: "create" }); + }, + [login], + ); + + useEffect(() => { + runInitialAuthCheck(); + }, []); + + const solidAuthFunctions = useMemo( + () => ({ + runInitialAuthCheck, + login, + logout, + signUp, + session, + ranInitialAuthCheck, + fetch: libraryFetch, + }), + [login, logout, ranInitialAuthCheck, runInitialAuthCheck, session, signUp], + ); + + return ( + + {children} + + ); +}; diff --git a/packages/solid-react/src/LdoContext.ts b/packages/solid-react/src/LdoContext.ts deleted file mode 100644 index 9a2e7cb..0000000 --- a/packages/solid-react/src/LdoContext.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createContext, useContext } from "react"; -import type { BinaryResourceStoreDependencies } from "./document/resource/binaryResource/BinaryResourceStore"; -import type { DataResourceStoreDependencies } from "./document/resource/dataResource/DataResourceStore"; -import type { AccessRulesStoreDependencies } from "./document/accessRules/AccessRulesStore"; -import type { ContainerResourceStoreDependencies } from "./document/resource/dataResource/containerResource/ContainerResourceStore"; - -export interface LdoContextData - extends BinaryResourceStoreDependencies, - DataResourceStoreDependencies, - AccessRulesStoreDependencies, - ContainerResourceStoreDependencies {} - -// No default parameter is required as it will be set in the provider -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -const LdoContext = createContext({}); - -export const LdoContextProvider = LdoContext.Provider; -export const useLdoContext = () => useContext(LdoContext); diff --git a/packages/solid-react/src/LdoProvider.tsx b/packages/solid-react/src/LdoProvider.tsx deleted file mode 100644 index 68dd8bd..0000000 --- a/packages/solid-react/src/LdoProvider.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { FunctionComponent, PropsWithChildren } from "react"; -import React, { useEffect, useMemo } from "react"; -import crossFetch from "cross-fetch"; -import { createLdoDataset } from "@ldo/ldo"; -import type { LdoContextData } from "./LdoContext"; -import { LdoContextProvider } from "./LdoContext"; -import { UpdateManager } from "./ldoHooks/helpers/UpdateManager"; -import type { Dataset } from "@rdfjs/types"; - -export interface LdoProviderProps extends PropsWithChildren { - fetch?: typeof fetch; - dataset?: Dataset; - onDocumentError?: LdoContextData["onDocumentError"]; -} - -/** - * Main Ldo Provider - */ -export const LdoProvider: FunctionComponent< - PropsWithChildren -> = ({ dataset, fetch, onDocumentError, children }) => { - const finalFetch = useMemo(() => fetch || crossFetch, [fetch]); - const ldoDataset = useMemo(() => createLdoDataset(dataset), [dataset]); - - // Initialize storeDependencies before render - const storeDependencies = useMemo(() => { - // Ingnoring this because we're setting up circular dependencies - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const dependencies: LdoContextData = { - onDocumentError, - fetch: finalFetch, - dataset: ldoDataset, - updateManager: new UpdateManager(), - }; - const binaryResourceStore = new BinaryResourceStore(dependencies); - const dataResourceStore = new DataResourceStore(dependencies); - const containerResourceStore = new ContainerResourceStore(dependencies); - const accessRulesStore = new AccessRulesStore(dependencies); - dependencies.binaryResourceStore = binaryResourceStore; - dependencies.dataResourceStore = dataResourceStore; - dependencies.containerResourceStore = containerResourceStore; - dependencies.accessRulesStore = accessRulesStore; - return dependencies; - }, []); - - // Update the resource manager in case fetch or ldo dataset changes - useEffect(() => { - storeDependencies.fetch = finalFetch; - storeDependencies.dataset = ldoDataset; - storeDependencies.onDocumentError = onDocumentError; - }, [finalFetch, ldoDataset, onDocumentError]); - - return ( - - {children} - - ); -}; diff --git a/packages/solid-react/src/SolidAuthContext.ts b/packages/solid-react/src/SolidAuthContext.ts new file mode 100644 index 0000000..e325d8f --- /dev/null +++ b/packages/solid-react/src/SolidAuthContext.ts @@ -0,0 +1,26 @@ +import type { + ISessionInfo, + ILoginInputOptions, +} from "@inrupt/solid-client-authn-core"; +import { createContext, useContext } from "react"; + +export type SessionInfo = ISessionInfo; +export type LoginOptions = ILoginInputOptions; + +export interface SolidAuthFunctions { + login: (issuer: string, loginOptions?: LoginOptions) => Promise; + logout: () => Promise; + signUp: (issuer: string, loginOptions?: LoginOptions) => Promise; + fetch: typeof fetch; + session: SessionInfo; + ranInitialAuthCheck: boolean; +} + +// There is no initial value for this context. It will be given in the provider +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export const SolidAuthContext = createContext(undefined); + +export function useSolidAuth() { + return useContext(SolidAuthContext); +} diff --git a/packages/solid-react/src/SolidAuthProvider.ts b/packages/solid-react/src/SolidAuthProvider.ts deleted file mode 100644 index 370641b..0000000 --- a/packages/solid-react/src/SolidAuthProvider.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; -import type { ISessionInfo } from "solid-authn-react-native"; -import { - handleIncomingRedirect, - login as libraryLogin, - getDefaultSession, - logout as libraryLogout, - fetch as libraryFetch, -} from "solid-authn-react-native"; - -import { createGlobalHook } from "./util/createGlobalHook"; - -const PRE_REDIRECT_URI = "PRE_REDIRECT_URI"; - -interface AuthGlobalHookReturn { - runInitialAuthCheck: () => Promise; - login: (issuer: string) => Promise; - logout: () => Promise; - signUp: (issuer: string) => Promise; - fetch: typeof fetch; - session: ISessionInfo; - ranInitialAuthCheck: boolean; -} - -function useAuthGlobalHookFunc(): AuthGlobalHookReturn { - const [session, setSession] = useState( - getDefaultSession().info, - ); - const [ranInitialAuthCheck, setRanInitialAuthCheck] = useState(false); - - const runInitialAuthCheck = useCallback(async () => { - // TODO: Change this to dependency injection so it works in React Native - if (!window.localStorage.getItem(PRE_REDIRECT_URI)) { - window.localStorage.setItem(PRE_REDIRECT_URI, window.location.href); - } - - await handleIncomingRedirect({ - restorePreviousSession: true, - }); - setSession({ ...getDefaultSession().info }); - - window.history.replaceState( - {}, - "", - window.localStorage.getItem(PRE_REDIRECT_URI), - ); - window.localStorage.removeItem(PRE_REDIRECT_URI); - - setRanInitialAuthCheck(true); - }, []); - - const login = useCallback( - async (issuer: string, clientName = "Solid App") => { - window.localStorage.setItem(PRE_REDIRECT_URI, window.location.href); - await libraryLogin({ - oidcIssuer: issuer, - // TODO: this ties this to in-browser use - redirectUrl: window.location.href, - clientName, - }); - setSession({ ...getDefaultSession().info }); - }, - [], - ); - - const logout = useCallback(async () => { - await libraryLogout(); - setSession({ ...getDefaultSession().info }); - }, []); - - const signUp = useCallback(async (issuer: string) => { - /* Do nothing for now */ - console.log(`Signup Pressed with issuer ${issuer}`); - }, []); - - useEffect(() => { - runInitialAuthCheck(); - }, []); - - return useMemo( - () => ({ - runInitialAuthCheck, - login, - logout, - signUp, - session, - ranInitialAuthCheck, - fetch: libraryFetch, - }), - [login, logout, ranInitialAuthCheck, runInitialAuthCheck, session, signUp], - ); -} - -const authGlobalHook = createGlobalHook(useAuthGlobalHookFunc); - -export const SolidAuthContext = authGlobalHook.Context; -export const SolidAuthProvider = authGlobalHook.Provider; -export const useSolidAuth = authGlobalHook.useGlobal; diff --git a/packages/solid-react/src/SolidLdoProvider.tsx b/packages/solid-react/src/SolidLdoProvider.tsx new file mode 100644 index 0000000..eb0dc5a --- /dev/null +++ b/packages/solid-react/src/SolidLdoProvider.tsx @@ -0,0 +1,62 @@ +import React, { createContext } from "react"; +import { + useMemo, + type FunctionComponent, + type PropsWithChildren, + useRef, + useEffect, +} from "react"; +import { useSolidAuth } from "./SolidAuthContext"; +import type { SolidLdoDataset, OnDocumentErrorCallback } from "@ldo/solid"; +import { createSolidLdoDataset } from "@ldo/solid"; + +export const SolidLdoDatasetReactContext = + // This will be set in the provider + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + createContext(undefined); + +export interface SolidLdoProviderProps extends PropsWithChildren { + onDocumentError?: OnDocumentErrorCallback; +} + +export const SolidLdoProvider: FunctionComponent = ({ + onDocumentError, + children, +}) => { + const { fetch } = useSolidAuth(); + const curOnDocumentError = useRef(onDocumentError); + + // Initialize storeDependencies before render + const solidLdoDataset = useMemo(() => { + const ldoDataset = createSolidLdoDataset({ + fetch, + }); + if (curOnDocumentError.current) { + ldoDataset.onDocumentError(curOnDocumentError.current); + } + return ldoDataset; + }, []); + + // Keep context in sync with props + useEffect(() => { + solidLdoDataset.context.fetch = fetch; + }, [fetch]); + useEffect(() => { + if (curOnDocumentError.current) { + solidLdoDataset.offDocumentError(curOnDocumentError.current); + } + if (onDocumentError) { + solidLdoDataset.onDocumentError(onDocumentError); + curOnDocumentError.current = onDocumentError; + } else { + curOnDocumentError.current = undefined; + } + }, [onDocumentError]); + + return ( + + {children} + + ); +}; diff --git a/packages/solid-react/src/documentHooks/useAccessRules.ts b/packages/solid-react/src/documentHooks/useAccessRules.ts deleted file mode 100644 index 321cc0f..0000000 --- a/packages/solid-react/src/documentHooks/useAccessRules.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useMemo } from "react"; -import { useLdoContext } from "../LdoContext"; -import type { UseDocumentOptions } from "./useDocument"; -import { useDocument } from "./useDocument"; -import type { Resource } from "../document/resource/Resource"; - -export function useAccessRules( - resource: string | Resource, - options?: UseDocumentOptions, -) { - const { dataResourceStore, accessRulesStore } = useLdoContext(); - const realResource = useMemo(() => { - return typeof resource === "string" - ? dataResourceStore.get(resource) - : resource; - }, [resource]); - return useDocument(realResource, accessRulesStore, options); -} diff --git a/packages/solid-react/src/documentHooks/useBinaryResource.ts b/packages/solid-react/src/documentHooks/useBinaryResource.ts deleted file mode 100644 index 00fe2a1..0000000 --- a/packages/solid-react/src/documentHooks/useBinaryResource.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useLdoContext } from "../LdoContext"; -import type { UseDocumentOptions } from "./useDocument"; -import { useDocument } from "./useDocument"; - -export function useBinaryResource(uri: string, options?: UseDocumentOptions) { - const { binaryResourceStore } = useLdoContext(); - return useDocument(uri, binaryResourceStore, options); -} diff --git a/packages/solid-react/src/documentHooks/useContainerResource.ts b/packages/solid-react/src/documentHooks/useContainerResource.ts deleted file mode 100644 index eaba97b..0000000 --- a/packages/solid-react/src/documentHooks/useContainerResource.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useLdoContext } from "../LdoContext"; -import type { UseDocumentOptions } from "./useDocument"; -import { useDocument } from "./useDocument"; - -export function useContainerResource( - uri: string, - options?: UseDocumentOptions, -) { - const { containerResourceStore } = useLdoContext(); - return useDocument(uri, containerResourceStore, options); -} diff --git a/packages/solid-react/src/documentHooks/useDataResource.ts b/packages/solid-react/src/documentHooks/useDataResource.ts deleted file mode 100644 index 5edb53f..0000000 --- a/packages/solid-react/src/documentHooks/useDataResource.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useLdoContext } from "../LdoContext"; -import type { UseDocumentOptions } from "./useDocument"; -import { useDocument } from "./useDocument"; - -export function useDataResource(uri: string, options?: UseDocumentOptions) { - const { dataResourceStore } = useLdoContext(); - return useDocument(uri, dataResourceStore, options); -} diff --git a/packages/solid-react/src/documentHooks/useDocument.ts b/packages/solid-react/src/documentHooks/useDocument.ts deleted file mode 100644 index 33e2002..0000000 --- a/packages/solid-react/src/documentHooks/useDocument.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect, useMemo } from "react"; -import type { - DocumentStore, - DocumentStoreDependencies, -} from "../document/DocumentStore"; -import type { FetchableDocument } from "../document/FetchableDocument"; -import { useForceUpdate } from "../util/useForceReload"; - -export interface UseDocumentOptions { - suppressLoadOnMount: boolean; -} - -export function useDocument< - DocumentType extends FetchableDocument, - Initializer, ->( - initializer: Initializer, - documentStore: DocumentStore< - DocumentType, - Initializer, - DocumentStoreDependencies - >, - options?: UseDocumentOptions, -) { - const document = useMemo(() => { - return documentStore.get(initializer); - }, [initializer, documentStore]); - - const forceUpdate = useForceUpdate(); - - useEffect(() => { - // Set up the listener for state update - function onStateUpdateCallback() { - forceUpdate(); - } - document.onStateUpdate(onStateUpdateCallback); - // Load the resource if load on mount is true - if (!options?.suppressLoadOnMount) { - document.read(); - } - return () => document.offStateUpdate(onStateUpdateCallback); - }, []); - - return document; -} diff --git a/packages/solid-react/src/index.ts b/packages/solid-react/src/index.ts index 3e52239..8cf6834 100644 --- a/packages/solid-react/src/index.ts +++ b/packages/solid-react/src/index.ts @@ -1,14 +1,2 @@ -// documentHooks -export * from "./documentHooks/useAccessRules"; -export * from "./documentHooks/useBinaryResource"; -export * from "./documentHooks/useContainerResource"; -export * from "./documentHooks/useDataResource"; -export * from "./documentHooks/useDocument"; - -// ldoHooks -export * from "./ldoHooks/useSubject"; - -// export -export * from "./useLdo"; -export * from "./LdoProvider"; -export * from "./SolidAuthProvider"; +export * from "./BrowserSolidLdoProvider"; +export * from "./SolidAuthContext"; diff --git a/packages/solid-react/src/ldo/solid.context.ts b/packages/solid-react/src/ldo/solid.context.ts deleted file mode 100644 index be97612..0000000 --- a/packages/solid-react/src/ldo/solid.context.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ContextDefinition } from "jsonld"; - -/** - * ============================================================================= - * solidContext: JSONLD Context for solid - * ============================================================================= - */ -export const solidContext: ContextDefinition = { - type: { - "@id": "@type", - "@container": "@set", - }, - Container: "http://www.w3.org/ns/ldp#Container", - Resource: "http://www.w3.org/ns/ldp#Resource", - modified: { - "@id": "http://purl.org/dc/terms/modified", - "@type": "http://www.w3.org/2001/XMLSchema#string", - "@container": "@set", - }, - contains: { - "@id": "http://www.w3.org/ns/ldp#contains", - "@type": "@id", - "@container": "@set", - }, - Resource2: "http://www.w3.org/ns/iana/media-types/text/turtle#Resource", - mtime: { - "@id": "http://www.w3.org/ns/posix/stat#mtime", - "@type": "http://www.w3.org/2001/XMLSchema#decimal", - "@container": "@set", - }, - size: { - "@id": "http://www.w3.org/ns/posix/stat#size", - "@type": "http://www.w3.org/2001/XMLSchema#integer", - "@container": "@set", - }, -}; diff --git a/packages/solid-react/src/ldo/solid.schema.ts b/packages/solid-react/src/ldo/solid.schema.ts deleted file mode 100644 index dabb685..0000000 --- a/packages/solid-react/src/ldo/solid.schema.ts +++ /dev/null @@ -1,214 +0,0 @@ -import type { Schema } from "shexj"; - -/** - * ============================================================================= - * solidSchema: ShexJ Schema for solid - * ============================================================================= - */ -export const solidSchema: Schema = { - type: "Schema", - shapes: [ - { - id: "http://www.w3.org/ns/lddps#Container", - type: "ShapeDecl", - shapeExpr: { - type: "Shape", - expression: { - id: "http://www.w3.org/ns/lddps#ContainerShape", - type: "EachOf", - expressions: [ - { - type: "TripleConstraint", - predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", - valueExpr: { - type: "NodeConstraint", - values: [ - "http://www.w3.org/ns/ldp#Container", - "http://www.w3.org/ns/ldp#Resource", - ], - }, - min: 0, - max: -1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "A container on a Solid server", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://purl.org/dc/terms/modified", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#string", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "Date modified", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://www.w3.org/ns/ldp#contains", - valueExpr: "http://www.w3.org/ns/lddps#Resource", - min: 0, - max: -1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "Defines a Solid Resource", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://www.w3.org/ns/posix/stat#mtime", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#decimal", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "?", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://www.w3.org/ns/posix/stat#size", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#integer", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "size of this container", - }, - }, - ], - }, - ], - }, - extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], - }, - }, - { - id: "http://www.w3.org/ns/lddps#Resource", - type: "ShapeDecl", - shapeExpr: { - type: "Shape", - expression: { - id: "http://www.w3.org/ns/lddps#ResourceShape", - type: "EachOf", - expressions: [ - { - type: "TripleConstraint", - predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", - valueExpr: { - type: "NodeConstraint", - values: [ - "http://www.w3.org/ns/ldp#Resource", - "http://www.w3.org/ns/iana/media-types/text/turtle#Resource", - ], - }, - min: 0, - max: -1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "Any resource on a Solid server", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://purl.org/dc/terms/modified", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#string", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "Date modified", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://www.w3.org/ns/posix/stat#mtime", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#decimal", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "?", - }, - }, - ], - }, - { - type: "TripleConstraint", - predicate: "http://www.w3.org/ns/posix/stat#size", - valueExpr: { - type: "NodeConstraint", - datatype: "http://www.w3.org/2001/XMLSchema#integer", - }, - min: 0, - max: 1, - annotations: [ - { - type: "Annotation", - predicate: "http://www.w3.org/2000/01/rdf-schema#comment", - object: { - value: "size of this container", - }, - }, - ], - }, - ], - }, - extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], - }, - }, - ], -}; diff --git a/packages/solid-react/src/ldo/solid.shapeTypes.ts b/packages/solid-react/src/ldo/solid.shapeTypes.ts deleted file mode 100644 index 705e60e..0000000 --- a/packages/solid-react/src/ldo/solid.shapeTypes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { ShapeType } from "@ldo/ldo"; -import { solidSchema } from "./solid.schema"; -import { solidContext } from "./solid.context"; -import type { Container, Resource } from "./solid.typings"; - -/** - * ============================================================================= - * LDO ShapeTypes solid - * ============================================================================= - */ - -/** - * Container ShapeType - */ -export const ContainerShapeType: ShapeType = { - schema: solidSchema, - shape: "http://www.w3.org/ns/lddps#Container", - context: solidContext, -}; - -/** - * Resource ShapeType - */ -export const ResourceShapeType: ShapeType = { - schema: solidSchema, - shape: "http://www.w3.org/ns/lddps#Resource", - context: solidContext, -}; diff --git a/packages/solid-react/src/ldo/solid.typings.ts b/packages/solid-react/src/ldo/solid.typings.ts deleted file mode 100644 index 51b107c..0000000 --- a/packages/solid-react/src/ldo/solid.typings.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { ContextDefinition } from "jsonld"; - -/** - * ============================================================================= - * Typescript Typings for solid - * ============================================================================= - */ - -/** - * Container Type - */ -export interface Container { - "@id"?: string; - "@context"?: ContextDefinition; - /** - * A container on a Solid server - */ - type?: ( - | { - "@id": "Container"; - } - | { - "@id": "Resource"; - } - )[]; - /** - * Date modified - */ - modified?: string; - /** - * Defines a Solid Resource - */ - contains?: Resource[]; - /** - * ? - */ - mtime?: number; - /** - * size of this container - */ - size?: number; -} - -/** - * Resource Type - */ -export interface Resource { - "@id"?: string; - "@context"?: ContextDefinition; - /** - * Any resource on a Solid server - */ - type?: ( - | { - "@id": "Resource"; - } - | { - "@id": "Resource2"; - } - )[]; - /** - * Date modified - */ - modified?: string; - /** - * ? - */ - mtime?: number; - /** - * size of this container - */ - size?: number; -} diff --git a/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts b/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts deleted file mode 100644 index 4864adf..0000000 --- a/packages/solid-react/src/ldoHooks/helpers/TrackingProxyContext.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { - ArrayProxyTarget, - SubjectProxyTarget, - ProxyContextOptions, -} from "@ldo/jsonld-dataset-proxy"; -import { ProxyContext } from "@ldo/jsonld-dataset-proxy"; -import type { UpdateManager } from "./UpdateManager"; -import { namedNode } from "@rdfjs/data-model"; - -export class TrackingProxyContext extends ProxyContext { - private updateManager: UpdateManager; - private listener: () => void; - - constructor( - options: ProxyContextOptions, - updateManager: UpdateManager, - listener: () => void, - ) { - super(options); - this.updateManager = updateManager; - this.listener = listener; - } - - protected createSubjectHandler(): ProxyHandler { - const baseHandler = super.createSubjectHandler(); - const oldGetFunction = baseHandler.get; - const newGetFunction: ProxyHandler["get"] = ( - target: SubjectProxyTarget, - key: string | symbol, - receiver, - ) => { - const subject = target["@id"]; - if (typeof key === "symbol") { - // Do Nothing - } else if (key === "@id") { - this.updateManager.registerListener( - [subject, null, null, null], - this.listener, - ); - } else if (!this.contextUtil.isArray(key)) { - const predicate = namedNode(this.contextUtil.keyToIri(key)); - this.updateManager.registerListener( - [subject, predicate, null, null], - this.listener, - ); - } - return oldGetFunction && oldGetFunction(target, key, receiver); - }; - baseHandler.get = newGetFunction; - baseHandler.set = () => { - console.warn( - "You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.", - ); - return true; - }; - return baseHandler; - } - - protected createArrayHandler(): ProxyHandler { - const baseHandler = super.createArrayHandler(); - const oldGetFunction = baseHandler.get; - const newGetFunction: ProxyHandler["get"] = ( - target: ArrayProxyTarget, - key: string | symbol, - receiver, - ) => { - if (qualifiedArrayMethods.has(key)) { - this.updateManager.registerListener( - [target[0][0], target[0][1], target[0][2], null], - this.listener, - ); - } - return oldGetFunction && oldGetFunction(target, key, receiver); - }; - baseHandler.get = newGetFunction; - return baseHandler; - } -} - -const qualifiedArrayMethods = new Set([ - "forEach", - "map", - "reduce", - Symbol.iterator, - "entries", - "every", - "filter", - "find", - "findIndex", - "findLast", - "findLastIndex", - "includes, indexOf", - "keys", - "lastIndexOf", - "reduceRight", - "some", - "values", -]); diff --git a/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts b/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts deleted file mode 100644 index ce85ff5..0000000 --- a/packages/solid-react/src/ldoHooks/helpers/UpdateManager.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { - DatasetChanges, - QuadMatch, - SubjectNode, - PredicateNode, - ObjectNode, -} from "@ldo/rdf-utils"; -import { createDataset } from "@ldo/dataset"; -import { quadMatchToString } from "@ldo/rdf-utils"; -import type { Quad } from "@rdfjs/types"; - -export class UpdateManager { - private quadMatchListenerMap: Record void>> = {}; - private listenerHashMap: Map<() => void, Set> = new Map(); - - registerListener(quadMatch: QuadMatch, callback: () => void): void { - const hash = quadMatchToString(quadMatch); - if (!this.quadMatchListenerMap[hash]) { - this.quadMatchListenerMap[hash] = new Set(); - } - if (!this.listenerHashMap.has(callback)) { - this.listenerHashMap.set(callback, new Set()); - } - this.quadMatchListenerMap[hash].add(callback); - this.listenerHashMap.get(callback)?.add(hash); - } - - removeListener(callback: () => void) { - const hashSet = this.listenerHashMap.get(callback); - if (hashSet) { - hashSet.forEach((hash) => { - this.quadMatchListenerMap[hash]?.delete(callback); - }); - } - } - - notifyListenersOfChanges(changes: DatasetChanges): void { - const listenersToNotify = new Set<() => void>(); - - const allQuads = createDataset(); - allQuads.addAll(changes.added || []); - allQuads.addAll(changes.removed || []); - - // Iterate through all quads looking for any dataset match they effect - allQuads.forEach((tempQuad) => { - // Cast the input because RDFJS types assume RDF 1.2 where a Subject can - // be a Quad - const quad = tempQuad as { - subject: SubjectNode; - predicate: PredicateNode; - object: ObjectNode; - }; - const quadMatches: QuadMatch[] = [ - [null, null, null, null], - [quad.subject, null, null, null], - [quad.subject, quad.predicate, null, null], - [quad.subject, null, quad.object, null], - [null, quad.predicate, null, null], - [null, quad.predicate, quad.object, null], - [null, null, quad.object, null], - [quad.subject, quad.predicate, quad.object, null], - ]; - - quadMatches.forEach((quadMatch) => { - const hash = quadMatchToString(quadMatch); - this.quadMatchListenerMap[hash]?.forEach((callback) => { - listenersToNotify.add(callback); - }); - delete this.quadMatchListenerMap[hash]; - }); - }); - listenersToNotify.forEach((listener) => { - listener(); - }); - } -} diff --git a/packages/solid-react/src/ldoHooks/useMatchingObjects.ts b/packages/solid-react/src/ldoHooks/useMatchingObjects.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/solid-react/src/ldoHooks/useMatchingSubjects.ts b/packages/solid-react/src/ldoHooks/useMatchingSubjects.ts deleted file mode 100644 index e69de29..0000000 diff --git a/packages/solid-react/src/ldoHooks/useSubject.ts b/packages/solid-react/src/ldoHooks/useSubject.ts deleted file mode 100644 index 79f0ec1..0000000 --- a/packages/solid-react/src/ldoHooks/useSubject.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { defaultGraph } from "@rdfjs/data-model"; -import type { SubjectNode } from "@ldo/rdf-utils"; -import { - ContextUtil, - JsonldDatasetProxyBuilder, -} from "@ldo/jsonld-dataset-proxy"; -import type { ShapeType, LdoBase } from "@ldo/ldo"; -import { LdoBuilder } from "@ldo/ldo"; -import { useLdoContext } from "../LdoContext"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { TrackingProxyContext } from "./helpers/TrackingProxyContext"; - -export function useSubject( - shapeType: ShapeType, - subject: string | SubjectNode, -): [Type, undefined] | [undefined, Error] { - const { dataset, updateManager } = useLdoContext(); - - const [forceUpdateCounter, setForceUpdateCounter] = useState(0); - const forceUpdate = useCallback( - () => setForceUpdateCounter((val) => val + 1), - [setForceUpdateCounter], - ); - - // The main linked data object - const linkedDataObject = useMemo(() => { - // Rebuild the LdoBuilder from scratch to inject TrackingProxyContext - const contextUtil = new ContextUtil(shapeType.context); - const proxyContext = new TrackingProxyContext( - { - dataset, - contextUtil, - writeGraphs: [defaultGraph()], - languageOrdering: ["none", "en", "other"], - }, - updateManager, - forceUpdate, - ); - const builder = new LdoBuilder( - new JsonldDatasetProxyBuilder(proxyContext), - shapeType, - ); - return builder.fromSubject(subject); - }, [ - shapeType, - subject, - dataset, - updateManager, - forceUpdateCounter, - forceUpdate, - ]); - - useEffect(() => { - // Unregister force update listener upon unmount - return () => updateManager.removeListener(forceUpdate); - }, [shapeType, subject]); - - return [linkedDataObject, undefined]; -} diff --git a/packages/solid-react/src/shapes/solid.shex b/packages/solid-react/src/shapes/solid.shex deleted file mode 100644 index 67dc06e..0000000 --- a/packages/solid-react/src/shapes/solid.shex +++ /dev/null @@ -1,36 +0,0 @@ -PREFIX xsd: -PREFIX rdf: -PREFIX rdfs: -PREFIX ldp: -PREFIX ldps: -PREFIX dct: -PREFIX stat: -PREFIX tur: - -ldps:Container EXTRA a { - $ldps:ContainerShape ( - a [ ldp:Container ldp:Resource ]* - // rdfs:comment "A container on a Solid server"; - dct:modified xsd:string? - // rdfs:comment "Date modified"; - ldp:contains @ldps:Resource* - // rdfs:comment "Defines a Solid Resource"; - stat:mtime xsd:decimal? - // rdfs:comment "?"; - stat:size xsd:integer? - // rdfs:comment "size of this container"; - ) -} - -ldps:Resource EXTRA a { - $ldps:ResourceShape ( - a [ ldp:Resource tur:Resource ]* - // rdfs:comment "Any resource on a Solid server"; - dct:modified xsd:string? - // rdfs:comment "Date modified"; - stat:mtime xsd:decimal? - // rdfs:comment "?"; - stat:size xsd:integer? - // rdfs:comment "size of this container"; - ) -} diff --git a/packages/solid-react/src/useLdo.ts b/packages/solid-react/src/useLdo.ts deleted file mode 100644 index 3283b12..0000000 --- a/packages/solid-react/src/useLdo.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { useCallback, useMemo } from "react"; -import { useLdoContext } from "./LdoContext"; -import type { LdoDataset, ShapeType, LdoBase } from "@ldo/ldo"; -import { startTransaction, transactionChanges, write } from "@ldo/ldo"; -import { splitChangesByGraph } from "./util/splitChangesByGraph"; -import type { Resource } from "./document/resource/Resource"; -import type { DataResource } from "./document/resource/dataResource/DataResource"; -import type { BinaryResource } from "./document/resource/binaryResource/BinaryResource"; -import type { ContainerResource } from "./document/resource/dataResource/containerResource/ContainerResource"; -import type { AccessRules } from "./document/accessRules/AccessRules"; -import type { DatasetChanges, SubjectNode } from "@ldo/rdf-utils"; -import type { Quad } from "@rdfjs/types"; - -export interface UseLdoReturn { - changeData(input: Type, ...resources: Resource[]): Type; - commitData(input: LdoBase): Promise; - createData( - shapeType: ShapeType, - subject: string | SubjectNode, - ...resources: Resource[] - ): Type; - dataset: LdoDataset; - getDataResource: (uri: string) => DataResource; - getBinaryResource: (uri: string) => BinaryResource; - getContainerResource: (uri: string) => ContainerResource; - getAccessRules: (resource: Resource) => AccessRules; -} - -export function useLdo(): UseLdoReturn { - const { - dataResourceStore, - containerResourceStore, - binaryResourceStore, - accessRulesStore, - dataset, - } = useLdoContext(); - /** - * Begins tracking changes to eventually commit - */ - const changeData = useCallback( - (input: Type, ...resources: Resource[]) => { - // Clone the input and set a graph - const [transactionLdo] = write(...resources.map((r) => r.uri)).usingCopy( - input, - ); - // Start a transaction with the input - startTransaction(transactionLdo); - // Return - return transactionLdo; - }, - [dataset], - ); - /** - * Begins tracking changes to eventually commit for a new subject - */ - const createData = useCallback( - ( - shapeType: ShapeType, - subject: string | SubjectType, - ...resources: Resource[] - ) => { - const linkedDataObject = dataset - .usingType(shapeType) - .write(...resources.map((r) => r.uri)) - .fromSubject(subject); - startTransaction(linkedDataObject); - return linkedDataObject; - }, - [], - ); - /** - * Commits the transaction to the global dataset, syncing all subscribing - * components and Solid Pods - */ - const commitData = useCallback( - async (input: LdoBase) => { - const changes = transactionChanges(input); - const changesByGraph = splitChangesByGraph( - changes as DatasetChanges, - ); - // Make queries - await Promise.all( - Array.from(changesByGraph.entries()).map( - async ([graph, datasetChanges]) => { - if (graph.termType === "DefaultGraph") { - return; - } - const resource = dataResourceStore.get(graph.value); - await resource.update(datasetChanges); - }, - ), - ); - }, - [dataset, fetch], - ); - // Returns the values - return useMemo( - () => ({ - dataset, - changeData, - createData, - commitData, - getDataResource: (uri) => dataResourceStore.get(uri), - getBinaryResource: (uri) => binaryResourceStore.get(uri), - getContainerResource: (uri) => containerResourceStore.get(uri), - getAccessRules: (resource) => accessRulesStore.get(resource), - }), - [dataset, changeData, commitData], - ); -} diff --git a/packages/solid-react/tsconfig.build.json b/packages/solid-react/tsconfig.build.json index 919962b..e375629 100644 --- a/packages/solid-react/tsconfig.build.json +++ b/packages/solid-react/tsconfig.build.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", - "jsx": "react-jsx" + "lib": ["dom"] }, "include": ["./src"] } \ No newline at end of file diff --git a/packages/solid/src/SolidLdoDataset.ts b/packages/solid/src/SolidLdoDataset.ts index 75da4a1..e94b9d7 100644 --- a/packages/solid/src/SolidLdoDataset.ts +++ b/packages/solid/src/SolidLdoDataset.ts @@ -6,11 +6,13 @@ import type { BinaryResource } from "./document/resource/binaryResource/BinaryRe import type { DocumentGetterOptions } from "./document/DocumentStore"; import type { Dataset, DatasetFactory } from "@rdfjs/types"; import type { Resource } from "./document/resource/Resource"; -import type { DocumentError } from "./document/errors/DocumentError"; -import type { SolidLdoDatasetContext } from "./SolidLdoDatasetContext"; +import type { + OnDocumentErrorCallback, + SolidLdoDatasetContext, +} from "./SolidLdoDatasetContext"; export class SolidLdoDataset extends LdoDataset { - protected context: SolidLdoDatasetContext; + public context: SolidLdoDatasetContext; constructor( context: SolidLdoDatasetContext, @@ -50,7 +52,11 @@ export class SolidLdoDataset extends LdoDataset { return this.context.binaryResourceStore.get(uri, options); } - onDocumentError(_callback: (error: DocumentError) => void): void { - throw new Error("Not Implemented"); + onDocumentError(callback: OnDocumentErrorCallback): void { + this.context.documentEventEmitter.on("documentError", callback); + } + + offDocumentError(callback: OnDocumentErrorCallback): void { + this.context.documentEventEmitter.off("documentError", callback); } } diff --git a/packages/solid/src/SolidLdoDatasetContext.ts b/packages/solid/src/SolidLdoDatasetContext.ts index 166f855..1697218 100644 --- a/packages/solid/src/SolidLdoDatasetContext.ts +++ b/packages/solid/src/SolidLdoDatasetContext.ts @@ -6,8 +6,10 @@ import type { DataResourceStore } from "./document/resource/dataResource/DataRes import type { BinaryResourceStore } from "./document/resource/binaryResource/BinaryResourceStore"; import type { DocumentError } from "./document/errors/DocumentError"; +export type OnDocumentErrorCallback = (error: DocumentError) => void; + export type DocumentEventEmitter = TypedEmitter<{ - documentError: (error: DocumentError) => void; + documentError: OnDocumentErrorCallback; }>; export interface SolidLdoDatasetContext { diff --git a/packages/solid/src/index.ts b/packages/solid/src/index.ts index d724aaf..8364839 100644 --- a/packages/solid/src/index.ts +++ b/packages/solid/src/index.ts @@ -24,3 +24,8 @@ export * from "./document/resource/dataResource/DataResourceStore"; // document/resource/containerResource export * from "./document/resource/dataResource/containerResource/ContainerResource"; export * from "./document/resource/dataResource/containerResource/ContainerResourceStore"; + +// / +export * from "./createSolidLdoDataset"; +export * from "./SolidLdoDataset"; +export * from "./SolidLdoDatasetContext"; diff --git a/packages/solid/tsconfig.build.json b/packages/solid/tsconfig.build.json index 919962b..4bd5a5e 100644 --- a/packages/solid/tsconfig.build.json +++ b/packages/solid/tsconfig.build.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", - "jsx": "react-jsx" }, "include": ["./src"] } \ No newline at end of file