From f2668210d72f6d70e9b25f42819e795ffb1132c5 Mon Sep 17 00:00:00 2001 From: Polle Pas Date: Mon, 2 Oct 2023 15:12:30 +0200 Subject: [PATCH] #648 streamline subject creation --- .../NewInstanceButton/NewOntologyButton.tsx | 3 +-- .../NewInstanceButton/useCreateAndNavigate.ts | 11 ++++++---- .../forms/NewForm/NewFormDialog.tsx | 22 +++++-------------- .../src/helpers/getNamePartFromProps.ts | 14 ++++++++++++ browser/lib/src/store.ts | 11 ++++++---- 5 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 browser/data-browser/src/helpers/getNamePartFromProps.ts diff --git a/browser/data-browser/src/components/NewInstanceButton/NewOntologyButton.tsx b/browser/data-browser/src/components/NewInstanceButton/NewOntologyButton.tsx index cadd2b2c9..25994e280 100644 --- a/browser/data-browser/src/components/NewInstanceButton/NewOntologyButton.tsx +++ b/browser/data-browser/src/components/NewInstanceButton/NewOntologyButton.tsx @@ -26,7 +26,6 @@ export function NewOntologyButton({ label, }: NewInstanceButtonProps): JSX.Element { const ontology = useResource(klass); - const [shortname, setShortname] = useState(''); const [valid, setValid] = useState(false); @@ -41,7 +40,7 @@ export function NewOntologyButton({ [properties.properties]: [], [properties.instances]: [], }); - }, [shortname]); + }, [shortname, createResourceAndNavigate]); const [dialogProps, show, hide] = useDialog({ onSuccess }); diff --git a/browser/data-browser/src/components/NewInstanceButton/useCreateAndNavigate.ts b/browser/data-browser/src/components/NewInstanceButton/useCreateAndNavigate.ts index 1280d7d3a..7ae28c84c 100644 --- a/browser/data-browser/src/components/NewInstanceButton/useCreateAndNavigate.ts +++ b/browser/data-browser/src/components/NewInstanceButton/useCreateAndNavigate.ts @@ -10,6 +10,7 @@ import { useCallback } from 'react'; import toast from 'react-hot-toast'; import { useNavigate } from 'react-router-dom'; import { constructOpenURL } from '../../helpers/navigation'; +import { getNamePartFromProps } from '../../helpers/getNamePartFromProps'; /** * Hook that builds a function that will create a new resource with the given @@ -34,11 +35,13 @@ export function useCreateAndNavigate(klass: string, parent?: string) { /** Do not set a parent for the new resource. Useful for top-level resources */ noParent?: boolean, ): Promise => { - const subject = store.createSubject( - className, + const namePart = getNamePartFromProps(propVals); + const newSubject = await store.buildUniqueSubjectFromParts( + [className, namePart], noParent ? undefined : parent, ); - const resource = new Resource(subject, true); + + const resource = new Resource(newSubject, true); await Promise.all([ ...Object.entries(propVals).map(([key, val]) => @@ -49,7 +52,7 @@ export function useCreateAndNavigate(klass: string, parent?: string) { try { await resource.save(store); - navigate(constructOpenURL(subject, extraParams)); + navigate(constructOpenURL(newSubject, extraParams)); toast.success(`${title} created`); store.notifyResourceManuallyCreated(resource); } catch (e) { diff --git a/browser/data-browser/src/components/forms/NewForm/NewFormDialog.tsx b/browser/data-browser/src/components/forms/NewForm/NewFormDialog.tsx index 484900780..6b620f3dd 100644 --- a/browser/data-browser/src/components/forms/NewForm/NewFormDialog.tsx +++ b/browser/data-browser/src/components/forms/NewForm/NewFormDialog.tsx @@ -1,10 +1,4 @@ -import { - JSONValue, - properties, - useResource, - useStore, - useTitle, -} from '@tomic/react'; +import { JSONValue, useResource, useStore, useTitle } from '@tomic/react'; import React, { useState, useCallback } from 'react'; import { useEffectOnce } from '../../../hooks/useEffectOnce'; import { Button } from '../../Button'; @@ -17,7 +11,7 @@ import { NewFormProps } from './NewFormPage'; import { NewFormTitle, NewFormTitleVariant } from './NewFormTitle'; import { SubjectField } from './SubjectField'; import { useNewForm } from './useNewForm'; -import { randomString } from '../../../helpers/randomString'; +import { getNamePartFromProps } from '../../../helpers/getNamePartFromProps'; export interface NewFormDialogProps extends NewFormProps { closeDialog: () => void; @@ -55,15 +49,11 @@ export const NewFormDialog = ({ // Onmount we generate a new subject based on the classtype and the user input. useEffectOnce(() => { (async () => { - const namePart = normalizeName( - (initialProps?.[properties.shortname] as string) ?? - (initialProps?.[properties.name] as string) ?? - randomString(8), - ); + const namePart = getNamePartFromProps(initialProps ?? {}); const uniqueSubject = await store.buildUniqueSubjectFromParts( - className, - namePart, + [className, namePart], + parent, ); await setSubjectValue(uniqueSubject); @@ -115,5 +105,3 @@ export const NewFormDialog = ({ ); }; - -const normalizeName = (name: string) => name.replaceAll('/t', '-'); diff --git a/browser/data-browser/src/helpers/getNamePartFromProps.ts b/browser/data-browser/src/helpers/getNamePartFromProps.ts new file mode 100644 index 000000000..40639ce41 --- /dev/null +++ b/browser/data-browser/src/helpers/getNamePartFromProps.ts @@ -0,0 +1,14 @@ +import { JSONValue, properties } from '@tomic/react'; +import { randomString } from './randomString'; + +const normalizeName = (name: string) => + encodeURIComponent(name.replaceAll('/t', '-')); + +export const getNamePartFromProps = ( + props: Record, +): string => + normalizeName( + (props?.[properties.shortname] as string) ?? + (props?.[properties.name] as string) ?? + randomString(8), + ); diff --git a/browser/lib/src/store.ts b/browser/lib/src/store.ts index e3efe2670..7ceb1354a 100644 --- a/browser/lib/src/store.ts +++ b/browser/lib/src/store.ts @@ -183,11 +183,13 @@ export class Store { * Will retry until it works. */ public async buildUniqueSubjectFromParts( - ...parts: string[] + parts: string[], + parent?: string, ): Promise { const path = parts.join('/'); + const parentUrl = parent ?? this.getServerUrl(); - return this.findAvailableSubject(path); + return this.findAvailableSubject(path, parentUrl); } /** Creates a random URL. Add a classnme (e.g. 'persons') to make a nicer name */ @@ -773,9 +775,10 @@ export class Store { private async findAvailableSubject( path: string, + parent: string, firstTry = true, ): Promise { - let url = `${this.getServerUrl()}/${path}`; + let url = `${parent}/${path}`; if (!firstTry) { const randomPart = this.randomPart(); @@ -785,7 +788,7 @@ export class Store { const taken = await this.checkSubjectTaken(url); if (taken) { - return this.findAvailableSubject(path, false); + return this.findAvailableSubject(path, parent, false); } return url;