Skip to content

Commit

Permalink
always provide a (dummy)document
Browse files Browse the repository at this point in the history
  • Loading branch information
lebalz committed Aug 7, 2024
1 parent 0f65e89 commit baace70
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 73 deletions.
11 changes: 6 additions & 5 deletions docs/string-answers.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import String from '@site/src/components/documents/String';
Einzeilige Texteingabe ohne Formatierung.

```md
<String id="c9c2ecab-98b0-4696-b44a-8fdf79b8daac" />
<String id="4caa49d4-5385-433e-9651-a48ebed29a9a" />
```

<String id="c9c2ecab-98b0-4696-b44a-8fdf79b8daac" />
<String id="4caa49d4-5385-433e-9651-a48ebed29a9a" />

## Label

Expand Down Expand Up @@ -147,8 +147,9 @@ Ohne `id` wird der Zustand nicht gespeichert, was mit einem :mdi[flash-triangle-
: Verwendet anstatt eines Block-Elements (`<div></div>` mit `display: flex`) ein Inline-Element (`<span></span>` mit `display: inline-flex`).
`stateIconsPosition`
: `inside` das Sync-Icon wird innerhalb des Inputs angezeigt (standard für `text`, `email`, `tel` und `url`)
: <String stateIconsPosition="inside" inline /> hello
: `outside` rechts neben dem Input (standard für alle anderen Felder)
: <String stateIconsPosition="outside" inline />
: `none` Icon wird nicht angezeigt
: <String stateIconsPosition="none" inline />

<String label="inside" stateIconsPosition="inside" />
<String label="outside" stateIconsPosition="outside" />
<String label="none" stateIconsPosition="none" />
77 changes: 41 additions & 36 deletions src/hooks/useDocumentRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import React, { useId } from 'react';
import { Access, Document, DocumentType } from '../api/document';
import { rootStore } from '../stores/rootStore';
import DocumentRoot, { TypeMeta } from '../models/DocumentRoot';
import { CreateDocumentModel } from '../stores/DocumentStore';

/**
* 1. create a dummy documentRoot with default (meta) data
* 2. create a dummy document with default (meta) data
* 2. when component mounts, check if the documentRoot is already in the store
* 3. if not
* 3.1. add the dummy documentRoot to the store
* 3.2. when no documentRootId is provided (dummyDocumentRoot.id==defaultRootDocId), return early
* 3.3. otherwise, load or create the documentRoot
* 3.1. add the dummy document to the store
* 3.2. add the dummy documentRoot to the store
* 3.3. if an id was provided, load or create the documentRoot and it's documents from the api
* 3.4. cleanup the dummy document
*/
export const useDocumentRoot = <Type extends DocumentType>(id: string | undefined, meta: TypeMeta<Type>) => {
const defaultRootDocId = useId();
Expand All @@ -22,46 +25,41 @@ export const useDocumentRoot = <Type extends DocumentType>(id: string | undefine
true
)
);
const [dummyDocument] = React.useState(
CreateDocumentModel({
id: defaultDocId,
type: meta.type,
data: meta.defaultData,
authorId: 'dummy',
documentRootId: id || defaultRootDocId,
parentId: null,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}, rootStore.documentStore)
);
/**
* only run the effect after the initial render to avoid
* unnecessary api calls and delays
*/
const [initRender, setInitRender] = React.useState(false);

/** initial load */
React.useEffect(() => {
const { sessionStore, documentRootStore } = rootStore;
if (!sessionStore.isLoggedIn) {
if (!initRender) {
return;
}
const { documentStore, documentRootStore } = rootStore;
const rootDoc = documentRootStore.find(dummyDocumentRoot.id);
if (rootDoc) {
return;
}
if (dummyDocumentRoot.isDummy) {
documentRootStore.addDocumentRoot(dummyDocumentRoot);
/** add default document when there are no mainDocs */
if (dummyDocumentRoot.mainDocuments.length === 0) {
const now = new Date().toISOString();
rootStore.documentStore.addToStore({
type: meta.type,
data: meta.defaultData,
authorId: rootStore.userStore.current?.id || '',
createdAt: now,
updatedAt: now,
documentRootId: dummyDocumentRoot.id,
id: defaultDocId,
parentId: null
} satisfies Document<Type>, 'dummy-root');
}
if (dummyDocumentRoot.id === defaultRootDocId) {
/** no according document in the backend can be expected - skip */
return () => {
documentRootStore.removeFromStore(dummyDocumentRoot.id);
};
}
}

/**
* dont create dummy documents, ever
*/
if (!id) {
return;
documentStore.addDocument(dummyDocument);
documentRootStore.addDocumentRoot(dummyDocumentRoot, true);
if (!id || dummyDocumentRoot.id === defaultRootDocId) {
/** no according document in the backend can be expected - skip */
return () => {
documentRootStore.removeFromStore(dummyDocumentRoot.id);
};
}

/**
Expand Down Expand Up @@ -89,8 +87,8 @@ export const useDocumentRoot = <Type extends DocumentType>(id: string | undefine
data: meta.defaultData
});
}
rootStore.documentStore.removeFromStore(defaultDocId);
}
rootStore.documentRootStore.removeFromStore(defaultDocId);
})
.catch((err) => {
/**
Expand All @@ -99,7 +97,14 @@ export const useDocumentRoot = <Type extends DocumentType>(id: string | undefine
*/
console.log('err loading', err);
});
}, [rootStore, rootStore.sessionStore.isLoggedIn]);
return () => {
documentRootStore.removeFromStore(dummyDocumentRoot.id);
};
}, [initRender, rootStore]);

React.useEffect(() => {
setInitRender(true);
}, []);

return rootStore.documentRootStore.find<Type>(dummyDocumentRoot.id);
};
17 changes: 8 additions & 9 deletions src/models/DocumentRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class DocumentRoot<T extends DocumentType> {
* This is useful to support interactive behavior even for not logged in users or
* in offline mode.
*/
readonly _isDummy: boolean;
readonly isDummy: boolean;

@observable accessor _access: Access;
@observable accessor sharedAccess: Access;
Expand All @@ -33,19 +33,14 @@ class DocumentRoot<T extends DocumentType> {
props: DocumentRootProps,
meta: TypeMeta<T>,
store: DocumentRootStore,
isDummy: boolean = false
isDummy?: boolean
) {
this.store = store;
this.meta = meta;
this.id = props.id;
this._access = props.access;
this.sharedAccess = props.sharedAccess;
this._isDummy = isDummy;
}

@computed
get isDummy() {
return this._isDummy || !this.store.root.sessionStore.isLoggedIn;
this.isDummy = !!isDummy;
}

get type() {
Expand Down Expand Up @@ -75,11 +70,12 @@ class DocumentRoot<T extends DocumentType> {

get documents() {
const currentUserId = this.store.root.userStore.current?.id;
if (!currentUserId) {
if (!currentUserId && !this.isDummy) {
return [];
}
return this.store.root.documentStore.findByDocumentRoot(this.id).filter((d) => {
return (
this.isDummy ||
d.authorId === currentUserId ||
highestAccess(new Set([this.permission]), this.sharedAccess) !== Access.None
);
Expand Down Expand Up @@ -109,6 +105,9 @@ class DocumentRoot<T extends DocumentType> {
const docs = this.documents
.filter((d) => d.isMain)
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()) as TypeModelMapping[T][];
if (this.isDummy) {
return docs;
}
const byUser = docs.filter((d) => d.authorId === this.viewedUserId);

if (
Expand Down
9 changes: 4 additions & 5 deletions src/models/iDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,12 @@ abstract class iDocument<Type extends DocumentType> {

@computed
get canEdit() {
/**
* here _isDummy must be used, because otherwise dummy docs would be editable
* by offline users...
*/
if (!this.root || this.root._isDummy) {
if (!this.root) {
return false;
}
if (this.root.isDummy) {
return this.root.permission === Access.RW;
}
if (!this.store.root.userStore.current) {
return this.root.permission === Access.RW;
}
Expand Down
1 change: 1 addition & 0 deletions src/stores/DocumentRootStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class DocumentRootStore extends iStore {
}
}
this.documentRoots.push(documentRoot);
return documentRoot;
}

find = computedFn(
Expand Down
36 changes: 18 additions & 18 deletions src/stores/DocumentStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ import ScriptVersion from '../models/documents/ScriptVersion';
import { ChangedDocument } from '../api/IoEventTypes';
import String from '../models/documents/String';


export function CreateDocumentModel<T extends DocumentType>(data: DocumentProps<T>, store: DocumentStore): TypeModelMapping[T];
export function CreateDocumentModel(data: DocumentProps<DocumentType>, store: DocumentStore): DocumentTypes {
switch (data.type) {
case DocumentType.Script:
return new Script(data as DocumentProps<DocumentType.Script>, store);
case DocumentType.TaskState:
return new TaskState(data as DocumentProps<DocumentType.TaskState>, store);
case DocumentType.ScriptVersion:
return new ScriptVersion(data as DocumentProps<DocumentType.ScriptVersion>, store);
case DocumentType.String:
return new String(data as DocumentProps<DocumentType.String>, store);
}
};
class DocumentStore extends iStore {
readonly root: RootStore;
documents = observable.array<DocumentTypes>([]);
Expand All @@ -30,20 +44,6 @@ class DocumentStore extends iStore {
this.root = root;
}

createModel<T extends DocumentType>(data: DocumentProps<T>): TypeModelMapping[T];
createModel(data: DocumentProps<DocumentType>): DocumentTypes {
switch (data.type) {
case DocumentType.Script:
return new Script(data as DocumentProps<DocumentType.Script>, this);
case DocumentType.TaskState:
return new TaskState(data as DocumentProps<DocumentType.TaskState>, this);
case DocumentType.ScriptVersion:
return new ScriptVersion(data as DocumentProps<DocumentType.ScriptVersion>, this);
case DocumentType.String:
return new String(data as DocumentProps<DocumentType.String>, this);
}
}

@action
addDocument(document: DocumentTypes) {
this.documents.push(document);
Expand Down Expand Up @@ -81,7 +81,7 @@ class DocumentStore extends iStore {
return;
}

const model = this.createModel(data);
const model = CreateDocumentModel(data, this);
if (onlyFor === 'dummy-root' && (!model.root || !model.root.isDummy)) {
return;
}
Expand Down Expand Up @@ -145,15 +145,15 @@ class DocumentStore extends iStore {
model: iDocument<Type>,
replaceStoreModel: boolean = false
): Promise<TypeModelMapping[Type] | 'error' | undefined> {
if (model.root?.isDummy || !this.root.sessionStore.isLoggedIn) {
if (!model.root || model.root?.isDummy || !this.root.sessionStore.isLoggedIn) {
return Promise.resolve('error');
}
if (model.isDirty) {
const { id } = model;
if (!model.canEdit) {
return Promise.resolve(undefined);
}
if ( model.root?.permission !== Access.RW) {
if ( model.root.permission !== Access.RW) {
return Promise.resolve(undefined);
}
return this.withAbortController(`save-${id}`, (sig) => {
Expand All @@ -165,7 +165,7 @@ class DocumentStore extends iStore {
if (replaceStoreModel) {
return this.addToStore(data, 'persisted-root');
}
return this.createModel(data);
return CreateDocumentModel(data, this);
}
return undefined;
})
Expand Down

0 comments on commit baace70

Please sign in to comment.