Preliminary support for user input
This commit is contained in:
parent
36acc56fa2
commit
a8544dfd39
|
@ -1,9 +0,0 @@
|
|||
## Client/UI
|
||||
|
||||
Voting consists of staking operations performed by software operated by owners of EOA.
|
||||
|
||||
This software may be referred to as "The UI". It may also be considered "a client".
|
||||
|
||||
It will need to be a network-connected application. It will need a certain minimum of RAM,
|
||||
and for some features disk storage,
|
||||
and for some features uptime .
|
|
@ -1,5 +1,18 @@
|
|||
# Client Operations
|
||||
## Client
|
||||
|
||||
Client must communicate with one or more servers.
|
||||
Clients play a key role in an MVPR DAO.
|
||||
|
||||
Client must build a local view
|
||||
Clients must be operated by reputation holders.
|
||||
|
||||
Clients are the agents that submit posts to the forum, initiate validation pools, and vote in validation pools.
|
||||
|
||||
We sometimes refer to the client as "the UI".
|
||||
|
||||
It will need to be a network-connected application. It will need a certain minimum of RAM,
|
||||
and for some features disk storage,
|
||||
and for some features uptime .
|
||||
|
||||
The behavior of the client constitutes what we refer to as the DAO's "soft protocols".
|
||||
|
||||
Malicious actors may freely modify their own client's behavior.
|
||||
Therefore honest clients must engage in policing to preserve the integrity of the network.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export class Client {
|
||||
constructor(dao, expert) {
|
||||
this.dao = dao;
|
||||
this.expert = expert;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,9 @@ export class Box {
|
|||
constructor(name, parentEl, options = {}) {
|
||||
this.name = name;
|
||||
this.el = document.createElement('div');
|
||||
this.el.id = `box_${randomID()}`;
|
||||
this.el.box = this;
|
||||
const id = options.id ?? randomID();
|
||||
this.el.id = `${parentEl.id}_box_${id}`;
|
||||
this.el.classList.add('box');
|
||||
if (name) {
|
||||
this.el.setAttribute('box-name', name);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { Box } from './box.js';
|
||||
import { Form } from './form.js';
|
||||
|
||||
/**
|
||||
* @example
|
||||
* ```typescript
|
||||
* const doc = new Document();
|
||||
* const form1 = doc.form();
|
||||
* ```
|
||||
*/
|
||||
export class Document extends Box {
|
||||
form() {
|
||||
return this.addElement(new Form(this));
|
||||
}
|
||||
|
||||
remarks(text, opts) {
|
||||
return this.addElement(new Box('Remark', this.el, opts).setInnerHTML(text));
|
||||
}
|
||||
|
||||
addElement(element) {
|
||||
this.elements = this.elements ?? [];
|
||||
this.elements.push(element);
|
||||
return this;
|
||||
}
|
||||
|
||||
get lastElement() {
|
||||
if (!this.elements?.length) return null;
|
||||
return this.elements[this.elements.length - 1];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import { randomID } from '../../util/helpers.js';
|
||||
import { Box } from './box.js';
|
||||
|
||||
const updateValuesOnEventTypes = ['keyup', 'mouseup'];
|
||||
|
||||
export class FormElement extends Box {
|
||||
constructor(name, parentEl, opts) {
|
||||
super(name, parentEl, opts);
|
||||
this.id = opts.id ?? name;
|
||||
const { cb } = opts;
|
||||
if (cb) {
|
||||
updateValuesOnEventTypes.forEach((eventType) => this.el.addEventListener(eventType, () => {
|
||||
cb(this);
|
||||
}));
|
||||
cb(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Button extends FormElement { }
|
||||
|
||||
export class TextField extends FormElement {
|
||||
constructor(name, parentEl, opts) {
|
||||
super(name, parentEl, opts);
|
||||
this.input = document.createElement('input');
|
||||
this.el.appendChild(this.input);
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.input?.value;
|
||||
}
|
||||
}
|
||||
|
||||
export class TextArea extends FormElement { }
|
||||
|
||||
export class Form {
|
||||
constructor(document, opts = {}) {
|
||||
this.document = document;
|
||||
this.items = [];
|
||||
this.id = opts.id ?? `form_${randomID()}`;
|
||||
}
|
||||
|
||||
button(opts) {
|
||||
this.items.push(new Button(opts.name, this.document.el, opts));
|
||||
return this;
|
||||
}
|
||||
|
||||
textField(opts) {
|
||||
this.items.push(new TextField(opts.name, this.document.el, opts));
|
||||
return this;
|
||||
}
|
||||
|
||||
textArea(opts) {
|
||||
this.items.push(new TextArea(opts.name, this.document.el, opts));
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import { Table } from './table.js';
|
|||
import { Flowchart } from './flowchart.js';
|
||||
import { Controls } from './controls.js';
|
||||
import { Box } from './box.js';
|
||||
import { Document } from './document.js';
|
||||
|
||||
export class Scene {
|
||||
constructor(name, rootBox) {
|
||||
|
@ -86,6 +87,24 @@ export class Scene {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {(Document): Document} cb
|
||||
* @returns {Scene}
|
||||
*/
|
||||
withDocument(name, cb) {
|
||||
this.documents = this.documents ?? [];
|
||||
const doc = new Document(name, this.middleSection.el);
|
||||
this.documents.push(cb ? cb(doc) : doc);
|
||||
return this;
|
||||
}
|
||||
|
||||
get lastDocument() {
|
||||
if (!this.documents?.length) return null;
|
||||
return this.documents[this.documents.length - 1];
|
||||
}
|
||||
|
||||
registerActor(actor) {
|
||||
this.actors.add(actor);
|
||||
if (actor.options.announce) {
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
<li><a href="./tests/forum11.test.html">Multiple posts with overlapping authors</a></li>
|
||||
</ol>
|
||||
</ul>
|
||||
<ul>
|
||||
<h3>Client</h3>
|
||||
<ol>
|
||||
<li><a href="./tests/client1.test.html">Expert can run a client</a></li>
|
||||
</ol>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="./tests/vm.test.html">VM</a></li>
|
||||
</ul>
|
||||
|
@ -38,6 +44,7 @@
|
|||
<li><a href="./tests/debounce.test.html">Debounce</a></li>
|
||||
<li><a href="./tests/flowchart.test.html">Flowchart</a></li>
|
||||
<li><a href="./tests/mocha.test.html">Mocha</a></li>
|
||||
<li><a href="./tests/input.test.html">Input</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<h4><a href="./tests/all.test.html">All</a></h4>
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
<script type="module" src="./scripts/forum/forum9.test.js"></script>
|
||||
<script type="module" src="./scripts/forum/forum10.test.js"></script>
|
||||
<script type="module" src="./scripts/forum/forum11.test.js"></script>
|
||||
<script type="module" src="./scripts/input.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<title>Client test 1</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2><a href="../">DGF Tests</a></h2>
|
||||
<div id="mocha"></div>
|
||||
<div id="scene"></div>
|
||||
</body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
|
||||
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script type="module" src="./scripts/client/client1.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
});
|
||||
chai.should();
|
||||
</script>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<title>Input</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
|
||||
<link type="text/css" rel="stylesheet" href="../index.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2><a href="../">DGF Tests</a></h2>
|
||||
<div id="mocha"></div>
|
||||
<div id="scene"></div>
|
||||
</body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
|
||||
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://unpkg.com/mocha/mocha.js"></script>
|
||||
<script src="https://unpkg.com/chai/chai.js"></script>
|
||||
<script type="module" src="./scripts/input.test.js"></script>
|
||||
<script defer class="mocha-init">
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
});
|
||||
window.should = chai.should();
|
||||
</script>
|
|
@ -0,0 +1,26 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
import { Client } from '../../../classes/dao/client.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
||||
const forumTest = new ForumTest({ displayAuthors: false });
|
||||
let client;
|
||||
|
||||
before(async () => {
|
||||
await forumTest.setup();
|
||||
await forumTest.newExpert();
|
||||
await forumTest.newExpert();
|
||||
|
||||
client = new Client(forumTest.dao, forumTest.experts[0]);
|
||||
});
|
||||
|
||||
it('Expert can run a client', async () => {
|
||||
client.should.not.be.undefined;
|
||||
client.dao.should.equal(forumTest.dao);
|
||||
client.expert.should.equal(forumTest.experts[0]);
|
||||
});
|
||||
});
|
||||
|
||||
mochaRun();
|
|
@ -1,9 +1,9 @@
|
|||
import { Box } from '../../../classes/display/box.js';
|
||||
import { Scene } from '../../../classes/display/scene.js';
|
||||
import { Expert } from '../../../classes/actors/expert.js';
|
||||
import { PostContent } from '../../../classes/supporting/post-content.js';
|
||||
import { DAO } from '../../../classes/dao/dao.js';
|
||||
import { delayOrWait } from '../../../classes/display/controls.js';
|
||||
import { Box } from '../../classes/display/box.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { Expert } from '../../classes/actors/expert.js';
|
||||
import { PostContent } from '../../classes/supporting/post-content.js';
|
||||
import { DAO } from '../../classes/dao/dao.js';
|
||||
import { delayOrWait } from '../../classes/display/controls.js';
|
||||
|
||||
export class ForumTest {
|
||||
constructor(options) {
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { EPSILON } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { INCINERATOR_ADDRESS } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { mochaRun } from '../../../util/helpers.js';
|
||||
import { INCINERATOR_ADDRESS } from '../../../util/constants.js';
|
||||
import { ForumTest } from './forum.test-util.js';
|
||||
import { ForumTest } from '../forum.test-util.js';
|
||||
|
||||
describe('Forum', function tests() {
|
||||
this.timeout(0);
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Box } from '../../classes/display/box.js';
|
||||
// import { Document } from '../../classes/display/document.js';
|
||||
import { Scene } from '../../classes/display/scene.js';
|
||||
import { mochaRun } from '../../util/helpers.js';
|
||||
|
||||
const rootElement = document.getElementById('scene');
|
||||
const rootBox = new Box('rootBox', rootElement).flex();
|
||||
const scene = window.scene = new Scene('Input test', rootBox);
|
||||
|
||||
scene.withDocument();
|
||||
|
||||
describe('Document', () => {
|
||||
it('Exists', () => {
|
||||
scene.withDocument('Document', (doc) => doc.remarks('Hello'));
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
it('Accepts input', () => {
|
||||
scene.withDocument('Document', (doc) => doc.form());
|
||||
const doc = scene.lastDocument;
|
||||
const form1 = doc.lastElement;
|
||||
const dvMap = new Map();
|
||||
const updateFieldValueDisplay = ({ name, value }) => {
|
||||
const dv = dvMap.get(name) ?? scene.addDisplayValue(name);
|
||||
dvMap.set(name, dv);
|
||||
dv.set(value);
|
||||
};
|
||||
|
||||
form1.textField({ id: 'input1', name: 'Input 1', cb: updateFieldValueDisplay });
|
||||
doc.remarks('Hmm...!');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
mochaRun();
|
Loading…
Reference in New Issue