history pane

pull/37/head
Niko PLP 3 months ago
parent 9659f1bbd2
commit f7ee5457ef
  1. 13
      Cargo.lock
  2. 5
      ng-app/src/apps/ListView.svelte
  3. 55
      ng-app/src/lib/FullLayout.svelte
  4. 37
      ng-app/src/lib/Pane.svelte
  5. 4
      ng-app/src/lib/Test.svelte
  6. 2
      ng-app/src/lib/components/BranchIcon.svelte
  7. 155
      ng-app/src/lib/panes/History.svelte
  8. 0
      ng-app/src/lib/panes/history/LICENSE.md
  9. 0
      ng-app/src/lib/panes/history/gitgraph-core/branch.ts
  10. 0
      ng-app/src/lib/panes/history/gitgraph-core/branches-paths.ts
  11. 0
      ng-app/src/lib/panes/history/gitgraph-core/commit.ts
  12. 59
      ng-app/src/lib/panes/history/gitgraph-core/gitgraph-user-api.ts
  13. 0
      ng-app/src/lib/panes/history/gitgraph-core/gitgraph.ts
  14. 0
      ng-app/src/lib/panes/history/gitgraph-core/graph-rows.ts
  15. 0
      ng-app/src/lib/panes/history/gitgraph-core/index.ts
  16. 0
      ng-app/src/lib/panes/history/gitgraph-core/regular-graph-rows.ts
  17. 0
      ng-app/src/lib/panes/history/gitgraph-core/template.ts
  18. 0
      ng-app/src/lib/panes/history/gitgraph-core/utils.ts
  19. 21
      ng-app/src/lib/panes/history/gitgraph-js/gitgraph.ts
  20. 7
      ng-app/src/lib/panes/history/gitgraph-js/svg-elements.ts
  21. 2
      ng-app/src/lib/panes/history/gitgraph-js/tooltip.ts
  22. 15
      ng-app/src/store.ts
  23. 3
      ng-app/src/styles.css
  24. 20
      ng-app/src/tab.ts
  25. 4
      ng-net/src/app_protocol.rs
  26. 6
      ng-repo/Cargo.toml
  27. 7
      ng-repo/src/commit.rs
  28. 79
      ng-repo/src/repo.rs
  29. 9
      ng-repo/src/types.rs
  30. 45
      ng-repo/src/utils.rs
  31. 15
      ng-sdk-js/src/lib.rs

13
Cargo.lock generated

@ -3459,6 +3459,7 @@ dependencies = [
"futures",
"getrandom 0.2.10",
"gloo-timers",
"lazy_static",
"log",
"num_enum",
"once_cell",
@ -3814,6 +3815,15 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -5928,7 +5938,10 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.6",
"js-sys",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",

@ -22,6 +22,11 @@
</script>
<div class="flex-col">
<h2>ListView</h2>
{#if Array.isArray(commits.history.commits)}
{#each commits.history.commits as c}
<div class="flex"> {c[0]} {JSON.stringify(c[1])}</div>
{/each}
{/if}
<div class="flex">
HEADS: {#each commits.heads as head} {head} , {/each}
</div>

@ -22,7 +22,7 @@
import { link, location, push } from "svelte-spa-router";
import MobileBottomBarItem from "./MobileBottomBarItem.svelte";
import MobileBottomBar from "./MobileBottomBar.svelte";
import NavIcon from "./components/NavIcon.svelte";
import Pane from "./Pane.svelte";
// @ts-ignore
import Logo from "./components/Logo.svelte";
import MenuItem from "./components/MenuItem.svelte";
@ -30,7 +30,7 @@
import BranchIcon from "./components/BranchIcon.svelte";
// @ts-ignore
import { t } from "svelte-i18n";
import { onMount, tick } from "svelte";
import { onMount, onDestroy, tick } from "svelte";
import { cur_tab, cur_viewer, cur_editor, toggle_graph_discrete, cur_tab_update,
available_editors, available_viewers, set_editor, set_viewer, set_view_or_edit, toggle_live_edit,
has_editor_chat, all_files_count, all_comments_count, nav_bar, save, hideMenu, show_modal_menu } from "../tab";
@ -215,6 +215,7 @@
let top;
let shareMenu;
let toolsMenu;
let unsub;
async function scrollToTop() {
await tick();
if (top) top.scrollIntoView();
@ -229,6 +230,24 @@
}
onMount(async () => {
await scrollToTop();
unsub = show_modal_menu.subscribe((new_val) => {
if (!new_val) {
cur_tab_update(ct => {
ct.show_menu = false;
if (panes_available === 0) {
ct.right_pane = "";
ct.folders_pane = false;
ct.toc_pane = false;
}
return ct;
});
}
});
});
onDestroy(() => {
if (unsub) unsub();
});
active_session.subscribe((as) => { if(!as) {
@ -291,37 +310,37 @@
hideMenu();
}
const find = (share:string) => {
const find = () => {
// TODO
hideMenu();
}
const bookmark = (share:string) => {
const bookmark = () => {
// TODO
hideMenu();
}
const annotate = (share:string) => {
const annotate = () => {
// TODO
hideMenu();
}
const openArchive = (share:string) => {
const openArchive = () => {
// TODO
hideMenu();
}
const closeModal = () => {
$show_modal_menu = false;
cur_tab_update(ct => {
ct.show_menu = false;
if (panes_available === 0) {
ct.right_pane = "";
ct.folders_pane = false;
ct.toc_pane = false;
}
return ct;
});
// cur_tab_update(ct => {
// ct.show_menu = false;
// if (panes_available === 0) {
// ct.right_pane = "";
// ct.folders_pane = false;
// ct.toc_pane = false;
// }
// return ct;
// });
}
const closePaneInModal = () => {
@ -657,17 +676,19 @@
<Icon tabindex="-1" class="ml-3 w-8 h-8 text-gray-400 dark:text-white focus:outline-none " variation="outline" color="currentColor" icon={pane_items[$cur_tab.right_pane]} />
<span class="ml-2 inline-block text-gray-500 select-none dark:text-white">{$t(`doc.menu.items.${$cur_tab.right_pane}.label`)}</span>
</div>
<Pane pane_name={$cur_tab.right_pane}/>
{:else if $cur_tab.folders_pane}
<div style="height:44px; background-color: rgb(251, 251, 251);" class="flex items-center">
<Icon tabindex="-1" class="ml-3 w-8 h-8 text-gray-400 dark:text-white focus:outline-none " variation="outline" color="currentColor" icon={pane_items["folders"]} />
<span class="ml-2 inline-block text-gray-500 select-none dark:text-white">{$t("doc.menu.items.folders.label")}</span>
</div>
<Pane pane_name="folders"/>
{:else if $cur_tab.toc_pane}
<div style="height:44px; background-color: rgb(251, 251, 251);" class="flex items-center">
<Icon tabindex="-1" class="ml-3 w-8 h-8 text-gray-400 dark:text-white focus:outline-none " variation="outline" color="currentColor" icon={pane_items["toc"]} />
<span class="ml-2 inline-block text-gray-500 select-none dark:text-white">{$t("doc.menu.items.toc.label")}</span>
</div>
<Pane pane_name="toc"/>
{/if}
</div>
</Modal>
@ -867,7 +888,7 @@
<div class="w-[321px;] full-layout h-full absolute top-0 right-0 bg-white border-l border-l-1 border-gray-200">
<div class="static">
<PaneHeader class="right-0" pane_name={pane_right_used} {pane_items}/>
<Pane pane_name={pane_right_used}/>
</div>
</div>
{/if}

@ -0,0 +1,37 @@
<!--
// 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">
export let pane_name = "";
const panes = {
"history": "History",
};
const load_pane = async (pane_name) => {
let component = await import(`./panes/${panes[pane_name]}.svelte`);
return component.default;
};
</script>
<div>
{#if pane_name}
{#await load_pane(pane_name) then pane}
<div class="flex w-full" style="overflow-wrap: anywhere;">
<svelte:component this={pane}/>
</div>
{/await}
{/if}
</div>

@ -14,7 +14,7 @@
createGitgraph,
templateExtend,
TemplateName,
} from "../history/gitgraph-js/gitgraph";
} from "./panes/history/gitgraph-js/gitgraph";
import ng from "../api";
import {
branch_subscribe,
@ -136,7 +136,7 @@
subject: "niko2",
branch: "C",
author: "",
parents: ["A"],
parents: ["B"],
x: 2,
y: 3,
},

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} >
viewBox="0 0 512 512" fill="currentColor" width="24" height="24" tabindex="-1" class={$$props.class || ''} style={$$props.style || ''}>
<path d="M365.1,74.6c-43.8,0-80.2,36.4-80.2,80.2c0,38.2,27,70.9,64.3,78.3c-0.9,21.4-12.1,33.6-30.8,48.5
c-23.3,17.7-53.2,23.3-74.6,27c-46.6,8.4-71.8,30.8-83,45.7V159.5c16.8-2.8,32.6-12.1,44.8-25.2c13.1-14.9,20.5-33.6,20.5-54.1
C226.2,36.4,189.8,0,146,0S65.7,36.4,65.7,80.2c0,19.6,7.5,38.2,19.6,53.2c11.2,13.1,26.1,21.4,42.9,25.2v195.8

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,155 @@
<!--
// 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,
cannot_load_offline,
online,
get_blob
} from "../../store";
import { get } from "svelte/store";
import { onMount, onDestroy, tick } from "svelte";
import {
Sun,
Cloud,
DocumentPlus,
DocumentMinus,
CircleStack,
Funnel,
FingerPrint,
Key,
Cog,
Icon,
ShieldCheck,
} from "svelte-heros-v2";
import BranchIcon from "../components/BranchIcon.svelte";
import { t } from "svelte-i18n";
import { Button, Progressbar, Spinner, Alert } from "flowbite-svelte";
import { cur_tab, nav_bar, can_have_header, header_icon, header_title, header_description, cur_branch, set_header_in_view, edit_header_button, cur_app, load_official_app } from "../../tab";
import ng from "../../api";
import {
createGitgraph,
templateExtend,
TemplateName,
} from "./history/gitgraph-js/gitgraph";
let gitgraph;
let history = [];
let unsub = () => {};
onMount(async ()=>{
const graphContainer = document.getElementById("graph-container");
gitgraph = createGitgraph(graphContainer, {
template: templateExtend(TemplateName.Metro, {
branch: { label: { display: false } },
commit: { message: { displayAuthor: false, displayHash: false } },
}),
});
let res = await ng.branch_history($active_session.session_id, "did:ng:"+$cur_tab.branch.nuri);
// for (const h of res.history) {
// console.log(h[0], h[1]);
// }
//console.log(res.swimlane_state);
history = [...res.history].reverse();
gitgraph.swimlanes(res.swimlane_state.map((s)=> s || false));
gitgraph.import(res.history.map((h)=>{return {
hash:h[0],
branch:h[1].branch,
author:h[1].author,
parents:h[1].past,
x:h[1].x,
y:h[1].y,
subject:h[1].timestamp,
onClick:()=>openCommit(h[0]),
onMessageClick:()=>openCommit(h[0])
};}));
let branch = branch_subscribe($cur_tab.branch.nuri,false);
unsub = branch.subscribe((b) => {
//console.log("subscription callbak",b.history.commits);
if (Array.isArray(b.history.commits)) {
for (var h; h = b.history.commits.pop(); ) {
//console.log(h);
history.unshift(h);
history = history;
gitgraph.commit({
hash: h[0],
author:h[1].author,
parents:h[1].past,
subject:h[1].timestamp,
onClick:()=>openCommit(h[0]),
onMessageClick:()=>openCommit(h[0])
});
}
}
});
get(branch).history.start();
});
onDestroy( ()=>{
let branch = branch_subscribe($cur_tab.branch.nuri,false);
get(branch).history.stop();
unsub();
});
const openCommit = (id:string) => {
console.log("open commit",id);
}
const commit_type_icons = {
"TransactionGraph": Sun,
"TransactionDiscrete": Cloud,
"TransactionBoth": Sun,
"FileAdd": DocumentPlus,
"FileRemove": DocumentMinus,
"Snapshot": CircleStack,
"Compact": Funnel,
"AsyncSignature": FingerPrint,
"SyncSignature": FingerPrint,
"Branch": BranchIcon,
"UpdateBranch": BranchIcon,
"BranchCapRefresh": Key,
"CapRefreshed": Key,
"Other": Cog,
};
</script>
<div style="width:120px; min-width:120px;font-family: monospace; font: Courier; font-size:16px;">
{#each history as commit}
<div class="w-full commit relative text-gray-500" style="height:60px;" role="button" title={commit[0]} tabindex=0 on:click={()=>openCommit(commit[0])} on:keypress={()=>openCommit(commit[0])}>
{#if commit[1].final_consistency}<ShieldCheck tabindex="-1" class="w-5 h-5 absolute text-primary-600" style="top:9px;right:20px;" />
{:else if commit[1].signature}<ShieldCheck tabindex="-1" class="w-5 h-5 absolute text-green-600" style="top:9px;right:20px;" />
{/if}
<Icon tabindex="-1" class="w-5 h-5 outline-none absolute " style="top:9px;right:0px;" variation="outline" color="currentColor" icon={commit_type_icons[commit[1].commit_type]} />
{#if commit[1].commit_type==="TransactionBoth"}<Cloud tabindex="-1" class="w-5 h-5 absolute " style="top:28px;right:0px;" />{/if}
<b>{commit[0].substring(0,7)}</b><br/>
<span class="text-xs leading-tight">{commit[1].author.substring(0,9)}</span>
</div>
{/each}
</div>
<div style="cursor:pointer;" id="graph-container"></div>
<style>
.commit {
padding: 8px;
}
</style>

@ -97,7 +97,14 @@ class GitgraphUserApi<TNode> {
data["parents"].forEach((parent) => {
let new_lane = this._graph.last_on_swimlanes.indexOf(parent);
if ( new_lane < lane ) {
if (lane != Number.MAX_VALUE)
this._graph.swimlanes[lane] = undefined;
lane = new_lane;
} else {
// we close that lane
//this._graph.swimlanes
this._graph.swimlanes[new_lane] = undefined;
}
});
branch = this._graph.swimlanes[lane];
@ -108,35 +115,39 @@ class GitgraphUserApi<TNode> {
this._graph.last_on_swimlanes[lane] = data["hash"];
} else {
branch = data["hash"];
// this._graph.swimlanes.some((b, col) => {
// console.log("is empty? ",col,!b);
// if (!b) {
// lane = col;
// return true;
// }
// });
// if (!lane) {
this._graph.swimlanes.some((b, col) => {
//console.log("is empty? ",col,!b);
if (!b) {
let r = this._graph.rows.getRowOf(this._graph.last_on_swimlanes[col]);
if (data["parents"].some( (parent) => this._graph.rows.getRowOf(parent) < r ))
return false;
lane = col;
return true;
}
});
if (!lane) {
lane = this._graph.swimlanes.length;
this._graph.swimlanes.push(branch);
this._graph.last_on_swimlanes.push(branch);
// } else {
// this._graph.swimlanes[lane] = branch;
// this._graph.last_on_swimlanes[lane] = branch;
// }
} else {
this._graph.swimlanes[lane] = branch;
this._graph.last_on_swimlanes[lane] = branch;
}
}
data["parents"].forEach((parent) => {
let r = this._graph.rows.getRowOf(parent);
let c = this._graph.commits[r];
let b = c.branch;
if (branch!=b) {
this._graph.swimlanes.forEach((bb, col) => {
if (bb == b) {
this._graph.swimlanes[col] = undefined;
}
});
}
});
// data["parents"].forEach((parent) => {
// let r = this._graph.rows.getRowOf(parent);
// let c = this._graph.commits[r];
// let b = c.branch;
// if (branch!=b) {
// this._graph.swimlanes.forEach((bb, col) => {
// if (bb == b) {
// this._graph.swimlanes[col] = undefined;
// }
// });
// }
// });
// if (!this._graph.branches.has(branch)) {
// this._graph.createBranch({name:branch});

@ -100,7 +100,8 @@ function createGitgraph(
createG({
// Translate graph left => left-most branch label is not cropped (horizontal)
// Translate graph down => top-most commit tooltip is not cropped
translate: { x: 0, y: TOOLTIP_PADDING },
translate: { x: 10, y: TOOLTIP_PADDING },
scale: 0.75,
children: [renderBranchesPaths(branchesPaths), $commits],
}),
);
@ -312,8 +313,17 @@ function createGitgraph(
return message;
}
let msg = commit.message.split(" ");
const text = createText({
content: commit.message,
content: msg[0],
fill: commit.style.message.color || "",
font: commit.style.message.font,
onClick: commit.onMessageClick,
});
const text2 = createText({
content: msg[1],
fill: commit.style.message.color || "",
font: commit.style.message.font,
onClick: commit.onMessageClick,
@ -324,6 +334,13 @@ function createGitgraph(
children: [text],
});
let message2 = createG({
translate: { x: 0, y: commit.style.dot.size*2 },
children: [text2],
});
message.appendChild(message2);
if (commit.body) {
const body = createForeignObject({
width: 600,

@ -49,6 +49,7 @@ interface GOptions {
x: number;
y: number;
};
scale?: number;
fill?: string;
stroke?: string;
strokeWidth?: number;
@ -60,11 +61,11 @@ interface GOptions {
function createG(options: GOptions): SVGGElement {
const g = document.createElementNS(SVG_NAMESPACE, "g");
options.children.forEach((child) => child && g.appendChild(child));
let scale = options.scale ? ` scale(${options.scale})`: "";
if (options.translate) {
g.setAttribute(
"transform",
`translate(${options.translate.x}, ${options.translate.y})`,
`translate(${options.translate.x}, ${options.translate.y})${scale}`,
);
}
@ -118,7 +119,7 @@ function createText(options: TextOptions): SVGTextElement {
}
if (options.font) {
text.setAttribute("style", `font: ${options.font}`);
text.setAttribute("style", `font-family:monospace;font: Courier`);
}
if (options.anchor) {

@ -11,7 +11,7 @@ function createTooltip(commit: Commit): SVGElement {
const path = createPath({ d: "", fill: "#EEE" });
const text = createText({
translate: { x: OFFSET + PADDING, y: 0 },
content: `${commit.hashAbbrev} - ${commit.subject}`,
content: `${commit.hashAbbrev}`,
fill: "#333",
});

@ -452,7 +452,12 @@ export const branch_subscribe = function(nuri:string, in_tab:boolean) {
//console.log("sub");
let already_subscribed = all_branches[nuri];
if (!already_subscribed) {
const { subscribe, set, update } = writable({graph:[], discrete:[], files:[], history: false, heads: []}); // create the underlying writable store
const { subscribe, set, update } = writable({graph:[], discrete:[], files:[], history: {start:()=>{}, stop:()=>{}, take:()=>{}, commits:false}, heads: []}); // create the underlying writable store
update((old)=> {
old.history.start = () => update((o) => {o.history.commits = true; return o;}) ;
old.history.stop = () => update((o) => {o.history.commits = false; return o;}) ;
old.history.take = () => { let res: boolean | Array<{}> = false; update((o) => {res = o.history.commits; o.history.commits = []; return o;}); return res;}
return old;});
let count = 0;
let unsub = () => { };
already_subscribed = {
@ -538,12 +543,12 @@ export const branch_subscribe = function(nuri:string, in_tab:boolean) {
}
}
old.heads.push(response.V0.Patch.commit_id);
if (old.history!==false) {
if (old.history.commits!==false) {
let commit = [response.V0.Patch.commit_id, response.V0.Patch.commit_info];
if (old.history === true) {
old.history = [commit];
if (old.history.commits === true) {
old.history.commits = [commit];
} else {
old.history.push(commit);
old.history.commits.push(commit);
}
}
if (response.V0.Patch.graph) {

@ -34,6 +34,9 @@ td.hljs {
#menu-modal div {
padding: 0;
}
#menu-modal div.commit {
padding: 8px;
}
#menu-modal > button {
display: none;
}

@ -160,19 +160,23 @@ const class_to_viewers_editors = (class_name: string) => {
}
export const open_branch = (nuri: string, in_tab: boolean) => {
all_tabs.update((old) => {
if (!old[nuri]) {
//console.log("creating tab for ",nuri)
old[nuri] = JSON.parse(JSON.stringify(old[""]));
old[nuri].branch.nuri = nuri;
}
return old;
});
if (!get(all_tabs)[nuri]) {
all_tabs.update((old) => {
if (!old[nuri]) {
//console.log("creating tab for ",nuri)
old[nuri] = JSON.parse(JSON.stringify(old[""]));
old[nuri].branch.nuri = nuri;
}
return old;
});
}
if (in_tab) {
cur_branch.set(nuri);
let store_type = get(all_tabs)[nuri].store.store_type;
if (store_type) change_nav_bar(`nav:${store_type}`,get(format)(`doc.${store_type}_store`), undefined);
}
}
export const update_class = (cur_tab, class_name) => {

@ -18,8 +18,8 @@ use ng_repo::errors::NgError;
use ng_repo::log::*;
use ng_repo::repo::CommitInfo;
use ng_repo::types::*;
use ng_repo::utils::decode_overlayid;
use ng_repo::utils::{decode_digest, decode_key, decode_sym_key};
use ng_repo::utils::{decode_overlayid, display_timestamp_local};
use crate::types::*;
@ -144,6 +144,7 @@ pub struct CommitInfoJs {
pub key: String,
pub signature: Option<String>,
pub author: String,
pub timestamp: String,
pub final_consistency: bool,
pub commit_type: CommitType,
pub branch: Option<String>,
@ -158,6 +159,7 @@ impl From<&CommitInfo> for CommitInfoJs {
key: info.key.to_string(),
signature: info.signature.as_ref().map(|s| NuriV0::object_ref(&s)),
author: info.author.clone(),
timestamp: display_timestamp_local(info.timestamp),
final_consistency: info.final_consistency,
commit_type: info.commit_type.clone(),
branch: info.branch.map(|b| b.to_string()),

@ -35,13 +35,14 @@ blake3 = "1.3.1"
chacha20 = "0.9.0"
ed25519-dalek = "1.0.1"
sbbf-rs-safe = "0.3.2"
lazy_static = "1.4.0"
curve25519-dalek = "3.2.0"
threshold_crypto = "0.4.0"
crypto_box = { version = "0.8.2", features = ["seal"] }
zeroize = { version = "1.7.0", features = ["zeroize_derive"] }
base64-url = "2.0.0"
web-time = "0.2.0"
time = { version= "0.3.36", features = ["formatting"] }
time = { version= "0.3.36", features = ["formatting","local-offset"] }
wasm-bindgen = "0.2"
os_info = "3"
current_platform = "0.2.0"
@ -52,4 +53,5 @@ log = "0.4"
getrandom = "0.2.7"
[target.'cfg(target_arch = "wasm32")'.dependencies]
gloo-timers = "0.2.6"
gloo-timers = "0.2.6"
time = { version= "0.3.36", features = ["formatting","local-offset","wasm-bindgen"] }

@ -70,6 +70,7 @@ impl CommitV0 {
branch,
header_keys: headers.1,
quorum,
timestamp: now_timestamp(),
metadata,
body,
});
@ -105,6 +106,7 @@ impl CommitV0 {
branch,
header_keys: headers.1,
quorum,
timestamp: now_timestamp(),
metadata,
body,
});
@ -435,6 +437,10 @@ impl Commit {
self.content().author()
}
pub fn timestamp(&self) -> Timestamp {
self.content().timestamp()
}
pub fn final_consistency(&self) -> bool {
self.content().final_consistency()
}
@ -459,6 +465,7 @@ impl Commit {
key: self.key().unwrap(),
signature: None,
author: repo.get_user_string(self.author()),
timestamp: self.timestamp(),
final_consistency: self.final_consistency(),
commit_type: self.get_type().unwrap(),
branch: None,

@ -167,6 +167,7 @@ pub struct CommitInfo {
pub key: ObjectKey,
pub signature: Option<ObjectRef>,
pub author: String,
pub timestamp: Timestamp,
pub final_consistency: bool,
pub commit_type: CommitType,
pub branch: Option<ObjectId>,
@ -204,6 +205,9 @@ impl Repo {
} else {
let commit_type = cobj.get_type().unwrap();
let acks = cobj.acks();
for a in acks.iter() {
log_debug!("ACKS of {} {}", id.to_string(), a.id.to_string());
}
let (past, real_acks, next_future) = match commit_type {
CommitType::SyncSignature => {
assert_eq!(acks.len(), 1);
@ -221,6 +225,7 @@ impl Repo {
key: o.key().unwrap(),
signature: Some(sign_ref.clone()),
author: self.get_user_string(o.author()),
timestamp: o.timestamp(),
final_consistency: o.final_consistency(),
commit_type: o.get_type().unwrap(),
branch: None,
@ -256,6 +261,7 @@ impl Repo {
key: cobj.key().unwrap(),
signature: None,
author: self.get_user_string(cobj.author()),
timestamp: cobj.timestamp(),
final_consistency: cobj.final_consistency(),
commit_type,
branch: None,
@ -276,9 +282,13 @@ impl Repo {
Ok(root)
}
fn past_is_all_in(past: &Vec<ObjectId>, already_in: &HashMap<ObjectId, ObjectId>) -> bool {
fn past_is_all_in(
past: &Vec<ObjectId>,
already_in: &HashMap<ObjectId, ObjectId>,
coming_from: &ObjectId,
) -> bool {
for p in past {
if !already_in.contains_key(p) {
if !already_in.contains_key(p) && p != coming_from {
return false;
}
}
@ -294,10 +304,10 @@ impl Repo {
//swimlanes: &mut Vec<Vec<ObjectId>>,
) -> Vec<(ObjectId, CommitInfo)> {
let (_, c) = dag.get(id).unwrap();
log_debug!("{id}");
if c.past.len() > 1 && !Self::past_is_all_in(&c.past, already_in) {
//log_debug!("processing {id}");
if c.past.len() > 1 && !Self::past_is_all_in(&c.past, already_in, id) {
// we postpone the merge until all the past commits have been added
// log_debug!("postponed {}", id);
//log_debug!("postponed {}", id);
vec![]
} else {
let (future, mut info) = dag.remove(id).unwrap();
@ -328,7 +338,7 @@ impl Repo {
// swimlane.push(branch.clone());
// }
let mut res = vec![(*id, info)];
let first_child_branch = branch.clone();
let mut first_child_branch = branch.clone();
already_in.insert(*id, branch);
let mut future = Vec::from_iter(future);
future.sort();
@ -345,9 +355,26 @@ impl Repo {
let close = if previous_ordinal > new_ordinal {
let _ = info.branch.insert(branch);
// we close the previous branch
// log_debug!(
// "closing previous {} {} in favor of new {} {}",
// previous_ordinal,
// b,
// new_ordinal,
// branch
// );
&b
} else {
// otherwise we close the new branch
if first_child_branch == branch {
first_child_branch = b;
}
// log_debug!(
// "closing new branch {} {} in favor of previous {} {}",
// new_ordinal,
// branch,
// previous_ordinal,
// b
// );
&branch
};
let i = branches.get(close).unwrap();
@ -356,6 +383,15 @@ impl Repo {
let _ = info.branch.insert(branch);
}
}
// log_debug!(
// "branches_order before children of {child} {:?}",
// branches_order
// .iter()
// .enumerate()
// .map(|(i, b)| b.map_or(format!("{i}:closed"), |bb| format!("{i}:{bb}")))
// .collect::<Vec<String>>()
// .join(" -- ")
// );
res.append(&mut Self::collapse(
child,
dag,
@ -364,18 +400,32 @@ impl Repo {
branches,
//swimlanes,
));
// log_debug!(
// "branches_order after children of {child} {:?}",
// branches_order
// .iter()
// .enumerate()
// .map(|(i, b)| b.map_or(format!("{i}:closed"), |bb| format!("{i}:{bb}")))
// .collect::<Vec<String>>()
// .join(" -- ")
// );
// each other child gets a new branch
if let Some(next) = iterator.peek() {
branch = **next;
if branches.contains_key(*next) {
continue;
}
let mut branch_inserted = false;
let mut first_child_branch_passed = false;
for (i, next_branch) in branches_order.iter_mut().enumerate() {
if let Some(b) = next_branch {
if b == &first_child_branch {
first_child_branch_passed = true;
//log_debug!("first_child_branch_passed");
}
}
if next_branch.is_none() && first_child_branch_passed {
//log_debug!("found empty lane {}, putting branch in it {}", i, branch);
let _ = next_branch.insert(branch.clone());
branches.insert(branch, i);
branch_inserted = true;
@ -384,6 +434,11 @@ impl Repo {
}
if !branch_inserted {
//swimlanes.push(Vec::new());
// log_debug!(
// "adding new lane {}, for branch {}",
// branches_order.len(),
// branch
// );
branches_order.push(Some(branch.clone()));
branches.insert(branch, branches_order.len() - 1);
}
@ -398,13 +453,23 @@ impl Repo {
heads: &[ObjectRef],
) -> Result<(Vec<(ObjectId, CommitInfo)>, Vec<Option<ObjectId>>), VerifierError> {
assert!(!heads.is_empty());
// for h in heads {
// log_debug!("HEAD {}", h.id);
// }
let mut visited = HashMap::new();
let mut root = None;
for id in heads {
if let Ok(cobj) = Commit::load(id.clone(), &self.store, true) {
root = self.load_causal_past(&cobj, &mut visited, None)?;
let r = self.load_causal_past(&cobj, &mut visited, None)?;
//log_debug!("ROOT? {:?}", r.map(|rr| rr.to_string()));
if r.is_some() {
root = r;
}
}
}
// for h in visited.keys() {
// log_debug!("VISITED {}", h);
// }
if root.is_none() {
return Err(VerifierError::MalformedDag);
}

@ -2592,7 +2592,9 @@ pub struct CommitContentV0 {
/// This commit can only be accepted if signed by this quorum
pub quorum: QuorumType,
/// App-specific metadata (commit message, creation time, etc)
pub timestamp: Timestamp,
/// App-specific metadata (commit message?)
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
@ -2618,6 +2620,11 @@ impl CommitContent {
CommitContent::V0(v0) => &v0.author,
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
CommitContent::V0(v0) => v0.timestamp,
}
}
pub fn branch(&self) -> &BranchId {
match self {
CommitContent::V0(v0) => &v0.branch,

@ -14,8 +14,7 @@ use ed25519_dalek::*;
use futures::channel::mpsc;
use rand::rngs::OsRng;
use rand::RngCore;
#[cfg(not(target_arch = "wasm32"))]
use time::OffsetDateTime;
use time::{OffsetDateTime, UtcOffset};
use web_time::{Duration, SystemTime, UNIX_EPOCH};
use zeroize::Zeroize;
@ -220,20 +219,52 @@ pub fn timestamp_after(duration: Duration) -> Timestamp {
/// displays the NextGraph Timestamp in UTC.
#[cfg(not(target_arch = "wasm32"))]
pub fn display_timestamp(ts: &Timestamp) -> String {
let st = SystemTime::UNIX_EPOCH
+ Duration::from_secs(EPOCH_AS_UNIX_TIMESTAMP)
+ Duration::from_secs(*ts as u64 * 60u64);
let dt: OffsetDateTime = st.into();
let dur =
Duration::from_secs(EPOCH_AS_UNIX_TIMESTAMP) + Duration::from_secs(*ts as u64 * 60u64);
let dt: OffsetDateTime = OffsetDateTime::UNIX_EPOCH + dur;
dt.format(&time::format_description::parse("[day]/[month]/[year] [hour]:[minute] UTC").unwrap())
.unwrap()
}
/// displays the NextGraph Timestamp in local time for the history (JS)
pub fn display_timestamp_local(ts: Timestamp) -> String {
let dur = Duration::from_secs(EPOCH_AS_UNIX_TIMESTAMP) + Duration::from_secs(ts as u64 * 60u64);
let dt: OffsetDateTime = OffsetDateTime::UNIX_EPOCH + dur;
let dt = dt.to_offset(TIMEZONE_OFFSET.clone());
dt.format(
&time::format_description::parse("[day]/[month]/[year repr:last_two] [hour]:[minute]")
.unwrap(),
)
.unwrap()
}
use lazy_static::lazy_static;
lazy_static! {
static ref TIMEZONE_OFFSET: UtcOffset = unsafe {
time::util::local_offset::set_soundness(time::util::local_offset::Soundness::Unsound);
UtcOffset::current_local_offset().unwrap()
};
}
pub(crate) type Receiver<T> = mpsc::UnboundedReceiver<T>;
#[cfg(test)]
mod test {
use crate::log::*;
use crate::{
log::*,
utils::{display_timestamp_local, now_timestamp},
};
#[test]
pub fn test_time() {
let time = now_timestamp() + 120; // 2 hours later
log_info!("{}", display_timestamp_local(time));
}
#[test]
pub fn test_locales() {
let list = vec!["C", "c", "aa-bb-cc-dd", "aa-ff_bb.456d"];

@ -270,8 +270,7 @@ pub async fn sparql_query(
let session_id: u64 = serde_wasm_bindgen::from_value::<u64>(session_id)
.map_err(|_| "Invalid session_id".to_string())?;
let nuri = if nuri.is_string() {
NuriV0::new_from(&nuri.as_string().unwrap())
.map_err(|_| "Deserialization error of Nuri".to_string())?
NuriV0::new_from(&nuri.as_string().unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_entire_user_site()
};
@ -320,7 +319,7 @@ pub async fn sparql_update(
) -> 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(|_| "Deserialization error of Nuri".to_string())?;
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_write_query(),
@ -350,8 +349,7 @@ pub async fn sparql_query(
.map_err(|_| "Invalid session_id".to_string())?;
let nuri = if nuri.is_string() {
NuriV0::new_from(&nuri.as_string().unwrap())
.map_err(|_| "Deserialization error of Nuri".to_string())?
NuriV0::new_from(&nuri.as_string().unwrap()).map_err(|e| e.to_string())?
} else {
NuriV0::new_entire_user_site()
};
@ -416,13 +414,13 @@ pub async fn rdf_dump(session_id: JsValue) -> Result<String, String> {
}
#[wasm_bindgen]
pub async fn branch_history(session_id: JsValue) -> Result<JsValue, String> {
pub async fn branch_history(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 request = AppRequest::V0(AppRequestV0 {
command: AppRequestCommandV0::new_history(),
nuri: NuriV0::new_private_store_target(),
nuri: NuriV0::new_from(&nuri).map_err(|e| e.to_string())?,
payload: None,
session_id,
});
@ -432,6 +430,7 @@ pub async fn branch_history(session_id: JsValue) -> Result<JsValue, String> {
.map_err(|e: NgError| e.to_string())?;
let AppResponse::V0(res) = res;
log_debug!("{:?}", res);
match res {
AppResponseV0::History(s) => Ok(serde_wasm_bindgen::to_value(&s.to_js()).unwrap()),
_ => Err("invalid response".to_string()),
@ -932,7 +931,7 @@ pub async fn file_get_from_private_store(
let session_id: u64 = serde_wasm_bindgen::from_value::<u64>(session_id)
.map_err(|_| "Deserialization error of session_id".to_string())?;
let nuri = NuriV0::new_from(&nuri).map_err(|_| "Deserialization error of Nuri".to_string())?;
let nuri = NuriV0::new_from(&nuri).map_err(|e| e.to_string())?;
let mut request = AppRequest::new(AppRequestCommandV0::FileGet, nuri.clone(), None);
request.set_session_id(session_id);

Loading…
Cancel
Save