Skip to content

Commit 5be4f28

Browse files
Pollepsjoepio
authored andcommitted
Fix new resource dialog for new resources
1 parent dc3c41c commit 5be4f28

File tree

9 files changed

+94
-23
lines changed

9 files changed

+94
-23
lines changed

browser/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ This changelog covers all three packages, as they are (for now) updated as a who
44

55
## UNRELEASED
66

7+
### @tomic/lib
8+
9+
- When saving a resource whose parent has not yet been saved we now add them to a batch that gets saved later when the parent is saved.
710
- The `scope` option in `SearchOpts` has changed to `parents` and now accepts an array of subjects instead of a single subject.
11+
- BREAKING: Removed `getCommitBuilder()` method from `Resource`
12+
- Added `hasUnsavedChanges()` method to `Resource`
813

914
### Atomic Browser
1015

browser/data-browser/src/components/forms/NewForm/NewFormDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { JSONValue, useResource, useStore, useTitle } from '@tomic/react';
1+
import { Core, JSONValue, useResource, useStore, useTitle } from '@tomic/react';
22
import React, { useState, useCallback } from 'react';
33
import { useEffectOnce } from '../../../hooks/useEffectOnce';
44
import { Button } from '../../Button';
@@ -28,7 +28,7 @@ export const NewFormDialog = ({
2828
onSave,
2929
parent,
3030
}: NewFormDialogProps): JSX.Element => {
31-
const klass = useResource(classSubject);
31+
const klass = useResource<Core.Class>(classSubject);
3232
const [className] = useTitle(klass);
3333
const store = useStore();
3434

browser/data-browser/src/components/forms/NewForm/useNewForm.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import {
55
useStore,
66
useResource,
77
useArray,
8+
Core,
89
} from '@tomic/react';
910
import { useState, useEffect } from 'react';
1011

1112
const resourseOpts = { newResource: true };
1213

1314
type UseNewForm = {
14-
klass: Resource;
15+
klass: Resource<Core.Class>;
1516
setSubject: (v: string) => void;
1617
initialSubject?: string;
1718
parent?: string;
@@ -22,10 +23,9 @@ export const useNewForm = (args: UseNewForm) => {
2223
const { klass, setSubject, initialSubject, parent } = args;
2324

2425
const store = useStore();
25-
const [klassShortname] = useString(klass, properties.shortname);
2626
const [subjectValue, setSubjectValueInternal] = useState<string>(() => {
2727
if (initialSubject === undefined) {
28-
return store.createSubject(klassShortname);
28+
return store.createSubject(klass.props.shortname);
2929
}
3030

3131
return initialSubject;

browser/data-browser/src/components/forms/hooks/useSaveResource.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type UseSaveResourceResult = [
1616
*/
1717
export const useSaveResource = (
1818
resource: Resource,
19-
onSaveSucces: () => void = () => void 0,
19+
onSaveSucces?: () => void,
2020
): UseSaveResourceResult => {
2121
const store = useStore();
2222
const [saving, setSaving] = useState(false);
@@ -31,7 +31,7 @@ export const useSaveResource = (
3131
try {
3232
await resource.save(store);
3333
setSaving(false);
34-
onSaveSucces();
34+
onSaveSucces?.();
3535
toast.success('Resource saved');
3636

3737
if (resource.new) {

browser/data-browser/src/routes/DataRoute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function Data(): JSX.Element {
101101
<AtomicLink subject={subject}>{subject}</AtomicLink>
102102
</PropValRow>
103103
<AllProps resource={resource} editable columns />
104-
{resource.getCommitBuilder().hasUnsavedChanges() ? (
104+
{resource.hasUnsavedChanges() ? (
105105
<>
106106
<h2>⚠️ contains uncommitted changes</h2>
107107
<p>

browser/data-browser/src/routes/ShareRoute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export function ShareRoute(): JSX.Element {
169169
{canWrite && (
170170
<span>
171171
<Button
172-
disabled={!resource.getCommitBuilder().hasUnsavedChanges()}
172+
disabled={!resource.hasUnsavedChanges()}
173173
onClick={handleSave}
174174
>
175175
Save

browser/lib/src/resource.test.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,5 @@ describe('resource.ts', () => {
3636
testsubject,
3737
testsubject,
3838
]);
39-
40-
resource.getCommitBuilder().push[urls.properties.subResources] = [
41-
testsubject,
42-
testsubject2,
43-
testsubject,
44-
testsubject,
45-
];
4639
});
4740
});

browser/lib/src/resource.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
QuickAccesPropType,
1919
getKnownNameBySubject,
2020
OptionalClass,
21+
core,
2122
} from './index.js';
2223

2324
/** Contains the PropertyURL / Value combinations */
@@ -239,12 +240,9 @@ export class Resource<C extends OptionalClass = any> {
239240
return this.set(properties.isA, Array.from(classesSet), store);
240241
}
241242

242-
/**
243-
* Returns the current Commit Builder, which describes the pending changes of
244-
* the resource
245-
*/
246-
public getCommitBuilder(): CommitBuilder {
247-
return this.commitBuilder;
243+
/** Returns true if the resource has changes in it's commit builder that are not yet saved to the server. */
244+
public hasUnsavedChanges(): boolean {
245+
return this.commitBuilder.hasUnsavedChanges();
248246
}
249247

250248
public getCommitsCollection(): string {
@@ -446,6 +444,13 @@ export class Resource<C extends OptionalClass = any> {
446444
throw new Error('No agent has been set or passed, you cannot save.');
447445
}
448446

447+
// If the parent of this resource is new we can't save yet so we add it to a batched that gets saved when the parent does.
448+
if (this.isParentNew(store)) {
449+
store.batchResource(this.getSubject());
450+
451+
return;
452+
}
453+
449454
// The previousCommit is required in Commits. We should use the `lastCommit` value on the resource.
450455
// This makes sure that we're making adjustments to the same version as the server.
451456
const lastCommit = this.get(properties.commit.lastCommit)?.toString();
@@ -500,6 +505,9 @@ export class Resource<C extends OptionalClass = any> {
500505
// That's why we need to repeat the process
501506
// https://github.com/atomicdata-dev/atomic-data-rust/issues/486
502507
store.subscribeWebSocket(this.subject);
508+
509+
// Save any children that have been batched while creating this resource
510+
await store.saveBatchForParent(this.getSubject());
503511
}
504512

505513
// Let all subscribers know that the commit has been applied
@@ -615,6 +623,18 @@ export class Resource<C extends OptionalClass = any> {
615623

616624
return ownValue === value;
617625
}
626+
627+
private isParentNew(store: Store) {
628+
const parentSubject = this.propvals.get(core.properties.parent) as string;
629+
630+
if (!parentSubject) {
631+
return false;
632+
}
633+
634+
const parent = store.getResourceLoading(parentSubject);
635+
636+
return parent.new;
637+
}
618638
}
619639

620640
/** Type of Rights (e.g. read or write) */

browser/lib/src/store.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
OptionalClass,
1919
UnknownClass,
2020
Server,
21+
core,
2122
} from './index.js';
2223
import { authenticate, fetchWebSocket, startWebsocket } from './websockets.js';
2324

@@ -89,6 +90,10 @@ export class Store {
8990
private serverUrl: string;
9091
/** All the resources of the store */
9192
private _resources: Map<string, Resource>;
93+
94+
/** List of resources that have parents that are not saved to the server, when a parent is saved it should also save its children */
95+
private batchedResources: Map<string, Set<string>> = new Map();
96+
9297
/** Current Agent, used for signing commits. Is required for posting things. */
9398
private agent?: Agent;
9499
/** Mapped from origin to websocket */
@@ -478,7 +483,9 @@ export class Store {
478483
}
479484

480485
// We clone for react, because otherwise it won't rerender
481-
Promise.allSettled(callbacks.map(async cb => cb(resource.clone())));
486+
const cloned = resource.clone();
487+
this._resources.set(subject, cloned);
488+
Promise.allSettled(callbacks.map(async cb => cb(cloned)));
482489
}
483490

484491
public async notifyResourceSaved(resource: Resource): Promise<void> {
@@ -772,6 +779,52 @@ export class Store {
772779
return Array.from(this.resources.values()).filter(filter);
773780
}
774781

782+
/**
783+
* @Internal
784+
* Add the resource to a batch that is saved when the parent is saved. Only gets saved when the parent is new.
785+
*/
786+
public batchResource(subject: string) {
787+
const resource = this._resources.get(subject);
788+
789+
if (!resource) {
790+
throw new Error(
791+
`Resource ${subject} can not be saved because it is not in the store.`,
792+
);
793+
}
794+
795+
const parent = resource.get(core.properties.parent);
796+
797+
if (parent === undefined) {
798+
throw new Error(
799+
`Resource ${subject} can not be added to a batch because it's missing a parent.`,
800+
);
801+
}
802+
803+
if (!this.batchedResources.has(parent)) {
804+
this.batchedResources.set(parent, new Set([subject]));
805+
} else {
806+
this.batchedResources.get(parent)!.add(subject);
807+
}
808+
}
809+
810+
/**
811+
* @Internal
812+
* Saves all resources that are in a batch for a parent.
813+
*/
814+
public async saveBatchForParent(subject: string) {
815+
const subjects = this.batchedResources.get(subject);
816+
817+
if (!subjects) return;
818+
819+
for (const resourceSubject of subjects) {
820+
const resource = this._resources.get(resourceSubject);
821+
822+
await resource?.save(this);
823+
}
824+
825+
this.batchedResources.delete(subject);
826+
}
827+
775828
private randomPart(): string {
776829
return Math.random().toString(36).substring(2);
777830
}

0 commit comments

Comments
 (0)