Skip to content

Post endpoint #296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This changelog covers all three packages, as they are (for now) updated as a who

- Add `Store.parseMetaTags` to load JSON-AD objects stored in the DOM. Speeds up initial page load by allowing server to set JSON-AD objects in the initial HTML response.
- Move static assets around, align build with server and fix PWA #292
- `store.createSubject` allows creating nested paths
- Add `useChildren` hook and `Store.getChildren` method
- Add `Store.postToServer` method, add `endpoints`, `import_json_ad_string`

## v0.35.0

Expand Down
11 changes: 5 additions & 6 deletions data-browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ You can set the Agent on the `/app/agent` route.
The tests are located in `tests` and have `.spec` in their filename.
They use the PlayWright framework and run in the browser.

- make sure the data-browser server is running (`pnpm start`) at `http://localhost:8080`
- make sure an [`atomic-server`](https://crates.io/crates/atomic-server/) instance is running at `http://localhost`
- make sure the data-browser server is running (`pnpm start`) at `http://localhost:5173`
- make sure an [`atomic-server`](https://crates.io/crates/atomic-server/) instance is running at `http://localhost:9883`
- make sure the `http://localhost/setup` invite has at least one available usage. You can set a higher amount [here](http://localhost/app/edit?subject=http%3A%2F%2Flocalhost%2Fsetup), or run `atomic-server --inititalize` to reset it to 1.
- Install the Playwright dependencies: `npx playwright install-deps`
- `pnpm test` launches the E2E tests (make sure the dev server is running at `http://localhost:8080`)
- Install the Playwright dependencies: `pnpm playwright-install`
- `pnpm test` launches the E2E tests (make sure the dev server is running at `http://localhost:5173`)
- `pnpm test-debug` launches the E2E tests in debug mode (a window opens with debug tools)
- `pnpm test-new` create new tests by clicking through the app
- Use the `data-test` attribute in HTML elements to make playwright tests more maintainable (and prevent failing tests on changing translations)
Expand All @@ -92,9 +92,8 @@ They use the PlayWright framework and run in the browser.
GitHub Action / Workflow is used for:

- Linting (ESlint)
- Testing (in the browser using `playwright`, using an `atomic-server` docker image)
- Building
- Deploying JS build artefacts & assets to GH pages (note that `atomic-server` hosts the JS assets by itself)
- Testing (in the browser using `playwright`, using an `atomic-server` docker image)

## Contribute

Expand Down
2 changes: 2 additions & 0 deletions data-browser/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
</style>
<!-- Meta tags and code added by Atomic-Server -->
<!-- { inject_html_head } -->
<!-- Javascript added by Atomic-Server -->
<!-- { inject_script } -->
</head>

<body>
Expand Down
8 changes: 6 additions & 2 deletions data-browser/src/components/ClassDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { properties, Resource, useString } from '@tomic/react';
import { ResourceInline } from '../views/ResourceInline';
import { Detail } from './Detail';
import { getIconForClass } from '../views/FolderPage/iconMap';

type Props = {
resource: Resource;
Expand All @@ -15,8 +16,11 @@ export function ClassDetail({ resource }: Props): JSX.Element {
<React.Fragment>
{klass && (
<Detail>
{'is a '}
<ResourceInline subject={klass} />
<>
{'is a '}
{getIconForClass(klass)}
<ResourceInline subject={klass} />
</>
</Detail>
)}
</React.Fragment>
Expand Down
5 changes: 4 additions & 1 deletion data-browser/src/components/Dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ interface DropdownMenuProps {
/** The list of menu items */
items: Item[];
trigger: DropdownTriggerRenderFunction;
/** Enables the keyboard shortcut */
isMainMenu?: boolean;
}

/** Gets the index of an array and loops around when at the beginning or end */
Expand Down Expand Up @@ -88,6 +90,7 @@ function normalizeItems(items: Item[]) {
export function DropdownMenu({
items,
trigger,
isMainMenu,
}: DropdownMenuProps): JSX.Element {
const menuId = useId();
const dropdownRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -167,7 +170,7 @@ export function DropdownMenu({
handleToggle();
setUseKeys(true);
},
{},
{ enabled: !!isMainMenu },
[isActive],
);
// Click / open the item
Expand Down
1 change: 1 addition & 0 deletions data-browser/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ function NavBar(): JSX.Element {

{showButtons && subject && (
<ResourceContextMenu
isMainMenu
subject={subject}
trigger={MenuBarDropdownTrigger}
/>
Expand Down
88 changes: 0 additions & 88 deletions data-browser/src/components/NewInstanceButton/NewFolderButton.tsx

This file was deleted.

2 changes: 0 additions & 2 deletions data-browser/src/components/NewInstanceButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import { NewBookmarkButton } from './NewBookmarkButton';
import { NewInstanceButtonProps } from './NewInstanceButtonProps';
import { NewInstanceButtonDefault } from './NewInstanceButtonDefault';
import { useSettings } from '../../helpers/AppSettings';
import { NewFolderButton } from './NewFolderButton';

type InstanceButton = (props: NewInstanceButtonProps) => JSX.Element;

/** If your New Instance button requires custom logic, such as a custom dialog */
const classMap = new Map<string, InstanceButton>([
[classes.bookmark, NewBookmarkButton],
[classes.folder, NewFolderButton],
]);

/** A button for creating a new instance of some thing */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useNavigate } from 'react-router-dom';
import { constructOpenURL } from '../../helpers/navigation';

/**
* Hook that builds a function that will create a new resoure with the given
* Hook that builds a function that will create a new resource with the given
* properties and then navigate to it.
*
* @param klass The type of resource to create a new instance of.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import { useSettings } from '../../helpers/AppSettings';
import { newURL } from '../../helpers/navigation';
import { useCreateAndNavigate } from './useCreateAndNavigate';

/**
* Returns a function that can be used to create a new instance of the given Class.
* This is the place where you can add custom behavior for certain classes.
* By default, we're redirected to an empty Form for the new instance.
* For some Classes, though, we'd rather have some values are pre-filled (e.g. a new ChatRoom with a `new chatroom` title).
* For others, we want to render a custom form, perhaps with a different layout.
*/
export function useDefaultNewInstanceHandler(klass: string, parent?: string) {
const store = useStore();
const { setDrive } = useSettings();
Expand All @@ -22,61 +29,77 @@ export function useDefaultNewInstanceHandler(klass: string, parent?: string) {
const createResourceAndNavigate = useCreateAndNavigate(klass, parent);

const onClick = useCallback(async () => {
switch (klass) {
case classes.chatRoom: {
createResourceAndNavigate('chatRoom', {
[properties.name]: 'New ChatRoom',
[properties.isA]: [classes.chatRoom],
});
break;
}

case classes.document: {
createResourceAndNavigate('documents', {
[properties.isA]: [classes.document],
[properties.name]: 'Untitled Document',
});
break;
}
try {
switch (klass) {
case classes.chatRoom: {
createResourceAndNavigate('chatRoom', {
[properties.name]: 'Untitled ChatRoom',
[properties.isA]: [classes.chatRoom],
});
break;
}

case classes.importer: {
createResourceAndNavigate('importer', {
[properties.isA]: [classes.importer],
});
break;
}
case classes.document: {
createResourceAndNavigate('document', {
[properties.isA]: [classes.document],
[properties.name]: 'Untitled Document',
});
break;
}

case classes.drive: {
const agent = store.getAgent();
case classes.folder: {
createResourceAndNavigate('folder', {
[properties.isA]: [classes.folder],
[properties.name]: 'Untitled Folder',
[properties.displayStyle]: classes.displayStyles.list,
});
break;
}

if (!agent || agent.subject === undefined) {
throw new Error(
'No agent set in the Store, required when creating a Drive',
);
case classes.importer: {
createResourceAndNavigate('importer', {
[properties.isA]: [classes.importer],
});
break;
}

const newResource = await createResourceAndNavigate(
'drive',
{
[properties.isA]: [classes.drive],
[properties.write]: [agent.subject],
[properties.read]: [agent.subject],
},
undefined,
true,
);
case classes.drive: {
const agent = store.getAgent();

const agentResource = await store.getResourceAsync(agent.subject);
agentResource.pushPropVal(properties.drives, newResource.getSubject());
agentResource.save(store);
setDrive(newResource.getSubject());
break;
}
if (!agent || agent.subject === undefined) {
throw new Error(
'No agent set in the Store, required when creating a Drive',
);
}

const newResource = await createResourceAndNavigate(
'drive',
{
[properties.isA]: [classes.drive],
[properties.write]: [agent.subject],
[properties.read]: [agent.subject],
},
undefined,
true,
);

default: {
// Opens an `Edit` form with the class and a decent subject name
navigate(newURL(klass, parent, store.createSubject(shortname)));
const agentResource = await store.getResourceAsync(agent.subject);
agentResource.pushPropVal(
properties.drives,
newResource.getSubject(),
);
agentResource.save(store);
setDrive(newResource.getSubject());
break;
}

default: {
// Opens an `Edit` form with the class and a decent subject name
navigate(newURL(klass, parent, store.createSubject(shortname)));
}
}
} catch (e) {
store.handleError(e);
}
}, [klass, store, parent, createResourceAndNavigate]);

Expand Down
11 changes: 10 additions & 1 deletion data-browser/src/components/ResourceContextMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export interface ResourceContextMenuProps {
hide?: string[];
trigger?: DropdownTriggerRenderFunction;
simple?: boolean;
/** If it's the primary menu in the navbar. Used for triggering keyboard shortcut */
isMainMenu?: boolean;
}

/** Dropdown menu that opens a bunch of actions for some resource */
Expand All @@ -39,6 +41,7 @@ function ResourceContextMenu({
hide,
trigger,
simple,
isMainMenu,
}: ResourceContextMenuProps) {
const store = useStore();
const navigate = useNavigate();
Expand Down Expand Up @@ -149,7 +152,13 @@ function ResourceContextMenu({

const triggerComp = trigger ?? buildDefaultTrigger(<FaEllipsisV />);

return <DropdownMenu items={filteredItems} trigger={triggerComp} />;
return (
<DropdownMenu
items={filteredItems}
trigger={triggerComp}
isMainMenu={isMainMenu}
/>
);
}

export default ResourceContextMenu;
2 changes: 2 additions & 0 deletions data-browser/src/components/SideBar/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function About() {
<AboutWrapper>
{aboutMenuItems.map(({ href, icon, helper }) => (
<IconButtonLink
target='_blank'
rel='noreferrer'
key={href}
href={href}
title={helper}
Expand Down
Loading