Skip to content

Commit 5a46c99

Browse files
Pollepsjoepio
authored andcommitted
#283 Atomic Lib Refactor
1 parent 35a76c6 commit 5a46c99

File tree

12 files changed

+107
-107
lines changed

12 files changed

+107
-107
lines changed

CHANGELOG.md

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

55
## v0.35.0
66

7+
### @tomic/react
8+
9+
#### Breaking changes
10+
11+
- Remove `initAgentFromLocalStorage()`.
12+
- No longer save agent to local storage.
13+
714
### @tomic/lib
815

916
- Add the ability to change the `fetch` function used to fetch resources over http.
1017
- `store.addResource` is depricated in favor of `store.addResources`.
18+
- Add `AgentChange` event on store that is fired whenever the stores agent changes.
1119

1220
#### Breaking Changes:
1321

22+
- `uploadFiles()` has moved to `store.uploadFiles()`.
23+
- Remove `Agent.fromJSON()`
1424
- `tryValidURL` and `isValidURL` are now static methods on `Client`.
1525
- Rename `store.fetchResource` to `store.fetchResourceFromServer`.
1626

data-browser/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@
7171
"test-query": "PWDEBUG=1 playwright test \"e2e.spec.ts\" -g",
7272
"typecheck": "tsc --noEmit"
7373
},
74-
"version": "0.34.0"
74+
"type": "module",
75+
"version": "0.35.0"
7576
}

data-browser/src/App.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import React from 'react';
22
import { BrowserRouter } from 'react-router-dom';
33
import { HelmetProvider } from 'react-helmet-async';
4-
import {
5-
initAgentFromLocalStorage,
6-
StoreContext,
7-
Store,
8-
urls,
9-
} from '@tomic/react';
4+
import { StoreContext, Store, urls, StoreEvents } from '@tomic/react';
105

116
import { GlobalStyle, ThemeWrapper } from './styling';
127
import { AppRoutes } from './routes/Routes';
@@ -23,6 +18,10 @@ import { DialogContainer } from './components/Dialog/DialogContainer';
2318
import { registerHandlers } from './handlers';
2419
import { ErrorBoundary } from './views/ErrorPage';
2520
import { NetworkIndicator } from './components/NetworkIndicator';
21+
import {
22+
getAgentFromLocalStorage,
23+
saveAgentToLocalStorage,
24+
} from './helpers/agentStorage';
2625

2726
function fixDevUrl(url: string) {
2827
if (isDev()) {
@@ -32,8 +31,15 @@ function fixDevUrl(url: string) {
3231
return url;
3332
}
3433

35-
/** Initialize the store */
36-
const store = new Store();
34+
const initalAgent = getAgentFromLocalStorage();
35+
36+
// Initialize the store
37+
const store = new Store({
38+
agent: initalAgent,
39+
});
40+
41+
store.on(StoreEvents.AgentChanged, saveAgentToLocalStorage);
42+
3743
/**
3844
* Defaulting to the current URL's origin will make sense in most non-dev environments.
3945
* In dev envs, we want to default to port 9883
@@ -42,7 +48,7 @@ const currentOrigin = window.location.origin;
4248

4349
store.setServerUrl(fixDevUrl(currentOrigin));
4450

45-
/** Show an error when things go wrong */
51+
// Show an error when things go wrong
4652
store.errorHandler = e => {
4753
handleError(e);
4854

@@ -58,19 +64,12 @@ declare global {
5864
bugsnagApiKey: string;
5965
}
6066
}
61-
/** Setup bugsnag for error handling, but only if there's an API key */
67+
// Setup bugsnag for error handling, but only if there's an API key
6268
const ErrBoundary = window.bugsnagApiKey
6369
? initBugsnag(window.bugsnagApiKey)
6470
: ErrorBoundary;
6571

66-
/** Initialize the agent from localstorage */
67-
const agent = initAgentFromLocalStorage();
68-
69-
if (agent) {
70-
store.setAgent(agent);
71-
}
72-
73-
/** Fetch all the Properties and Classes - this helps speed up the app. */
72+
// Fetch all the Properties and Classes - this helps speed up the app.
7473
store.fetchResourceFromServer(urls.properties.getAll);
7574
store.fetchResourceFromServer(urls.classes.getAll);
7675

data-browser/src/helpers/AppSettings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const AppSettingsContextProvider = (
3939
setBaseURL(url.origin);
4040
}
4141

42-
const setAgentToast = (newAgent: Agent | undefined) => {
42+
const setAgentAndShowToast = (newAgent: Agent | undefined) => {
4343
try {
4444
setAgent(newAgent);
4545
newAgent?.subject && toast.success('Signed in!');
@@ -67,7 +67,7 @@ export const AppSettingsContextProvider = (
6767
sideBarLocked,
6868
setSideBarLocked,
6969
agent,
70-
setAgent: setAgentToast,
70+
setAgent: setAgentAndShowToast,
7171
}}
7272
>
7373
{props.children}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Agent } from '@tomic/react';
2+
3+
const AGENT_LOCAL_STORAGE_KEY = 'agent';
4+
5+
export function getAgentFromLocalStorage(): Agent | undefined {
6+
const secret = localStorage.getItem(AGENT_LOCAL_STORAGE_KEY);
7+
8+
if (!secret) {
9+
return undefined;
10+
}
11+
12+
try {
13+
return Agent.fromSecret(secret);
14+
} catch (e) {
15+
console.error(e);
16+
17+
return undefined;
18+
}
19+
}
20+
21+
export function saveAgentToLocalStorage(agent: Agent | undefined): void {
22+
if (agent) {
23+
localStorage.setItem(AGENT_LOCAL_STORAGE_KEY, agent.buildSecret());
24+
} else {
25+
localStorage.removeItem(AGENT_LOCAL_STORAGE_KEY);
26+
}
27+
}

data-browser/tests/e2e.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ test.describe('data-browser', async () => {
334334
page.click('button:has-text("Upload file")'),
335335
]);
336336
await fileChooser.setFiles(demoFile);
337-
await page.click(`[data-test]:has-text("${demoFileName}")`);
338-
const image = await page.locator('[data-test="image-viewer"]');
337+
await page.click(`a:has-text("${demoFileName}")`);
338+
const image = page.locator('[data-test="image-viewer"]');
339339
await expect(image).toBeVisible();
340340
await expect(image).toHaveScreenshot({ maxDiffPixelRatio: 0.1 });
341341
});
@@ -387,10 +387,10 @@ test.describe('data-browser', async () => {
387387
// Fetch `example.com
388388
const input = page.locator('[placeholder="https\\:\\/\\/example\\.com"]');
389389
await input.click();
390-
await input.fill('https://example.com');
390+
await input.fill('https://ontola.io');
391391
await page.locator(currentDialogOkButton).click();
392392

393-
await expect(page.locator('text=This domain is ')).toBeVisible();
393+
await expect(page.locator(':text-is("Full-service")')).toBeVisible();
394394
});
395395

396396
test('folder', async ({ page }) => {
@@ -525,7 +525,6 @@ test.describe('data-browser', async () => {
525525
const d1 = 'depth1';
526526
await setTitle(d1);
527527

528-
// Not sure why we need this, I'd prefer to wait for commits...
529528
await expect(
530529
page.locator(`[data-test="sidebar"] >> text=${d1}`),
531530
).toBeVisible();

data-browser/tests/test-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const demoFileName = 'testimage.svg';
33

44
export const testConfig: TestConfig = {
55
demoFileName,
6-
demoFile: `./tests/${demoFileName}`,
6+
demoFile: `${process.cwd()}/tests/${demoFileName}`,
77
demoInviteName: 'document demo',
88
serverUrl: process.env.SERVER_URL || 'http://localhost:9883',
99
frontEndUrl: 'http://localhost:5173',

lib/src/agent.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ export class Agent implements AgentInterface {
4343
return agent;
4444
}
4545

46-
/** Parses a JSON object containing a privateKey and an Agent subject */
47-
public static fromJSON(obj: AgentInterface): Agent {
48-
return new Agent(obj.privateKey, obj.subject);
49-
}
50-
5146
/** Returns existing public key or generates one using the private key */
5247
public async getPublicKey(): Promise<string> {
5348
if (!this.publicKey) {

lib/src/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export class Client {
109109
opts: FetchResourceOptions = {},
110110
): Promise<HTTPResult> {
111111
const { signInfo, from } = opts;
112-
const createdResources: Resource[] = [];
112+
let createdResources: Resource[] = [];
113113
const parser = new JSONADParser();
114114
let resource = new Resource(subject);
115115

@@ -170,7 +170,7 @@ export class Client {
170170
}
171171
} catch (e) {
172172
resource.setError(e);
173-
createdResources.push(resource);
173+
createdResources = [resource];
174174
console.error(subject, e);
175175
}
176176

lib/src/store.ts

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ import {
1212
import { authenticate, fetchWebSocket, startWebsocket } from './websockets.js';
1313

1414
/** Function called when a resource is updated or removed */
15-
type Callback = (resource: Resource) => void;
15+
type ResourceCallback = (resource: Resource) => void;
16+
/** Callback called when the stores agent changes */
17+
type AgentCallback = (agent: Agent | undefined) => void;
1618

1719
/** Returns True if the client has WebSocket support */
1820
const supportsWebSockets = () => typeof WebSocket !== 'undefined';
1921
type Fetch = typeof fetch;
2022

23+
export interface StoreOpts {
24+
/** The default store URL, where to send commits and where to create new instances */
25+
serverUrl?: string;
26+
/** Default Agent, used for signing commits. Is required for posting things. */
27+
agent?: Agent;
28+
}
29+
2130
export enum StoreEvents {
2231
/**
2332
* Whenever `Resource.save()` is called, so only when the user of this library
@@ -31,15 +40,18 @@ export enum StoreEvents {
3140
* the SideBar.
3241
*/
3342
ResourceManuallyCreated = 'resource-manually-created',
43+
/** Event that gets called whenever the stores agent changes */
44+
AgentChanged = 'agent-changed',
3445
}
3546

3647
/**
3748
* Handlers are functions that are called when a certain event occurs.
3849
*/
3950
type StoreEventHandlers = {
40-
[StoreEvents.ResourceSaved]: Callback;
41-
[StoreEvents.ResourceRemoved]: Callback;
42-
[StoreEvents.ResourceManuallyCreated]: Callback;
51+
[StoreEvents.ResourceSaved]: ResourceCallback;
52+
[StoreEvents.ResourceRemoved]: ResourceCallback;
53+
[StoreEvents.ResourceManuallyCreated]: ResourceCallback;
54+
[StoreEvents.AgentChanged]: AgentCallback;
4355
};
4456

4557
/**
@@ -50,7 +62,7 @@ type StoreEventHandlers = {
5062
*/
5163
export class Store {
5264
/** A list of all functions that need to be called when a certain resource is updated */
53-
public subscribers: Map<string, Array<Callback>>;
65+
public subscribers: Map<string, Array<ResourceCallback>>;
5466
/**
5567
* Is called when the store encounters an error. By default simply throws the
5668
* error, but can be overwritten
@@ -73,14 +85,7 @@ export class Store {
7385

7486
private client: Client;
7587

76-
public constructor(
77-
opts: {
78-
/** The default store URL, where to send commits and where to create new instances */
79-
serverUrl?: string;
80-
/** Default Agent, used for signing commits. Is required for posting things. */
81-
agent?: Agent;
82-
} = {},
83-
) {
88+
public constructor(opts: StoreOpts = {}) {
8489
this._resources = new Map();
8590
this.webSockets = new Map();
8691
this.subscribers = new Map();
@@ -91,6 +96,10 @@ export class Store {
9196
this.errorHandler = (e: Error) => {
9297
throw e;
9398
};
99+
100+
// We need to bind this method because it is passed down by other functions
101+
this.getAgent = this.getAgent.bind(this);
102+
this.setAgent = this.setAgent.bind(this);
94103
}
95104

96105
/** All the resources of the store */
@@ -195,10 +204,15 @@ export class Store {
195204
) {
196205
fetchWebSocket(ws, subject);
197206
} else {
207+
const signInfo = this.agent
208+
? { agent: this.agent, serverURL: this.getServerUrl() }
209+
: undefined;
210+
198211
const [_, createdResources] = await this.client.fetchResourceHTTP(
199212
subject,
200213
{
201214
from: opts.fromProxy ? this.getServerUrl() : undefined,
215+
signInfo,
202216
},
203217
);
204218

@@ -440,15 +454,19 @@ export class Store {
440454

441455
if (agent) {
442456
setCookieAuthentication(this.serverUrl, agent);
457+
443458
this.webSockets.forEach(ws => {
444459
authenticate(ws, this);
445460
});
461+
446462
this.resources.forEach(r => {
447463
if (r.isUnauthorized()) {
448464
this.fetchResourceFromServer(r.getSubject());
449465
}
450466
});
451467
}
468+
469+
this.eventManager.emit(StoreEvents.AgentChanged, agent);
452470
}
453471

454472
/** Sets the Server base URL, without the trailing slash. */
@@ -483,7 +501,7 @@ export class Store {
483501
* this, you should probably also call .unsubscribe some time later.
484502
*/
485503
// TODO: consider subscribing to properties, maybe add a second subscribe function, use that in useValue
486-
public subscribe(subject: string, callback: Callback): void {
504+
public subscribe(subject: string, callback: ResourceCallback): void {
487505
if (subject === undefined) {
488506
throw Error('Cannot subscribe to undefined subject');
489507
}
@@ -533,7 +551,7 @@ export class Store {
533551
}
534552

535553
/** Unregisters the callback (see `subscribe()`) */
536-
public unsubscribe(subject: string, callback: Callback): void {
554+
public unsubscribe(subject: string, callback: ResourceCallback): void {
537555
if (subject === undefined) {
538556
return;
539557
}

0 commit comments

Comments
 (0)