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