parent
a9ddf9ad32
commit
07ec377202
@ -0,0 +1,24 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,59 @@ |
||||
# example-webapp-react-socialquery |
||||
|
||||
Example of a Web app made with NextGraph, using React and LDO, and Vite. |
||||
|
||||
It demonstrate the feature of Social Queries. |
||||
|
||||
## NextGraph |
||||
|
||||
> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. |
||||
> |
||||
> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. |
||||
> |
||||
> More info here [https://nextgraph.org](https://nextgraph.org) |
||||
|
||||
## For developing against a public Broker |
||||
|
||||
``` |
||||
npm install |
||||
npm run dev |
||||
``` |
||||
|
||||
You will have to use a Wallet that was created on one of our public Broker Service Providers ([nextgraph.eu](https://nextgraph.eu) by example) before you can actually login. We didn't implement yet the option to create a Wallet while you are using or developing a 3rd party app. |
||||
|
||||
## For developing locally |
||||
|
||||
you need to have a running local ngd server. See those [instructions first](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/DEV.md#first-run). |
||||
|
||||
If you are running a local devenv for the frontend of nextGraph on http://localhost:1421 , then (and only then) you need to compile the nextgraphweb package in dev mode: |
||||
|
||||
``` |
||||
pnpm run -C ../../helpers/nextgraphweb builddev |
||||
``` |
||||
Due to the way `npm link` works, you will have to run this command again, after each time you use `npm install`. |
||||
|
||||
Otherwise, if you are using http://localhost:14400 in your browser, just skip the line above, and continue with those: |
||||
|
||||
``` |
||||
npm install |
||||
npm link ../../helpers/nextgraphweb |
||||
npm run dev |
||||
``` |
||||
|
||||
Open this URL in browser : [http://localhost:5173](http://localhost:5173) |
||||
|
||||
See the example code in [src/main.tsx](./src/App.tsx) |
||||
|
||||
## License |
||||
|
||||
Licensed under either of |
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) |
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) |
||||
at your option. |
||||
|
||||
`SPDX-License-Identifier: Apache-2.0 OR MIT` |
||||
|
||||
--- |
||||
|
||||
NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/assure) and the [NGI Zero Commons Fund](https://nlnet.nl/commonsfund/), both funds established by [NLnet](https://nlnet.nl/) Foundation with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreements No 957073 and No 101092990, respectively. |
@ -0,0 +1,28 @@ |
||||
import js from '@eslint/js' |
||||
import globals from 'globals' |
||||
import reactHooks from 'eslint-plugin-react-hooks' |
||||
import reactRefresh from 'eslint-plugin-react-refresh' |
||||
import tseslint from 'typescript-eslint' |
||||
|
||||
export default tseslint.config( |
||||
{ ignores: ['dist'] }, |
||||
{ |
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended], |
||||
files: ['**/*.{ts,tsx}'], |
||||
languageOptions: { |
||||
ecmaVersion: 2020, |
||||
globals: globals.browser, |
||||
}, |
||||
plugins: { |
||||
'react-hooks': reactHooks, |
||||
'react-refresh': reactRefresh, |
||||
}, |
||||
rules: { |
||||
...reactHooks.configs.recommended.rules, |
||||
'react-refresh/only-export-components': [ |
||||
'warn', |
||||
{ allowConstantExport: true }, |
||||
], |
||||
}, |
||||
}, |
||||
) |
@ -0,0 +1,12 @@ |
||||
<!doctype html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>NextGraph WebApp React SocialQuery example</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/main.tsx"></script> |
||||
</body> |
||||
</html> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@ |
||||
{ |
||||
"name": "example-webapp-react-socialquery", |
||||
"private": true, |
||||
"version": "0.0.1", |
||||
"type": "module", |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "tsc --noEmit && vite build", |
||||
"lint": "eslint .", |
||||
"preview": "vite preview", |
||||
"build:ldo": "../../../ldo/packages/cli/dist/index.js build --input src/.shapes --output src/.ldo" |
||||
}, |
||||
"dependencies": { |
||||
"@heroicons/react": "^2.2.0", |
||||
"@ldo/connected-nextgraph": "^1.0.0-alpha.11", |
||||
"@ldo/ldo": "^1.0.0-alpha.11", |
||||
"@ldo/react": "^1.0.0-alpha.11", |
||||
"nextgraphweb": "^0.1.1-alpha.4", |
||||
"react": "^19.0.0", |
||||
"react-dom": "^19.0.0" |
||||
}, |
||||
"devDependencies": { |
||||
"@eslint/js": "^9.22.0", |
||||
"@ldo/cli": "^1.0.0-alpha.11", |
||||
"@types/jsonld": "^1.5.15", |
||||
"@types/react": "^19.0.10", |
||||
"@types/react-dom": "^19.0.4", |
||||
"@types/shexj": "^2.1.7", |
||||
"@vitejs/plugin-react": "^4.3.4", |
||||
"autoprefixer": "^10.4.21", |
||||
"eslint": "^9.22.0", |
||||
"eslint-plugin-react-hooks": "^5.2.0", |
||||
"eslint-plugin-react-refresh": "^0.4.19", |
||||
"globals": "^16.0.0", |
||||
"postcss": "^8.5.3", |
||||
"tailwindcss": "^3.4.17", |
||||
"typescript": "~5.7.2", |
||||
"typescript-eslint": "^8.26.1", |
||||
"vite": "^6.3.1" |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
export default { |
||||
plugins: { |
||||
tailwindcss: {}, |
||||
autoprefixer: {}, |
||||
}, |
||||
} |
@ -0,0 +1,119 @@ |
||||
import { LdoJsonldContext } from "@ldo/ldo"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* contactContext: JSONLD Context for contact |
||||
* ============================================================================= |
||||
*/ |
||||
export const contactContext: LdoJsonldContext = { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
Individual: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#Individual", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
fn: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasEmail: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasRating: { |
||||
"@id": "did:ng:x:skills#hasRating", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
}, |
||||
}, |
||||
Person: { |
||||
"@id": "http://schema.org/Person", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
fn: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasEmail: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasRating: { |
||||
"@id": "did:ng:x:skills#hasRating", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
}, |
||||
}, |
||||
Person2: { |
||||
"@id": "http://xmlns.com/foaf/0.1/Person", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
fn: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasEmail: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasRating: { |
||||
"@id": "did:ng:x:skills#hasRating", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
}, |
||||
}, |
||||
fn: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#fn", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasEmail: { |
||||
"@id": "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
hasRating: { |
||||
"@id": "did:ng:x:skills#hasRating", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
Rating: { |
||||
"@id": "did:ng:x:skills#Rating", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
}, |
||||
rated: { |
||||
"@id": "did:ng:x:skills#rated", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
skill: { |
||||
"@id": "did:ng:x:skills#skill", |
||||
}, |
||||
}, |
||||
}, |
||||
rated: { |
||||
"@id": "did:ng:x:skills#rated", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
skill: { |
||||
"@id": "did:ng:x:skills#skill", |
||||
}, |
||||
"ng:k:skills:programming:svelte": "did:ng:k:skills:programming:svelte", |
||||
"ng:k:skills:programming:nextjs": "did:ng:k:skills:programming:nextjs", |
||||
"ng:k:skills:programming:react": "did:ng:k:skills:programming:react", |
||||
"ng:k:skills:programming:vuejs": "did:ng:k:skills:programming:vuejs", |
||||
"ng:k:skills:programming:tailwind": "did:ng:k:skills:programming:tailwind", |
||||
"ng:k:skills:programming:rdf": "did:ng:k:skills:programming:rdf", |
||||
"ng:k:skills:programming:rust": "did:ng:k:skills:programming:rust", |
||||
"ng:k:skills:programming:yjs": "did:ng:k:skills:programming:yjs", |
||||
"ng:k:skills:programming:automerge": "did:ng:k:skills:programming:automerge", |
||||
}; |
@ -0,0 +1,166 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* contactSchema: ShexJ Schema for contact |
||||
* ============================================================================= |
||||
*/ |
||||
export const contactSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "did:ng:x:class#SocialContact", |
||||
type: "ShapeDecl", |
||||
shapeExpr: { |
||||
type: "Shape", |
||||
expression: { |
||||
type: "EachOf", |
||||
expressions: [ |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: ["http://www.w3.org/2006/vcard/ns#Individual"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines the node as an Individual (from vcard)", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: ["http://schema.org/Person"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines the node as a Person (from Schema.org)", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: ["http://xmlns.com/foaf/0.1/Person"], |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines the node as a Person (from foaf)", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/2006/vcard/ns#fn", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: |
||||
"The formatted name of a person. Example: John Smith", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/2006/vcard/ns#hasEmail", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "The person's email.", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "did:ng:x:skills#hasRating", |
||||
valueExpr: "did:ng:x:class#HasRating", |
||||
min: 0, |
||||
max: -1, |
||||
}, |
||||
], |
||||
}, |
||||
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||
}, |
||||
}, |
||||
{ |
||||
id: "did:ng:x:class#HasRating", |
||||
type: "ShapeDecl", |
||||
shapeExpr: { |
||||
type: "Shape", |
||||
expression: { |
||||
type: "EachOf", |
||||
expressions: [ |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: ["did:ng:x:skills#Rating"], |
||||
}, |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "did:ng:x:skills#rated", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "did:ng:x:skills#skill", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: [ |
||||
"did:ng:k:skills:programming:svelte", |
||||
"did:ng:k:skills:programming:nextjs", |
||||
"did:ng:k:skills:programming:react", |
||||
"did:ng:k:skills:programming:vuejs", |
||||
"did:ng:k:skills:programming:tailwind", |
||||
"did:ng:k:skills:programming:rdf", |
||||
"did:ng:k:skills:programming:rust", |
||||
"did:ng:k:skills:programming:yjs", |
||||
"did:ng:k:skills:programming:automerge", |
||||
], |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,28 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { contactSchema } from "./contact.schema"; |
||||
import { contactContext } from "./contact.context"; |
||||
import { SocialContact, HasRating } from "./contact.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes contact |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* SocialContact ShapeType |
||||
*/ |
||||
export const SocialContactShapeType: ShapeType<SocialContact> = { |
||||
schema: contactSchema, |
||||
shape: "did:ng:x:class#SocialContact", |
||||
context: contactContext, |
||||
}; |
||||
|
||||
/** |
||||
* HasRating ShapeType |
||||
*/ |
||||
export const HasRatingShapeType: ShapeType<HasRating> = { |
||||
schema: contactSchema, |
||||
shape: "did:ng:x:class#HasRating", |
||||
context: contactContext, |
||||
}; |
@ -0,0 +1,78 @@ |
||||
import { LdoJsonldContext, LdSet } from "@ldo/ldo"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for contact |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* SocialContact Type |
||||
*/ |
||||
export interface SocialContact { |
||||
"@id"?: string; |
||||
"@context"?: LdoJsonldContext; |
||||
/** |
||||
* Defines the node as an Individual (from vcard) | Defines the node as a Person (from Schema.org) | Defines the node as a Person (from foaf) |
||||
*/ |
||||
type: LdSet< |
||||
| { |
||||
"@id": "Individual"; |
||||
} |
||||
| { |
||||
"@id": "Person"; |
||||
} |
||||
| { |
||||
"@id": "Person2"; |
||||
} |
||||
>; |
||||
/** |
||||
* The formatted name of a person. Example: John Smith |
||||
*/ |
||||
fn: string; |
||||
/** |
||||
* The person's email. |
||||
*/ |
||||
hasEmail?: string; |
||||
hasRating?: LdSet<HasRating>; |
||||
} |
||||
|
||||
/** |
||||
* HasRating Type |
||||
*/ |
||||
export interface HasRating { |
||||
"@id"?: string; |
||||
"@context"?: LdoJsonldContext; |
||||
type: { |
||||
"@id": "Rating"; |
||||
}; |
||||
rated: number; |
||||
skill: |
||||
| { |
||||
"@id": "ng:k:skills:programming:svelte"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:nextjs"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:react"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:vuejs"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:tailwind"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:rdf"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:rust"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:yjs"; |
||||
} |
||||
| { |
||||
"@id": "ng:k:skills:programming:automerge"; |
||||
}; |
||||
} |
@ -0,0 +1,82 @@ |
||||
import { LdoJsonldContext } from "@ldo/ldo"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* containerContext: JSONLD Context for container |
||||
* ============================================================================= |
||||
*/ |
||||
export const containerContext: LdoJsonldContext = { |
||||
type: { |
||||
"@id": "@type", |
||||
"@isCollection": true, |
||||
}, |
||||
Container: { |
||||
"@id": "http://www.w3.org/ns/ldp#Container", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
"@isCollection": true, |
||||
}, |
||||
modified: { |
||||
"@id": "http://purl.org/dc/terms/modified", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
contains: { |
||||
"@id": "http://www.w3.org/ns/ldp#contains", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
mtime: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||
}, |
||||
size: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
}, |
||||
}, |
||||
Resource: { |
||||
"@id": "http://www.w3.org/ns/ldp#Resource", |
||||
"@context": { |
||||
type: { |
||||
"@id": "@type", |
||||
"@isCollection": true, |
||||
}, |
||||
modified: { |
||||
"@id": "http://purl.org/dc/terms/modified", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
contains: { |
||||
"@id": "http://www.w3.org/ns/ldp#contains", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
mtime: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||
}, |
||||
size: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
}, |
||||
}, |
||||
modified: { |
||||
"@id": "http://purl.org/dc/terms/modified", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
contains: { |
||||
"@id": "http://www.w3.org/ns/ldp#contains", |
||||
"@type": "@id", |
||||
"@isCollection": true, |
||||
}, |
||||
mtime: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#mtime", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#decimal", |
||||
}, |
||||
size: { |
||||
"@id": "http://www.w3.org/ns/posix/stat#size", |
||||
"@type": "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
}; |
@ -0,0 +1,124 @@ |
||||
import { Schema } from "shexj"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* containerSchema: ShexJ Schema for container |
||||
* ============================================================================= |
||||
*/ |
||||
export const containerSchema: Schema = { |
||||
type: "Schema", |
||||
shapes: [ |
||||
{ |
||||
id: "http://www.w3.org/ns/lddps#Container", |
||||
type: "ShapeDecl", |
||||
shapeExpr: { |
||||
type: "Shape", |
||||
expression: { |
||||
id: "http://www.w3.org/ns/lddps#ContainerShape", |
||||
type: "EachOf", |
||||
expressions: [ |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
values: [ |
||||
"http://www.w3.org/ns/ldp#Container", |
||||
"http://www.w3.org/ns/ldp#Resource", |
||||
], |
||||
}, |
||||
min: 0, |
||||
max: -1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "A container", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://purl.org/dc/terms/modified", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#string", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Date modified", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/ldp#contains", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
nodeKind: "iri", |
||||
}, |
||||
min: 0, |
||||
max: -1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "Defines a Resource", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/posix/stat#mtime", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#decimal", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "?", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
type: "TripleConstraint", |
||||
predicate: "http://www.w3.org/ns/posix/stat#size", |
||||
valueExpr: { |
||||
type: "NodeConstraint", |
||||
datatype: "http://www.w3.org/2001/XMLSchema#integer", |
||||
}, |
||||
min: 0, |
||||
max: 1, |
||||
annotations: [ |
||||
{ |
||||
type: "Annotation", |
||||
predicate: "http://www.w3.org/2000/01/rdf-schema#comment", |
||||
object: { |
||||
value: "size of this container", |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], |
||||
}, |
||||
}, |
||||
], |
||||
}; |
@ -0,0 +1,19 @@ |
||||
import { ShapeType } from "@ldo/ldo"; |
||||
import { containerSchema } from "./container.schema"; |
||||
import { containerContext } from "./container.context"; |
||||
import { Container } from "./container.typings"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* LDO ShapeTypes container |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* Container ShapeType |
||||
*/ |
||||
export const ContainerShapeType: ShapeType<Container> = { |
||||
schema: containerSchema, |
||||
shape: "http://www.w3.org/ns/lddps#Container", |
||||
context: containerContext, |
||||
}; |
@ -0,0 +1,44 @@ |
||||
import { LdoJsonldContext, LdSet } from "@ldo/ldo"; |
||||
|
||||
/** |
||||
* ============================================================================= |
||||
* Typescript Typings for container |
||||
* ============================================================================= |
||||
*/ |
||||
|
||||
/** |
||||
* Container Type |
||||
*/ |
||||
export interface Container { |
||||
"@id"?: string; |
||||
"@context"?: LdoJsonldContext; |
||||
/** |
||||
* A container |
||||
*/ |
||||
type?: LdSet< |
||||
| { |
||||
"@id": "Container"; |
||||
} |
||||
| { |
||||
"@id": "Resource"; |
||||
} |
||||
>; |
||||
/** |
||||
* Date modified |
||||
*/ |
||||
modified?: string; |
||||
/** |
||||
* Defines a Resource |
||||
*/ |
||||
contains?: LdSet<{ |
||||
"@id": string; |
||||
}>; |
||||
/** |
||||
* ? |
||||
*/ |
||||
mtime?: number; |
||||
/** |
||||
* size of this container |
||||
*/ |
||||
size?: number; |
||||
} |
@ -0,0 +1,35 @@ |
||||
|
||||
# Platform ontologies: |
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
PREFIX owl: <http://www.w3.org/2002/07/owl#> |
||||
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||
PREFIX dc: <http://purl.org/dc/terms/> |
||||
|
||||
# Domain ontology for Contacts in vcard-like form |
||||
PREFIX vcard: <http://www.w3.org/2006/vcard/ns#> |
||||
PREFIX schem: <http://schema.org/> |
||||
PREFIX foaf: <http://xmlns.com/foaf/0.1/> |
||||
PREFIX ngc: <did:ng:x:class#> |
||||
PREFIX xskills: <did:ng:x:skills#> |
||||
PREFIX ksp: <did:ng:k:skills:programming:> |
||||
|
||||
ngc:SocialContact EXTRA a { |
||||
a [ vcard:Individual ] |
||||
// rdfs:comment "Defines the node as an Individual (from vcard)" ; |
||||
a [ schem:Person ] |
||||
// rdfs:comment "Defines the node as a Person (from Schema.org)" ; |
||||
a [ foaf:Person ] |
||||
// rdfs:comment "Defines the node as a Person (from foaf)" ; |
||||
vcard:fn xsd:string |
||||
// rdfs:comment "The formatted name of a person. Example: John Smith" ; |
||||
vcard:hasEmail xsd:string ? |
||||
// rdfs:comment "The person's email." ; |
||||
xskills:hasRating @ngc:HasRating *; |
||||
} |
||||
|
||||
ngc:HasRating { |
||||
a [ xskills:Rating ]; |
||||
xskills:rated xsd:integer; |
||||
xskills:skill [ ksp:svelte ksp:nextjs ksp:react ksp:vuejs ksp:tailwind ksp:rdf ksp:rust ksp:yjs ksp:automerge ] |
||||
} |
@ -0,0 +1,24 @@ |
||||
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> |
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||
PREFIX ldp: <http://www.w3.org/ns/ldp#> |
||||
PREFIX ldps: <http://www.w3.org/ns/lddps#> |
||||
PREFIX dct: <http://purl.org/dc/terms/> |
||||
PREFIX stat: <http://www.w3.org/ns/posix/stat#> |
||||
PREFIX tur: <http://www.w3.org/ns/iana/media-types/text/turtle#> |
||||
PREFIX pim: <http://www.w3.org/ns/pim/space#> |
||||
|
||||
ldps:Container EXTRA a { |
||||
$ldps:ContainerShape ( |
||||
a [ ldp:Container ldp:Resource ]* |
||||
// rdfs:comment "A container"; |
||||
dct:modified xsd:string? |
||||
// rdfs:comment "Date modified"; |
||||
ldp:contains IRI * |
||||
// rdfs:comment "Defines a Resource"; |
||||
stat:mtime xsd:decimal? |
||||
// rdfs:comment "?"; |
||||
stat:size xsd:integer? |
||||
// rdfs:comment "size of this container"; |
||||
) |
||||
} |
@ -0,0 +1,46 @@ |
||||
|
||||
|
||||
.centered { |
||||
/*max-width: 1280px;*/ |
||||
margin: 0 auto; |
||||
padding: 0rem; |
||||
text-align: center; |
||||
width: fit-content; |
||||
} |
||||
|
||||
.contact { |
||||
width: 300px; |
||||
height: 300px; |
||||
background-color: #f6f6f6; |
||||
position: relative; |
||||
overflow-wrap: anywhere; |
||||
} |
||||
|
||||
.name { |
||||
padding: 5px; |
||||
height: 35px; |
||||
overflow: hidden; |
||||
background-color: #e0e0e0d0; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
white-space: nowrap; |
||||
} |
||||
|
||||
.email-logo { |
||||
|
||||
} |
||||
|
||||
.email { |
||||
padding: 5px; |
||||
overflow-wrap: anywhere; |
||||
} |
||||
|
||||
input { |
||||
margin: 5px; |
||||
} |
||||
|
||||
#save { |
||||
background-color:rgb(73, 114, 165); |
||||
color:white; |
||||
cursor:pointer; |
||||
} |
@ -0,0 +1,22 @@ |
||||
|
||||
import React, { FunctionComponent } from 'react'; |
||||
import { Header } from './Header'; |
||||
import { Contacts } from './Contacts'; |
||||
import { BrowserNGLdoProvider } from './reactMethods'; |
||||
|
||||
import './App.css' |
||||
import "../../../common/src/styles.css"; |
||||
|
||||
const App: FunctionComponent = () => { |
||||
|
||||
return ( |
||||
<div className="App"> |
||||
<BrowserNGLdoProvider> |
||||
<Header /> |
||||
<Contacts />
|
||||
</BrowserNGLdoProvider> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default App |
@ -0,0 +1,118 @@ |
||||
import { default as React, FunctionComponent } from "react"; |
||||
import { useNextGraphAuth } from "./reactMethods"; |
||||
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts"; |
||||
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts"; |
||||
import { StarIcon } from '@heroicons/react/24/solid' |
||||
import { StarIcon as StarIconOutline, NoSymbolIcon } from '@heroicons/react/24/outline' |
||||
|
||||
export const Contact: FunctionComponent = ({nuri}) => { |
||||
const { session } = useNextGraphAuth(); |
||||
|
||||
useResource(session.sessionId && nuri ? nuri : undefined, { subscribe: true }); |
||||
let contact = useSubject(SocialContactShapeType, session.sessionId && nuri ? nuri.substring(0,53) : undefined); |
||||
|
||||
const ksp = "did:ng:k:skills:programming:"; |
||||
|
||||
const ksp_mapping = [ |
||||
"svelte", |
||||
"nextjs", |
||||
"react", |
||||
"vuejs", |
||||
"tailwind", |
||||
"rdf", |
||||
"rust", |
||||
"yjs", |
||||
"automerge", |
||||
]; |
||||
|
||||
const ksp_name = [ |
||||
"Svelte", |
||||
"NextJS", |
||||
"React", |
||||
"VueJS", |
||||
"Tailwind", |
||||
"RDF/SPARQL", |
||||
"Rust", |
||||
"Yjs", |
||||
"Automerge", |
||||
] |
||||
|
||||
const [skills, setSkills] = React.useState([ |
||||
0, |
||||
0, |
||||
5, |
||||
3, |
||||
1, |
||||
2, |
||||
4, |
||||
0, |
||||
0, |
||||
]) |
||||
|
||||
React.useEffect(() => { |
||||
console.log(contact.hasRating?.entries()) |
||||
let nextSkills = skills.map((s) => { |
||||
return 0; |
||||
}); |
||||
contact.hasRating?.map((r) => { |
||||
nextSkills[ksp_mapping.indexOf(r.skill["@id"].substring(28))] = r.rated +1; |
||||
}); |
||||
setSkills(nextSkills); |
||||
}, [contact]) |
||||
|
||||
if (!session.sessionId || !nuri) return <></>; |
||||
|
||||
function rate(skill: number, rating: number) { |
||||
console.log("rate", skill, rating); |
||||
|
||||
const nextSkills = skills.map((s, i) => { |
||||
if (i === skill) { |
||||
if (s == rating) { |
||||
s = s - 1; |
||||
} else { |
||||
s = rating; |
||||
} |
||||
return s; |
||||
} else { |
||||
return s; |
||||
} |
||||
}); |
||||
setSkills(nextSkills); |
||||
} |
||||
|
||||
return <> |
||||
{contact.fn? (
|
||||
<div className="contact text-left flex flex-col" title={nuri}> |
||||
<div className="name text-center">
|
||||
{contact.fn} |
||||
</div> |
||||
<div className="p-2"> |
||||
<span className="email"> |
||||
{contact.hasEmail} |
||||
{JSON.stringify(contact.hasRating?.entries())} |
||||
</span> |
||||
</div> |
||||
{ |
||||
skills.map( |
||||
(skill,s) =>
|
||||
<div key={s} className="px-2 flex flex-row cursor-pointer text-yellow-500"> |
||||
{/* <NoSymbolIcon className="size-6"/> */} |
||||
{ |
||||
[...Array(5)].map( |
||||
(e,i) => { |
||||
if (i + 1 <= skill) { |
||||
return <StarIcon key={s * 10 + i} onClick={()=>rate(s,i+1)} className="size-6"/> |
||||
} else { |
||||
return <StarIconOutline key={s * 10 + i} onClick={()=>rate(s,i+1)} className=" size-6"/> |
||||
} |
||||
} |
||||
) |
||||
} |
||||
<span className="text-black ml-2">{ksp_name[s]}</span> |
||||
</div> |
||||
) |
||||
} |
||||
</div> |
||||
) : <></>} |
||||
</>; |
||||
}; |
@ -0,0 +1,38 @@ |
||||
import { FunctionComponent } from "react"; |
||||
import { useNextGraphAuth } from "./reactMethods"; |
||||
import { ContainerShapeType } from "./.ldo/container.shapeTypes.ts"; |
||||
import { useSubscribeToResource, useResource, useSubject } from "./reactMethods.ts"; |
||||
import { Contact } from "./Contact"; |
||||
import { MakeContact } from "./MakeContact"; |
||||
|
||||
export const Contacts: FunctionComponent = () => { |
||||
const { session } = useNextGraphAuth(); |
||||
|
||||
let container_overlay: string; |
||||
|
||||
useResource(session.sessionId ? "did:ng:"+session.privateStoreId : undefined, { subscribe: true }); |
||||
let myContainer = useSubject(ContainerShapeType, session.sessionId ? "did:ng:"+(session.privateStoreId.substring(0,46)) : undefined); |
||||
|
||||
if (session.sessionId) { |
||||
container_overlay = session.privateStoreId.substring(46) as string; |
||||
} |
||||
|
||||
if (!session.sessionId) return <></>; |
||||
|
||||
return <> |
||||
<div className="centered"> |
||||
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-10"> |
||||
<MakeContact/> |
||||
</div> |
||||
<div className="flex flex-wrap justify-center gap-5 mb-10"> |
||||
{
|
||||
myContainer.contains?.map( |
||||
(contained) =>
|
||||
<Contact key={contained["@id"]} nuri={contained["@id"]+container_overlay}/> |
||||
) |
||||
} |
||||
</div> |
||||
</div> |
||||
</>; |
||||
}; |
||||
|
@ -0,0 +1,45 @@ |
||||
import { FunctionComponent } from "react"; |
||||
import { useNextGraphAuth } from "./reactMethods"; |
||||
|
||||
|
||||
export const Header: FunctionComponent = () => { |
||||
|
||||
const { session, login, logout } = useNextGraphAuth(); |
||||
|
||||
return ( |
||||
<div className="full-layout"> |
||||
|
||||
{session.sessionId ? ( |
||||
// If the session is logged in
|
||||
<div className="p-1 text-white text-center fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||
You are logged in. |
||||
{/* <span className="font-bold clickable" onClick={logout}> Log out</span> */} |
||||
</div> |
||||
) : ( |
||||
// If the session is not logged in
|
||||
<> |
||||
<h1 className="text-2xl text-center mb-10">Welcome to the mini-LinkedIn demo</h1> |
||||
<h1 className="text-lg text-center mb-10">An example use of Social Queries with NextGraph</h1> |
||||
<div className="text-center text-xl p-1 text-white fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||
Please <span className="font-bold clickable" onClick={login}> Log in</span> |
||||
</div> |
||||
|
||||
<div className="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-800"> |
||||
|
||||
<svg className="mt-10 h-16 w-16 mx-auto" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15M12 9l3 3m0 0-3 3m3-3H2.25"></path> |
||||
</svg> |
||||
|
||||
<button |
||||
onClick={login} |
||||
onKeyUp={login} |
||||
className="select-none ml-0 mt-2 mb-10 text-white bg-blue-800 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||
> |
||||
Please Log in |
||||
</button> |
||||
</div> |
||||
</> |
||||
)} |
||||
</div> |
||||
); |
||||
}; |
@ -0,0 +1,60 @@ |
||||
import { FormEvent, FunctionComponent, useCallback, useState } from "react"; |
||||
import { BrowserNGLdoProvider, useLdo, dataset } from './reactMethods'; |
||||
import { SocialContactShapeType } from "./.ldo/contact.shapeTypes.ts"; |
||||
import { LdSet } from "@ldo/ldo"; |
||||
|
||||
export const MakeContact: FunctionComponent = () => { |
||||
const [name, setName] = useState(""); |
||||
const [email, setEmail] = useState(""); |
||||
|
||||
const { createData, commitData } = useLdo(); |
||||
|
||||
const onSubmit = useCallback( |
||||
async (e: FormEvent<HTMLFormElement>) => { |
||||
e.preventDefault(); |
||||
const new_name = name.trim(); |
||||
const new_email = email.trim(); |
||||
if (new_name.trim().length > 2 && new_email.trim().length > 6 && new_email.indexOf("@") >= 0) {
|
||||
setName(""); |
||||
setEmail(""); |
||||
const resource = await dataset.createResource("nextgraph"); |
||||
if (!resource.isError) { |
||||
//console.log("Created resource:", resource.uri);
|
||||
|
||||
const contact = createData( |
||||
SocialContactShapeType, |
||||
resource.uri.substring(0,53), |
||||
resource |
||||
); |
||||
|
||||
contact.type = { "@id": "Individual" }; |
||||
contact.fn = new_name; |
||||
contact.hasEmail = new_email; |
||||
const result = await commitData(contact); |
||||
if (result.isError) { |
||||
console.error(result.message); |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
[name, email] |
||||
); |
||||
|
||||
return ( |
||||
<form onSubmit={onSubmit}> |
||||
<input |
||||
type="text" |
||||
placeholder="Enter name" |
||||
value={name} |
||||
onChange={(e) => setName(e.target.value)} |
||||
/> |
||||
<input |
||||
type="text" |
||||
placeholder="Enter email address" |
||||
value={email} |
||||
onChange={(e) => setEmail(e.target.value)} |
||||
/> |
||||
<input type="submit" id="save" value="Save" /> |
||||
</form> |
||||
); |
||||
}; |
@ -0,0 +1,20 @@ |
||||
import { createContext, useContext } from "react"; |
||||
|
||||
/** |
||||
* Functions for authenticating with NextGraph |
||||
*/ |
||||
export interface NGWalletAuthFunctions { |
||||
login: () => Promise<void>; |
||||
logout: () => Promise<void>; |
||||
session: unknown; |
||||
ranInitialAuthCheck: boolean; |
||||
} |
||||
|
||||
// There is no initial value for this context. It will be given in the provider
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
export const NextGraphAuthContext = createContext<NGWalletAuthFunctions>(undefined); |
||||
|
||||
export function useNextGraphAuth(): NGWalletAuthFunctions { |
||||
return useContext(NextGraphAuthContext); |
||||
} |
@ -0,0 +1,112 @@ |
||||
import React, { useCallback, useEffect, useMemo, useState } from "react"; |
||||
import type { FunctionComponent, PropsWithChildren } from "react"; |
||||
import { NextGraphAuthContext, useNextGraphAuth } from "./NextGraphAuthContext"; |
||||
|
||||
import {default as ng, init} from "nextgraphweb"; |
||||
|
||||
import type { ConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; |
||||
import type { NextGraphConnectedPlugin, NextGraphConnectedContext } from "@ldo/connected-nextgraph"; |
||||
|
||||
/** |
||||
* Creates special react methods specific to the NextGraph Auth |
||||
* @param dataset the connectedLdoDataset with a nextGraphConnectedPlugin |
||||
* @returns { BrowserNGLdoProvider, useNextGraphAuth } |
||||
*/ |
||||
export function createBrowserNGReactMethods( |
||||
dataset: ConnectedLdoDataset<(NextGraphConnectedPlugin | ConnectedPlugin)[]>, |
||||
) { |
||||
|
||||
const BrowserNGLdoProvider: FunctionComponent<PropsWithChildren> = ({ |
||||
children, |
||||
}) => { |
||||
const [session, setSession] = useState<NextGraphConnectedContext>( |
||||
{ |
||||
ng: undefined, |
||||
} |
||||
); |
||||
const [ranInitialAuthCheck, setRanInitialAuthCheck] = useState(false); |
||||
|
||||
const runInitialAuthCheck = useCallback(async () => { |
||||
//console.log("runInitialAuthCheck called", ranInitialAuthCheck)
|
||||
if (ranInitialAuthCheck) return; |
||||
|
||||
//console.log("init called");
|
||||
setRanInitialAuthCheck(true); |
||||
// TODO: export the types for the session object coming from NG.
|
||||
await init( (event: { status: string; session: { session_id: unknown; protected_store_id: unknown; private_store_id: unknown; public_store_id: unknown; }; }) => { |
||||
//console.log("called back in react", event)
|
||||
|
||||
// callback
|
||||
// once you receive event.status == "loggedin"
|
||||
// you can use the full API
|
||||
if (event.status == "loggedin") { |
||||
setSession({
|
||||
ng,
|
||||
sessionId: event.session.session_id as string, //FIXME: sessionId should be a Number.
|
||||
protectedStoreId: event.session.protected_store_id as string, |
||||
privateStoreId: event.session.private_store_id as string, |
||||
publicStoreId: event.session.public_store_id as string |
||||
}); // TODO: add event.session.user too
|
||||
|
||||
dataset.setContext("nextgraph", { |
||||
ng, |
||||
sessionId: event.session.session_id as string |
||||
}); |
||||
} |
||||
else if (event.status == "cancelled" || event.status == "error" || event.status == "loggedout") { |
||||
setSession({ ng: undefined }); |
||||
dataset.setContext("nextgraph", { |
||||
ng: undefined, |
||||
}); |
||||
} |
||||
} |
||||
, true // singleton: boolean (will your app create many docs in the system, or should it be launched as a unique instance)
|
||||
, []); //list of AccessRequests (for now, leave this empty)
|
||||
|
||||
}, []); |
||||
|
||||
|
||||
const login = useCallback( |
||||
async () => { |
||||
await ng.login(); |
||||
}, |
||||
[], |
||||
); |
||||
|
||||
const logout = useCallback(async () => { |
||||
await ng.logout(); |
||||
}, []); |
||||
|
||||
useEffect(() => { |
||||
runInitialAuthCheck(); |
||||
}, []); |
||||
|
||||
const nextGraphAuthFunctions = useMemo( |
||||
() => ({ |
||||
runInitialAuthCheck, |
||||
login, |
||||
logout, |
||||
session, |
||||
ranInitialAuthCheck, |
||||
}), |
||||
[ |
||||
login, |
||||
logout, |
||||
ranInitialAuthCheck, |
||||
runInitialAuthCheck, |
||||
session, |
||||
], |
||||
); |
||||
|
||||
return ( |
||||
<NextGraphAuthContext.Provider value={nextGraphAuthFunctions}> |
||||
{children} |
||||
</NextGraphAuthContext.Provider> |
||||
); |
||||
}; |
||||
|
||||
return { |
||||
BrowserNGLdoProvider, |
||||
useNextGraphAuth: useNextGraphAuth |
||||
}; |
||||
}; |
@ -0,0 +1,3 @@ |
||||
@tailwind base; |
||||
@tailwind components; |
||||
@tailwind utilities; |
@ -0,0 +1,10 @@ |
||||
import { StrictMode } from 'react' |
||||
import { createRoot } from 'react-dom/client' |
||||
import './index.css' |
||||
import App from './App.tsx' |
||||
|
||||
createRoot(document.getElementById('app')!).render( |
||||
// <StrictMode>
|
||||
<App /> |
||||
// </StrictMode>,
|
||||
) |
@ -0,0 +1,18 @@ |
||||
import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; |
||||
import { createLdoReactMethods } from "@ldo/react"; |
||||
import { createBrowserNGReactMethods } from "./createBrowserNGReactMethods"; |
||||
|
||||
export const { |
||||
dataset, |
||||
useLdo, |
||||
useMatchObject, |
||||
useMatchSubject, |
||||
useResource, |
||||
useSubject, |
||||
useSubscribeToResource, |
||||
} = createLdoReactMethods([nextGraphConnectedPlugin]); |
||||
|
||||
const methods = createBrowserNGReactMethods(dataset); |
||||
|
||||
export const { BrowserNGLdoProvider, useNextGraphAuth } = methods; |
||||
|
@ -0,0 +1 @@ |
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,12 @@ |
||||
/** @type {import('tailwindcss').Config} */ |
||||
export default { |
||||
content: [ |
||||
"./index.html", |
||||
"./src/**/*.{js,jsx,ts,tsx}" |
||||
], |
||||
theme: { |
||||
extend: {}, |
||||
}, |
||||
plugins: [], |
||||
} |
||||
|
@ -0,0 +1,26 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", |
||||
"target": "ES2020", |
||||
"useDefineForClassFields": true, |
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"], |
||||
"module": "ESNext", |
||||
"skipLibCheck": true, |
||||
|
||||
/* Bundler mode */ |
||||
"moduleResolution": "bundler", |
||||
"allowImportingTsExtensions": true, |
||||
"isolatedModules": true, |
||||
"moduleDetection": "force", |
||||
"noEmit": true, |
||||
"jsx": "react-jsx", |
||||
|
||||
/* Linting */ |
||||
"strict": true, |
||||
"noUnusedLocals": true, |
||||
"noUnusedParameters": true, |
||||
"noFallthroughCasesInSwitch": true, |
||||
"noUncheckedSideEffectImports": true |
||||
}, |
||||
"include": ["src"] |
||||
} |
@ -0,0 +1,7 @@ |
||||
{ |
||||
"files": [], |
||||
"references": [ |
||||
{ "path": "./tsconfig.app.json" }, |
||||
{ "path": "./tsconfig.node.json" } |
||||
] |
||||
} |
@ -0,0 +1,24 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", |
||||
"target": "ES2022", |
||||
"lib": ["ES2023"], |
||||
"module": "ESNext", |
||||
"skipLibCheck": true, |
||||
|
||||
/* Bundler mode */ |
||||
"moduleResolution": "bundler", |
||||
"allowImportingTsExtensions": true, |
||||
"isolatedModules": true, |
||||
"moduleDetection": "force", |
||||
"noEmit": true, |
||||
|
||||
/* Linting */ |
||||
"strict": true, |
||||
"noUnusedLocals": true, |
||||
"noUnusedParameters": true, |
||||
"noFallthroughCasesInSwitch": true, |
||||
"noUncheckedSideEffectImports": true |
||||
}, |
||||
"include": ["vite.config.ts"] |
||||
} |
@ -0,0 +1,7 @@ |
||||
import { defineConfig } from 'vite' |
||||
import react from '@vitejs/plugin-react' |
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({ |
||||
plugins: [react()], |
||||
}) |
Loading…
Reference in new issue