edit title and intro

master
Niko PLP 3 weeks ago
parent 711fa42621
commit 254ad56e5b
  1. 1
      Cargo.lock
  2. 1
      RELEASE-NOTE.md
  3. 9
      ng-app/index-web.html
  4. 49
      ng-app/src-tauri/src/lib.rs
  5. 2
      ng-app/src/api.ts
  6. 34
      ng-app/src/apps/ContainerView.svelte
  7. 4
      ng-app/src/apps/SparqlQueryEditor.svelte
  8. 77
      ng-app/src/classes.ts
  9. 4
      ng-app/src/lib/Document.svelte
  10. 123
      ng-app/src/lib/FullLayout.svelte
  11. 10
      ng-app/src/lib/icons/DataClassIcon.svelte
  12. 70
      ng-app/src/lib/popups/Header.svelte
  13. 13
      ng-app/src/locales/en.json
  14. 4
      ng-app/src/routes/WalletInfo.svelte
  15. 42
      ng-app/src/routes/WalletLogin.svelte
  16. 61
      ng-app/src/store.ts
  17. 6
      ng-app/src/styles.css
  18. 7
      ng-app/src/tab.ts
  19. 32
      ng-app/src/zeras.ts
  20. 56
      ng-net/src/app_protocol.rs
  21. 26
      ng-oxigraph/src/oxigraph/sparql/dataset.rs
  22. 12
      ng-oxigraph/src/oxigraph/storage/mod.rs
  23. 11
      ng-repo/src/repo.rs
  24. 90
      ng-repo/src/store.rs
  25. 17
      ng-repo/src/types.rs
  26. 62
      ng-sdk-js/app-node/index.js
  27. 110
      ng-sdk-js/src/lib.rs
  28. 1
      ng-verifier/Cargo.toml
  29. 99
      ng-verifier/src/commits/transaction.rs
  30. 159
      ng-verifier/src/request_processor.rs
  31. 2
      ng-verifier/src/site.rs
  32. 13
      ng-verifier/src/types.rs
  33. 100
      ng-verifier/src/verifier.rs

1
Cargo.lock generated

@ -3507,6 +3507,7 @@ dependencies = [
"either", "either",
"futures", "futures",
"getrandom 0.2.10", "getrandom 0.2.10",
"lazy_static",
"ng-net", "ng-net",
"ng-oxigraph", "ng-oxigraph",
"ng-repo", "ng-repo",

@ -40,6 +40,5 @@ The `ngcli` daemon is release with the basic features listed in `ngcli --help`.
- you cannot share documents with other users. Everything is ready for this internally, but there is still some wiring to do that will take some more time. - you cannot share documents with other users. Everything is ready for this internally, but there is still some wiring to do that will take some more time.
- the Rich text editors (both for normal Post/Article and in Markdown) do not let you insert images nor links to other documents. - the Rich text editors (both for normal Post/Article and in Markdown) do not let you insert images nor links to other documents.
- your documents listed in your 3 stores do not display any title or content type, making it difficult to understand which document is which by just reading the 7-character ID of the documents. This will be addressed very quickly, as soon as the "Header branch" feature will be implemented. For the same reason (lack of this feature), and in the web-app only, when you will have created many docs with many modifications, the loading of your app can take some time because it is loading all the content of all the docs at startup. The native apps are already working well and do not suffer from this caveat. For the web-app, it is not the intended behaviour of course, but we need the "Header branch" feature to fix this.
- The webapp has some limitation for now when it is offline, because it doesn't have a UserStorage. it works differently than the native apps, as it has to replay all the commits at every load. This will stay like that for now, as the feature "Web UserStorage" based on IndexedDB will take some time to be coded. - The webapp has some limitation for now when it is offline, because it doesn't have a UserStorage. it works differently than the native apps, as it has to replay all the commits at every load. This will stay like that for now, as the feature "Web UserStorage" based on IndexedDB will take some time to be coded.
- JSON-LD isn't ready yet as we need the "Context branch" feature in order to enter the list of ontologies each document is based on. - JSON-LD isn't ready yet as we need the "Context branch" feature in order to enter the list of ontologies each document is based on.

@ -116,19 +116,24 @@
</div> </div>
<script> <script>
const supported = (() => { const supported = (() => {
if (RegExp().hasIndices === undefined) return false; if (RegExp().hasIndices === undefined) {console.log("no RegExp().hasIndices");return false;}
try { try {
if (Worker === undefined) return false; if (Worker === undefined) {console.log("no Worker");return false;}
new Worker(URL.createObjectURL(new Blob([';'], {type: 'application/javascript'}))); new Worker(URL.createObjectURL(new Blob([';'], {type: 'application/javascript'})));
if (typeof WebAssembly === "object" if (typeof WebAssembly === "object"
&& typeof WebAssembly.instantiate === "function") { && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
if (module instanceof WebAssembly.Module) if (module instanceof WebAssembly.Module)
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
else {
console.log("no WebAssembly module");
}
} }
} catch (e) { } catch (e) {
{console.log(e);return false;}
} }
console.log("no WebAssembly");
return false; return false;
})(); })();
if (!supported ) { if (!supported ) {

@ -551,6 +551,53 @@ async fn branch_history(session_id: u64, nuri: String) -> Result<AppHistoryJs, S
} }
} }
#[tauri::command(rename_all = "snake_case")]
async fn update_header(
session_id: u64,
nuri: String,
title: Option<String>,
about: Option<String>,
) -> Result<(), String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_header(),
nuri,
payload: Some(AppRequestPayload::new_header(title, about)),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[tauri::command(rename_all = "snake_case")]
async fn fetch_header(session_id: u64, nuri: String) -> Result<AppHeader, String> {
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_fetch_header(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
match res {
AppResponse::V0(AppResponseV0::Error(e)) => Err(e),
AppResponse::V0(AppResponseV0::Header(h)) => Ok(h),
_ => Err("invalid response".to_string()),
}
}
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn sparql_update( async fn sparql_update(
session_id: u64, session_id: u64,
@ -1023,6 +1070,8 @@ impl AppBuilder {
signature_status, signature_status,
signature_request, signature_request,
signed_snapshot_request, signed_snapshot_request,
update_header,
fetch_header,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

@ -53,6 +53,8 @@ const mapping = {
"signature_status": ["session_id", "nuri"], "signature_status": ["session_id", "nuri"],
"signed_snapshot_request": ["session_id", "nuri"], "signed_snapshot_request": ["session_id", "nuri"],
"signature_request": ["session_id", "nuri"], "signature_request": ["session_id", "nuri"],
"update_header": ["session_id","nuri","title","about"],
"fetch_header": ["session_id", "nuri"]
} }

@ -11,8 +11,7 @@
<script lang="ts"> <script lang="ts">
import { import ng from "../api";
} from "../store";
import { link } from "svelte-spa-router"; import { link } from "svelte-spa-router";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte"; import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import{ PlusCircle } from "svelte-heros-v2"; import{ PlusCircle } from "svelte-heros-v2";
@ -20,9 +19,16 @@
import { import {
in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_branch_class, cur_tab_doc_can_edit, cur_tab in_memory_discrete, open_viewer, set_viewer, set_editor, set_view_or_edit, cur_tab_branch_class, cur_tab_doc_can_edit, cur_tab
} from "../tab"; } from "../tab";
import DataClassIcon from "../lib/icons/DataClassIcon.svelte";
import { import {
openModalCreate openModalCreate,
sparql_query,
active_session
} from "../store"; } from "../store";
import {
Clipboard
} from "svelte-heros-v2";
export let commits; export let commits;
function contained(graph) { function contained(graph) {
@ -30,23 +36,41 @@
for (const g of graph) { for (const g of graph) {
if (g.substring(57,90) === "http://www.w3.org/ns/ldp#contains") { if (g.substring(57,90) === "http://www.w3.org/ns/ldp#contains") {
let nuri = g.substring(93,146); let nuri = g.substring(93,146);
let repo = nuri;
nuri = nuri + ":" + $cur_tab.store.overlay; nuri = nuri + ":" + $cur_tab.store.overlay;
let hash = nuri.substring(9,16); let hash = nuri.substring(9,16);
ret.push({nuri,hash}); ret.push({nuri,hash,repo});
} }
} }
ret.sort((a, b) => a.hash.localeCompare(b.hash)); ret.sort((a, b) => a.hash.localeCompare(b.hash));
return ret; return ret;
} }
async function fetch_header(repo) {
try {
let res = await ng.fetch_header($active_session.session_id, repo);
return res;
}catch(e){
console.error(e);
return {};
}
}
const create = () => { const create = () => {
openModalCreate(); openModalCreate();
} }
const config = {
class: "mr-2 w-6 h-6 shrink-0 focus:outline-none"
}
</script> </script>
<div class="flex-col p-5"> <div class="flex-col p-5">
{#each contained(commits.graph) as doc} {#each contained(commits.graph) as doc}
<div class="flex font-mono mb-3"> <a use:link href="/{doc.nuri}">{doc.hash}</a> </div> {#await fetch_header(doc.repo)}
<div class="flex"> <Clipboard tabindex="-1" class="mr-2 w-6 h-6 shrink-0 focus:outline-none"/><div class="flex font-mono mb-3"> <a use:link href="/{doc.nuri}">{doc.hash}</a> </div> </div>
{:then header}
<div class="flex" title="{header.about || ''}"> {#if header.class}<DataClassIcon {config} dataClass={header.class}/>{:else}<Clipboard tabindex="-1" class="mr-2 w-6 h-6 shrink-0 focus:outline-none"/>{/if}<div class="flex font-mono mb-3"> <a use:link href="/{doc.nuri}">{header.title || doc.hash}</a> </div></div>
{/await}
{/each} {/each}
{#if commits.graph.length == 0 || contained(commits.graph).length == 0} {#if commits.graph.length == 0 || contained(commits.graph).length == 0}
<p>{$t("doc.empty_container")}</p> <p>{$t("doc.empty_container")}</p>

@ -55,7 +55,7 @@
await reset_toasts(); await reset_toasts();
results = await sparql_query($in_memory_discrete, union); results = await sparql_query($in_memory_discrete, union);
} catch(e) { } catch(e) {
console.log(e) console.error(e)
toast_error(display_error(e)); toast_error(display_error(e));
} }
} }
@ -112,7 +112,7 @@
{#each results.results.bindings as row} {#each results.results.bindings as row}
<TableBodyRow> <TableBodyRow>
{#each results.head.vars as variable} {#each results.head.vars as variable}
<TableBodyCell class="px-6 py-4 whitespace-break-spaces font-medium">{row[variable].value}</TableBodyCell> <TableBodyCell class="px-6 py-4 whitespace-break-spaces font-medium">{#if row[variable]} {row[variable].value }{/if}</TableBodyCell>
{/each} {/each}
</TableBodyRow> </TableBodyRow>
{/each} {/each}

@ -12,7 +12,7 @@
// "data:graph", "data:json", "data:array", "data:map", "data:xml", "data:table", "data:collection", "data:board", "data:grid", "data:geomap", // "data:graph", "data:json", "data:array", "data:map", "data:xml", "data:table", "data:collection", "data:board", "data:grid", "data:geomap",
// "e:email", "e:web", "e:http://[url of class in ontology]", "e:rdf" (read-only cache of RDF fetched from web2.0) // "e:email", "e:web", "e:http://[url of class in ontology]", "e:rdf" (read-only cache of RDF fetched from web2.0)
// "mc:text", "mc:link", "mc:card", "mc:pad", // "mc:text", "mc:link", "mc:card", "mc:pad",
// "doc:diagram","doc:chart", "doc:pdf", "doc:odf", "doc:latex", "doc:ps", "doc:music", "doc:maths", "doc:chemistry", "doc:braille", "doc:ancientscript", // "diagram","chart", "doc:pdf", "doc:odf", "doc:latex", "doc:ps", "doc:music", "doc:maths", "doc:chemistry", "doc:braille", "doc:ancientscript",
// "media:image", "media:reel", "media:album", "media:video", "media:audio", "media:song", "media:subtitle", "media:overlay", // "media:image", "media:reel", "media:album", "media:video", "media:audio", "media:song", "media:subtitle", "media:overlay",
// "social:channel", "social:stream", "social:contact", "social:event", "social:calendar", "social:scheduler", "social:reaction", "social:chatroom", // "social:channel", "social:stream", "social:contact", "social:event", "social:calendar", "social:scheduler", "social:reaction", "social:chatroom",
// "prod:task", "prod:project", "prod:issue", "prod:form", "prod:filling", "prod:cad", "prod:slides", "prod:question", "prod:answer", "prod:poll", "prod:vote" // "prod:task", "prod:project", "prod:issue", "prod:form", "prod:filling", "prod:cad", "prod:slides", "prod:question", "prod:answer", "prod:poll", "prod:vote"
@ -165,35 +165,24 @@ export const official_classes = {
}, },
"ng:compat": ["rdfs:Class"], "ng:compat": ["rdfs:Class"],
}, },
"schema:rdfs": { "schema": { // display with https://github.com/VisualDataWeb/WebVOWL
"ng:crdt": "Graph", "ng:crdt": "Graph",
"ng:n": "Schema - RDFS", "ng:n": "Schema - RDFS/OWL",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with RDFS", "ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with RDFS and/or OWL",
"ng:o": "n:g:z:json_ld_viewer", // default viewer "ng:o": "n:g:z:ontology_viewer", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor "ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": { "ng:x": {
"rdfs":true, "rdfs":true,
},
"ng:include": ["data:graph"],
"ng:compat": ["rdfs:*","class"],
},
"schema:owl": { // display with https://github.com/VisualDataWeb/WebVOWL
"ng:crdt": "Graph",
"ng:n": "Schema - OWL",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with OWL",
"ng:o": "n:g:z:owl_viewer", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": {
"owl":true, "owl":true,
}, },
"ng:include": ["data:graph"], "ng:include": ["data:graph"],
"ng:compat": ["owl:Ontology"], "ng:compat": ["rdfs:*","class","owl:Ontology"],
}, },
"schema:shacl": { "schema:shacl": {
"ng:crdt": "Graph", "ng:crdt": "Graph",
"ng:n": "Schema - SHACL", "ng:n": "Schema - SHACL",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with SHACL", "ng:a": "Define the rules for your data with SHACL",
"ng:o": "n:g:z:json_ld_viewer", // default viewer "ng:o": "n:g:z:ontology_viewer", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor "ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": { "ng:x": {
"sh":true, "sh":true,
@ -204,8 +193,8 @@ export const official_classes = {
"schema:shex": { "schema:shex": {
"ng:crdt": "Graph", "ng:crdt": "Graph",
"ng:n": "Schema - SHEX", "ng:n": "Schema - SHEX",
"ng:a": "Define the Schema, Ontology or Vocabulary for your data and the relations between them, with SHEX", "ng:a": "Define the rules for your data with SHEX",
"ng:o": "n:g:z:json_ld_viewer", // default viewer "ng:o": "n:g:z:ontology_viewer", // default viewer
"ng:w": "n:g:z:ontology_editor", // default editor "ng:w": "n:g:z:ontology_editor", // default editor
"ng:x": { "ng:x": {
"shex":true, "shex":true,
@ -426,13 +415,13 @@ export const official_classes = {
"ng:n": "Link", "ng:n": "Link",
"ng:a": "Link to a document. kept in Magic Carpet", "ng:a": "Link to a document. kept in Magic Carpet",
}, },
"plato/card": { "plato:card": {
"ng:crdt": "Graph", "ng:crdt": "Graph",
"ng:n": "Card", "ng:n": "Card",
"ng:a": "Card representation of a document", "ng:a": "Card representation of a document",
"ng:o": "n:g:z:card", "ng:o": "n:g:z:card",
}, },
"plato/pad": { "plato:pad": {
"ng:crdt": "Graph", "ng:crdt": "Graph",
"ng:n": "Pad", "ng:n": "Pad",
"ng:a": "Pad representation of a document", "ng:a": "Pad representation of a document",
@ -445,62 +434,62 @@ export const official_classes = {
"ng:o": "n:g:z:compose:viewer", "ng:o": "n:g:z:compose:viewer",
"ng:w": "n:g:z:compose:editor", "ng:w": "n:g:z:compose:editor",
}, },
"doc:diagram:mermaid" : { "diagram:mermaid" : {
"ng:crdt": "YText", "ng:crdt": "YText",
"ng:n": "Diagram - Mermaid", "ng:n": "Diagram - Mermaid",
"ng:a": "Describe Diagrams with Mermaid", "ng:a": "Describe Diagrams with Mermaid",
"ng:compat": ["file:iana:application:vnd.mermaid"] "ng:compat": ["file:iana:application:vnd.mermaid"]
}, },
"doc:diagram:drawio" : { "diagram:drawio" : {
"ng:crdt": "YXml", "ng:crdt": "YXml",
"ng:n": "Diagram - DrawIo", "ng:n": "Diagram - DrawIo",
"ng:a": "Draw Diagrams with DrawIo", "ng:a": "Draw Diagrams with DrawIo",
"ng:compat": ["file:iana:application:vnd.jgraph.mxfile","file:iana:application:x-drawio"] "ng:compat": ["file:iana:application:vnd.jgraph.mxfile","file:iana:application:x-drawio"]
}, },
"doc:diagram:graphviz" : { "diagram:graphviz" : {
"ng:crdt": "YText", "ng:crdt": "YText",
"ng:n": "Diagram - Graphviz", "ng:n": "Diagram - Graphviz",
"ng:a": "Describe Diagrams with Graphviz", "ng:a": "Describe Diagrams with Graphviz",
"ng:compat": ["file:iana:text:vnd.graphviz"] "ng:compat": ["file:iana:text:vnd.graphviz"]
}, },
"doc:diagram:excalidraw" : { "diagram:excalidraw" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Diagram - Excalidraw", "ng:n": "Diagram - Excalidraw",
"ng:a": "Collaborate on Diagrams with Excalidraw", "ng:a": "Collaborate on Diagrams with Excalidraw",
"ng:compat": ["file:iana:application:vnd.excalidraw+json"] "ng:compat": ["file:iana:application:vnd.excalidraw+json"]
}, },
"doc:diagram:gantt" : { //https://github.com/frappe/gantt "diagram:gantt" : { //https://github.com/frappe/gantt
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Diagram - Gantt", "ng:n": "Diagram - Gantt",
"ng:a": "Interactive gantt chart", "ng:a": "Interactive gantt chart",
"ng:compat": [] "ng:compat": []
}, },
"doc:diagram:flowchart" : { //https://github.com/adrai/flowchart.js "diagram:flowchart" : { //https://github.com/adrai/flowchart.js
"ng:crdt": "YText", "ng:crdt": "YText",
"ng:n": "Diagram - Flowchart", "ng:n": "Diagram - Flowchart",
"ng:a": "flow chart diagrams", "ng:a": "flow chart diagrams",
"ng:compat": [] "ng:compat": []
}, },
"doc:diagram:sequence" : { //https://github.com/bramp/js-sequence-diagrams "diagram:sequence" : { //https://github.com/bramp/js-sequence-diagrams
"ng:crdt": "YText", "ng:crdt": "YText",
"ng:n": "Diagram - Sequence", "ng:n": "Diagram - Sequence",
"ng:a": "sequence diagrams", "ng:a": "sequence diagrams",
"ng:compat": [] "ng:compat": []
}, },
// checkout https://www.mindmaps.app/ but it is AGPL // checkout https://www.mindmaps.app/ but it is AGPL
"doc:diagram:markmap" : { //https://github.com/markmap/markmap "diagram:markmap" : { //https://github.com/markmap/markmap
"ng:crdt": "YText", "ng:crdt": "YText",
"ng:n": "Diagram - Markmap", "ng:n": "Diagram - Markmap",
"ng:a": "mindmaps with markmap", "ng:a": "mindmaps with markmap",
"ng:compat": [] "ng:compat": []
}, },
"doc:diagram:mymind" : { //https://github.com/markmap/markmap "diagram:mymind" : { //https://github.com/markmap/markmap
"ng:crdt": "YText", // see MyMind format, MindMup JSON, FreeMind XML and MindMap Architect XML "ng:crdt": "YText", // see MyMind format, MindMup JSON, FreeMind XML and MindMap Architect XML
"ng:n": "Diagram - Mymind", "ng:n": "Diagram - Mymind",
"ng:a": "mindmaps with mymind", "ng:a": "mindmaps with mymind",
"ng:compat": [] // https://github.com/ondras/my-mind/wiki/Saving-and-loading#file-formats "ng:compat": [] // https://github.com/ondras/my-mind/wiki/Saving-and-loading#file-formats
}, },
"doc:diagram:jsmind" : { //https://github.com/hizzgdev/jsmind "diagram:jsmind" : { //https://github.com/hizzgdev/jsmind
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Diagram - jsmind", "ng:n": "Diagram - jsmind",
"ng:a": "mindmaps with jsmind", "ng:a": "mindmaps with jsmind",
@ -514,69 +503,69 @@ export const official_classes = {
// https://github.com/Rich-Harris/pancake // https://github.com/Rich-Harris/pancake
// https://github.com/williamngan/pts // https://github.com/williamngan/pts
// https://visjs.org/ // https://visjs.org/
"doc:viz:cytoscape" : { "viz:cytoscape" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Viz - Cytoscape", "ng:n": "Viz - Cytoscape",
"ng:a": "Graph theory (network) visualization", "ng:a": "Graph theory (network) visualization",
"ng:compat": [] // https://github.com/cytoscape/cytoscape.js "ng:compat": [] // https://github.com/cytoscape/cytoscape.js
}, },
"doc:viz:vega" : { "viz:vega" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Viz - Vega", "ng:n": "Viz - Vega",
"ng:a": "Grammar for interactive graphics", "ng:a": "Grammar for interactive graphics",
"ng:compat": [] // https://vega.github.io/vega-lite/docs/ https://github.com/vega/editor "ng:compat": [] // https://vega.github.io/vega-lite/docs/ https://github.com/vega/editor
}, },
"doc:viz:vizzu" : { "viz:vizzu" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Viz - Vizzu", "ng:n": "Viz - Vizzu",
"ng:a": "Animated data visualizations and data stories", "ng:a": "Animated data visualizations and data stories",
"ng:compat": [] // https://github.com/vizzuhq/vizzu-lib "ng:compat": [] // https://github.com/vizzuhq/vizzu-lib
}, },
"doc:viz:plotly" : { //https://github.com/plotly/plotly.js "viz:plotly" : { //https://github.com/plotly/plotly.js
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Viz - Plotly", "ng:n": "Viz - Plotly",
"ng:a": "Declarative charts", "ng:a": "Declarative charts",
"ng:compat": [] // https://github.com/cytoscape/cytoscape.js "ng:compat": [] // https://github.com/cytoscape/cytoscape.js
}, },
"doc:viz:avail" : { "viz:avail" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Viz - Avail", "ng:n": "Viz - Avail",
"ng:a": "Time Data Availability Visualization", "ng:a": "Time Data Availability Visualization",
"ng:compat": [] // https://github.com/flrs/visavail "ng:compat": [] // https://github.com/flrs/visavail
}, },
"doc:chart:frappecharts" : { "chart:frappecharts" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - Frappe", "ng:n": "Charts - Frappe",
"ng:a": "GitHub-inspired responsive charts", "ng:a": "GitHub-inspired responsive charts",
"ng:compat": [] // https://github.com/frappe/charts "ng:compat": [] // https://github.com/frappe/charts
}, },
"doc:chart:financial" : { "chart:financial" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - Financial", "ng:n": "Charts - Financial",
"ng:a": "Financial charts", "ng:a": "Financial charts",
"ng:compat": [] //https://github.com/tradingview/lightweight-charts "ng:compat": [] //https://github.com/tradingview/lightweight-charts
}, },
// have a look at https://github.com/cube-js/cube and https://awesome.cube.dev/ and https://frappe.io/products // have a look at https://github.com/cube-js/cube and https://awesome.cube.dev/ and https://frappe.io/products
"doc:chart:apexcharts" : { "chart:apexcharts" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - ApexCharts", "ng:n": "Charts - ApexCharts",
"ng:a": "Interactive data visualizations", "ng:a": "Interactive data visualizations",
"ng:compat": [] // https://github.com/apexcharts/apexcharts.js "ng:compat": [] // https://github.com/apexcharts/apexcharts.js
}, },
//realtime data with https://github.com/square/cubism //realtime data with https://github.com/square/cubism
"doc:chart:billboard" : { "chart:billboard" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - BillBoard", "ng:n": "Charts - BillBoard",
"ng:a": "Interactive data visualizations based on D3", "ng:a": "Interactive data visualizations based on D3",
"ng:compat": [] // https://github.com/naver/billboard.js "ng:compat": [] // https://github.com/naver/billboard.js
}, },
"doc:chart:echarts" : { "chart:echarts" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - ECharts", "ng:n": "Charts - ECharts",
"ng:a": "Interactive charting and data visualization with Apache ECharts", "ng:a": "Interactive charting and data visualization with Apache ECharts",
"ng:compat": [] // https://github.com/apache/echarts "ng:compat": [] // https://github.com/apache/echarts
}, },
"doc:chart:chartjs" : { "chart:chartjs" : {
"ng:crdt": "Automerge", "ng:crdt": "Automerge",
"ng:n": "Charts - Chart.js", "ng:n": "Charts - Chart.js",
"ng:a": "Simple yet flexible charting for designers & developers with Chart.js", "ng:a": "Simple yet flexible charting for designers & developers with Chart.js",

@ -14,7 +14,7 @@
branch_subscribe, branch_subscribe,
active_session, active_session,
cannot_load_offline, cannot_load_offline,
online, open_doc_popup
} from "../store"; } from "../store";
import { import {
@ -42,7 +42,7 @@
const inview_options = {};//{rootMargin: "-44px"}; const inview_options = {};//{rootMargin: "-44px"};
function openEditHeader() { function openEditHeader() {
//TODO open_doc_popup("header");
} }
</script> </script>

@ -34,7 +34,6 @@
import BranchIcon from "./icons/BranchIcon.svelte"; import BranchIcon from "./icons/BranchIcon.svelte";
import Message from "./components/Message.svelte"; import Message from "./components/Message.svelte";
import Signature from "./popups/Signature.svelte";
import { import {
get, get,
} from "svelte/store"; } from "svelte/store";
@ -47,11 +46,11 @@
has_editor_chat, all_files_count, all_comments_count, hideMenu, show_modal_menu, show_modal_create, has_editor_chat, all_files_count, all_comments_count, hideMenu, show_modal_menu, show_modal_create,
cur_tab_branch_nuri, cur_tab_doc_can_edit, cur_tab_doc_is_member, cur_tab_right_pane, cur_tab_folders_pane, cur_tab_branch_nuri, cur_tab_doc_can_edit, cur_tab_doc_is_member, cur_tab_right_pane, cur_tab_folders_pane,
cur_tab_toc_pane, cur_tab_show_menu, cur_tab_branch_has_discrete, cur_tab_graph_or_discrete, cur_tab_view_or_edit, show_spinner, cur_tab_toc_pane, cur_tab_show_menu, cur_tab_branch_has_discrete, cur_tab_graph_or_discrete, cur_tab_view_or_edit, show_spinner,
in_private_store, show_doc_popup, cur_doc_popup, open_doc_popup } from "../tab"; in_private_store, show_doc_popup, cur_doc_popup } from "../tab";
import { import {
active_session, redirect_after_login, toasts, check_has_camera, toast_error, active_session, redirect_after_login, toasts, check_has_camera, toast_error,
reset_toasts, redirect_if_wallet_is, active_wallet, reset_toasts, redirect_if_wallet_is, active_wallet,
display_error, openModalCreate display_error, openModalCreate, open_doc_popup
} from "../store"; } from "../store";
import ZeraIcon from "./icons/ZeraIcon.svelte"; import ZeraIcon from "./icons/ZeraIcon.svelte";
import ng from "../api"; import ng from "../api";
@ -300,33 +299,33 @@
]; ];
const create_chart_items = [ const create_chart_items = [
"doc:chart:frappecharts", "chart:frappecharts",
"doc:chart:financial", "chart:financial",
"doc:chart:apexcharts", "chart:apexcharts",
"doc:chart:billboard", "chart:billboard",
"doc:chart:echarts", "chart:echarts",
"doc:chart:chartjs", "chart:chartjs",
]; ];
const create_viz_items = [ const create_viz_items = [
"doc:viz:cytoscape", "viz:cytoscape",
"doc:viz:vega", "viz:vega",
"doc:viz:vizzu", "viz:vizzu",
"doc:viz:plotly", "viz:plotly",
"doc:viz:avail", "viz:avail",
]; ];
const create_diagram_items = [ const create_diagram_items = [
"doc:diagram:mermaid", "diagram:mermaid",
"doc:diagram:drawio", "diagram:drawio",
"doc:diagram:graphviz", "diagram:graphviz",
"doc:diagram:excalidraw", "diagram:excalidraw",
"doc:diagram:gantt", "diagram:gantt",
"doc:diagram:flowchart", "diagram:flowchart",
"doc:diagram:sequence", "diagram:sequence",
"doc:diagram:markmap", "diagram:markmap",
"doc:diagram:mymind", "diagram:mymind",
"doc:diagram:jsmind", "diagram:jsmind",
]; ];
const create_doc_items = [ const create_doc_items = [
@ -367,6 +366,19 @@
"app:n:xxx.xx.xx", "app:n:xxx.xx.xx",
]; ];
import Signature from "./popups/Signature.svelte";
import Header from "./popups/Header.svelte";
const doc_popups = {
"signature": Signature,
"header": Header,
}
const doc_popups_size = {
"signature": "xs",
"header": "md",
}
let top; let top;
let shareMenu; let shareMenu;
let toolsMenu; let toolsMenu;
@ -428,7 +440,7 @@
const openAction = (action:string) => { const openAction = (action:string) => {
hideMenu(); hideMenu();
if (doc_popups[action]) open_doc_popup(action); open_doc_popup(action);
} }
const openPane = (pane:string) => { const openPane = (pane:string) => {
@ -564,10 +576,6 @@
"mc":Sparkles, "mc":Sparkles,
}; };
const doc_popups = {
"signature": Signature,
}
let destination = "store"; let destination = "store";
$: destination = $cur_tab_branch_nuri === "" ? "mc" : destination == "mc" ? "store" : destination; $: destination = $cur_tab_branch_nuri === "" ? "mc" : destination == "mc" ? "store" : destination;
@ -755,7 +763,8 @@
{/if} {/if}
{#if $cur_tab_doc_can_edit} {#if $cur_tab_doc_can_edit}
<MenuItem title={$t("doc.menu.items.new_block.desc")} clickable={ ()=> openAction("new_block") }> <!-- ()=> openAction("new_block") -->
<MenuItem title={$t("doc.menu.items.new_block.desc")} clickable={ undefined }>
<PlusCircle <PlusCircle
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -764,7 +773,8 @@
</MenuItem> </MenuItem>
{/if} {/if}
{#if $has_editor_chat} {#if $has_editor_chat}
<MenuItem title={$t("doc.menu.items.editor_chat.desc")} selected={$cur_tab_right_pane == "chat"} clickable={ ()=> openPane("chat") }> <!-- ()=> openPane("chat") -->
<MenuItem title={$t("doc.menu.items.editor_chat.desc")} selected={$cur_tab_right_pane == "chat"} clickable={ undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["chat"]} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["chat"]} />
<span class="ml-3">{$t("doc.menu.items.editor_chat.label")}</span> <span class="ml-3">{$t("doc.menu.items.editor_chat.label")}</span>
</MenuItem> </MenuItem>
@ -794,14 +804,16 @@
</MenuItem> </MenuItem>
{#if open_share } {#if open_share }
{#each share_items as share} {#each share_items as share}
<MenuItem title={$t(`doc.menu.items.${share.n}.desc`)} extraClass="submenu" clickable={ () => openShare(share.n) }> <!-- () => openShare(share.n) -->
<MenuItem title={$t(`doc.menu.items.${share.n}.desc`)} extraClass="submenu" clickable={ undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={share.i} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={share.i} />
<span class="ml-3">{$t(`doc.menu.items.${share.n}.label`)}</span> <span class="ml-3">{$t(`doc.menu.items.${share.n}.label`)}</span>
</MenuItem> </MenuItem>
{/each} {/each}
{/if} {/if}
{:else} {:else}
<MenuItem title={$t(`doc.menu.items.download.desc`)} clickable={ () => openShare("download") }> <!-- () => openShare("download") -->
<MenuItem title={$t(`doc.menu.items.download.desc`)} clickable={ undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={DocumentArrowDown} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={DocumentArrowDown} />
<span class="ml-3">{$t(`doc.menu.items.download.label`)}</span> <span class="ml-3">{$t(`doc.menu.items.download.label`)}</span>
</MenuItem> </MenuItem>
@ -823,37 +835,37 @@
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["history"]} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["history"]} />
<span class="ml-3">{$t("doc.menu.items.history.label")}</span> <span class="ml-3">{$t("doc.menu.items.history.label")}</span>
</MenuItem> </MenuItem>
<!-- find -->
<MenuItem title={$t("doc.menu.items.find.desc")} clickable={ find }> <MenuItem title={$t("doc.menu.items.find.desc")} clickable={ undefined }>
<MagnifyingGlass <MagnifyingGlass
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
/> />
<span class="ml-3">{$t("doc.menu.items.find.label")}</span> <span class="ml-3">{$t("doc.menu.items.find.label")}</span>
</MenuItem> </MenuItem>
<!-- bookmark -->
<MenuItem title={$t("doc.menu.items.bookmark.desc")} clickable={ bookmark }> <MenuItem title={$t("doc.menu.items.bookmark.desc")} clickable={ undefined }>
<Bookmark <Bookmark
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
/> />
<span class="ml-3">{$t("doc.menu.items.bookmark.label")}</span> <span class="ml-3">{$t("doc.menu.items.bookmark.label")}</span>
</MenuItem> </MenuItem>
<!-- annotate -->
<MenuItem title={$t("doc.menu.items.annotate.desc")} clickable={ annotate }> <MenuItem title={$t("doc.menu.items.annotate.desc")} clickable={ undefined }>
<ChatBubbleLeftEllipsis <ChatBubbleLeftEllipsis
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
/> />
<span class="ml-3">{$t("doc.menu.items.annotate.label")}</span> <span class="ml-3">{$t("doc.menu.items.annotate.label")}</span>
</MenuItem> </MenuItem>
<!-- ()=> openPane("info") -->
<MenuItem title={$t("doc.menu.items.info.desc")} selected={$cur_tab_right_pane == "info"} clickable={ ()=> openPane("info") }> <MenuItem title={$t("doc.menu.items.info.desc")} selected={$cur_tab_right_pane == "info"} clickable={ undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["info"]} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["info"]} />
<span class="ml-3">{$t("doc.menu.items.info.label")}</span> <span class="ml-3">{$t("doc.menu.items.info.label")}</span>
</MenuItem> </MenuItem>
<!-- ()=> openAction("notifs") -->
<MenuItem title={$t("doc.menu.items.notifs.desc")} clickable={ ()=> openAction("notifs") }> <MenuItem title={$t("doc.menu.items.notifs.desc")} clickable={ undefined }>
<Bell <Bell
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -861,7 +873,8 @@
<span class="ml-3">{$t("doc.menu.items.notifs.label")}</span> <span class="ml-3">{$t("doc.menu.items.notifs.label")}</span>
</MenuItem> </MenuItem>
{#if $cur_tab_doc_is_member && !$in_private_store} {#if $cur_tab_doc_is_member && !$in_private_store}
<MenuItem title={$t("doc.menu.items.permissions.desc")} clickable={ ()=> openAction("permissions") }> <!-- ()=> openAction("permissions") -->
<MenuItem title={$t("doc.menu.items.permissions.desc")} clickable={ undefined }>
<LockOpen <LockOpen
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -869,7 +882,8 @@
<span class="ml-3">{$t("doc.menu.items.permissions.label")}</span> <span class="ml-3">{$t("doc.menu.items.permissions.label")}</span>
</MenuItem> </MenuItem>
{/if} {/if}
<MenuItem title={$t("doc.menu.items.settings.desc")} clickable={ ()=> openAction("settings") }> <!-- ()=> openAction("settings") -->
<MenuItem title={$t("doc.menu.items.settings.desc")} clickable={ undefined }>
<Cog6Tooth <Cog6Tooth
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -887,7 +901,8 @@
{#if open_tools } {#if open_tools }
{#each tools_items as tool} {#each tools_items as tool}
{#if !$in_private_store || tool.n !== "signature" } {#if !$in_private_store || tool.n !== "signature" }
<MenuItem title={$t(`doc.menu.items.${tool.n}.desc`)} extraClass="submenu" clickable={ () => openAction(tool.n) }> <!-- () => openAction(tool.n) -->
<MenuItem title={$t(`doc.menu.items.${tool.n}.desc`)} extraClass="submenu" clickable={ tool.n === "signature" ? () => openAction(tool.n) : undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={tool.i} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white " variation="outline" color="currentColor" icon={tool.i} />
<span class="ml-3">{$t(`doc.menu.items.${tool.n}.label`)}</span> <span class="ml-3">{$t(`doc.menu.items.${tool.n}.label`)}</span>
</MenuItem> </MenuItem>
@ -895,11 +910,13 @@
{/each} {/each}
{/if} {/if}
{/if} {/if}
<MenuItem title={$t("doc.menu.items.mc.desc")} selected={$cur_tab_right_pane == "mc"} clickable={ ()=> openPane("mc") }> <!-- ()=> openPane("mc") -->
<MenuItem title={$t("doc.menu.items.mc.desc")} selected={$cur_tab_right_pane == "mc"} clickable={ undefined }>
<Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["mc"]} /> <Icon tabindex="-1" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" variation="outline" color="currentColor" icon={pane_items["mc"]} />
<span class="ml-3">{$t("doc.menu.items.mc.label")}</span> <span class="ml-3">{$t("doc.menu.items.mc.label")}</span>
</MenuItem> </MenuItem>
<MenuItem title={$t("doc.menu.items.archive.desc")} clickable={ ()=> openArchive() }> <!-- ()=> openArchive() -->
<MenuItem title={$t("doc.menu.items.archive.desc")} clickable={ undefined }>
<ArchiveBox <ArchiveBox
tabindex="-1" tabindex="-1"
class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white" class="w-7 h-7 text-gray-700 focus:outline-none dark:text-white"
@ -945,12 +962,12 @@
<Modal class="document-popup" <Modal class="document-popup"
outsideclose outsideclose
bind:open={$show_doc_popup} bind:open={$show_doc_popup}
size = 'xs' size = {doc_popups_size[$cur_doc_popup]}
placement = 'center' placement = 'center'
defaultClass="bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-400 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative flex flex-col mx-auto w-full" defaultClass="bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-400 rounded-lg border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md relative flex flex-col mx-auto w-full"
backdropClass="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 popup-bg-modal" backdropClass="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 popup-bg-modal"
> >
<svelte:component this={doc_popups[$cur_doc_popup]}/> {#if doc_popups[$cur_doc_popup]}<svelte:component this={doc_popups[$cur_doc_popup]}/>{/if}
</Modal> </Modal>
<Modal class="menu-modal" <Modal class="menu-modal"
outsideclose outsideclose
@ -1121,7 +1138,7 @@
<div style="padding:0;" bind:this={createMenu.chart}></div> <div style="padding:0;" bind:this={createMenu.chart}></div>
<MenuItem title={$t("doc.chart")} dropdown={createMenuOpened.chart} clickable={ () => { createMenuOpened.chart = !createMenuOpened.chart; scrollToCreateMenu("chart"); } }> <MenuItem title={$t("doc.chart")} dropdown={createMenuOpened.chart} clickable={ () => { createMenuOpened.chart = !createMenuOpened.chart; scrollToCreateMenu("chart"); } }>
<DataClassIcon dataClass="doc:chart" {config}/> <DataClassIcon dataClass="chart" {config}/>
<span class="ml-3">{$t("doc.chart")}</span> <span class="ml-3">{$t("doc.chart")}</span>
</MenuItem> </MenuItem>
{#if createMenuOpened.chart } {#if createMenuOpened.chart }
@ -1136,7 +1153,7 @@
<div style="padding:0;" bind:this={createMenu.viz}></div> <div style="padding:0;" bind:this={createMenu.viz}></div>
<MenuItem title={$t("doc.viz")} dropdown={createMenuOpened.viz} clickable={ () => { createMenuOpened.viz = !createMenuOpened.viz; scrollToCreateMenu("viz"); } }> <MenuItem title={$t("doc.viz")} dropdown={createMenuOpened.viz} clickable={ () => { createMenuOpened.viz = !createMenuOpened.viz; scrollToCreateMenu("viz"); } }>
<DataClassIcon dataClass="doc:viz" {config}/> <DataClassIcon dataClass="viz" {config}/>
<span class="ml-3">{$t("doc.viz")}</span> <span class="ml-3">{$t("doc.viz")}</span>
</MenuItem> </MenuItem>
{#if createMenuOpened.viz } {#if createMenuOpened.viz }
@ -1151,7 +1168,7 @@
<div style="padding:0;" bind:this={createMenu.diagram}></div> <div style="padding:0;" bind:this={createMenu.diagram}></div>
<MenuItem title={$t("doc.diagram")} dropdown={createMenuOpened.diagram} clickable={ () => { createMenuOpened.diagram = !createMenuOpened.diagram; scrollToCreateMenu("diagram"); } }> <MenuItem title={$t("doc.diagram")} dropdown={createMenuOpened.diagram} clickable={ () => { createMenuOpened.diagram = !createMenuOpened.diagram; scrollToCreateMenu("diagram"); } }>
<DataClassIcon dataClass="doc:diagram" {config}/> <DataClassIcon dataClass="diagram" {config}/>
<span class="ml-3">{$t("doc.diagram")}</span> <span class="ml-3">{$t("doc.diagram")}</span>
</MenuItem> </MenuItem>
{#if createMenuOpened.diagram } {#if createMenuOpened.diagram }

@ -116,8 +116,8 @@
"e:link": Link, "e:link": Link,
"mc:text": Bars3BottomLeft, "mc:text": Bars3BottomLeft,
"mc:link": Link, "mc:link": Link,
"plato/card": Clipboard, "plato:card": Clipboard,
"plato/pad": Square2Stack, "plato:pad": Square2Stack,
"media:image": Photo, "media:image": Photo,
"media:reel": VideoCamera, "media:reel": VideoCamera,
"media:video": Film, "media:video": Film,
@ -171,9 +171,9 @@
"app:": StopCircle, "app:": StopCircle,
"query:": RocketLaunch, "query:": RocketLaunch,
"data:": CircleStack, "data:": CircleStack,
"doc:diagram": DocumentChartBar, "diagram": DocumentChartBar,
"doc:chart": ChartPie, "chart": ChartPie,
"doc:viz": ArrowTrendingUp, "viz": ArrowTrendingUp,
"doc:": ClipboardDocumentList, "doc:": ClipboardDocumentList,
file: Document, file: Document,
}; };

@ -0,0 +1,70 @@
<!--
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import {
branch_subscribe,
active_session,
toast_error,
toast_success,
display_error,
online,
change_header
} from "../../store";
import {
cur_tab,
show_doc_popup
} from "../../tab";
import { get } from "svelte/store";
import { onMount, onDestroy, tick } from "svelte";
import ng from "../../api";
import { t } from "svelte-i18n";
import {
CheckCircle
} from "svelte-heros-v2";
import {
} from "flowbite-svelte";
let is_tauri = import.meta.env.TAURI_PLATFORM;
onMount(()=>{
title = $cur_tab.doc.title;
about = $cur_tab.doc.description;
});
async function update_header() {
await change_header(title,about);
$show_doc_popup = false;
}
let title;
let about;
</script>
<div class="flex flex-col">
<span class="font-bold text-xl mb-3">{$t("doc.header.buttons.edit_intro")}</span>
{$t("doc.header.doc.title")} :
<input placeholder="Enter the title of the document" bind:value={title} class="mb-3"/>
{$t("doc.header.doc.about")} :
<textarea rows=6 class="my-4 col-span-6 pr-11 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Enter the introduction" bind:value={about}/>
<button
style="width:120px;"
on:click|once={update_header}
class="mt-4 text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-3 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
<CheckCircle class="w-8 h-8 mr-3"/>
{$t("doc.header.buttons.save")}
</button>
</div>

@ -21,7 +21,7 @@
"chart": "Chart", "chart": "Chart",
"viz": "Visualization", "viz": "Visualization",
"diagram": "Diagram", "diagram": "Diagram",
"other": "Other file formats", "other": "Other formats",
"data": "Data", "data": "Data",
"code": "Code", "code": "Code",
"apps": "Apps", "apps": "Apps",
@ -71,7 +71,16 @@
"groups": "Groups", "groups": "Groups",
"channels": "Channels", "channels": "Channels",
"inbox": "Inbox", "inbox": "Inbox",
"chat": "Chat" "chat": "Chat",
"save": "Save"
},
"doc":{
"title": "Title",
"about": "Introduction"
},
"profile": {
"title": "Name",
"about": "Bio"
} }
}, },
"signature": { "signature": {

@ -357,7 +357,7 @@
{/if} {/if}
<!-- Remove Wallet --> <!-- Remove Wallet -->
<li <!-- <li
tabindex="0" tabindex="0"
role="menuitem" role="menuitem"
class="text-left flex items-center p-2 text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700" class="text-left flex items-center p-2 text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
@ -372,7 +372,7 @@
</div> </div>
<span class="ml-3">{$t("pages.wallet_info.remove_wallet")}</span <span class="ml-3">{$t("pages.wallet_info.remove_wallet")}</span
> >
</li> </li> -->
<!-- Confirm Remove Wallet Modal --> <!-- Confirm Remove Wallet Modal -->
<Modal <Modal
autoclose autoclose

@ -147,6 +147,9 @@
async function gotWallet(event) { async function gotWallet(event) {
try { try {
if (importing) { if (importing) {
step = "loggedin";
$redirect_after_login=undefined;
$redirect_if_wallet_is=undefined;
let in_memory = !event.detail.trusted; let in_memory = !event.detail.trusted;
//console.log("IMPORTING", in_memory, event.detail.wallet, wallet); //console.log("IMPORTING", in_memory, event.detail.wallet, wallet);
let client = await ng.wallet_import( let client = await ng.wallet_import(
@ -188,6 +191,7 @@
if (importing) {wallet = undefined;} if (importing) {wallet = undefined;}
importing = false; importing = false;
error = e; error = e;
step = "open";
return; return;
} }
//await tick(); //await tick();
@ -252,6 +256,25 @@
{$t("buttons.start_over")} {$t("buttons.start_over")}
</button> </button>
</div> </div>
{:else if step == "loggedin"}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
{@html $t("pages.wallet_login.logged_in")}...
<svg
class="my-10 h-16 w-16 mx-auto"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/>
</svg>
</div>
{:else if $wallet_from_import} {:else if $wallet_from_import}
<!-- Imported a wallet --> <!-- Imported a wallet -->
@ -437,25 +460,6 @@
</a> </a>
</div> </div>
</div> </div>
{:else if step == "loggedin"}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
{@html $t("pages.wallet_login.logged_in")}...
<svg
class="my-10 h-16 w-16 mx-auto"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/>
</svg>
</div>
{/if} {/if}
</CenteredLayout> </CenteredLayout>
</div> </div>

@ -17,7 +17,7 @@ import {
} from "svelte/store"; } from "svelte/store";
import { register, init, locale, format } from "svelte-i18n"; import { register, init, locale, format } from "svelte-i18n";
import ng from "./api"; import ng from "./api";
import { persistent_error, update_class, update_branch_display, open_branch, tab_update, change_nav_bar, cur_branch, cur_tab, show_modal_create, save, nav_bar,in_memory_save } from "./tab"; import { persistent_error, update_class, update_branch_display, open_branch, tab_update, change_nav_bar, cur_branch, cur_tab, show_modal_create, save, nav_bar,in_memory_save, cur_doc_popup, show_doc_popup } from "./tab";
import { encode } from "./base64url"; import { encode } from "./base64url";
let all_branches = {}; let all_branches = {};
@ -423,6 +423,43 @@ export const discrete_update = async (update, crdt, heads) => {
// the editor then process those updates and calls live_discrete_update // the editor then process those updates and calls live_discrete_update
} }
export const open_doc_popup = async (popup_name) => {
await reset_toasts();
cur_doc_popup.set(popup_name);
show_doc_popup.set(true);
}
export const change_header = async (title_, about_) => {
let session = get(active_session);
if (!session) {
persistent_error(get(cur_branch), {
title: get(format)("doc.errors.no_session"),
desc: get(format)("doc.errors_details.no_session")
});
throw new Error("no session");
}
let nuri = "did:ng:"+get(cur_tab).doc.nuri+":"+get(cur_tab).store.overlay;
let title = undefined;
let about = undefined;
if ( get(cur_tab).doc.title != title_ ) {
title = title_;
}
if ( get(cur_tab).doc.description != about_ ) {
about = about_;
}
if (title === undefined && about === undefined) {
//console.log("identical");
return;
}
try {
await ng.update_header(session.session_id, nuri, title, about);
}
catch (e) {
toast_error(display_error(e));
}
}
export const live_discrete_update = async (update, crdt, heads) => { export const live_discrete_update = async (update, crdt, heads) => {
// send directly to verifier with AppRequest Update // send directly to verifier with AppRequest Update
let session = get(active_session); let session = get(active_session);
@ -552,7 +589,7 @@ export const branch_subscribe = function(nuri:string, in_tab:boolean) {
req.V0.session_id = session.session_id; req.V0.session_id = session.session_id;
unsub = await ng.app_request_stream(req, unsub = await ng.app_request_stream(req,
async (response) => { async (response) => {
//console.log("GOT APP RESPONSE", response); console.log("GOT APP RESPONSE", response);
if (response.V0.TabInfo) { if (response.V0.TabInfo) {
tab_update(nuri, ($cur_tab) => { tab_update(nuri, ($cur_tab) => {
if (response.V0.TabInfo.branch?.id) { if (response.V0.TabInfo.branch?.id) {
@ -568,15 +605,21 @@ export const branch_subscribe = function(nuri:string, in_tab:boolean) {
if (response.V0.TabInfo.doc?.nuri) { if (response.V0.TabInfo.doc?.nuri) {
$cur_tab.doc.nuri = response.V0.TabInfo.doc.nuri; $cur_tab.doc.nuri = response.V0.TabInfo.doc.nuri;
} }
if (response.V0.TabInfo.doc?.can_edit) { if (typeof response.V0.TabInfo.doc?.can_edit === "boolean" ) {
$cur_tab.doc.can_edit = response.V0.TabInfo.doc.can_edit; $cur_tab.doc.can_edit = response.V0.TabInfo.doc.can_edit;
} }
if (response.V0.TabInfo.doc?.is_store) { if (typeof response.V0.TabInfo.doc?.is_store === "boolean") {
$cur_tab.doc.is_store = response.V0.TabInfo.doc.is_store; $cur_tab.doc.is_store = response.V0.TabInfo.doc.is_store;
} }
if (response.V0.TabInfo.doc?.is_member) { if (response.V0.TabInfo.doc?.is_member) {
$cur_tab.doc.is_member = response.V0.TabInfo.doc.is_member; $cur_tab.doc.is_member = response.V0.TabInfo.doc.is_member;
} }
if (response.V0.TabInfo.doc?.title !== undefined && response.V0.TabInfo.doc?.title !== null) {
$cur_tab.doc.title = response.V0.TabInfo.doc.title;
}
if (response.V0.TabInfo.doc?.description !== undefined && response.V0.TabInfo.doc?.description !== null) {
$cur_tab.doc.description = response.V0.TabInfo.doc.description;
}
if (response.V0.TabInfo.store?.overlay) { if (response.V0.TabInfo.store?.overlay) {
$cur_tab.store.overlay = response.V0.TabInfo.store.overlay; $cur_tab.store.overlay = response.V0.TabInfo.store.overlay;
} }
@ -643,13 +686,17 @@ export const branch_subscribe = function(nuri:string, in_tab:boolean) {
onUpdate(response.V0.Patch.discrete); onUpdate(response.V0.Patch.discrete);
} }
if (response.V0.Patch.graph) { if (response.V0.Patch.graph) {
//console.log(response.V0.Patch.graph)
let duplicates = []; let duplicates = [];
for (let i = 0; i < old.graph.length; i++) { for (let i = 0; i < old.graph.length; i++) {
if (response.V0.Patch.graph.inserts.includes(old.graph[i])) { if (response.V0.Patch.graph.inserts.includes(old.graph[i])) {
duplicates.push(old.graph[i]) duplicates.push(old.graph[i])
} else } else {
if (response.V0.Patch.graph.removes.includes(old.graph[i])) {//TODO: optimization: remove this triple from the removes list. //console.log("remove?", i, old.graph[i], JSON.stringify(old.graph))
old.graph.splice(i, 1); if (response.V0.Patch.graph.removes.includes(old.graph[i])) {//TODO: optimization: remove this triple from the removes list.
old.graph.splice(i, 1);
//console.log("yes",JSON.stringify(old.graph))
}
} }
} }
for (const insert of response.V0.Patch.graph.inserts){ for (const insert of response.V0.Patch.graph.inserts){

@ -220,13 +220,17 @@ td.hljs {
.container3 { .container3 {
margin: 0; margin: 0;
min-width: 280px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
} }
.container3 aside {
width: 20rem !important;
}
div[role="alert"] div { div[role="alert"] div {
display: block; display: block;
} }

@ -208,11 +208,6 @@ export const show_doc_popup = writable(false);
export const cur_doc_popup = writable(""); export const cur_doc_popup = writable("");
export const show_modal_create = writable(false); export const show_modal_create = writable(false);
export const open_doc_popup = (popup_name) => {
cur_doc_popup.set(popup_name);
show_doc_popup.set(true);
}
export const in_memory_graph = writable(""); export const in_memory_graph = writable("");
export const in_memory_discrete = writable(""); export const in_memory_discrete = writable("");
@ -351,7 +346,7 @@ export const cur_branch = writable("");
export const cur_tab = derived([cur_branch, all_tabs], ([cb, all]) => {return all[cb];}); export const cur_tab = derived([cur_branch, all_tabs], ([cb, all]) => {return all[cb];});
export const can_have_header = derived(cur_tab, ($cur_tab) => { export const can_have_header = derived(cur_tab, ($cur_tab) => {
return !($cur_tab.doc.is_store && ( $cur_tab.store.store_type === "private" || $cur_tab.store.store_type === "dialog")); return !($cur_tab.doc.is_store); // && ( $cur_tab.store.store_type === "private" || $cur_tab.store.store_type === "dialog"));
}); });
export const cur_tab_branch_nuri = derived(cur_tab, ($cur_tab) => { export const cur_tab_branch_nuri = derived(cur_tab, ($cur_tab) => {
return $cur_tab.branch.nuri; return $cur_tab.branch.nuri;

@ -210,16 +210,16 @@ export const official_apps = {
"ng:g": "n:g:z:ontology_editor", "ng:g": "n:g:z:ontology_editor",
"ng:b": "JsonLdEditor", "ng:b": "JsonLdEditor",
"ng:o": [], "ng:o": [],
"ng:w": ["schema:*"], "ng:w": ["schema*"],
}, },
"n:g:z:owl_viewer": { "n:g:z:ontology_viewer": {
"ng:n": "OWL Ontology", "ng:n": "Ontology",
"ng:a": "View the OWL Ontology", "ng:a": "View the OWL Ontology",
"ng:c": "app", "ng:c": "app",
"ng:u": "ontology_viewer",//favicon. can be a did:ng:j "ng:u": "ontology_viewer",//favicon. can be a did:ng:j
"ng:g": "n:g:z:owl_viewer", "ng:g": "n:g:z:ontology_viewer",
"ng:b": "OwlViewer", // display with https://github.com/VisualDataWeb/WebVOWL "ng:b": "OntologyViewer", // display with https://github.com/VisualDataWeb/WebVOWL
"ng:o": ["schema:owl"], "ng:o": ["schema*"],
"ng:w": [], "ng:w": [],
}, },
"n:g:z:sparql:invoke": { // displayed with highlight.js https://github.com/highlightjs/highlightjs-turtle/tree/master "n:g:z:sparql:invoke": { // displayed with highlight.js https://github.com/highlightjs/highlightjs-turtle/tree/master
@ -313,7 +313,7 @@ export const official_apps = {
"ng:u": "source",//favicon. can be a did:ng:j "ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:xml", "ng:g": "n:g:z:crdt_source_viewer:xml",
"ng:b": "XmlSource", // displayed with highlight.js , with option to download "ng:b": "XmlSource", // displayed with highlight.js , with option to download
"ng:o": ["post:rich","post:md","post:html","page","data:xml", "doc:diagram:drawio"], "ng:o": ["post:rich","post:md","post:html","page","data:xml", "diagram:drawio"],
"ng:w": [], "ng:w": [],
implemented: true, implemented: true,
}, },
@ -335,7 +335,7 @@ export const official_apps = {
"ng:u": "source",//favicon. can be a did:ng:j "ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:json", "ng:g": "n:g:z:crdt_source_viewer:json",
"ng:b": "AutomergeJsonSource", // displayed with highlight.js , with option to download "ng:b": "AutomergeJsonSource", // displayed with highlight.js , with option to download
"ng:o": ["data:json", "data:table", "doc:diagram:jsmind", "doc:diagram:gantt", "doc:diagram:excalidraw", "doc:viz:*", "doc:chart:*", "prod:cad"], "ng:o": ["data:json", "data:table", "diagram:jsmind", "diagram:gantt", "diagram:excalidraw", "viz:*", "chart:*", "prod:cad"],
"ng:w": [], "ng:w": [],
"full_width": true, "full_width": true,
implemented: true, implemented: true,
@ -369,8 +369,8 @@ export const official_apps = {
"ng:u": "source",//favicon. can be a did:ng:j "ng:u": "source",//favicon. can be a did:ng:j
"ng:g": "n:g:z:crdt_source_viewer:text", "ng:g": "n:g:z:crdt_source_viewer:text",
"ng:b": "TextViewer", // displayed with highlight.js , with option to download and copy paste "ng:b": "TextViewer", // displayed with highlight.js , with option to download and copy paste
"ng:o": ["post:asciidoc", "service*", "contract", "query:sparql*","query:graphql","doc:diagram:mermaid","doc:diagram:graphviz","doc:diagram:flowchart", "ng:o": ["post:asciidoc", "service*", "contract", "query:sparql*","query:graphql","diagram:mermaid","diagram:graphviz","diagram:flowchart",
"doc:diagram:sequence","doc:diagram:markmap","doc:diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"], "diagram:sequence","diagram:markmap","diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"],
"ng:w": [], "ng:w": [],
implemented: true, implemented: true,
}, },
@ -454,7 +454,7 @@ export const official_apps = {
"ng:u": "pad",//favicon. can be a did:ng:j "ng:u": "pad",//favicon. can be a did:ng:j
"ng:g": "n:g:z:pad", "ng:g": "n:g:z:pad",
"ng:b": "Pad", "ng:b": "Pad",
"ng:o": ["plato/pad"], "ng:o": ["plato:pad"],
"ng:w": [], "ng:w": [],
}, },
"n:g:z:card": { "n:g:z:card": {
@ -464,7 +464,7 @@ export const official_apps = {
"ng:u": "card",//favicon. can be a did:ng:j "ng:u": "card",//favicon. can be a did:ng:j
"ng:g": "n:g:z:card", "ng:g": "n:g:z:card",
"ng:b": "Card", "ng:b": "Card",
"ng:o": ["plato/card"], "ng:o": ["plato:card"],
"ng:w": [], "ng:w": [],
}, },
"n:g:z:gallery": { "n:g:z:gallery": {
@ -673,7 +673,7 @@ export const official_services = {
"ng:c": "service", "ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j "ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_json", "ng:g": "n:g:z:dump_json",
"ng:o": ["data:json", "data:map", "data:array", "data:table", "doc:diagram:jsmind", "doc:diagram:gantt", "doc:diagram:excalidraw", "doc:viz:*", "doc:chart:*", "prod:cad"], "ng:o": ["data:json", "data:map", "data:array", "data:table", "diagram:jsmind", "diagram:gantt", "diagram:excalidraw", "viz:*", "chart:*", "prod:cad"],
"ng:w": [], "ng:w": [],
"ng:result": ["file:iana:application:json"], "ng:result": ["file:iana:application:json"],
}, },
@ -683,7 +683,7 @@ export const official_services = {
"ng:c": "service", "ng:c": "service",
"ng:u": "data",// favicon. can be a did:ng:j "ng:u": "data",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_xml", "ng:g": "n:g:z:dump_xml",
"ng:o": ["post:rich","post:md","post:html","page","data:xml", "doc:diagram:drawio"], "ng:o": ["post:rich","post:md","post:html","page","data:xml", "diagram:drawio"],
"ng:w": [], "ng:w": [],
"ng:result": ["file:iana:text:xml"], "ng:result": ["file:iana:text:xml"],
}, },
@ -693,8 +693,8 @@ export const official_services = {
"ng:c": "service", "ng:c": "service",
"ng:u": "dump",// favicon. can be a did:ng:j "ng:u": "dump",// favicon. can be a did:ng:j
"ng:g": "n:g:z:dump_text", "ng:g": "n:g:z:dump_text",
"ng:o": ["post:text", "post:asciidoc", "code*", "service*", "contract", "query:sparql*","query:graphql","doc:diagram:mermaid","doc:diagram:graphviz","doc:diagram:flowchart", "ng:o": ["post:text", "post:asciidoc", "code*", "service*", "contract", "query:sparql*","query:graphql","diagram:mermaid","diagram:graphviz","diagram:flowchart",
"doc:diagram:sequence","doc:diagram:markmap","doc:diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"], "diagram:sequence","diagram:markmap","diagram:mymind","doc:music*", "doc:maths", "doc:chemistry", "doc:ancientscript", "doc:braille", "media:subtitle"],
"ng:w": [], "ng:w": [],
"ng:result": ["file:iana:text:plain"], "ng:result": ["file:iana:text:plain"],
}, },

@ -60,6 +60,7 @@ pub enum AppFetchContentV0 {
SignatureStatus, SignatureStatus,
SignatureRequest, SignatureRequest,
SignedSnapshotRequest, SignedSnapshotRequest,
Header,
//Invoke, //Invoke,
} }
@ -261,6 +262,14 @@ impl NuriV0 {
format!("{DID_PREFIX}:o:{repo_id}:v:{overlay_id}") format!("{DID_PREFIX}:o:{repo_id}:v:{overlay_id}")
} }
pub fn branch_repo_graph_name(
branch_id: &BranchId,
repo_id: &RepoId,
overlay_id: &OverlayId,
) -> String {
format!("{DID_PREFIX}:o:{repo_id}:v:{overlay_id}:b:{branch_id}")
}
pub fn repo_skolem( pub fn repo_skolem(
repo_id: &RepoId, repo_id: &RepoId,
peer_id: &Vec<u8>, peer_id: &Vec<u8>,
@ -576,6 +585,7 @@ pub enum AppRequestCommandV0 {
Create, Create,
FileGet, // needs the Nuri of branch/doc/store AND ObjectId FileGet, // needs the Nuri of branch/doc/store AND ObjectId
FilePut, // needs the Nuri of branch/doc/store FilePut, // needs the Nuri of branch/doc/store
Header,
} }
impl AppRequestCommandV0 { impl AppRequestCommandV0 {
@ -587,6 +597,7 @@ impl AppRequestCommandV0 {
| Self::Delete | Self::Delete
| Self::UnPin | Self::UnPin
| Self::Pin | Self::Pin
| Self::Header
| Self::Fetch(_) => false, | Self::Fetch(_) => false,
} }
} }
@ -617,6 +628,12 @@ impl AppRequestCommandV0 {
pub fn new_create() -> Self { pub fn new_create() -> Self {
AppRequestCommandV0::Create AppRequestCommandV0::Create
} }
pub fn new_header() -> Self {
AppRequestCommandV0::Header
}
pub fn new_fetch_header() -> Self {
AppRequestCommandV0::Fetch(AppFetchContentV0::Header)
}
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -793,6 +810,12 @@ pub struct DocAddFile {
pub object: ObjectRef, pub object: ObjectRef,
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DocHeader {
pub title: Option<String>,
pub about: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DocCreateDestination { pub enum DocCreateDestination {
Store, Store,
@ -836,8 +859,10 @@ pub enum AppRequestPayloadV0 {
SmallFilePut(SmallFile), SmallFilePut(SmallFile),
RandomAccessFilePut(String), // content_type (iana media type) RandomAccessFilePut(String), // content_type (iana media type)
RandomAccessFilePutChunk((u32, serde_bytes::ByteBuf)), // end the upload with an empty vec RandomAccessFilePutChunk((u32, serde_bytes::ByteBuf)), // end the upload with an empty vec
//RemoveFile
//Invoke(InvokeArguments), Header(DocHeader),
//RemoveFile
//Invoke(InvokeArguments),
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -849,6 +874,9 @@ impl AppRequestPayload {
pub fn new_sparql_query(sparql: String, base: Option<String>) -> Self { pub fn new_sparql_query(sparql: String, base: Option<String>) -> Self {
AppRequestPayload::V0(AppRequestPayloadV0::Query(DocQuery::V0 { sparql, base })) AppRequestPayload::V0(AppRequestPayloadV0::Query(DocQuery::V0 { sparql, base }))
} }
pub fn new_header(title: Option<String>, about: Option<String>) -> Self {
AppRequestPayload::V0(AppRequestPayloadV0::Header(DocHeader { title, about }))
}
pub fn new_discrete_update( pub fn new_discrete_update(
head_strings: Vec<String>, head_strings: Vec<String>,
crdt: String, crdt: String,
@ -1019,6 +1047,22 @@ pub struct AppTabDocInfo {
//TODO branches //TODO branches
} }
impl AppTabDocInfo {
pub fn new() -> Self {
AppTabDocInfo {
nuri: None,
is_store: None,
is_member: None,
title: None,
icon: None,
description: None,
authors: None,
inbox: None,
can_edit: None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AppTabBranchInfo { pub struct AppTabBranchInfo {
pub id: Option<String>, //+ pub id: Option<String>, //+
@ -1034,6 +1078,13 @@ pub struct AppTabInfo {
pub store: Option<AppTabStoreInfo>, pub store: Option<AppTabStoreInfo>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AppHeader {
pub about: Option<String>,
pub title: Option<String>,
pub class: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AppResponseV0 { pub enum AppResponseV0 {
SessionStart(AppSessionStartResponse), SessionStart(AppSessionStartResponse),
@ -1059,6 +1110,7 @@ pub enum AppResponseV0 {
Error(String), Error(String),
EndOfStream, EndOfStream,
Nuri(String), Nuri(String),
Header(AppHeader),
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]

@ -16,7 +16,7 @@ use crate::oxigraph::storage::numeric_encoder::{
}; };
use crate::oxigraph::storage::{MatchBy, StorageError, StorageReader}; use crate::oxigraph::storage::{MatchBy, StorageError, StorageReader};
use crate::oxigraph::store::CorruptionError; use crate::oxigraph::store::CorruptionError;
use crate::oxrdf::GraphName; use crate::oxrdf::{GraphName, NamedNodeRef};
use crate::sparopt::algebra::NamedNode; use crate::sparopt::algebra::NamedNode;
use std::cell::RefCell; use std::cell::RefCell;
@ -43,30 +43,44 @@ impl Iterator for ErrorIterator {
impl DatasetView { impl DatasetView {
pub fn new( pub fn new(
reader: StorageReader, reader: StorageReader,
dataset: &QueryDataset, query_dataset: &QueryDataset,
default_graph: &Option<String>, default_graph: &Option<String>,
) -> Self { ) -> Self {
let dataset = EncodedDatasetSpec { let dataset = EncodedDatasetSpec {
default: if dataset.has_no_default_dataset() && default_graph.is_some() { default: if query_dataset.has_no_default_dataset() && default_graph.is_some() {
Some(vec![GraphName::NamedNode(NamedNode::new_unchecked( Some(vec![GraphName::NamedNode(NamedNode::new_unchecked(
default_graph.to_owned().unwrap(), default_graph.to_owned().unwrap(),
)) ))
.as_ref() .as_ref()
.into()]) .into()])
} else { } else {
dataset query_dataset
.default_graph_graphs() .default_graph_graphs()
.map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::<Vec<_>>()) .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::<Vec<_>>())
}, },
named: dataset named: query_dataset
.available_named_graphs() .available_named_graphs()
.map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::<Vec<_>>()), .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::<Vec<_>>()),
}; };
Self { let res = Self {
reader, reader,
extra: RefCell::new(HashMap::default()), extra: RefCell::new(HashMap::default()),
dataset, dataset,
};
if let Some(default_graph) = default_graph {
res.encode_term(NamedNodeRef::new_unchecked(default_graph));
}
if !query_dataset.has_no_default_dataset() {
query_dataset.default_graph_graphs().map(|graphs| {
graphs.iter().for_each(|g| match g {
GraphName::NamedNode(nn) => {
let _a = res.encode_term(nn);
}
_ => {}
})
});
} }
res
} }
fn parse_graph_name(&self, graph_name: &EncodedTerm) -> Result<MatchBy, StorageError> { fn parse_graph_name(&self, graph_name: &EncodedTerm) -> Result<MatchBy, StorageError> {

@ -2185,12 +2185,14 @@ impl<'a> StorageWriter<'a> {
let value = [value]; let value = [value];
self.buffer.clear(); self.buffer.clear();
write_spog_quad(&mut self.buffer, encoded); write_spog_quad(&mut self.buffer, encoded);
let result = if self let result =
.transaction // if self
.contains_key_for_update(&self.storage.spog_cf, &self.buffer)? // .transaction
// .contains_key_for_update(&self.storage.spog_cf, &self.buffer)?
// {
// false
// } else
{ {
false
} else {
self.transaction self.transaction
.insert(&self.storage.spog_cf, &self.buffer, &value)?; .insert(&self.storage.spog_cf, &self.buffer, &value)?;

@ -87,7 +87,7 @@ impl UserInfo {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct BranchInfo { pub struct BranchInfo {
pub id: BranchId, pub id: BranchId,
@ -641,6 +641,15 @@ impl Repo {
None None
} }
pub fn header_branch(&self) -> Option<&BranchInfo> {
for (_, branch) in self.branches.iter() {
if branch.branch_type == BranchType::Header {
return Some(branch);
}
}
None
}
pub fn root_branch(&self) -> Option<&BranchInfo> { pub fn root_branch(&self) -> Option<&BranchInfo> {
for (_, branch) in self.branches.iter() { for (_, branch) in self.branches.iter() {
if branch.branch_type == BranchType::Root { if branch.branch_type == BranchType::Root {

@ -220,8 +220,6 @@ impl Store {
)?; )?;
let branch_read_cap = branch_commit.reference().unwrap(); let branch_read_cap = branch_commit.reference().unwrap();
//log_debug!("{:?} BRANCH COMMIT {}", branch_type, branch_commit);
// creating the AddBranch commit (on root_branch), deps to the RootBranch commit // creating the AddBranch commit (on root_branch), deps to the RootBranch commit
// author is the owner // author is the owner
@ -247,12 +245,6 @@ impl Store {
self, self,
)?; )?;
// log_debug!(
// "ADD_BRANCH {:?} BRANCH COMMIT {}",
// &branch_type,
// add_branch_commit
// );
let branch_info = BranchInfo { let branch_info = BranchInfo {
id: branch_pub_key, id: branch_pub_key,
branch_type, branch_type,
@ -369,7 +361,6 @@ impl Store {
&self, &self,
)?; )?;
//log_debug!("ROOT_BRANCH COMMIT {}", root_branch_commit);
let root_branch_readcap = root_branch_commit.reference().unwrap(); let root_branch_readcap = root_branch_commit.reference().unwrap();
let root_branch_readcap_id = root_branch_readcap.id; let root_branch_readcap_id = root_branch_readcap.id;
// adding the 2 events for the Repository and Rootbranch commits // adding the 2 events for the Repository and Rootbranch commits
@ -378,6 +369,32 @@ impl Store {
events.push((root_branch_commit, vec![])); events.push((root_branch_commit, vec![]));
// creating the header branch
let (header_add_branch_commit, header_branch_info, next_dep) = if !is_private_store {
let (header_branch_commit, header_add_branch_commit, header_branch_info) =
self.as_ref().create_branch(
BranchType::Header,
BranchCrdt::Graph(branch_crdt.as_ref().unwrap().class().clone()),
creator,
creator_priv_key,
repo_pub_key,
repository_commit_ref.clone(),
root_branch_readcap_id,
&repo_write_cap_secret,
vec![root_branch_readcap.clone()],
vec![],
)?;
let header_add_branch_readcap = header_add_branch_commit.reference().unwrap();
events_postponed.push((header_branch_commit, vec![]));
(
Some(header_add_branch_commit),
Some(header_branch_info),
header_add_branch_readcap,
)
} else {
(None, None, root_branch_readcap.clone())
};
// creating the main branch // creating the main branch
let (main_branch_commit, main_add_branch_commit, main_branch_info) = let (main_branch_commit, main_add_branch_commit, main_branch_info) =
@ -390,10 +407,9 @@ impl Store {
repository_commit_ref.clone(), repository_commit_ref.clone(),
root_branch_readcap_id, root_branch_readcap_id,
&repo_write_cap_secret, &repo_write_cap_secret,
vec![root_branch_readcap.clone()], vec![next_dep],
vec![], vec![],
)?; )?;
events_postponed.push((main_branch_commit, vec![])); events_postponed.push((main_branch_commit, vec![]));
// TODO: optional AddMember and AddPermission, that should be added as deps to the SynSignature below (and to the commits of the SignatureContent) // TODO: optional AddMember and AddPermission, that should be added as deps to the SynSignature below (and to the commits of the SignatureContent)
@ -414,7 +430,6 @@ impl Store {
vec![main_add_branch_commit.reference().unwrap()], vec![main_add_branch_commit.reference().unwrap()],
vec![], vec![],
)?; )?;
events_postponed.push((store_branch_commit, vec![])); events_postponed.push((store_branch_commit, vec![]));
// creating the overlay or user branch // creating the overlay or user branch
@ -467,7 +482,20 @@ impl Store {
// creating signature for RootBranch, AddBranch and Branch commits // creating signature for RootBranch, AddBranch and Branch commits
// signed with owner threshold signature (threshold = 0) // signed with owner threshold signature (threshold = 0)
let mut signed_commits = vec![main_branch_info.read_cap.as_ref().unwrap().id]; let mut signed_commits = if header_branch_info.is_some() {
vec![
header_branch_info
.as_ref()
.unwrap()
.read_cap
.as_ref()
.unwrap()
.id,
main_branch_info.read_cap.as_ref().unwrap().id,
]
} else {
vec![main_branch_info.read_cap.as_ref().unwrap().id]
};
if let Some((_, store_branch, oou_add_branch, oou_branch)) = &extra_branches { if let Some((_, store_branch, oou_add_branch, oou_branch)) = &extra_branches {
signed_commits.append(&mut vec![ signed_commits.append(&mut vec![
@ -563,16 +591,32 @@ impl Store {
&self, &self,
)?; )?;
let mut branches = vec![(main_branch_info.id, main_branch_info)]; let mut branches = if header_branch_info.is_some() {
vec![
(
header_branch_info.as_ref().unwrap().id,
header_branch_info.unwrap(),
),
(main_branch_info.id, main_branch_info),
]
} else {
vec![(main_branch_info.id, main_branch_info)]
};
// adding the event for the sync_sig_on_root_branch_commit // adding the event for the sync_sig_on_root_branch_commit
let mut additional_blocks = Vec::with_capacity( let mut capacity =
cert_obj_blocks.len() + sig_obj_blocks.len() + main_add_branch_commit.blocks().len(), cert_obj_blocks.len() + sig_obj_blocks.len() + main_add_branch_commit.blocks().len();
); if header_add_branch_commit.is_some() {
capacity += header_add_branch_commit.as_ref().unwrap().blocks().len()
}
let mut additional_blocks = Vec::with_capacity(capacity);
additional_blocks.extend(cert_obj_blocks.iter()); additional_blocks.extend(cert_obj_blocks.iter());
additional_blocks.extend(sig_obj_blocks.iter()); additional_blocks.extend(sig_obj_blocks.iter());
additional_blocks.extend(main_add_branch_commit.blocks().iter()); additional_blocks.extend(main_add_branch_commit.blocks().iter());
if header_add_branch_commit.is_some() {
additional_blocks.extend(header_add_branch_commit.unwrap().blocks().iter());
}
if let Some((store_add_branch, store_branch_info, oou_add_branch, oou_branch_info)) = if let Some((store_add_branch, store_branch_info, oou_add_branch, oou_branch_info)) =
extra_branches extra_branches
{ {
@ -582,7 +626,7 @@ impl Store {
branches.push((oou_branch_info.id, oou_branch_info)); branches.push((oou_branch_info.id, oou_branch_info));
} }
// creating the SyncSignature for all 3 branches with deps to the Branch commit and acks also to this commit as it is its direct causal future. // creating the SyncSignature for all (2+ optional 2) branches with deps to the Branch commit and acks also to this commit as it is its direct causal future.
for (branch_id, branch_info) in &mut branches { for (branch_id, branch_info) in &mut branches {
let sync_sig_on_branch_commit = Commit::new_with_body_acks_deps_and_save( let sync_sig_on_branch_commit = Commit::new_with_body_acks_deps_and_save(
@ -600,12 +644,10 @@ impl Store {
// adding the event for the sync_sig_on_branch_commit // adding the event for the sync_sig_on_branch_commit
let mut additional_blocks = let mut additional = Vec::with_capacity(cert_obj_blocks.len() + sig_obj_blocks.len());
Vec::with_capacity(cert_obj_blocks.len() + sig_obj_blocks.len()); additional.extend(cert_obj_blocks.iter());
additional_blocks.extend(cert_obj_blocks.iter()); additional.extend(sig_obj_blocks.iter());
additional_blocks.extend(sig_obj_blocks.iter()); events_postponed.push((sync_sig_on_branch_commit, additional));
events_postponed.push((sync_sig_on_branch_commit, additional_blocks));
branch_info.current_heads = vec![sync_sig_on_branch_commit_ref]; branch_info.current_heads = vec![sync_sig_on_branch_commit_ref];

@ -810,6 +810,17 @@ impl fmt::Display for StoreRepo {
} }
impl StoreRepo { impl StoreRepo {
pub fn from_type_and_repo(store_type: &String, repo_id_str: &String) -> Result<Self, NgError> {
let repo_id: RepoId = repo_id_str.as_str().try_into()?;
Ok(StoreRepo::V0(match store_type.as_str() {
"public" => StoreRepoV0::PublicStore(repo_id),
"protected" => StoreRepoV0::ProtectedStore(repo_id),
"private" => StoreRepoV0::PrivateStore(repo_id),
"group" => StoreRepoV0::Group(repo_id),
"dialog" | _ => unimplemented!(),
}))
}
pub fn store_type_for_app(&self) -> String { pub fn store_type_for_app(&self) -> String {
match self { match self {
Self::V0(v0) => match v0 { Self::V0(v0) => match v0 {
@ -1533,6 +1544,12 @@ impl BranchType {
_ => false, _ => false,
} }
} }
pub fn is_header(&self) -> bool {
match self {
Self::Header => true,
_ => false,
}
}
} }
impl fmt::Display for BranchType { impl fmt::Display for BranchType {

@ -25,10 +25,10 @@ ng.init_headless(config).then( async() => {
//let user_id = await ng.admin_create_user(config); //let user_id = await ng.admin_create_user(config);
//console.log("user created: ",user_id); //console.log("user created: ",user_id);
let user_id = "NnAJWxO-KapuWyCm7RGwO5VszZwaJARGit-i3i1mXbkA"; let user_id = "tPY8WDkgXdqJsQMXI6U5ztj_SK54djy9XtHJhKlzyGQA";
let base = "did:ng:o:8mqfhoSprneBjkAASinRk0OYvFpbiyhjMBVHKQIarDEA";
//let base;
let session = await ng.session_headless_start(user_id); let session = await ng.session_headless_start(user_id);
session_id = session.session_id; session_id = session.session_id;
console.log(session); console.log(session);
@ -36,13 +36,23 @@ ng.init_headless(config).then( async() => {
let dump = await ng.rdf_dump(session.session_id); let dump = await ng.rdf_dump(session.session_id);
console.log(dump); console.log(dump);
//let nuri = await ng.doc_create(session.session_id, "Graph", "data:graph", "protected", "RMKIXeT9RvGT5wc2uJQaRqEFjaJpC19haeaSx4iRenIA", "store");
let nuri = "did:ng:o:b70vk7Bj4eInXgG8pLysrFpEL-YSOiRYEmihPGiM1EsA:v:_0hm2qIpq443C7rMEdCGnhPDhsaWR2XruTIaF-9LKbkA";
console.log("nuri=",nuri);
let base = "did:ng:o:b70vk7Bj4eInXgG8pLysrFpEL-YSOiRYEmihPGiM1EsA";
console.log("******** SELECT") console.log("******** SELECT")
let sparql_result = await ng.sparql_query(session.session_id, "SELECT ?s ?p ?o WHERE { ?s ?p ?o }", base); let header_branch = "did:ng:o:b70vk7Bj4eInXgG8pLysrFpEL-YSOiRYEmihPGiM1EsA:v:_0hm2qIpq443C7rMEdCGnhPDhsaWR2XruTIaF-9LKbkA:b:TokczMya9WDpQ-_FYFi7QJVbHmllWS3lD-vjtzHHQa0A";
console.log(sparql_result);
for (const q of sparql_result.results.bindings) { // let sparql_result = await ng.sparql_query(session.session_id, "SELECT ?s ?p ?o WHERE { ?s ?p ?o }", base, header_branch);
console.log(q); // console.log(sparql_result);
} // for (const q of sparql_result.results.bindings) {
// console.log(q);
// }
await ng.sparql_update(session.session_id, "WITH <"+header_branch+"> \
DELETE { <> <did:ng:x:ng#n> ?n. } INSERT {<> <did:ng:x:ng#n> \"ddd6\". } WHERE {OPTIONAL { <> <did:ng:x:ng#n> ?n } }",nuri);
// let history = await ng.branch_history(session.session_id); // let history = await ng.branch_history(session.session_id);
// for (const h of history.history) { // for (const h of history.history) {
@ -50,6 +60,12 @@ ng.init_headless(config).then( async() => {
// } // }
// console.log(history.swimlane_state); // console.log(history.swimlane_state);
sparql_result = await ng.sparql_query(session.session_id, "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+header_branch+"> { ?s ?p ?o } }", base);
console.log("******** CONSTRUCT")
for (const r of sparql_result) console.log(r.subject.value, r.predicate.value,r.object.value);
//await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:o:8mqfhoSprneBjkAASinRk0OYvFpbiyhjMBVHKQIarDEA> <did:ng:i> <did:ng:j> }"); //await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:o:8mqfhoSprneBjkAASinRk0OYvFpbiyhjMBVHKQIarDEA> <did:ng:i> <did:ng:j> }");
// await ng.sparql_update(session.session_id, "DELETE DATA { <did:ng:t:AJQ5gCLoXXjalC9diTDCvxxWu5ZQUcYWEE821nhVRMcE> <did:ng:i> <did:ng:j> }"); // await ng.sparql_update(session.session_id, "DELETE DATA { <did:ng:t:AJQ5gCLoXXjalC9diTDCvxxWu5ZQUcYWEE821nhVRMcE> <did:ng:i> <did:ng:j> }");
@ -65,29 +81,29 @@ ng.init_headless(config).then( async() => {
//await ng.sparql_update(session.session_id, "INSERT { _:_ <did:ng:ok> <did:ng:v> . } WHERE { _:_ <did:ng:m> <did:ng:n> } "); //await ng.sparql_update(session.session_id, "INSERT { _:_ <did:ng:ok> <did:ng:v> . } WHERE { _:_ <did:ng:m> <did:ng:n> } ");
//await ng.sparql_update(session.session_id, "INSERT DATA { _:_ <abc:a> <d:a> . _:_a <abceee:a> <d:a> . }"); //await ng.sparql_update(session.session_id, "INSERT DATA { _:_ <abc:a> <d:a> . _:_a <abceee:a> <d:a> . }");
await ng.sparql_update(session.session_id, "INSERT DATA { <> <a:selftest> <a:selftest> . }",base); //await ng.sparql_update(session.session_id, "INSERT DATA { <> <a:selftest> <a:selftest> . }",base);
//await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:TEST4> <did:ng:j> _:_ . _:_ <did:ng:m> <did:ng:n> . }"); //await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:TEST4> <did:ng:j> _:_ . _:_ <did:ng:m> <did:ng:n> . }");
//await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:TEST5> <did:ng:j> [ <did:ng:m> <did:ng:n> ]. }"); //await ng.sparql_update(session.session_id, "INSERT DATA { <did:ng:TEST5> <did:ng:j> [ <did:ng:m> <did:ng:n> ]. }");
sparql_result = await ng.sparql_query(session.session_id, "SELECT ?a WHERE { ?a <did:ng:j> _:abc. _:abc <did:ng:m> <did:ng:n> }", base); // sparql_result = await ng.sparql_query(session.session_id, "SELECT ?a WHERE { ?a <did:ng:j> _:abc. _:abc <did:ng:m> <did:ng:n> }", base);
console.log(sparql_result); // console.log(sparql_result);
for (const q of sparql_result.results.bindings) { // for (const q of sparql_result.results.bindings) {
console.log(q); // console.log(q);
} // }
sparql_result = await ng.sparql_query(session.session_id, "SELECT ?s ?a WHERE { ?s <did:ng:j> ?a }", base); // sparql_result = await ng.sparql_query(session.session_id, "SELECT ?s ?a WHERE { ?s <did:ng:j> ?a }", base);
console.log(sparql_result); // console.log(sparql_result);
for (const q of sparql_result.results.bindings) { // for (const q of sparql_result.results.bindings) {
console.log(q); // console.log(q);
} // }
console.log("******** CONSTRUCT") // console.log("******** CONSTRUCT2")
let quads = await ng.sparql_query(session.session_id, "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }",base); // let quads = await ng.sparql_query(session.session_id, "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }",base);
for (const q of quads) { // for (const q of quads) {
console.log(q.subject.toString(), q.predicate.toString(), q.object.toString(), q.graph.toString()) // console.log(q.subject.toString(), q.predicate.toString(), q.object.toString(), q.graph.toString())
} // }
// let file_nuri = await ng.file_put_to_private_store(session.session_id,"LICENSE-MIT","text/plain"); // let file_nuri = await ng.file_put_to_private_store(session.session_id,"LICENSE-MIT","text/plain");
// console.log(file_nuri); // console.log(file_nuri);

@ -399,6 +399,71 @@ pub async fn sparql_update(
} }
} }
#[wasm_bindgen]
pub async fn update_header(
session_id: JsValue,
nuri: String,
title: JsValue,
about: JsValue,
) -> Result<(), String> {
let session_id: u64 = serde_wasm_bindgen::from_value::<u64>(session_id)
.map_err(|_| "Invalid session_id".to_string())?;
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let title = if title.is_string() {
Some(title.as_string().unwrap())
} else {
None
};
let about = if about.is_string() {
Some(about.as_string().unwrap())
} else {
None
};
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_header(),
nuri,
payload: Some(AppRequestPayload::new_header(title, about)),
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Error(e)) = res {
Err(e)
} else {
Ok(())
}
}
#[wasm_bindgen]
pub async fn fetch_header(session_id: JsValue, nuri: String) -> Result<JsValue, String> {
let session_id: u64 = serde_wasm_bindgen::from_value::<u64>(session_id)
.map_err(|_| "Invalid session_id".to_string())?;
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_fetch_header(),
nuri,
payload: None,
session_id,
});
let res = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
match res {
AppResponse::V0(AppResponseV0::Error(e)) => Err(e),
AppResponse::V0(AppResponseV0::Header(h)) => Ok(serde_wasm_bindgen::to_value(&h).unwrap()),
_ => Err("invalid response".to_string()),
}
}
#[cfg(not(wasmpack_target = "nodejs"))] #[cfg(not(wasmpack_target = "nodejs"))]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn sparql_query( pub async fn sparql_query(
@ -1211,6 +1276,7 @@ pub async fn app_request_with_nuri_command(
Ok(serde_wasm_bindgen::to_value(&response).unwrap()) Ok(serde_wasm_bindgen::to_value(&response).unwrap())
} }
#[cfg(not(wasmpack_target = "nodejs"))]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn doc_create( pub async fn doc_create(
session_id: JsValue, session_id: JsValue,
@ -1253,6 +1319,50 @@ pub async fn doc_create(
} }
} }
#[cfg(wasmpack_target = "nodejs")]
#[wasm_bindgen]
pub async fn doc_create(
session_id: JsValue,
crdt: String,
class_name: String,
store_type: String,
store_repo: String,
destination: String,
) -> Result<JsValue, String> {
let session_id: u64 = serde_wasm_bindgen::from_value::<u64>(session_id)
.map_err(|_| "Deserialization error of session_id".to_string())?;
let class = BranchCrdt::from(crdt, class_name).map_err(|e| e.to_string())?;
let store = StoreRepo::from_type_and_repo(&store_type, &store_repo)
.map_err(|_| "invalid store_repo".to_string())?;
let destination = DocCreateDestination::from(destination).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
session_id,
command: AppRequestCommandV0::new_create(),
nuri: NuriV0::new_empty(),
payload: Some(AppRequestPayload::V0(AppRequestPayloadV0::Create(
DocCreate {
store,
class,
destination,
},
))),
});
let response = nextgraph::local_broker::app_request(request)
.await
.map_err(|e: NgError| e.to_string())?;
if let AppResponse::V0(AppResponseV0::Nuri(nuri)) = response {
Ok(serde_wasm_bindgen::to_value(&nuri).unwrap())
} else {
Err("invalid response".to_string())
}
}
#[wasm_bindgen] #[wasm_bindgen]
pub async fn file_get_from_private_store( pub async fn file_get_from_private_store(
session_id: JsValue, session_id: JsValue,

@ -27,6 +27,7 @@ rand = { version = "0.7", features = ["getrandom"] }
web-time = "0.2.0" web-time = "0.2.0"
either = "1.8.1" either = "1.8.1"
futures = "0.3.24" futures = "0.3.24"
lazy_static = "1.4.0"
async-trait = "0.1.64" async-trait = "0.1.64"
async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] } async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
automerge = "0.5.11" automerge = "0.5.11"

@ -33,7 +33,7 @@ use crate::verifier::Verifier;
struct BranchUpdateInfo { struct BranchUpdateInfo {
branch_id: BranchId, branch_id: BranchId,
branch_is_main: bool, branch_type: BranchType,
repo_id: RepoId, repo_id: RepoId,
topic_id: TopicId, topic_id: TopicId,
token: Digest, token: Digest,
@ -268,7 +268,7 @@ impl Verifier {
if body.graph.is_some() { if body.graph.is_some() {
let info = BranchUpdateInfo { let info = BranchUpdateInfo {
branch_id: *branch_id, branch_id: *branch_id,
branch_is_main: branch.branch_type.is_main(), branch_type: branch.branch_type.clone(),
repo_id: *repo_id, repo_id: *repo_id,
topic_id: branch.topic.clone().unwrap(), topic_id: branch.topic.clone().unwrap(),
token: branch.read_cap.as_ref().unwrap().tokenize(), token: branch.read_cap.as_ref().unwrap().tokenize(),
@ -330,7 +330,10 @@ impl Verifier {
fn find_branch_and_repo_for_quad( fn find_branch_and_repo_for_quad(
&self, &self,
quad: &Quad, quad: &Quad,
branches: &mut HashMap<BranchId, (StoreRepo, RepoId, bool, TopicId, Digest, OverlayId)>, branches: &mut HashMap<
BranchId,
(StoreRepo, RepoId, BranchType, TopicId, Digest, OverlayId),
>,
nuri_branches: &mut HashMap<String, (RepoId, BranchId, bool)>, nuri_branches: &mut HashMap<String, (RepoId, BranchId, bool)>,
) -> Result<(RepoId, BranchId, bool), VerifierError> { ) -> Result<(RepoId, BranchId, bool), VerifierError> {
match &quad.graph_name { match &quad.graph_name {
@ -348,13 +351,13 @@ impl Verifier {
nuri.overlay.unwrap().outer().to_slice(), nuri.overlay.unwrap().outer().to_slice(),
))?; ))?;
let repo = self.get_repo(nuri.target.repo_id(), store.get_store_repo())?; let repo = self.get_repo(nuri.target.repo_id(), store.get_store_repo())?;
let (branch_id, is_publisher, is_main, topic_id, token) = match nuri.branch { let (branch_id, is_publisher, branch_type, topic_id, token) = match nuri.branch {
None => { None => {
let b = repo.main_branch().ok_or(VerifierError::BranchNotFound)?; let b = repo.main_branch().ok_or(VerifierError::BranchNotFound)?;
( (
b.id, b.id,
b.topic_priv_key.is_some(), b.topic_priv_key.is_some(),
true, b.branch_type.clone(),
b.topic.clone().unwrap(), b.topic.clone().unwrap(),
b.read_cap.as_ref().unwrap().tokenize(), b.read_cap.as_ref().unwrap().tokenize(),
) )
@ -365,7 +368,7 @@ impl Verifier {
( (
id, id,
b.topic_priv_key.is_some(), b.topic_priv_key.is_some(),
false, b.branch_type.clone(),
b.topic.clone().unwrap(), b.topic.clone().unwrap(),
b.read_cap.as_ref().unwrap().tokenize(), b.read_cap.as_ref().unwrap().tokenize(),
) )
@ -376,7 +379,7 @@ impl Verifier {
let _ = branches.entry(branch_id).or_insert(( let _ = branches.entry(branch_id).or_insert((
store.get_store_repo().clone(), store.get_store_repo().clone(),
repo.id, repo.id,
is_main, branch_type,
topic_id, topic_id,
token, token,
store.overlay_id, store.overlay_id,
@ -392,7 +395,7 @@ impl Verifier {
} }
} }
async fn prepare_sparql_update( pub(crate) async fn prepare_sparql_update(
&mut self, &mut self,
inserts: Vec<Quad>, inserts: Vec<Quad>,
removes: Vec<Quad>, removes: Vec<Quad>,
@ -405,8 +408,10 @@ impl Verifier {
// for now we just do skip, without giving option to user // for now we just do skip, without giving option to user
let mut inserts_map: HashMap<BranchId, HashSet<Triple>> = HashMap::with_capacity(1); let mut inserts_map: HashMap<BranchId, HashSet<Triple>> = HashMap::with_capacity(1);
let mut removes_map: HashMap<BranchId, HashSet<Triple>> = HashMap::with_capacity(1); let mut removes_map: HashMap<BranchId, HashSet<Triple>> = HashMap::with_capacity(1);
let mut branches: HashMap<BranchId, (StoreRepo, RepoId, bool, TopicId, Digest, OverlayId)> = let mut branches: HashMap<
HashMap::with_capacity(1); BranchId,
(StoreRepo, RepoId, BranchType, TopicId, Digest, OverlayId),
> = HashMap::with_capacity(1);
let mut nuri_branches: HashMap<String, (RepoId, BranchId, bool)> = let mut nuri_branches: HashMap<String, (RepoId, BranchId, bool)> =
HashMap::with_capacity(1); HashMap::with_capacity(1);
let mut inserts_len = inserts.len(); let mut inserts_len = inserts.len();
@ -462,8 +467,7 @@ impl Verifier {
let mut updates = Vec::with_capacity(branches.len()); let mut updates = Vec::with_capacity(branches.len());
for (branch_id, (store_repo, repo_id, branch_is_main, topic_id, token, overlay_id)) in for (branch_id, (store_repo, repo_id, branch_type, topic_id, token, overlay_id)) in branches
branches
{ {
let graph_transac = GraphTransaction { let graph_transac = GraphTransaction {
inserts: Vec::from_iter(inserts_map.remove(&branch_id).unwrap_or(HashSet::new())), inserts: Vec::from_iter(inserts_map.remove(&branch_id).unwrap_or(HashSet::new())),
@ -497,7 +501,7 @@ impl Verifier {
let info = BranchUpdateInfo { let info = BranchUpdateInfo {
branch_id, branch_id,
branch_is_main, branch_type,
repo_id, repo_id,
topic_id, topic_id,
token, token,
@ -526,13 +530,15 @@ impl Verifier {
let reader = transaction.ng_get_reader(); let reader = transaction.ng_get_reader();
for update in updates_ref.iter_mut() { for update in updates_ref.iter_mut() {
let branch_is_main = update.branch_type.is_main();
let commit_name = let commit_name =
NuriV0::commit_graph_name(&update.commit_id, &update.overlay_id); NuriV0::commit_graph_name(&update.commit_id, &update.overlay_id);
let commit_encoded = numeric_encoder::StrHash::new(&commit_name); let commit_encoded = numeric_encoder::StrHash::new(&commit_name);
let cv_graphname = NamedNode::new_unchecked(commit_name); let cv_graphname = NamedNode::new_unchecked(commit_name);
let cv_graphname_ref = GraphNameRef::NamedNode((&cv_graphname).into()); let cv_graphname_ref = GraphNameRef::NamedNode((&cv_graphname).into());
let ov_main = if update.branch_is_main { let ov_main = if branch_is_main {
let ov_graphname = NamedNode::new_unchecked(NuriV0::repo_graph_name( let ov_graphname = NamedNode::new_unchecked(NuriV0::repo_graph_name(
&update.repo_id, &update.repo_id,
&update.overlay_id, &update.overlay_id,
@ -541,7 +547,7 @@ impl Verifier {
} else { } else {
None None
}; };
let value = if update.branch_is_main { let value = if branch_is_main {
ADDED_IN_MAIN ADDED_IN_MAIN
} else { } else {
ADDED_IN_OTHER ADDED_IN_OTHER
@ -611,7 +617,7 @@ impl Verifier {
let at_current_heads = current_heads == direct_causal_past_encoded; let at_current_heads = current_heads == direct_causal_past_encoded;
// if not, we need to base ourselves on the materialized state of the direct_causal_past of the commit // if not, we need to base ourselves on the materialized state of the direct_causal_past of the commit
let value = if update.branch_is_main { let value = if branch_is_main {
REMOVED_IN_MAIN REMOVED_IN_MAIN
} else { } else {
REMOVED_IN_OTHER REMOVED_IN_OTHER
@ -678,18 +684,53 @@ impl Verifier {
.map_err(|e| VerifierError::OxigraphError(e.to_string())); .map_err(|e| VerifierError::OxigraphError(e.to_string()));
if res.is_ok() { if res.is_ok() {
for update in updates { for update in updates {
let graph_patch = update.transaction.as_patch(); if update.branch_type.is_header() {
self.push_app_response( let mut tab_doc_info = AppTabDocInfo::new();
&update.branch_id, for removed in update.transaction.removes {
AppResponse::V0(AppResponseV0::Patch(AppPatch { match removed.predicate.as_str() {
commit_id: update.commit_id.to_string(), NG_ONTOLOGY_ABOUT => tab_doc_info.description = Some("".to_string()),
commit_info: update.commit_info, NG_ONTOLOGY_TITLE => tab_doc_info.title = Some("".to_string()),
graph: Some(graph_patch), _ => {}
discrete: None, }
other: None, }
})), for inserted in update.transaction.inserts {
) match inserted.predicate.as_str() {
.await; NG_ONTOLOGY_ABOUT => {
if let Term::Literal(l) = inserted.object {
tab_doc_info.description = Some(l.value().to_string())
}
}
NG_ONTOLOGY_TITLE => {
if let Term::Literal(l) = inserted.object {
tab_doc_info.title = Some(l.value().to_string())
}
}
_ => {}
}
}
self.push_app_response(
&update.branch_id,
AppResponse::V0(AppResponseV0::TabInfo(AppTabInfo {
branch: None,
doc: Some(tab_doc_info),
store: None,
})),
)
.await;
} else {
let graph_patch = update.transaction.as_patch();
self.push_app_response(
&update.branch_id,
AppResponse::V0(AppResponseV0::Patch(AppPatch {
commit_id: update.commit_id.to_string(),
commit_info: update.commit_info,
graph: Some(graph_patch),
discrete: None,
other: None,
})),
)
.await;
}
} }
} }
res res
@ -714,7 +755,7 @@ impl Verifier {
); );
match res { match res {
Err(e) => Err(e.to_string()), Err(e) => Err(e.to_string()),
Ok((mut inserts, removes)) => { Ok((inserts, removes)) => {
if inserts.is_empty() && removes.is_empty() { if inserts.is_empty() && removes.is_empty() {
Ok(()) Ok(())
} else { } else {

@ -16,6 +16,7 @@ use futures::channel::mpsc;
use futures::SinkExt; use futures::SinkExt;
use futures::StreamExt; use futures::StreamExt;
use ng_oxigraph::oxigraph::sparql::{results::*, Query, QueryResults}; use ng_oxigraph::oxigraph::sparql::{results::*, Query, QueryResults};
use ng_oxigraph::oxrdf::{Literal, NamedNode, Quad, Term};
use ng_repo::errors::*; use ng_repo::errors::*;
use ng_repo::file::{RandomAccessFile, ReadFile}; use ng_repo::file::{RandomAccessFile, ReadFile};
@ -146,6 +147,23 @@ impl Verifier {
} }
} }
fn resolve_header_branch(
&self,
target: &NuriTargetV0,
) -> Result<(RepoId, BranchId, StoreRepo), NgError> {
Ok(match target {
NuriTargetV0::Repo(repo_id) => {
let (branch, store_repo) = {
let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?;
let branch = repo.header_branch().ok_or(NgError::BranchNotFound)?;
(branch.id, repo.store.get_store_repo().clone())
};
(*repo_id, branch, store_repo)
}
_ => return Err(NgError::NotImplemented),
})
}
pub(crate) fn resolve_target_for_sparql( pub(crate) fn resolve_target_for_sparql(
&self, &self,
target: &NuriTargetV0, target: &NuriTargetV0,
@ -532,6 +550,80 @@ impl Verifier {
payload: Option<AppRequestPayload>, payload: Option<AppRequestPayload>,
) -> Result<AppResponse, NgError> { ) -> Result<AppResponse, NgError> {
match command { match command {
AppRequestCommandV0::Header => {
if let Some(AppRequestPayload::V0(AppRequestPayloadV0::Header(doc_header))) =
payload
{
let (repo_id, branch_id, store_repo) =
match self.resolve_header_branch(&nuri.target) {
Err(e) => return Ok(AppResponse::error(e.to_string())),
Ok(a) => a,
};
let graph_name = NuriV0::branch_repo_graph_name(
&branch_id,
&repo_id,
&store_repo.overlay_id_for_storage_purpose(),
);
let base = NuriV0::repo_id(&repo_id);
let mut deletes = String::new();
let mut wheres = String::new();
let mut inserts = String::new();
if let Some(about) = doc_header.about {
deletes += &format!("<> <{NG_ONTOLOGY_ABOUT}> ?a. ");
wheres += &format!("OPTIONAL {{ <> <{NG_ONTOLOGY_ABOUT}> ?a }} ");
if about.len() > 0 {
inserts += &format!(
"<> <{NG_ONTOLOGY_ABOUT}> \"{}\". ",
about.replace("\\", "\\\\").replace("\"", "\\\"")
);
}
}
if let Some(title) = doc_header.title {
deletes += &format!("<> <{NG_ONTOLOGY_TITLE}> ?n. ");
wheres += &format!("OPTIONAL {{ <> <{NG_ONTOLOGY_TITLE}> ?n }} ");
if title.len() > 0 {
inserts += &format!(
"<> <{NG_ONTOLOGY_TITLE}> \"{}\". ",
title.replace("\\", "\\\\").replace("\"", "\\\"")
);
}
}
let query = format!(
"DELETE {{ {deletes} }} INSERT {{ {inserts} }} WHERE {{ {wheres} }}"
);
let oxistore = self.graph_dataset.as_ref().unwrap();
let update = ng_oxigraph::oxigraph::sparql::Update::parse(&query, Some(&base))
.map_err(|e| NgError::InternalError)?;
let res = oxistore.ng_update(update, Some(graph_name));
return Ok(match res {
Err(e) => AppResponse::error(NgError::InternalError.to_string()),
Ok((inserts, removes)) => {
if inserts.is_empty() && removes.is_empty() {
AppResponse::ok()
} else {
match self
.prepare_sparql_update(
Vec::from_iter(inserts),
Vec::from_iter(removes),
self.get_peer_id_for_skolem(),
)
.await
{
Err(e) => AppResponse::error(e.to_string()),
Ok(_) => AppResponse::ok(),
}
}
}
});
} else {
return Err(NgError::InvalidPayload);
}
}
AppRequestCommandV0::Create => { AppRequestCommandV0::Create => {
if let Some(AppRequestPayload::V0(AppRequestPayloadV0::Create(doc_create))) = if let Some(AppRequestPayload::V0(AppRequestPayloadV0::Create(doc_create))) =
payload payload
@ -540,6 +632,7 @@ impl Verifier {
let user_id = self.user_id().clone(); let user_id = self.user_id().clone();
let user_priv_key = self.user_privkey().clone(); let user_priv_key = self.user_privkey().clone();
let primary_class = doc_create.class.class().clone();
let repo_id = self let repo_id = self
.new_repo_default( .new_repo_default(
&user_id, &user_id,
@ -549,6 +642,11 @@ impl Verifier {
) )
.await?; .await?;
let header_branch_id = {
let repo = self.get_repo(&repo_id, &doc_create.store)?;
repo.header_branch().ok_or(NgError::BranchNotFound)?.id
};
// adding an AddRepo commit to the Store branch of store. // adding an AddRepo commit to the Store branch of store.
self.send_add_repo_to_store(&repo_id, &doc_create.store) self.send_add_repo_to_store(&repo_id, &doc_create.store)
.await?; .await?;
@ -570,12 +668,73 @@ impl Verifier {
self.add_doc(&repo_id, &overlay_id)?; self.add_doc(&repo_id, &overlay_id)?;
// adding the class triple to the header branch
let header_branch_nuri = format!("{nuri_result}:b:{}", header_branch_id);
let quad = Quad {
subject: NamedNode::new_unchecked(&nuri).into(),
predicate: NG_ONTOLOGY_CLASS_NAME.clone().into(),
object: Literal::new_simple_literal(primary_class).into(),
graph_name: NamedNode::new_unchecked(&header_branch_nuri).into(),
};
let ret = self.prepare_sparql_update(vec![quad], vec![], vec![]).await;
if let Err(e) = ret {
return Ok(AppResponse::error(e.to_string()));
}
return Ok(AppResponse::V0(AppResponseV0::Nuri(nuri_result))); return Ok(AppResponse::V0(AppResponseV0::Nuri(nuri_result)));
} else { } else {
return Err(NgError::InvalidPayload); return Err(NgError::InvalidPayload);
} }
} }
AppRequestCommandV0::Fetch(fetch) => match fetch { AppRequestCommandV0::Fetch(fetch) => match fetch {
AppFetchContentV0::Header => {
let (repo_id, branch_id, store_repo) =
match self.resolve_header_branch(&nuri.target) {
Err(e) => return Ok(AppResponse::error(e.to_string())),
Ok(a) => a,
};
self.open_branch(&repo_id, &branch_id, true).await?;
let graph_name = NuriV0::branch_repo_graph_name(
&branch_id,
&repo_id,
&store_repo.overlay_id_for_storage_purpose(),
);
let base = NuriV0::repo_id(&repo_id);
let oxistore = self.graph_dataset.as_ref().unwrap();
let parsed = Query::parse(
&format!("SELECT ?class ?title ?about WHERE {{ OPTIONAL {{ <> <{NG_ONTOLOGY_CLASS}> ?class }} OPTIONAL {{ <> <{NG_ONTOLOGY_ABOUT}> ?about }} OPTIONAL {{ <> <{NG_ONTOLOGY_TITLE}> ?title }} }}"), Some(&base));
if parsed.is_err() {
return Ok(AppResponse::error(parsed.unwrap_err().to_string()));
}
let results = oxistore.query(parsed.unwrap(), Some(graph_name));
match results {
Err(e) => return Ok(AppResponse::error(e.to_string())),
Ok(QueryResults::Solutions(mut sol)) => {
let mut title = None;
let mut about = None;
let mut class = None;
if let Some(Ok(s)) = sol.next() {
if let Some(Term::Literal(l)) = s.get("title") {
title = Some(l.value().to_string());
}
if let Some(Term::Literal(l)) = s.get("about") {
about = Some(l.value().to_string());
}
if let Some(Term::Literal(l)) = s.get("class") {
class = Some(l.value().to_string());
}
}
return Ok(AppResponse::V0(AppResponseV0::Header(AppHeader {
about,
title,
class,
})));
}
_ => return Err(NgError::InvalidResponse),
};
}
AppFetchContentV0::ReadQuery => { AppFetchContentV0::ReadQuery => {
if let Some(AppRequestPayload::V0(AppRequestPayloadV0::Query(DocQuery::V0 { if let Some(AppRequestPayload::V0(AppRequestPayloadV0::Query(DocQuery::V0 {
sparql, sparql,

@ -106,7 +106,7 @@ impl SiteV0 {
let protected_store = Self::site_store_to_store_repo(&protected); let protected_store = Self::site_store_to_store_repo(&protected);
let private_store = Self::site_store_to_store_repo(&private); let private_store = Self::site_store_to_store_repo(&private);
verifier.reserve_more(33)?; verifier.reserve_more(37)?;
let mut signer_caps = Vec::with_capacity(3); let mut signer_caps = Vec::with_capacity(3);

@ -17,11 +17,24 @@ use serde::{Deserialize, Serialize};
//use oxigraph::store::Store; //use oxigraph::store::Store;
//use oxigraph::model::GroundQuad; //use oxigraph::model::GroundQuad;
//use yrs::{StateVector, Update}; //use yrs::{StateVector, Update};
use lazy_static::lazy_static;
use ng_net::{app_protocol::*, types::*}; use ng_net::{app_protocol::*, types::*};
use ng_oxigraph::oxrdf::{GraphName, GraphNameRef, NamedNode, Quad, Triple, TripleRef}; use ng_oxigraph::oxrdf::{GraphName, GraphNameRef, NamedNode, Quad, Triple, TripleRef};
use ng_repo::{errors::*, types::*}; use ng_repo::{errors::*, types::*};
pub const NG_ONTOLOGY: &str = "did:ng:x:ng#";
pub const NG_ONTOLOGY_ABOUT: &str = "did:ng:x:ng#a";
pub const NG_ONTOLOGY_TITLE: &str = "did:ng:x:ng#n";
pub const NG_ONTOLOGY_CLASS: &str = "did:ng:x:ng#c";
lazy_static! {
pub static ref NG_ONTOLOGY_ABOUT_NAME: NamedNode = NamedNode::new_unchecked(NG_ONTOLOGY_ABOUT);
pub static ref NG_ONTOLOGY_TITLE_NAME: NamedNode = NamedNode::new_unchecked(NG_ONTOLOGY_TITLE);
pub static ref NG_ONTOLOGY_CLASS_NAME: NamedNode = NamedNode::new_unchecked(NG_ONTOLOGY_CLASS);
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GraphTransaction { pub struct GraphTransaction {
pub inserts: Vec<Triple>, pub inserts: Vec<Triple>,

@ -25,6 +25,9 @@ use async_std::stream::StreamExt;
use async_std::sync::{Mutex, RwLockReadGuard}; use async_std::sync::{Mutex, RwLockReadGuard};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::SinkExt; use futures::SinkExt;
use ng_oxigraph::oxigraph::sparql::Query;
use ng_oxigraph::oxigraph::sparql::QueryResults;
use ng_oxigraph::oxrdf::Term;
use ng_repo::utils::derive_key; use ng_repo::utils::derive_key;
use sbbf_rs_safe::Filter; use sbbf_rs_safe::Filter;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -253,10 +256,11 @@ impl Verifier {
} }
fn branch_get_tab_info( fn branch_get_tab_info(
&self,
repo: &Repo, repo: &Repo,
branch: &BranchId, branch: &BranchId,
outer: String, outer: String,
) -> Result<AppTabInfo, NgError> { ) -> Result<(AppTabInfo, Option<BranchId>), NgError> {
let branch_info = repo.branch(branch)?; let branch_info = repo.branch(branch)?;
let branch_tab_info = AppTabBranchInfo { let branch_tab_info = AppTabBranchInfo {
@ -266,6 +270,40 @@ impl Verifier {
comment_branch: None, //TODO comment_branch: None, //TODO
}; };
// Retrieve Header branch info (title and about)
let header_branch_info = repo.header_branch();
let mut about = None;
let mut title = None;
if let Some(header_branch_info) = header_branch_info {
let oxistore = self.graph_dataset.as_ref().unwrap();
let header_graph = NuriV0::branch_repo_graph_name(
&header_branch_info.id,
&repo.id,
&repo.store.overlay_id,
);
let base = NuriV0::repo_id(&repo.id);
let parsed = Query::parse(&format!("SELECT ?title ?about WHERE {{ OPTIONAL {{ <> <{NG_ONTOLOGY_ABOUT}> ?about }} OPTIONAL {{ <> <{NG_ONTOLOGY_TITLE}> ?title }} }}"),
Some(&base)).map_err(|e| NgError::OxiGraphError(e.to_string()))?;
let results = oxistore
.query(parsed, Some(header_graph))
.map_err(|e| NgError::OxiGraphError(e.to_string()))?;
match results {
QueryResults::Solutions(mut sol) => {
if let Some(Ok(s)) = sol.next() {
if let Some(Term::Literal(l)) = s.get("title") {
title = Some(l.value().to_string());
}
if let Some(Term::Literal(l)) = s.get("about") {
about = Some(l.value().to_string());
}
}
}
_ => return Err(NgError::InvalidResponse),
}
}
let root_branch_info = repo.branch(&repo.id)?; let root_branch_info = repo.branch(&repo.id)?;
let doc_tab_info = AppTabDocInfo { let doc_tab_info = AppTabDocInfo {
@ -275,9 +313,9 @@ impl Verifier {
authors: None, // TODO authors: None, // TODO
inbox: None, // TODO inbox: None, // TODO
can_edit: Some(true), can_edit: Some(true),
title: None, title,
icon: None, icon: None,
description: None, description: about,
}; };
let store_tab_info = AppTabStoreInfo { let store_tab_info = AppTabStoreInfo {
@ -293,11 +331,14 @@ impl Verifier {
description: None, description: None,
}; };
Ok(AppTabInfo { Ok((
branch: Some(branch_tab_info), AppTabInfo {
doc: Some(doc_tab_info), branch: Some(branch_tab_info),
store: Some(store_tab_info), doc: Some(doc_tab_info),
}) store: Some(store_tab_info),
},
header_branch_info.map(|i| i.id),
))
} }
pub(crate) async fn create_branch_subscription( pub(crate) async fn create_branch_subscription(
@ -317,19 +358,45 @@ impl Verifier {
//return Err(VerifierError::DoubleBranchSubscription); //return Err(VerifierError::DoubleBranchSubscription);
} }
} }
let (heads, head_keys, tab_info, header_branch_id, crdt) = {
let repo = self.get_repo(&repo_id, &store_repo)?;
let branch = repo.branch(&branch_id)?;
let repo = self.get_repo(&repo_id, &store_repo)?; let heads: Vec<ObjectId> = branch.current_heads.iter().map(|h| h.id.clone()).collect();
let branch = repo.branch(&branch_id)?; let head_keys: Vec<ObjectKey> =
branch.current_heads.iter().map(|h| h.key.clone()).collect();
//let tx = self.branch_subscriptions.entry(branch).or_insert_with(|| {});
let (tab_info, header_branch_id) =
self.branch_get_tab_info(repo, &branch_id, self.outer.clone())?;
(
heads,
head_keys,
tab_info,
header_branch_id,
branch.crdt.clone(),
)
};
if let Some(header_branch_id) = header_branch_id {
if let Some(returned) = self
.branch_subscriptions
.insert(header_branch_id, tx.clone())
{
if !returned.is_closed() {
returned.close_channel();
}
}
}
//let tx = self.branch_subscriptions.entry(branch).or_insert_with(|| {});
let files = self let files = self
.user_storage .user_storage
.as_ref() .as_ref()
.unwrap() .unwrap()
.branch_get_all_files(&branch_id)?; .branch_get_all_files(&branch_id)?;
let tab_info = Self::branch_get_tab_info(repo, &branch_id, self.outer.clone())?;
// let tab_info = self.user_storage.as_ref().unwrap().branch_get_tab_info( // let tab_info = self.user_storage.as_ref().unwrap().branch_get_tab_info(
// &branch_id, // &branch_id,
// &repo_id, // &repo_id,
@ -356,7 +423,6 @@ impl Verifier {
} }
} }
let crdt = &repo.branch(&branch_id)?.crdt;
let discrete = if crdt.is_graph() { let discrete = if crdt.is_graph() {
None None
} else { } else {
@ -366,7 +432,7 @@ impl Verifier {
.unwrap() .unwrap()
.branch_get_discrete_state(&branch_id) .branch_get_discrete_state(&branch_id)
{ {
Ok(state) => Some(match repo.branch(&branch_id)?.crdt { Ok(state) => Some(match crdt {
BranchCrdt::Automerge(_) => DiscreteState::Automerge(state), BranchCrdt::Automerge(_) => DiscreteState::Automerge(state),
BranchCrdt::YArray(_) => DiscreteState::YArray(state), BranchCrdt::YArray(_) => DiscreteState::YArray(state),
BranchCrdt::YMap(_) => DiscreteState::YMap(state), BranchCrdt::YMap(_) => DiscreteState::YMap(state),
@ -380,8 +446,8 @@ impl Verifier {
}; };
let state = AppState { let state = AppState {
heads: branch.current_heads.iter().map(|h| h.id.clone()).collect(), heads,
head_keys: branch.current_heads.iter().map(|h| h.key.clone()).collect(), head_keys,
graph: if results.is_empty() { graph: if results.is_empty() {
None None
} else { } else {

Loading…
Cancel
Save