diff --git a/packages/demo-react/src/App.tsx b/packages/demo-react/src/App.tsx
index 8cc2ffd..721b93e 100644
--- a/packages/demo-react/src/App.tsx
+++ b/packages/demo-react/src/App.tsx
@@ -1,12 +1,12 @@
import type { FunctionComponent } from "react";
import React from "react";
-import { Layout } from "./Layout";
+import { Router } from "./Layout";
import { BrowserSolidLdoProvider } from "@ldo/solid-react";
const ProfileApp: FunctionComponent = () => {
return (
-
+
);
};
diff --git a/packages/demo-react/src/Header.tsx b/packages/demo-react/src/Header.tsx
index 289f044..49f751d 100644
--- a/packages/demo-react/src/Header.tsx
+++ b/packages/demo-react/src/Header.tsx
@@ -3,6 +3,7 @@ import type { FunctionComponent } from "react";
import React from "react";
import { useResource, useSolidAuth, useSubject } from "@ldo/solid-react";
import { SolidProfileShapeShapeType } from "./.ldo/solidProfile.shapeTypes";
+import { Link } from "react-router-dom";
const DEFAULT_ISSUER = "https://solidweb.me";
@@ -27,20 +28,27 @@ export const Header: FunctionComponent = () => {
const [issuer, setIssuer] = useState(DEFAULT_ISSUER);
const { login, signUp, session } = useSolidAuth();
return (
-
- {session.isLoggedIn ? (
-
- ) : (
- <>
- setIssuer(e.target.value)}
- />
-
-
- >
- )}
+
);
};
diff --git a/packages/demo-react/src/Layout.tsx b/packages/demo-react/src/Layout.tsx
index bdc30a2..0521d3f 100644
--- a/packages/demo-react/src/Layout.tsx
+++ b/packages/demo-react/src/Layout.tsx
@@ -1,35 +1,50 @@
import { useSolidAuth } from "@ldo/solid-react";
-import React from "react";
+import React, { Fragment } from "react";
import type { FunctionComponent } from "react";
-import { createBrowserRouter, RouterProvider } from "react-router-dom";
+import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
import { Blog } from "./blog/Blog";
import { PostPage } from "./post/PostPage";
import { Header } from "./Header";
import { MainContainerProvider } from "./MainContainerProvider";
-
-const router = createBrowserRouter([
- {
- path: "/",
- element: ,
- },
- {
- path: "/media/:uri",
- element: ,
- },
-]);
+import { Profile } from "./profile/Profile";
export const Layout: FunctionComponent = () => {
- const { session, ranInitialAuthCheck } = useSolidAuth();
- if (!ranInitialAuthCheck) {
- return Loading
;
- }
+ const { session } = useSolidAuth();
return (
- {session.isLoggedIn ? : undefined}
+ {session.isLoggedIn ? : }
);
};
+
+const router = createBrowserRouter([
+ {
+ element: ,
+ children: [
+ {
+ path: "/",
+ element: ,
+ },
+ {
+ path: "/media/:uri",
+ element: ,
+ },
+ {
+ path: "/profile",
+ element: ,
+ },
+ ],
+ },
+]);
+
+export const Router: FunctionComponent = () => {
+ const { ranInitialAuthCheck } = useSolidAuth();
+ if (!ranInitialAuthCheck) {
+ return Loading
;
+ }
+ return ;
+};
diff --git a/packages/demo-react/src/profile/Profile.tsx b/packages/demo-react/src/profile/Profile.tsx
new file mode 100644
index 0000000..ae57f90
--- /dev/null
+++ b/packages/demo-react/src/profile/Profile.tsx
@@ -0,0 +1,34 @@
+import {
+ useLdo,
+ useResource,
+ useSolidAuth,
+ useSubject,
+} from "@ldo/solid-react";
+import type { ChangeEvent } from "react";
+import React, { useCallback, type FunctionComponent } from "react";
+import { SolidProfileShapeShapeType } from "../.ldo/solidProfile.shapeTypes";
+
+export const Profile: FunctionComponent = () => {
+ const { session } = useSolidAuth();
+ const profile = useSubject(SolidProfileShapeShapeType, session.webId);
+ const webIdResource = useResource(session.webId);
+ const { changeData, commitData } = useLdo();
+
+ const onNameChange = useCallback(
+ async (e: ChangeEvent) => {
+ if (profile && webIdResource) {
+ const cProfile = changeData(profile, webIdResource);
+ cProfile.fn = e.target.value;
+ await commitData(cProfile);
+ }
+ },
+ [profile, webIdResource],
+ );
+
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/packages/solid/src/requester/LeafRequester.ts b/packages/solid/src/requester/LeafRequester.ts
index 9ca93eb..86b605a 100644
--- a/packages/solid/src/requester/LeafRequester.ts
+++ b/packages/solid/src/requester/LeafRequester.ts
@@ -56,12 +56,18 @@ export class LeafRequester extends Requester {
async updateDataResource(
changes: DatasetChanges,
): Promise {
+ const transaction = this.context.solidLdoDataset.startTransaction();
+ transaction.addAll(changes.added || []);
+ changes.removed?.forEach((quad) => transaction.delete(quad));
+ // Commit data optimistically
+ transaction.commit();
+
const result = await this.requestBatcher.queueProcess({
name: UPDATE_KEY,
args: [
this.uri,
changes,
- { fetch: this.context.fetch, dataset: this.context.solidLdoDataset },
+ { fetch: this.context.fetch, onRollback: () => transaction.rollback() },
],
perform: updateDataResource,
modifyQueue: (queue, isLoading, [, changes]) => {
diff --git a/packages/solid/src/requester/requests/updateDataResource.ts b/packages/solid/src/requester/requests/updateDataResource.ts
index d76ddbb..3705796 100644
--- a/packages/solid/src/requester/requests/updateDataResource.ts
+++ b/packages/solid/src/requester/requests/updateDataResource.ts
@@ -1,9 +1,5 @@
import type { DatasetChanges } from "@ldo/rdf-utils";
import { changesToSparqlUpdate } from "@ldo/rdf-utils";
-import type {
- SubscribableDataset,
- TransactionalDataset,
-} from "@ldo/subscribable-dataset";
import type { Quad } from "@rdfjs/types";
import { guaranteeFetch } from "../../util/guaranteeFetch";
import type { LeafUri } from "../../util/uriTypes";
@@ -19,19 +15,11 @@ export type UpdateResultError = HttpErrorResultType | UnexpectedResourceError;
export async function updateDataResource(
uri: LeafUri,
datasetChanges: DatasetChanges,
- options?: BasicRequestOptions & { dataset?: SubscribableDataset },
+ options?: BasicRequestOptions & { onRollback?: () => void },
): Promise {
try {
const fetch = guaranteeFetch(options?.fetch);
- // Put Changes in transactional dataset
- let transaction: TransactionalDataset | undefined;
- if (options?.dataset) {
- transaction = options.dataset.startTransaction();
- transaction.addAll(datasetChanges.added || []);
- datasetChanges.removed?.forEach((quad) => transaction!.delete(quad));
- // Commit data optimistically
- transaction.commit();
- }
+
// Make request
const sparqlUpdate = await changesToSparqlUpdate(datasetChanges);
const response = await fetch(uri, {
@@ -44,8 +32,8 @@ export async function updateDataResource(
const httpError = HttpErrorResult.checkResponse(uri, response);
if (httpError) {
// Handle error rollback
- if (transaction) {
- transaction.rollback();
+ if (options?.onRollback) {
+ options.onRollback();
}
return httpError;
}
diff --git a/packages/solid/src/util/RequestBatcher.ts b/packages/solid/src/util/RequestBatcher.ts
index 3f0a287..0f95309 100644
--- a/packages/solid/src/util/RequestBatcher.ts
+++ b/packages/solid/src/util/RequestBatcher.ts
@@ -66,11 +66,14 @@ export class RequestBatcher {
const timeSinceLastTrigger = Date.now() - lastRequestTimestamp;
const triggerProcess = async () => {
- this.isWaiting = false;
+ if (this.isWaiting) {
+ return;
+ }
this.lastRequestTimestampMap[processName] = Date.now();
this.lastRequestTimestampMap[ANY_KEY] = Date.now();
const processToTrigger = this.processQueue.shift();
if (processToTrigger) {
+ this.isWaiting = true;
try {
const returnValue = await processToTrigger.perform(
...processToTrigger.args,
@@ -83,6 +86,7 @@ export class RequestBatcher {
callback(err);
});
}
+ this.isWaiting = false;
// Reset loading
if (
@@ -101,8 +105,7 @@ export class RequestBatcher {
}
};
- if (timeSinceLastTrigger < this.batchMillis && !this.isWaiting) {
- this.isWaiting = true;
+ if (timeSinceLastTrigger < this.batchMillis) {
setTimeout(triggerProcess, this.batchMillis - timeSinceLastTrigger);
} else {
triggerProcess();