diff --git a/CHANGELOG.md b/CHANGELOG.md
index c85f170..4371be3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.3.0 (2020-05-24)
+
+### Feature
+- Allow to get or update context transparently, keeping state and backend in sync
+
# 1.2.1 (2020-05-03)
### Improvement
diff --git a/README.md b/README.md
index 66de454..e2e116d 100644
--- a/README.md
+++ b/README.md
@@ -175,6 +175,16 @@ import { Grange } from 'grange';
constructor(private grange: Grange) {}
```
+### The Grange service itself
+
+The `grange` service provides handy methods for the most common needs.
+
+`getContext()`: returns an Observable with the current traversed context.
+
+`updateContext(changes)`: update the current context with the provided changes.
+The update will be performed in the state immediately, and the corresponding PATCH will be done on the backend.
+If the backend call fails, a GET call is done to update the state with the actual value.
+
#### The Traverser service
`this.grange.traverser`
@@ -247,6 +257,43 @@ Grange uses ngx-schema-form to render any JSON Schema as a dynamic form. The for
TO BE COMPLETED
+## Guillotina
+
+Useful Guillotina operations:
+
+- create an app container for a new project
+```
+curl -XPOST --user root:root http://127.0.0.1:8081/db -d '{
+ "@type": "Container",
+ "id": "my-app"
+}'
+```
+- create a user
+```
+curl -XPOST --user root:root http://127.0.0.1:8081/db/site/users -d '{
+ "@type": "User",
+ "id": "inewton",
+ "username": "inewton",
+ "name": "Isaac Newton",
+ "email": "isaac@newton.org",
+ "password": "inewton",
+ "user_roles": ["guillotina.Member"]
+}'
+```
+- give reader access to a user
+```
+curl -XPOST --user root:root http://127.0.0.1:8081/db/site/@sharing -d '{
+ "prinrole": [
+ {
+ "principal": "inewton",
+ "role": "guillotina.Reader",
+ "setting": "Allow"
+ }
+ ]
+}'
+```
+
+
## Developers
If we want to run Grange into an Anugular project using the GitHub master branches of all the dependencies, we need to use mrs-developer:
diff --git a/angular.json b/angular.json
index 249976b..72efcde 100644
--- a/angular.json
+++ b/angular.json
@@ -78,6 +78,7 @@
}
],
"styles": [
+ "projects/demo/src/pastanaga.scss",
"projects/demo/src/styles.scss"
],
"scripts": []
diff --git a/g-api/config.yaml b/g-api/config.yaml
index 9a23bb0..869da65 100644
--- a/g-api/config.yaml
+++ b/g-api/config.yaml
@@ -10,6 +10,9 @@ databases:
pool_size: 100
store_json: true
allow_register: true
+jwk:
+ k: hGplrewNAVxULSApZavdXuxdiR_2Ner73oc-2Z7TVVY
+ kty: oct
cache:
driver: guillotina.contrib.redis
updates_channel: guillotina
@@ -99,6 +102,15 @@ behaviors:
title: Text
default: Hello
contents:
+ canvas:
+ title: Canvas
+ inherited_interface: guillotina.interfaces.IItem
+ inherited_class: guillotina.content.Item
+ add_permission: guillotina.AddContent
+ properties:
+ points:
+ type: guillotina.schema.JSONField
+ title: Points
mydoc:
title: My Doc
inherited_interface: guillotina.interfaces.IFolder
diff --git a/package.json b/package.json
index 37e317b..953775b 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"test": "jest",
"lint": "ng lint",
"test:watch": "jest --watch",
+ "guillotina": "docker-compose -f g-api/docker-compose.yaml up",
"get_version": "cat ./projects/grange/package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]'"
},
"private": true,
diff --git a/projects/demo/src/app/app.component.ts b/projects/demo/src/app/app.component.ts
index a6c17ba..ba90b8d 100644
--- a/projects/demo/src/app/app.component.ts
+++ b/projects/demo/src/app/app.component.ts
@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { GrangeViews, Grange } from '../../../grange/src';
+import { CanvasComponent } from './canvas/canvas.component';
@Component({
selector: 'app-root',
@@ -14,6 +15,7 @@ export class AppComponent {
) {
this.views.initialize();
this.grange.core.auth.isAuthenticated.subscribe(auth => this.isAuthenticated = auth.state);
+ this.grange.traverser.addView('view', 'canvas', CanvasComponent);
}
logout() {
diff --git a/projects/demo/src/app/app.module.ts b/projects/demo/src/app/app.module.ts
index 9adf982..56e74a1 100644
--- a/projects/demo/src/app/app.module.ts
+++ b/projects/demo/src/app/app.module.ts
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
import { environment } from '../environments/environment';
import { AppComponent } from './app.component';
+import { CanvasComponent } from './canvas/canvas.component';
import { GrangeRootModule } from '../../../grange/src';
import { TraversalModule } from 'angular-traversal';
import { StoreModule } from '@ngrx/store';
@@ -12,7 +13,8 @@ import { AngularSvgIconModule } from 'angular-svg-icon';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ CanvasComponent,
],
imports: [
BrowserModule,
diff --git a/projects/demo/src/app/canvas/canvas.component.html b/projects/demo/src/app/canvas/canvas.component.html
new file mode 100644
index 0000000..215b5ed
--- /dev/null
+++ b/projects/demo/src/app/canvas/canvas.component.html
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/projects/demo/src/app/canvas/canvas.component.scss b/projects/demo/src/app/canvas/canvas.component.scss
new file mode 100644
index 0000000..23a075a
--- /dev/null
+++ b/projects/demo/src/app/canvas/canvas.component.scss
@@ -0,0 +1,12 @@
+.row {
+ height: 20px;
+ div {
+ display: inline-block;
+ height: 20px;
+ width: 20px;
+ background-color: grey;
+ &.fill {
+ background-color: pink;
+ }
+ }
+}
\ No newline at end of file
diff --git a/projects/demo/src/app/canvas/canvas.component.ts b/projects/demo/src/app/canvas/canvas.component.ts
new file mode 100644
index 0000000..2d56772
--- /dev/null
+++ b/projects/demo/src/app/canvas/canvas.component.ts
@@ -0,0 +1,39 @@
+import { Component, OnInit } from '@angular/core';
+import { Grange } from '../../../../grange/src';
+import { select } from '@ngrx/store';
+import { TraverserSelectors } from '@guillotinaweb/ngx-state-traverser';
+import { map, tap, concatMap } from 'rxjs/operators';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-canvas',
+ templateUrl: 'canvas.component.html',
+ styleUrls: ['./canvas.component.scss'],
+})
+
+export class CanvasComponent implements OnInit {
+ points = this.grange.getContext().pipe(
+ tap(context => {
+ this.path = context['@id'];
+ }),
+ map(context => context.points || {}),
+ );
+ canvas: Observable = this.points.pipe(
+ map((points: {[coord: string]: boolean}) => Object.entries(points).reduce((all, [coordStr, value]) => {
+ const coord = coordStr.split('-').map(x => parseInt(x, 10));
+ all[coord[0]][coord[1]] = value;
+ return all;
+ }, Array.from(new Array(10), x => Array.from(new Array(10), () => false)))
+ ),
+ );
+ path?: string;
+
+ constructor(private grange: Grange) { }
+
+ ngOnInit() {}
+
+ toggle(x: number, y: number, current: boolean) {
+ const coord = `${x}-${y}`;
+ return this.grange.updateContext({points: {[coord]: !current}});
+ }
+}
diff --git a/projects/demo/src/pastanaga.scss b/projects/demo/src/pastanaga.scss
new file mode 100644
index 0000000..4bdee96
--- /dev/null
+++ b/projects/demo/src/pastanaga.scss
@@ -0,0 +1,7 @@
+@import "~@guillotinaweb/pastanaga-angular/lib/styles/common-reset";
+$font-path: "~@guillotinaweb/pastanaga-angular/lib/assets/fonts";
+@import "~@guillotinaweb/pastanaga-angular/lib/styles/fonts";
+
+html, body {
+ overflow: auto;
+}
diff --git a/projects/demo/src/styles.scss b/projects/demo/src/styles.scss
index 2e62a36..e69de29 100644
--- a/projects/demo/src/styles.scss
+++ b/projects/demo/src/styles.scss
@@ -1,3 +0,0 @@
-@import "../../../node_modules/@guillotinaweb/pastanaga-angular/lib/styles/common-reset";
-$font-path: "../../../node_modules/@guillotinaweb/pastanaga-angular/lib/assets/fonts";
-@import "../../../node_modules/@guillotinaweb/pastanaga-angular/lib/styles/fonts";
diff --git a/projects/grange/package.json b/projects/grange/package.json
index 72e8814..a40230d 100644
--- a/projects/grange/package.json
+++ b/projects/grange/package.json
@@ -1,6 +1,6 @@
{
"name": "@guillotinaweb/grange",
- "version": "1.2.1",
+ "version": "1.3.0",
"license": "MIT",
"author": {
"name": "Eric Brehault",
diff --git a/projects/grange/src/lib/grange.service.ts b/projects/grange/src/lib/grange.service.ts
index df7918e..f0e7006 100644
--- a/projects/grange/src/lib/grange.service.ts
+++ b/projects/grange/src/lib/grange.service.ts
@@ -1,9 +1,12 @@
import { Injectable } from '@angular/core';
-import { Store } from '@ngrx/store';
+import { Store, select } from '@ngrx/store';
import { Traverser } from 'angular-traversal';
import { GrangeCore } from '@guillotinaweb/grange-core';
import { GrangeState } from './state/state';
import { PastanagaService } from '@guillotinaweb/pastanaga-angular';
+import { Observable } from 'rxjs';
+import { TraverserSelectors, TraverserActions } from '@guillotinaweb/ngx-state-traverser';
+import { take } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
@@ -18,4 +21,17 @@ export class Grange {
) {
this.store.dispatch({ type: '[Traversing] Watch'});
}
+
+ getContext(): Observable {
+ return this.store.pipe(select(TraverserSelectors.getContext));
+ }
+
+ updateContext(changes: any) {
+ this.getContext().pipe(
+ take(1)
+ ).subscribe(context => this.store.dispatch(new TraverserActions.UpdateTraverserResource({
+ path: this.core.api.getPath(context['@id']),
+ changes
+ })));
+ }
}
diff --git a/projects/grange/src/lib/state/effects.ts b/projects/grange/src/lib/state/effects.ts
index c3fd376..0b48094 100644
--- a/projects/grange/src/lib/state/effects.ts
+++ b/projects/grange/src/lib/state/effects.ts
@@ -1,7 +1,11 @@
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
-import { tap } from 'rxjs/operators';
-import { TraverserActions } from '@guillotinaweb/ngx-state-traverser';
+import { tap, switchMap, take, concatMap, filter, catchError, map } from 'rxjs/operators';
+import { TraverserActions, TraverserSelectors } from '@guillotinaweb/ngx-state-traverser';
+import { select, Store } from '@ngrx/store';
+import { GrangeState } from './state';
+import { GrangeCore } from '@guillotinaweb/grange-core';
+import { EMPTY, of } from 'rxjs';
@Injectable()
export class GrangeEffects {
@@ -12,7 +16,24 @@ export class GrangeEffects {
tap(() => window.scrollTo(0, 0))
);
+ @Effect()
+ updateResource = this.actions.pipe(
+ ofType(TraverserActions.Types.UpdateTraverserResource),
+ switchMap(action => this.store.pipe(
+ select(TraverserSelectors.getObjectByPath(action.payload.path)),
+ take(1),
+ filter(resource => !!resource['@id']),
+ concatMap(resource => this.core.resource.update(resource['@id'], resource).pipe(
+ map(() => EMPTY),
+ // if error on update, we traverse the resource in order to put the backend version in our local state
+ catchError(() => of(new TraverserActions.Traverse(this.core.api.getPath(resource['@id']))))
+ )),
+ )),
+ );
+
constructor(
private readonly actions: Actions,
+ private readonly store: Store,
+ private core: GrangeCore,
) {}
}
diff --git a/src/pastanaga-overrides.scss b/src/pastanaga-overrides.scss
index 62b1787..8b9f764 100644
--- a/src/pastanaga-overrides.scss
+++ b/src/pastanaga-overrides.scss
@@ -1,5 +1 @@
$labels-text-transform: none;
-
-html, body {
- overflow: auto;
-}