parent
0dbe6a5e21
commit
a603cc02f4
@ -0,0 +1,37 @@ |
||||
import type { FunctionComponent, PropsWithChildren } from "react"; |
||||
import React, { useCallback } from "react"; |
||||
import { useSolidAuth } from "@ldo/solid-react"; |
||||
|
||||
const Layout: FunctionComponent<PropsWithChildren> = ({ 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 ( |
||||
<div> |
||||
<h1>LDO Solid React Test</h1> |
||||
{session.isLoggedIn ? ( |
||||
<div> |
||||
<p> |
||||
Logged in as {session.webId}{" "} |
||||
<button onClick={logout}>Log Out</button> |
||||
</p> |
||||
<hr /> |
||||
{children} |
||||
</div> |
||||
) : ( |
||||
<button onClick={loginCb}>Log In</button> |
||||
)} |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
export default Layout; |
@ -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 ( |
||||
<SolidAuthProvider> |
||||
<LdoProvider fetch={fetch}> |
||||
<Layout> |
||||
<Profile /> |
||||
</Layout> |
||||
</LdoProvider> |
||||
</SolidAuthProvider> |
||||
<BrowserSolidLdoProvider> |
||||
<Layout /> |
||||
</BrowserSolidLdoProvider> |
||||
); |
||||
}; |
||||
export default ProfileApp; |
||||
|
@ -0,0 +1,10 @@ |
||||
import React from "react"; |
||||
import type { FunctionComponent } from "react"; |
||||
|
||||
export const Dashboard: FunctionComponent = () => { |
||||
return ( |
||||
<div> |
||||
<p>Dashboard</p> |
||||
</div> |
||||
); |
||||
}; |
@ -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 ( |
||||
<div> |
||||
<p>Media: {uri}</p> |
||||
</div> |
||||
); |
||||
}; |
@ -1,16 +0,0 @@ |
||||
{ |
||||
"sourceType": "unambiguous", |
||||
"presets": [ |
||||
[ |
||||
"@babel/preset-env", |
||||
{ |
||||
"targets": { |
||||
"chrome": 100 |
||||
} |
||||
} |
||||
], |
||||
"@babel/preset-typescript", |
||||
"@babel/preset-react" |
||||
], |
||||
"plugins": [] |
||||
} |
@ -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<PropsWithChildren> = ({ |
||||
children, |
||||
}) => { |
||||
const [session, setSession] = useState<SessionInfo>(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 ( |
||||
<SolidAuthContext.Provider value={solidAuthFunctions}> |
||||
{children} |
||||
</SolidAuthContext.Provider> |
||||
); |
||||
}; |
@ -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<LdoContextData>({}); |
||||
|
||||
export const LdoContextProvider = LdoContext.Provider; |
||||
export const useLdoContext = () => useContext(LdoContext); |
@ -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<LdoProviderProps> |
||||
> = ({ 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 ( |
||||
<LdoContextProvider value={storeDependencies}> |
||||
{children} |
||||
</LdoContextProvider> |
||||
); |
||||
}; |
@ -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<void>; |
||||
logout: () => Promise<void>; |
||||
signUp: (issuer: string, loginOptions?: LoginOptions) => Promise<void>; |
||||
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<SolidAuthFunctions>(undefined); |
||||
|
||||
export function useSolidAuth() { |
||||
return useContext(SolidAuthContext); |
||||
} |
@ -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<void>; |
||||
login: (issuer: string) => Promise<void>; |
||||
logout: () => Promise<void>; |
||||
signUp: (issuer: string) => Promise<void>; |
||||
fetch: typeof fetch; |
||||
session: ISessionInfo; |
||||
ranInitialAuthCheck: boolean; |
||||
} |
||||
|
||||
function useAuthGlobalHookFunc(): AuthGlobalHookReturn { |
||||
const [session, setSession] = useState<ISessionInfo>( |
||||
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; |
@ -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<SolidLdoDataset>(undefined); |
||||
|
||||
export interface SolidLdoProviderProps extends PropsWithChildren { |
||||
onDocumentError?: OnDocumentErrorCallback; |
||||
} |
||||
|
||||
export const SolidLdoProvider: FunctionComponent<SolidLdoProviderProps> = ({ |
||||
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 ( |
||||
<SolidLdoDatasetReactContext.Provider value={solidLdoDataset}> |
||||
{children} |
||||
</SolidLdoDatasetReactContext.Provider> |
||||
); |
||||
}; |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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); |
||||
} |
@ -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; |
||||
} |
@ -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"; |
||||
|
@ -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", |
||||
}, |
||||
}; |
@ -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"], |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -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<Container> = { |
||||
schema: solidSchema, |
||||
shape: "http://www.w3.org/ns/lddps#Container", |
||||
context: solidContext, |
||||
}; |
||||
|
||||
/** |
||||
* Resource ShapeType |
||||
*/ |
||||
export const ResourceShapeType: ShapeType<Resource> = { |
||||
schema: solidSchema, |
||||
shape: "http://www.w3.org/ns/lddps#Resource", |
||||
context: solidContext, |
||||
}; |
@ -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; |
||||
} |
@ -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<SubjectProxyTarget> { |
||||
const baseHandler = super.createSubjectHandler(); |
||||
const oldGetFunction = baseHandler.get; |
||||
const newGetFunction: ProxyHandler<SubjectProxyTarget>["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<ArrayProxyTarget> { |
||||
const baseHandler = super.createArrayHandler(); |
||||
const oldGetFunction = baseHandler.get; |
||||
const newGetFunction: ProxyHandler<ArrayProxyTarget>["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", |
||||
]); |
@ -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<string, Set<() => void>> = {}; |
||||
private listenerHashMap: Map<() => void, Set<string>> = 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<Quad>): 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(); |
||||
}); |
||||
} |
||||
} |
@ -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<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
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]; |
||||
} |
@ -1,36 +0,0 @@ |
||||
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
PREFIX ldp: <http://www.w3.org/ns/ldp#> |
||||
PREFIX ldps: <http://www.w3.org/ns/lddps#> |
||||
PREFIX dct: <http://purl.org/dc/terms/> |
||||
PREFIX stat: <http://www.w3.org/ns/posix/stat#> |
||||
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#> |
||||
|
||||
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"; |
||||
) |
||||
} |
@ -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<Type extends LdoBase>(input: Type, ...resources: Resource[]): Type; |
||||
commitData(input: LdoBase): Promise<void>; |
||||
createData<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
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( |
||||
<Type extends LdoBase>(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( |
||||
<Type extends LdoBase>( |
||||
shapeType: ShapeType<Type>, |
||||
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<Quad>, |
||||
); |
||||
// 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], |
||||
); |
||||
} |
Loading…
Reference in new issue