Compare commits

..

3 Commits

Author SHA1 Message Date
ee35e6cf1f HTML BIKESHED 2025-12-29 15:41:29 -08:00
3343dcf137 basic grids demo seems to work... 2025-12-29 14:04:03 -08:00
f0d1097f1f qr demo 2025-12-29 08:49:42 -08:00
16 changed files with 491 additions and 39 deletions

View File

@ -1,9 +1,43 @@
# fewd = front end web dev fewd = front end web dev
=====================================================================
this is me (PRH) trying to learn some front end web dev because pixels are this is me (PRH) trying to learn some front end web dev because pixels are
important despite my wishes. important despite my wishes.
# notes Building/Running
---------------------------------------------------------------------
## goal queue ### Prereqs
1. [Install Erlang and ZX](https://git.qpq.swiss/QPQ-AG/research-megadoc/wiki/Installing-Erlang-and-zx)
2. **DEV ONLY**: `apt install node-typescript` (Devuan Excalibur)
This is needed if you want to **edit** the `.ts` files found in
`/priv/static/js/ts/*.ts`. The built JS files are under version control and can
be found in `/priv/static/js/dist/`
### Building/Running HTTP Server
If you are only changing the Erlang or simply just want to run the program
without developing it, then just run
```
zxh runlocal
```
### Building TS->JS
**This is only necessary if you edited the `.ts` files and want to transpile
them over to JS.**
This requires you installed `tsc` as above.
```
make tsc
```
If you're doing development you may want
```
make watch
```

0
priv/skel/css.css Normal file
View File

20
priv/skel/html.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>FIXME</title>
<link rel="stylesheet" href="/css/default.css">
</head>
<body>
<div id="titlebar">
<div class="content">
<a href="/" class="tb-home">Home</a>
</div>
</div>
<div class="content">
<h1 class="content-title">FEWD: FIXME</h1>
</div>
</body>
</html>

9
priv/skel/ts.ts Normal file
View File

@ -0,0 +1,9 @@
/**
* Title: Title
* Description: Description
* Author: Peter Harpending <peterharpending@qpq.swiss>
* Date: YYYY-MM-DD
* Last-Updated: YYYY-MM-DD
*
* @module
*/

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Basic GRIDS Demo</title>
<link rel="stylesheet" href="/css/default.css">
</head>
<body>
<div id="titlebar">
<div class="content">
<a href="/" class="tb-home">Home</a>
</div>
</div>
<div class="content">
<h1 class="content-title">FEWD: GRIDS DEMO</h1>
<h2>Making a Spend</h2>
<label for="grids-n">Network ID:</label>
<input type = "text"
id = "grids-n"
value = "groot.testnet"
disabled>
<br>
<label for="grids-r">Recipient:</label>
<input type = "text"
id = "grids-r"
value = "ak_n6aVQ6PkBdVdv7kRRcfnzDVmBsH6hqEwVWSB6UAEb3kkjrPMe"
disabled>
<br>
<label for="grids-a">Amount (P):</label>
<input type = "number"
id = "grids-a"
value = "6000000">
<br>
<label for="grids-p">Payload:</label>
<input type = "text"
id = "grids-p"
value = "test payload">
<br>
<input type = "button"
id = "grids-submit"
value = "Generate">
<br>
<textarea disabled id="grids-url" hidden></textarea>
<br>
<img id="grids-png" hidden>
</div>
<script src="/js/dist/grids-basic.js"></script>
</body>
</html>

View File

@ -17,6 +17,7 @@
<ul> <ul>
<li><a href="/echo.html">Echo</a></li> <li><a href="/echo.html">Echo</a></li>
<li><a href="/grids-basic.html">GRIDS: Basic Demo</a></li>
<li><a href="/wfc.html">WFC</a></li> <li><a href="/wfc.html">WFC</a></li>
</ul> </ul>
</div> </div>

29
priv/static/js/dist/grids-basic.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
/**
* Title: GRIDS Basic Page Script
* Description: Page Script for /grids-basic.html
* Author: Peter Harpending <peterharpending@qpq.swiss>
* Date: 2025-12-29
* Last-Updated: 2025-12-29
*
* @module
*/
/**
* Runs on page load
*/
declare function main(): Promise<void>;
declare function on_submit(n_input: HTMLInputElement, r_input: HTMLInputElement, a_input: HTMLInputElement, p_input: HTMLInputElement, grids_url_elt: HTMLTextAreaElement, grids_png_elt: HTMLImageElement): Promise<void>;
type Safe<t> = {
ok: true;
result: t;
} | {
ok: false;
error: string;
};
type GridsResult = {
url: string;
png_base64: string;
};
/**
* gets the grids url
*/
declare function grids_request(net_id: string, recipient: string, amount: number, payload: string): Promise<Safe<GridsResult>>;

83
priv/static/js/dist/grids-basic.js vendored Normal file
View File

@ -0,0 +1,83 @@
"use strict";
/**
* Title: GRIDS Basic Page Script
* Description: Page Script for /grids-basic.html
* Author: Peter Harpending <peterharpending@qpq.swiss>
* Date: 2025-12-29
* Last-Updated: 2025-12-29
*
* @module
*/
main();
/**
* Runs on page load
*/
async function main() {
let n_input = document.getElementById('grids-n');
let r_input = document.getElementById('grids-r');
let a_input = document.getElementById('grids-a');
let p_input = document.getElementById('grids-p');
let submit_btn = document.getElementById('grids-submit');
let grids_url_elt = document.getElementById('grids-url');
let grids_png_elt = document.getElementById('grids-png');
// Page initialization
submit_btn.addEventListener('click', async function (e) {
await on_submit(n_input, r_input, a_input, p_input, grids_url_elt, grids_png_elt);
});
// enable buttons
submit_btn.disabled = false;
}
async function on_submit(n_input, r_input, a_input, p_input, grids_url_elt, grids_png_elt) {
// pull out values
let network_id = n_input.value;
let recipient = r_input.value;
let amount = parseInt(a_input.value);
let payload = p_input.value;
let result = await grids_request(network_id, recipient, amount, payload);
// show url field and png
if (result.ok) {
let url = result.result.url;
let png_base64 = result.result.png_base64;
let src_prefix = 'data:image/png;base64,';
let src = src_prefix + png_base64;
grids_url_elt.innerText = url;
grids_png_elt.src = src;
grids_url_elt.hidden = false;
grids_png_elt.hidden = false;
}
else {
alert('ERROR: ' + result.error);
}
}
/**
* gets the grids url
*/
async function grids_request(net_id, recipient, amount, payload) {
// format for network transmission
let obj = { 'network_id': net_id,
'recipient': recipient,
'amount': amount,
'payload': payload };
let obj_text = JSON.stringify(obj, undefined, 4);
let url = '/grids-spend';
let req_options = { method: 'POST',
headers: { 'content-type': 'application/json' },
body: obj_text };
let result = { ok: false,
error: 'IT DO BE LIKE THAT MISTA STANCIL' };
try {
let response = await fetch(url, req_options);
if (response.ok)
result = await response.json();
else {
console.log('bad http response:', response);
result = { ok: false, error: 'BAD HTTP RESPONSE' };
}
}
catch (x) {
console.log('network error:', x);
result = { ok: false, error: 'NETWORK ERROR' };
}
return result;
}
//# sourceMappingURL=grids-basic.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"grids-basic.js","sourceRoot":"","sources":["../ts/grids-basic.ts"],"names":[],"mappings":"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"}

View File

@ -1 +1 @@
{"version":3,"file":"libfewd.js","sourceRoot":"","sources":["../ts/libfewd.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,WAAW,EACX,qBAAqB,EACxB,CAAC;AAGF,SACA,WAAW,CACN,gBAAmC,EACnC,cAAsC,EACtC,UAAyB;IAG1B,+DAA+D;IAC/D,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,aAAa,GAAW,cAAc,CAAC,YAAY,CAAC;QACxD,sCAAsC;QACtC,IAAI,aAAa,GAAG,UAAU;YAC1B,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;;YAE3D,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAChE,CAAC;AACL,CAAC;AAGD,SACA,qBAAqB,CAChB,gBAAmC,EACnC,cAAsC;IAGvC,IAAI,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAC3B,mBAAmB;QACnB,cAAc,CAAC,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC;IAC3D,CAAC;AACL,CAAC"} {"version":3,"file":"libfewd.js","sourceRoot":"","sources":["../ts/libfewd.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,WAAW,EACX,qBAAqB,EACxB,CAAC;AAGF,SACA,WAAW,CACN,gBAAmC,EACnC,cAAsC,EACtC,UAAyB;IAG1B,+DAA+D;IAC/D,IAAI,gBAAgB,CAAC,OAAO,EAAE;QAC1B,IAAI,aAAa,GAAW,cAAc,CAAC,YAAY,CAAC;QACxD,sCAAsC;QACtC,IAAI,aAAa,GAAG,UAAU;YAC1B,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;;YAE3D,cAAc,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;KAC/D;AACL,CAAC;AAGD,SACA,qBAAqB,CAChB,gBAAmC,EACnC,cAAsC;IAGvC,IAAI,gBAAgB,CAAC,OAAO,EAAE;QAC1B,mBAAmB;QACnB,cAAc,CAAC,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC;KAC1D;AACL,CAAC"}

View File

@ -1 +1 @@
{"version":3,"file":"wfc.js","sourceRoot":"","sources":["../ts/wfc.ts"],"names":[],"mappings":"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"} {"version":3,"file":"wfc.js","sourceRoot":"","sources":["../ts/wfc.ts"],"names":[],"mappings":"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"}

View File

@ -0,0 +1,131 @@
/**
* Title: GRIDS Basic Page Script
* Description: Page Script for /grids-basic.html
* Author: Peter Harpending <peterharpending@qpq.swiss>
* Date: 2025-12-29
* Last-Updated: 2025-12-29
*
* @module
*/
main();
/**
* Runs on page load
*/
async function
main
()
{
let n_input = document.getElementById('grids-n') as HTMLInputElement;
let r_input = document.getElementById('grids-r') as HTMLInputElement;
let a_input = document.getElementById('grids-a') as HTMLInputElement;
let p_input = document.getElementById('grids-p') as HTMLInputElement;
let submit_btn = document.getElementById('grids-submit') as HTMLInputElement;
let grids_url_elt = document.getElementById('grids-url') as HTMLTextAreaElement;
let grids_png_elt = document.getElementById('grids-png') as HTMLImageElement;
// Page initialization
submit_btn.addEventListener(
'click',
async function(e) {
await on_submit(n_input, r_input, a_input, p_input, grids_url_elt, grids_png_elt)
}
);
// enable buttons
submit_btn.disabled = false;
}
async function
on_submit
(n_input : HTMLInputElement,
r_input : HTMLInputElement,
a_input : HTMLInputElement,
p_input : HTMLInputElement,
grids_url_elt : HTMLTextAreaElement,
grids_png_elt : HTMLImageElement)
: Promise<void>
{
// pull out values
let network_id : string = n_input.value;
let recipient : string = r_input.value;
let amount : number = parseInt(a_input.value);
let payload : string = p_input.value;
let result: Safe<GridsResult> = await grids_request(network_id, recipient, amount, payload);
// show url field and png
if (result.ok) {
let url : string = result.result.url;
let png_base64 : string = result.result.png_base64;
let src_prefix : string = 'data:image/png;base64,'
let src = src_prefix + png_base64;
grids_url_elt.innerText = url;
grids_png_elt.src = src;
grids_url_elt.hidden = false;
grids_png_elt.hidden = false;
}
else {
alert('ERROR: ' + result.error);
}
}
type Safe<t> = {ok: true, result: t}
| {ok: false, error: string};
type GridsResult = {url : string,
png_base64: string};
/**
* gets the grids url
*/
async function
grids_request
(net_id : string,
recipient : string,
amount : number,
payload : string)
: Promise<Safe<GridsResult>>
{
// format for network transmission
let obj : object = {'network_id' : net_id,
'recipient' : recipient,
'amount' : amount,
'payload' : payload};
let obj_text : string = JSON.stringify(obj, undefined, 4);
let url = '/grids-spend';
let req_options = {method: 'POST',
headers: {'content-type': 'application/json'},
body: obj_text};
let result: Safe<GridsResult> =
{ok : false,
error : 'IT DO BE LIKE THAT MISTA STANCIL'};
try {
let response : Response = await fetch(url, req_options);
if (response.ok)
result = await response.json() as Safe<GridsResult>;
else {
console.log('bad http response:', response);
result = {ok: false, error: 'BAD HTTP RESPONSE'};
}
}
catch (x: any) {
console.log('network error:', x);
result = {ok: false, error: 'NETWORK ERROR'};
}
return result;
}

View File

@ -9,7 +9,7 @@
-export([ -export([
%% caller context %% caller context
get_url/2, mk_spend/4,
%% api %% api
start_link/0, start_link/0,
@ -21,8 +21,6 @@
-include("$zx_include/zx_logger.hrl"). -include("$zx_include/zx_logger.hrl").
-type mh_str() :: string().
%% for craig's autism %% for craig's autism
-record(sp, -record(sp,
{recipient :: string(), {recipient :: string(),
@ -30,13 +28,11 @@
payload :: binary()}). payload :: binary()}).
-type search_pattern() :: #sp{}. -type search_pattern() :: #sp{}.
-type sp() :: search_pattern().
-record(s, -record(s,
{current_gen_height :: pos_integer(), {looking_for = [] :: [{sp(), NotifyWhenSeen :: pid()}]}).
current_gen_hash :: string(),
current_gen_seen_mb_hashes :: [mh_str()],
past_gen_seen_mb_hashes :: [mh_str()],
looking_for :: [search_pattern()]}).
-type state() :: #s{}. -type state() :: #s{}.
@ -44,14 +40,20 @@
%% caller context %% caller context
%%----------------------------------------------------------------------------- %%-----------------------------------------------------------------------------
-spec get_url(Amount, Payload) -> {ok, URL, QR_PNG} | {error, term()} -spec mk_spend(NetworkId, Recipient, Amount, Payload) -> Result
when Amount :: none | pos_integer(), when NetworkId :: string(),
Payload :: none | binary(), Recipient :: string(),
URL :: string(), Amount :: non_neg_integer(),
QR_PNG :: binary(). Payload :: binary(),
Result :: {ok, URL, QR_PNG}
| {error, string()},
URL :: string(),
QR_PNG :: binary().
% @doc
% Very important: amount MUST be an integer >= 0
get_url(Amount, Payload) -> mk_spend(NetworkId, Recipient, Amount, Payload) ->
gen_server:call(?MODULE, {get_url, Amount, Payload}). gen_server:call(?MODULE, {mk_spend, NetworkId, Recipient, Amount, Payload}).
%% gen_server callbacks %% gen_server callbacks
@ -71,8 +73,8 @@ init(none) ->
{ok, InitState}. {ok, InitState}.
handle_call({get_url, Amount, Payload}, From, State) -> handle_call({mk_spend, NetworkId, Recipient, Amount, Payload}, From, State) ->
case i_get_url(Amount, Payload, From, State) of case i_mk_spend(NetworkId, Recipient, Amount, Payload, From, State) of
{ok, URL, PNG, NewState} -> {ok, URL, PNG, NewState} ->
{reply, {ok, URL, PNG}, NewState}; {reply, {ok, URL, PNG}, NewState};
Error -> Error ->
@ -104,19 +106,67 @@ terminate(_, _) ->
%% internals %% internals
%%----------------------------------------------------------------------------- %%-----------------------------------------------------------------------------
-spec i_get_url(Amount, Payload, From, State) -> Result -spec i_mk_spend(NetworkId, Recipient, Amount, Payload, From, State) -> Result
when Amount :: none | pos_integer(), when NetworkId :: string(),
Payload :: none | binary(), Recipient :: string(),
From :: {pid(), reference()}, Amount :: non_neg_integer(),
State :: state(), Payload :: binary(),
URL :: string(), From :: {pid(), reference()},
QR_PNG :: binary(). State :: state(),
Result :: {ok, URL, QR_PNG, NewState} Result :: {ok, URL, QR_PNG, NewState}
| {error, term()}. | {error, string()},
URL :: string(),
QR_PNG :: binary(),
NewState :: state().
i_get_url(Amount, Payload, From, State) -> i_mk_spend(NetworkId, Recipient, Amount, Payload, {FromPID, _}, State)
NetworkId = fewd:network_id(), when is_integer(Amount), Amount >= 0,
Pubkey = fewd:pubkey(), is_binary(Payload) ->
URL = gmgrids:encode({spend, NetworkId, Pubkey}, URL = gmgrids:encode({spend, NetworkId, Recipient},
[{amount, Amount}, {payload, Payload}]), [{amount, Amount},
{payload, Payload}]),
URLBin = unicode:characters_to_binary(URL),
PNG = qr:encode_png(URLBin),
case i_register(Recipient, Amount, Payload, FromPID, State) of
{ok, NewState} -> {ok, URL, PNG, NewState};
Error -> Error
end;
i_mk_spend(_, _, Amount, _, _, _) when (not is_integer(Amount)) ->
{error, "non_integer_amount"};
i_mk_spend(_, _, Amount, _, _, _) when Amount < 0 ->
{error, "negative_amount"};
i_mk_spend(_, _, _, _, _, _) ->
{error, "bad_payload"}.
i_register(Recipient, Amount, Payload, FromPID, State = #s{looking_for = Patterns}) ->
SP = i_sp(Recipient, Amount, Payload),
case i_lookup(SP, State) of
not_found ->
NewPattern = {SP, FromPID},
NewPatterns = [NewPattern | Patterns],
NewState = State#s{looking_for = NewPatterns},
{ok, NewState};
{found, _} ->
{error, already_registered}
end.
%% future proofing
i_sp(Recipient, Amount, Payload) ->
{sp, Recipient, Amount, Payload}.
-spec i_lookup(SearchPattern, State) -> Result
when SearchPattern :: sp(),
State :: state(),
Result :: {found, NotifyPID :: pid()}
| not_found.
% @private look up search pattern and see if we're looking for it
i_lookup(SearchPattern, State) ->
#s{looking_for = Patterns} = State,
case lists:keyfind(SearchPattern, 1, Patterns) of
false -> not_found;
{_, Notify} -> {found, Notify}
end.

View File

@ -238,6 +238,7 @@ route(Sock, get, Route, Request, Received) ->
end; end;
route(Sock, post, Route, Request, Received) -> route(Sock, post, Route, Request, Received) ->
case Route of case Route of
<<"/grids-spend">> -> grids_spend(Sock, Request) , Received;
<<"/wfcin">> -> wfcin(Sock, Request) , Received; <<"/wfcin">> -> wfcin(Sock, Request) , Received;
_ -> fd_httpd_utils:http_err(Sock, 404) , Received _ -> fd_httpd_utils:http_err(Sock, 404) , Received
end; end;
@ -319,6 +320,36 @@ ws_echo_loop(Sock, Frames, Received) ->
error(Error) error(Error)
end. end.
%% ------------------------------
%% grids
%% ------------------------------
grids_spend(Sock, #request{enctype = json,
body = B = #{"network_id" := NetId,
"recipient" := Recipient,
"amount" := Amount,
"payload" := Payload}}) ->
tell("grids_spend good request: ~tp", [B]),
RespObj =
case fd_gridsd:mk_spend(NetId, Recipient, Amount, unicode:characters_to_binary(Payload)) of
{ok, URL, PNG} ->
#{"ok" => true,
"result" => #{"url" => URL,
"png_base64" => unicode:characters_to_list(base64:encode(PNG))}};
{error, String} ->
#{"ok" => false,
"error" => String}
end,
Body = zj:encode(RespObj),
% update cache with new context
Response = #response{headers = [{"content-type", "application/json"}],
body = Body},
fd_httpd_utils:respond(Sock, Response);
grids_spend(Sock, Request) ->
tell("grids_spend: bad request: ~tp", [Request]),
fd_httpd_utils:http_err(Sock, 400).
%% ------------------------------ %% ------------------------------
%% wfc %% wfc

View File

@ -9,7 +9,7 @@
-copyright("Peter Harpending <peterharpending@qpq.swiss>"). -copyright("Peter Harpending <peterharpending@qpq.swiss>").
-license("BSD-2-Clause-FreeBSD"). -license("BSD-2-Clause-FreeBSD").
-export([network_id/0, pubkey/0]). -export([network_id/0, pubkey/0, akstr/0]).
-export([listen/1, ignore/0]). -export([listen/1, ignore/0]).
-export([start/2, stop/1]). -export([start/2, stop/1]).
@ -17,7 +17,8 @@
network_id() -> "groot.testnet". network_id() -> "groot.testnet".
pubkey() -> pad32("fewd demo"). pubkey() -> pad32(<<"fewd demo">>).
akstr() -> gmgrids:akstr(pubkey()).
pad32(Bytes) -> pad32(Bytes) ->

View File

@ -7,6 +7,9 @@
{package_id,{"otpr","fewd",{0,2,0}}}. {package_id,{"otpr","fewd",{0,2,0}}}.
{deps,[{"otpr","hakuzaru",{0,7,0}}, {deps,[{"otpr","hakuzaru",{0,7,0}},
{"otpr","qr",{0,1,0}}, {"otpr","qr",{0,1,0}},
{"otpr","gmserialization",{0,1,3}},
{"otpr","eblake2",{1,0,1}},
{"otpr","base58",{0,1,1}},
{"otpr","zj",{1,1,2}}]}. {"otpr","zj",{1,1,2}}]}.
{key_name,none}. {key_name,none}.
{a_email,"peterharpending@qpq.swiss"}. {a_email,"peterharpending@qpq.swiss"}.