Skip to content

Instantly share code, notes, and snippets.

@Lightnet
Created August 26, 2022 01:03
Show Gist options
  • Save Lightnet/ec5dcddcb862e95bb8fa1254d7e46cf1 to your computer and use it in GitHub Desktop.
Save Lightnet/ec5dcddcb862e95bb8fa1254d7e46cf1 to your computer and use it in GitHub Desktop.
Solid-js gunjs simple ui, account and tests for buildless

By using the solid-js and gun to help improve and keep it simple.

For reason to choose solid-js is that render once. It does not render all by render when it needed. It did say docs some where.

As well to learn some basic using buildless without compiler.

Like react, preact and vuejs it would render all variables chanage.

https://www.solidjs.com/

Solid-js 1.4.0 is about 18 KB file size. Jquery 3.3.1 is about 28.56 KB

Gun file size but depend on the modules.

// CLEAR GUN DATABASE
localStorage.clear();
let gunurl = window.location.origin+'/gun';
//console.log(gunurl);
var gun = Gun(gunurl);
gun.on('hi', peer => {//peer connect
//console.log('connect peer to',peer);
//console.log('peer connect!');
});
gun.on('bye', (peer)=>{// peer disconnect
//console.log('disconnected from', peer);
//console.log('disconnected from peer!');
});
//console.log(String.random(16))
// https://github.com/amark/gun/blob/master/gun.js#L260
//console.log(Gun.state.is)
import {
createSignal
, createEffect
, onMount
, onCleanup
, createMemo
//, on
} from "https://cdn.skypack.dev/solid-js";
import { render } from "https://cdn.skypack.dev/solid-js/web";
import html from "https://cdn.skypack.dev/solid-js/html";
import h from "https://cdn.skypack.dev/solid-js/h";
//const App = () => {
//const [count, setCount] = createSignal(0),
//timer = setInterval(() => setCount(count() + 1), 1000);
//onCleanup(() => clearInterval(timer));
//return html`<div>${count}</div>`;
// or
//return h("div", {}, count);
//};
//render(App, document.body);
let dispose;// for render() clean up
let disposeModal;// for render() clean up
let userName="Guest";
let userPublicKey="";
const PageNavMenu = () =>{
function btnAccount(e){
e.preventDefault();
dispose();
dispose = render(PageAccount, document.getElementById('app'));
}
function btnPrivateMessage(e){
e.preventDefault();
dispose();
dispose = render(PagePrivateMessage, document.getElementById('app'));
}
function btnChatPublic(e){
e.preventDefault();
dispose();
dispose = render(PagePublicChat, document.getElementById('app'));
}
function btnTestLab(e){
e.preventDefault();
dispose();
dispose = render(PageTestLab, document.getElementById('app'));
}
function btnGunGraph(e){
e.preventDefault();
dispose();
dispose = render(PageGunGraph, document.getElementById('app'));
}
function btnToDoList(e){
e.preventDefault();
dispose();
dispose = render(PageToDoList, document.getElementById('app'));
}
return html`<div>
<a href="#" onClick="${btnAccount}"> Account </a> <span> | </span>
<a href="#" onClick="${btnPrivateMessage}"> Private Message </a><span> | </span>
<a href="#" onClick="${btnChatPublic}"> Public Chat </a><span> | </span>
<a href="#" onClick="${btnGunGraph}"> Gun Graph</a><span> | </span>
<a href="#" onClick="${btnToDoList}"> To Do List </a><span> | </span>
<a href="#" onClick="${btnTestLab}"> Test Lab </a><span> | </span>
</div>`;
}
const PageLogin = () => {
const [alias, setAlias] = createSignal("test")
const [passphrase, setPassphrase] = createSignal("12345678")
const [pair, setPair] = createSignal(null)
const [textPair, setTextPair] = createSignal("")
const [isPair, setIsPair] = createSignal(false)
function inputAlias(e){setAlias(e.target.value)}
function inputPassphrase(e){setPassphrase(e.target.value)}
function btnLogin(){
//console.log(alias())
//console.log(passphrase())
let user = gun.user();
if(isPair()){
user.auth(pair(),(ack)=>{//user login pair
if(ack.err){
console.log(ack.err)
return;
}
dispose()
dispose = render(PageAccount, document.getElementById('app'));
});
}else{
user.auth(alias(), passphrase(),(ack)=>{//user login username and password
if(ack.err){
console.log(ack.err)
return;
}
dispose()
dispose = render(PageAccount, document.getElementById('app'));
});
}
}
function btnSignUp(){
dispose()
dispose = render(PageSignUp, document.getElementById('app'));
}
function btnForgot(){
dispose()
dispose = render(PageForgot, document.getElementById('app'));
}
async function btnGeneratePair(){
let _pair= await Gun.SEA.pair();
console.log(_pair)
setPair(_pair)
setTextPair(JSON.stringify(_pair))
}
const displayType = createMemo(() => {
if(isPair()){
return html`
<tr>
<td>
<button onClick="${btnGeneratePair}">Generate Pair</button>
</td>
</tr>
<tr>
<td>
<label>SEA Pair:</label>
</td>
<td>
<textarea value="${textPair}" onInput="${inputAlias}" placeholder="JSON String"/>
</td>
</tr>`;
}else{
return html`<tr>
<td>
<label>Alias:</label>
</td>
<td>
<input value="${alias}" onInput="${inputAlias}"/>
</td>
</tr>
<tr>
<td>
<label>Passphrase:</label>
</td><td>
<input value="${passphrase()}" onInput="${inputPassphrase}"/>
</td>
</tr>`;
}
});
const isCheckPair = createMemo(() => isPair());
function togglePair(){
setIsPair(state=>!state)
}
return html`<div>
<label>Login</label> <span> | </span>
<label> SEA Pair <input type="checkbox" checked="${isCheckPair}" onClick="${togglePair}"> </label>
<table>
<tbody>
${displayType}
<tr>
<td colspan="2">
<button onClick="${btnLogin}">Login</button>
<button onClick="${btnSignUp}">Sign Up</button>
<button onClick="${btnForgot}">Forgot</button>
</td>
</tr>
</tbody>
</table>
</div>`;
}
const PageAccountMenu = (props) =>{
//const [message, setMessage] = createSignal(props.message || "None")
function btnProfile(e){
e.preventDefault();
dispose();
dispose = render(PageAccount, document.getElementById('app'));
}
function btnChangePassphrase(e){
e.preventDefault();
dispose();
dispose = render(PageChangePassphrase, document.getElementById('app'));
}
function btnPassphraseHint(e){
e.preventDefault();
dispose();
dispose = render(PagePassphraseHint, document.getElementById('app'));
}
return html`<div>
<a href="#" onClick="${btnProfile}"> Profile </a> <span> | </span>
<a href="#" onClick="${btnChangePassphrase}"> Change Passphrase </a> <span> | </span>
<a href="#" onClick="${btnPassphraseHint}"> Passphrase Hint </a> <span> | </span>
</div>`;
}
const PageAccount = (props) =>{
return html`<div>
${PageNavMenu()}
${PageAccountMenu()}
<br/>
${PageAliasInfo()}
<br/>
${PageProfile()}
<br/>
${PageSearchProfile()}
</div>`;
}
//${PageSearchProfile()}
const PageSignUp = () => {
const [alias, setAlias] = createSignal("test")
const [passphrase, setPassphrase] = createSignal("12345678")
function inputAlias(e){setAlias(e.target.value)}
function inputPassphrase(e){setPassphrase(e.target.value)}
function btnRegister(){
let user = gun.user();
user.create(alias(), passphrase(),(ack)=>{//create user and password
if(ack.err){
console.log(ack.err);//if user exist or error
return;
}
console.log(ack);//pass if created
//modalmessage("Created " + $('#alias').val() + "!");
});
}
function btnCancel(){
dispose()
dispose = render(PageLogin, document.getElementById('app'));
}
return html`<div>
<label>Register</label>
<table>
<tbody>
<tr>
<td>
<label>Alias:</label>
</td>
<td>
<input value="${alias()}" onInput="${inputAlias}"/>
</td>
</tr>
<tr>
<td>
<label>Passphrase:</label>
</td><td>
<input value="${passphrase()}" onInput="${inputPassphrase}"/>
</td>
</tr>
<tr>
<td colspan="2">
<button onClick="${btnRegister}">Register</button>
<button onClick="${btnCancel}">Cancel</button>
</td>
</tr>
</tbody>
</table>
</div>`;
}
const PageForgot = () => {
const [alias, setAlias] = createSignal("test")
const [question1, setQuestion1] = createSignal("")
const [question2, setQuestion2] = createSignal("")
const [hint, setHint] = createSignal("")
const [status, setStatus] = createSignal("Idle")
function inputAlias(e){setAlias(e.target.value)}
function inputQuestion1(e){setQuestion1(e.target.value)}
function inputQuestion2(e){setQuestion2(e.target.value)}
function inputHint(e){setHint(e.target.value)}
async function btnGetHint(){
setStatus('Checking...')
let _alias = alias();
_alias = await gun.get('~@'+_alias).then(); // reused variable
if(!_alias){//check user exist if not return false.
//modalmessage('Not Found Alias!');
setStatus('Not Found Alias!')
return;
}
let publickey;
for(let obj in _alias){//object
//console.log(obj);
publickey = obj;//property name for public key
}
//console.log(SEA.opt.pub)
publickey = SEA.opt.pub(publickey);//check and convert to key or null?
//console.log(publickey);
let q1 = (question1() || '').trim(); //get id question1 input
let q2 = (question2() || '').trim(); //get id question2 input
if((!q1)||(!q2)){
//console.log('Q Empty!');
//modalmessage('"Question (1 || 2) Empty!"');
setStatus("Question 1|2 Empty!")
return;
}
let to = gun.user(publickey);//get user alias graph
let _hint = await to.get('hint').then();//get encrypt hint key graph
let dec = await Gun.SEA.work(q1,q2);//get fquestion1 and fquestion2 string to mix key
_hint = await Gun.SEA.decrypt(_hint,dec);//get hint and key decrypt message
//console.log("hint:",_hint)
if(_hint){//check if hint is string or null
setHint(_hint)
}else{
//modalmessage("Fail Decrypt!");
setStatus("Fail Decrypt!")
}
}
function btnCancel(){
dispose();
dispose = render(PageLogin, document.getElementById('app'));
}
return html`<div>
<label>Forgot</label>
<table>
<tbody>
<tr>
<td>
<label>Alias:</label>
</td>
<td>
<input value="${alias}" onInput="${inputAlias}"/>
</td>
</tr>
<tr>
<td>
<label>Question #1:</label>
</td><td>
<input value="${question1}" onInput="${inputQuestion1}"/>
</td>
</tr>
<tr>
<td>
<label>Question #2:</label>
</td><td>
<input value="${question2}" onInput="${inputQuestion2}"/>
</td>
</tr>
<tr>
<td>
<label>Hint: </label>
</td><td>
<input value="${hint}" onInput="${inputHint}"/>
</td>
</tr>
<tr>
<td colspan="2">
<label> Status: ${status} </label>
<span style="float:right;">
<button onClick="${btnGetHint}">Hint</button>
<button onClick="${btnCancel}">Cancel</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>`;
}
const PageAliasInfo = () =>{
let user = gun.user();
const [alias, setAlias] = createSignal(user?.is?.alias || "Guest")
const [publicKey, setPublicKey] = createSignal(user?.is?.pub || "")
const [toggleKey, setToggleKey] = createSignal(true)
function copyPubKey(){
console.log("KEY:",publicKey())
navigator.clipboard.writeText(publicKey());
}
function togglePubKey(){
console.log("toggle")
setToggleKey(state=>!state)
}
const isExpand = createMemo(() =>{//watch variable change
if(toggleKey()==true){
return "-"
}else{
return "+"
}
});
const ShowPubKey = createMemo(() =>{//watch variable change
if(toggleKey()==true){
return String(publicKey())
}else{
return "Hidden"
}
});
return html`<div>
<label>Alias: ${alias}</label><br/>
<label onClick="${copyPubKey}">Public Key:</label>
<input value="${ShowPubKey}" readonly />
<label onClick="${togglePubKey}">[${isExpand}]</label>
<label onClick="${copyPubKey}">[copy]</label>
<br/>
</div>`;
}
const PageProfile = () =>{
const [alias, setAlias] = createSignal("")
const [born, setBorn] = createSignal("")
const [education, setEducation] = createSignal("")
const [skills, setSkills] = createSignal("")
function getProfileParam(_name){
return new Promise((resolve,reject)=>{
let user = gun.user();
if(!user.is){ return reject(null)} //check for user auth
user.get('profile').get(_name).once((data)=>{
//console.log(data);
resolve(data)
})
})
}
function inputProfileParam(event){
//console.log("typing...")
//console.log(event.target.name)
if(event.target.name){
if (event.keyCode == 13) {//enter key
//console.log("Enter...")
let user = gun.user();
user.get('profile').get(event.target.name).put(String(event.target.value),ack=>{
if(ack.err){
console.log(`Profile save ${event.target.name} error!`)
return;
}
console.log(ack);
console.log(`Profile save ${event.target.name}!`)
});
return false;
}
}
}
onMount(async ()=>{
setAlias(await getProfileParam('alias'))
setBorn(await getProfileParam('born'))
setEducation(await getProfileParam('education'))
setSkills(await getProfileParam('skills'))
})
return html`<div>
<label>Alias:</label> <input name="alias" value=${alias} onKeyUp="${inputProfileParam}" /><br/>
<label>Born:</label> <input name="born" value=${born} onKeyUp="${inputProfileParam}" /><br/>
<label>Education:</label> <input name="education" value=${education} onKeyUp="${inputProfileParam}" /><br/>
<label>Skills:</label> <input name="skills" value=${skills} onKeyUp="${inputProfileParam}" /><br/>
</div>`;
}
const PageSearchProfile = () =>{
//let user = gun.user();
//console.log(user)
const [publicKey, setPublicKey] = createSignal("")
const [status, setStatus] = createSignal("Idle")
const [name, setName] = createSignal("")
const [alias, setAlias] = createSignal("")
const [born, setBorn] = createSignal("")
const [education , setEducation ] = createSignal("")
const [skills, setSkills] = createSignal("")
async function searchPublicKey(event){
setPublicKey(event.target.value)
setStatus("checking...")
let pub = (publicKey() || "").trim()
if(!pub){
console.log("EMPTY!")
setStatus("EMPTY!")
return;
}
var find = gun.user(pub);
//console.log(find);
let who = await find.then() || {};//get alias information
//console.log(who);
if(!who.alias){//check for alias from gun user
setStatus('No Alias!')
return;
}else{
//console.log(who)
setName(who.alias)
setStatus('Found! ' + who.alias)
}
find.get('profile').map().once((data, key)=>{
//console.log(data)
//console.log(key)
if(key=="alias"){
setAlias(String(data))
}
if(key=="born"){
setBorn(String(data))
}
if(key=="education"){
setEducation(String(data))
}
if(key=="skills"){
setSkills(String(data))
}
});
}
//${PageNavMenu()}
return html`<div>
<div>
<label>Search Public Key:</label> <input value="${publicKey}" onInput="${searchPublicKey}"/> <label>Status: ${status}</label> <br/>
<label>Name: </label> <input value="${name}" /><br/>
<label>Alias: </label> <input value="${alias}" /><br/>
<label>Born:</label> <input value="${born}" /><br/>
<label>Education</label> <input value="${education}" /> <br/>
<label>Skills</label> <input value="${skills}" /> <br/>
</div>`;
}
const PageChangePassphrase = () =>{
const [oldPassphrase, setOldPassphrase] = createSignal("12345678")
const [newPassphrase, setNewPassphrase] = createSignal("12345678")
const [status, setSatus] = createSignal("Idle")
function inputOldPassphrase(e){setOldPassphrase(e.target.value)}
function inputNewPassphrase(e){setNewPassphrase(e.target.value)}
function btnChange(event){
setSatus("Check...");
let user = gun.user();
user.auth(user.is.alias, oldPassphrase(), (ack) => {//user auth call
//console.log(ack);
const check = ack.err || "Saved!";//check if there error else saved message.
//console.log(check);
//setSatus(_status);
setSatus(check);
//modalmessage(status);
},{change: newPassphrase()});//set config to change password
}
function btnCancel(event){
dispose();
dispose = render(PageAccount, document.getElementById('app'));
}
return html`<div>
${PageNavMenu()}
${PageAccountMenu()}
<div>
<label> Change Passphrase </label>
<table>
<tbody>
<tr>
<td>
<label>Old Passphrase:</label>
</td>
<td>
<input value="${oldPassphrase()}" onInput="${inputOldPassphrase}"/>
</td>
</tr>
<tr>
<td>
<label>New Passphrase:</label>
</td>
<td>
<input value="${newPassphrase()}" onInput="${inputNewPassphrase}"/>
</td>
</tr>
<tr>
<td colspan="2">
<label>Status: ${status}</label>
<span style="float:right;">
<button onClick="${btnChange}">Apply</button>
<button onClick="${btnCancel}">Cancel</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>`;
}
const PagePassphraseHint = () =>{
const [question1, setQuestion1] = createSignal("")
const [question2, setQuestion2] = createSignal("")
const [hint, setHint] = createSignal("")
const [status, setStatus] = createSignal("Idle")
function inputQuestion1(e){setQuestion1(e.target.value)}
function inputQuestion2(e){setQuestion2(e.target.value)}
function inputHint(e){setHint(e.target.value)}
async function applyHint(){
setStatus("Checking...")
console.log("hint...")
console.log(question1())
console.log(question2())
console.log(hint())
let user = gun.user();
let q1 = question1(); //get input id question 1
let q2 = question2(); //get input id question 2
let _hint = hint(); //get input id hint
let sec = await Gun.SEA.secret(user.is.epub, user._.sea);//mix key to decrypt
let enc_q1 = await Gun.SEA.encrypt(q1, sec);//encrypt q1
user.get('forgot').get('q1').put(enc_q1);//set hash q1 to user data store
let enc_q2 = await Gun.SEA.encrypt(q2, sec);//encrypt q1
user.get('forgot').get('q2').put(enc_q2); //set hash q2 to user data store
sec = await Gun.SEA.work(q1,q2);//encrypt key
let enc = await Gun.SEA.encrypt(_hint, sec);//encrypt hint
user.get('hint').put(enc,ack=>{//set hash hint
//console.log(ack);
if(ack.err){
console.log("Error!");
//modalmessage(ack.err);
setStatus("Fail! Error!")
return;
}
if(ack.ok){
console.log('Hint Apply!');
setStatus("Hint Apply!")
//modalmessage('Hint Apply!');
}
});
}
async function getHint(){
setStatus("Checking...")
let user = gun.user();
let q1,q2,_hint;
let sec = await Gun.SEA.secret(user.is.epub, user._.sea);// mix key to decrypt
q1 = await user.get('forgot').get('q1').then();
q1 = await Gun.SEA.decrypt(q1, sec);//decrypt question1
q2 = await user.get('forgot').get('q2').then();
q2 = await Gun.SEA.decrypt(q2, sec);//decrypt question2
setQuestion1(q1)
setQuestion2(q2)
sec = await Gun.SEA.work(q1,q2);//encrypt key
_hint = await user.get('hint').then();//get encrypt hint
_hint = await Gun.SEA.decrypt(_hint, sec);//decrypt hint
setHint(_hint)
setStatus("Done!")
}
return html`<div>
${PageNavMenu()}
${PageAccountMenu()}
<div>
<label> Passphrase Set Hint </label>
<table>
<tbody>
<tr>
<td>
<label>Question 1:</label>
</td>
<td>
<input value="${question1}" onInput="${inputQuestion1}"/>
</td>
</tr>
<tr>
<td>
<label>Question 2:</label>
</td>
<td>
<input value="${question2}" onInput="${inputQuestion2}"/>
</td>
</tr>
<tr>
<td>
<label>Hint:</label>
</td>
<td>
<input value="${hint}" onInput="${inputHint}"/>
</td>
</tr>
<tr>
<td colspan="2">
<label> Status: ${status} </label>
<span style="float:right;">
<button onClick="${applyHint}"> Apply </button>
<button onClick="${getHint}"> Get </button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>`;
}
function timeStamp(){
let currentDate = new Date();
//console.log(currentDate);
let year = currentDate.getFullYear();
let month = ("0" + (currentDate.getMonth() + 1 ) ).slice(-2);
let date = ("0" +currentDate.getDate()).slice(-2);
let hour = ("0" +currentDate.getHours()).slice(-2);
let minute = ("0" +currentDate.getMinutes()).slice(-2);
let second = ("0" +currentDate.getSeconds()).slice(-2);
let millisecond = currentDate.getMilliseconds();
return year + "/" + (month) + "/" + date + ":" + hour+ ":" + minute+ ":" + second+ ":" + millisecond;
}
// https://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript
//
const PagePublicChat = () =>{
const [message, setMessage] = createSignal("");
let refPublicMessages;
let encmsg;
let gunchat;
async function inputMessage(event){
console.log("TYPE...")
if(event.key == "Enter"){
//console.log(event.target.value)
setMessage(event.target.value)
console.log("ENTER message?")
let user = gun.user();
if(!user.is){ return }//check if user exist
let msg = (message() || '').trim();
console.log("msg:",msg)
if(!msg) return;//check if not id empty
//let encmsg = await SEA.work("public","chat");//encrypttion key default?
//console.log(encmsg);
let enc = await SEA.encrypt(msg,encmsg);
//console.log(enc);
let who = await user.get('alias').then();
//console.log(who);
//console.log(typeof enc)
enc = window.btoa(enc);
let dateTime = timeStamp()
console.log(dateTime)
gun.get('chat').get(dateTime).put({
alias:who,
message:enc
});
console.log("send message...");
}
}
function scrollPublicMessage(){
//let element = document.getElementById("publicchatlist");
let element = refPublicMessages;
element.scrollTop = element.scrollHeight;
}
async function qcallback(data,key){
console.log('incoming messages...')
//console.log("key",key);
//console.log("data",data);
if(data == null)return;
if(data.message != null){
let message = window.atob(data.message);
//console.log(message);
let dec = await SEA.decrypt(message,encmsg);
//console.log(dec)
if(dec!=null){
//$('#publicchatlist').append($('<div/>', {
//id: key,
//text : data.alias + ": " + dec
//}));
let divMsg = document.createElement("div")
divMsg.setAttribute('id',key)
divMsg.append(data.alias + ": " + dec)
refPublicMessages.appendChild(divMsg)
scrollPublicMessage();
}
}
}
async function initChat(){
console.log("Init Chat...")
//$('#publicchatlist').empty();
let encmsg = await SEA.work("public","chat"); //encrypttion key default?
let currentDate = new Date();
let year = currentDate.getFullYear();
let month = ("0" + (currentDate.getMonth() + 1 ) ).slice(-2);
let date = ("0" +currentDate.getDate()).slice(-2);
let timestring = year + "/" + month + "/" + date + ":";
//console.log(timestring);
if(gunchat !=null){
gunchat.off()
}
gunchat = gun.get('chat');
//gunchat.get({'.': {'*': '2019/08/'}}).map().once(qcallback);
//gunchat.get({'.': {'*': timestring}}).map().once(qcallback);
console.log("timestring: ", timestring)
gunchat.get({'.': {'*': timestring},'%': 50000}).map().once(qcallback);
console.log("END SETUP...")
}
onMount(async ()=>{
console.log("onMount chat...")
encmsg = await SEA.work("public","chat"); //encrypttion key default?
console.log(document.getElementById('refPublicMessages'))
refPublicMessages = document.getElementById('refPublicMessages')
//console.log(refPublicMessages)
//refPublicMessages.innerHTML = "";
await initChat();
})
onCleanup(()=>{
if(gunchat !=null){
gunchat.off()
}
})
return html`<div style="height:100vh;width:100%">
${PageNavMenu()}
<div style="height:calc(100vh - 18px);width:100%">
<label> Public Chat </label>
<div id="refPublicMessages" style="background-color: darkgray;overflow-y: scroll; height:calc(100vh - 58px);">
Not Init.
</div>
<div style="height:22px;width:100%">
<input value="${message}" onKeyUp="${inputMessage}" placeholder="type here and press enter"><button> Enter </button>
</div>
</div>
</div>`;
}
// https://github.com/Lightnet/gunjstrustsharekey/blob/master/client.js#L640
const aliasContacts = (props) =>{
const [pubKey, setPublicKey] = createSignal("")
const [status, setStatus] = createSignal("Idle")
const [contacts, setContacts] = createSignal([])
const [contactID, setContactID] = createSignal("")
function inputPublicKey(e){
setPublicKey(e.target.value)
console.log("typing...")
if(e.target.value != "None" && e.target.value.length !== 0){
setStatus("...")
CheckPublicKey(e.target.value)
}
}
async function CheckPublicKey(_pub){
if(!_pub){
//console.log("EMPTY!")
setStatus("EMPTY!")
return;
}
let find = gun.user(_pub);
console.log(find)
let who = await find.then() || {};//get alias information
if(!who.alias){//check for alias from gun user
setStatus('No Alias!')
return;
}
setStatus('Found! ' + who.alias)
if(typeof props.onChange === 'function'){
props.onChange(_pub)
}
}
async function clickAddContact(e){
let _publickey = (pubKey() || '').trim();
if(!_publickey){console.log("Public Key EMPTY!");return;}
let user = gun.user();
let to = gun.user(_publickey);//get alias
let who = await to.then() || {};//get alias data
if(!who.alias){console.log("No Alias!");return;}
//need encrypt contact?
console.log(who)
const uid = String.random(32);
user.get("contacts").get(uid).put({alias:who.alias,pub:_publickey});
setContacts(state=>[...state,{id:uid,name:who.alias,pub:_publickey}])
}
async function clickRemoveContact(e){
let _publickey = (pubKey() || '').trim();
if(!_publickey){console.log("Public Key EMPTY!");return;}
let user = gun.user();
let userData = contacts().find(item=>item.pub==_publickey)
console.log(userData)
user.get("contacts").get(userData.id).put(null);
setContacts(state=>state.filter(item=>item.pub!==_publickey))
//let to = gun.user(_publickey);//get alias
//let who = await to.then() || {};//get alias data
//if(!who.alias){console.log("No Alias!");return;}
//user.get("contacts").get(_publickey).put(null);
}
function selectContact(event){
console.log("SELECT Contact", event.target.value)
let pubID ="";
pubID=event.target.value;
if(event.target.value == "none"){
pubID="";
}
setContactID(pubID)
setPublicKey(pubID)
if(typeof props.onChange === 'function'){
props.onChange(pubID)
}
}
//> ${item.pub}
const contactList = createMemo(() => contacts().map(item=>html`<option id="${item.id}" value="${item.pub}">${item.name} </option>`));
onMount(()=>{
let user = gun.user();
user.get('contacts').once().map().once((data,key)=>{
console.log(data)
console.log(key)
if(data !=null && key.length == 32 && data?.pub != null){
setContacts(state=>[...state,{id:key,name:data.alias,pub:data.pub}])
}
})
})
return html`<span>
<select value="${contactID}" onChange="${selectContact}">
<option value="none"> NONE </option>
${contactList}
</select>
<input value="${pubKey}" onInput="${inputPublicKey}" />
<button onClick="${clickAddContact}"> Add </button>
<button onClick="${clickRemoveContact}"> Remove </button>
<label>Status: ${status}</label>
</span>`;
}
const PagePrivateMessage = () =>{
const [message, setMessage] = createSignal("")
const [publicKey, setPublicKey] = createSignal("")
const [who, setWho] = createSignal("")
let UIdec; //for message decode key
let refPublicMessages;
let userMessage;
let toMessage;
async function inputMessage(event){
console.log("TYPE...")
setMessage(event.target.value)
if(event.key == "Enter"){
sendPrivateMessage();
}
}
async function sendPrivateMessage(){
let msg = (message() || '').trim();
let pubkey = (publicKey() || '').trim();
if(!msg){console.log("Message EMPTY!");return;}
if(!pubkey){console.log("Public Key EMPTY!");return;}
let user = gun.user();
let to = gun.user(pubkey);//get alias
let who = await to.then() || {};//get alias data
if(!who.alias){console.log("No Alias!");return;}
let sec = await Gun.SEA.secret(who.epub, user._.sea); // Diffie-Hellman
let enc = await Gun.SEA.encrypt(msg, sec); //encrypt message
user.get('messages').get(pubkey).set(enc);
console.log("finish...");
}
function scrollPublicMessages(){
//let element = document.getElementById("publicchatlist");
let element = refPublicMessages;
element.scrollTop = element.scrollHeight;
}
async function viewPrivateMessages(){
if(userMessage!=null){
userMessage.off()
}
if(toMessage!=null){
toMessage.off()
}
let user = gun.user();
if(!user.is){ return }//check if user exist
//messages = [];
//CleanMessages();
let pub = (publicKey() || '').trim();
if(!pub) return;//check if not id empty
let to = gun.user(pub);//get alias
let who = await to.then() || {};//get alias data
if(!who.alias){
console.log("No Alias!");
//$('#mwho').text("who?");
setWho("No Alias!")
return;
}
//$('#mwho').text(who.alias);
setWho(who.alias)
UIdec = await Gun.SEA.secret(who.epub, user._.sea); // Diffie-Hellman
//user.get('messages').get(pub).map().once((data,id)=>{
//UI(data,id,user.is.alias)
//});
//to.get('messages').get(user._.sea.pub).map().once((data,id)=>{
//UI(data,id,who.alias)
//});
userMessage = user.get('messages').get(pub);
userMessage.map().once((data,id)=>{
UI(data,id,user.is.alias)
});
toMessage = to.get('messages').get(user._.sea.pub);
toMessage.map().once((data,id)=>{
UI(data,id,who.alias)
});
}
async function UI(say, id, alias){
say = await Gun.SEA.decrypt(say, UIdec);
//messages.push({id:id,alias:alias,message:say});
if($("#" + id).length){
//console.log("found!?");
}else{
//$('#messagelist').append($('<div/>', {
//id: id,
//text : alias + ": " + say
//}));
let divMsg = document.createElement("div")
divMsg.setAttribute('id',id)
divMsg.append(alias + ": " + say)
refPublicMessages.appendChild(divMsg)
}
//let element = document.getElementById("messagelist");
//element.scrollTop = element.scrollHeight;
scrollPublicMessages();
}
onMount(async ()=>{
console.log("onMount chat...")
refPublicMessages = document.getElementById('refPublicMessages')
})
onCleanup(()=>{
if(userMessage!=null){
userMessage.off()
}
if(toMessage!=null){
toMessage.off()
}
})
function onSelectPub(value){
console.log("value ID:",value)
setPublicKey(value)
refPublicMessages.replaceChildren();
let divInit = document.createElement('div')
divInit.append('Init Message!')
refPublicMessages.appendChild(divInit)
viewPrivateMessages();
}
return html`<div style="height:100vh;width:100%">
${PageNavMenu()}
<div style="height:calc(100vh - 18px);width:100%">
<label> Private Message </label> ${aliasContacts({onChange:onSelectPub})}
<div id="refPublicMessages" style="background-color: darkgray;overflow-y: scroll; height:calc(100vh - 58px);">
Not Init.
</div>
<div style="height:22px;width:100%">
<input value="${message}" onKeyUp="${inputMessage}" placeholder="type here and press enter"><button> Enter </button>
</div>
</div>
</div>`;
}
const PageGroupMessage = () =>{
let gunGroupMessage;
onMount(async ()=>{
console.log("onMount chat...")
})
onCleanup(()=>{
})
return html`<div>
${PageNavMenu()}
<div>
<label> Private Message </label>
<table>
<tbody>
<tr>
<td>
<label></label>
</td>
<td>
<input />
</td>
</tr>
</tbody>
</table>
</div>
</div>`;
}
const Modal = (props) =>{
const [message, setMessage] = createSignal(props?.message || "None")
function btnClose(){
disposeModal();
}
return html`<div>
<div>
<label>Modal</label> <button onClick="${btnClose}"> x </button>
</div>
<div>
${message()}
</div>
</div>`;
}
const PageToDoList = (props) =>{
const [toDoList, setToDoList] = createSignal([])
const [content, setContent] = createSignal("")
const [editID, setEditID] = createSignal("")
const [editContent, setEditContent] = createSignal("")
function inputContent(e){
setContent(e.target.value);
}
function addContent(){
if(content()){
let uid = String.random(16);
setToDoList(state=>[...state,{id:uid,content:content()}])
gun.get('todolist').get(uid).put(content());
}
}
function removeContent(_id){
setToDoList(state=>state.filter(item=>item.id!==_id))
gun.get('todolist').get(_id).put(null);
}
function toggleEditContent(_id){
if(setEditID() != _id){
setEditID(_id)
}else{
setEditID("")
}
}
function updateContent(){
//console.log(editContent())
setToDoList(state=>state.map((item)=>{
if(item.id == editID()){
let textEdit = editContent();
return {...item, content:textEdit }
}
return item;
}))
gun.get('todolist').get(editID()).put(editContent());
setEditID("")
}
function inputEditContent(e){
setEditContent(e.target.value);
}
const doListListMap = createMemo(()=>toDoList().map(item=>{
if(editID() == item.id){
return html`<li value="${item.id}"><input value="${item.content}" onInput="${inputEditContent}" /> <button onClick="${updateContent}">Update</button><button onClick="${()=>removeContent(item.id)}"> Del </button> </li>`
}else{
return html`<li value="${item.id}">${item.content} <button onClick="${()=>toggleEditContent(item.id)}"> Edit </button> <button onClick="${()=>removeContent(item.id)}"> Del </button> </li>`
}
}))
onMount(()=>{
gun.get('todolist').map().once((data,key)=>{
//console.log("KEY:",key);
//console.log("DATA:",data);
if(data!=null){
let isItem = toDoList().find(item=>item.id==key);
if(!isItem){//note there triggers 2x if added
setToDoList(state=>[...state,{id:key,content:data}])
}
}
})
})
return html`<div>
${PageNavMenu()}
<div>
<input value="${content}" onInput="${inputContent}" /><button onClick="${addContent}"> Add </button>
</div>
<div>
<ul>
${doListListMap}
</ul>
</div>
</div>`;
}
const GunNode = (props) =>{
const [nodeList, setNodeList] = createSignal([])
//console.log(props)
let node;
//let nodeList=[]
if(props.node){
node=props.node
//console.log("NODE:",props.node)
node.once().map().once((data,key)=>{
console.log(key)
console.log(data)
//nodeList.push(key)
setNodeList(state=>[...state,{id:key, value:data}])
})
}
const Glist = createMemo(() => nodeList().map(item=>
html`<li id="${item.id}"> ${item.id}: ${item.value} </li>`
));
//${GunNode({node:node.get(item)})}
return html`<ul>
${Glist}
</ul>`;
}
const PageGunGraph = () =>{
console.log(gun)
function getGraph(){
//for (const key in gun._.graph) {
//console.log(key)
//if (Object.hasOwnProperty.call(gun._.graph, key)) {
//const element = gun._.graph[key];
//console.log(element)
//}
//}
let dataList = [];
Object.entries(gun._.graph).forEach(([key, value]) => {
//console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
dataList.push(html`<li id="${key}">${key} ${GunNode({node:gun.get(key)})} </li>`);
});
//console.log(dataList)
return html`<ul>${dataList}</ul>`
}
return html`<div>
${PageNavMenu()}
${getGraph()}
</div>`;
}
const ListArrayItems = (props) =>{
const [cats, setCats] = createSignal([
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
]);
//let timer = setInterval(() =>{
//setCats(item=>[...item,{id:crypto.randomUUID(), name:crypto.randomUUID()}])
//console.log(cats())
//}, 1000);
const catlist = createMemo(() => cats().map(item=>html`<li id="${item.id}">${item.name}</li>`));
onCleanup(() => {
//clearInterval(timer)
});
return html`<div>
<ul>
${catlist}
</ul>
</div>`;
}
// TEST AREA
const PageTestLab = (props) =>{
const [message, setMessage] = createSignal(props?.message || "None")
return html`<div>
${PageNavMenu()}
${ListArrayItems()}
</div>`;
}
// BLANK COMPONENT
const PageBlank = (props) =>{
//const [message, setMessage] = createSignal(props?.message || "None")
return html`<div>
</div>`;
}
//init app
dispose = render(PageLogin, document.getElementById('app'));
//disposeModal = render(()=>Modal({message:"test"}), document.getElementById('modal'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>gun js</title>
<meta name="description" content="">
<!--<link id="favicon" rel="icon" href="" type="image/x-icon">-->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- import the webpage's stylesheet -->
<link rel="stylesheet" href="/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/mix.js"></script>
<!-- RAD/LEX -->
<script src="https://cdn.jsdelivr.net/npm/gun/lib/radix.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/radisk.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/store.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/rindexed.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/path.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/list.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/lib/promise.js"></script>
<!--
<script src="https://cdn.jsdelivr.net/npm/gun/lib/then.js"></script>
<script src="/gunjstrustsharekeyv3.js"></script>
<script src="/sea.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
<script src="/gunadapter.js"></script>
-->
</head>
<body>
<div id="app"></div>
<div id="modal"></div>
<script type="module" src="/client.js"></script>
</body>
</html>
{
"name": "gunjs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "node server.js"
},
"keywords": [
"gunjs",
"seajs"
],
"author": "Lightnet",
"license": "MIT",
"dependencies": {
"gun": "^0.2020.1238"
}
}
import fs from "fs";
import path from "path";
import http from "http";
import Gun from "gun";
//require("gun/sea");//note make sure python 2.7 is install to work.
var { PORT = 8080, NODE_ENV } = process.env;
const dev = NODE_ENV === "development";
console.log("dev: " + dev);
//console.log(process.versions.node);
//create server
const server = http.createServer(function(request, response) {
//console.log("request starting...");
if (Gun.serve(request, response)) {//get gun.js ex. <script src="/gun.js">
return;
} // filters gun requests!
//handle files as public folder
var filePath = "." + request.url;
if (filePath === "./") filePath = "./index.html";
var extname = path.extname(filePath);
var contentType = "text/html";
switch (extname) {
case ".js":
contentType = "text/javascript";
break;
case ".css":
contentType = "text/css";
break;
case ".json":
contentType = "application/json";
break;
case ".png":
contentType = "image/png";
break;
case ".jpg":
contentType = "image/jpg";
break;
case ".wav":
contentType = "audio/wav";
break;
default:
contentType = "text/html";
}
fs.readFile(filePath, function(error, content) {
if (error) {
if (error.code === "ENOENT") {
fs.readFile("./404.html", function(error, content) {
response.writeHead(200, { "Content-Type": contentType });
response.end(content, "utf-8");
});
} else {
response.writeHead(500);
response.end(
"Sorry, check with the site admin for error: " + error.code + " ..\n"
);
response.end();
}
} else {
response.writeHead(200, { "Content-Type": contentType });
response.end(content, "utf-8");
}
});
});
server.listen(PORT, err => {
if (err) throw err;
//console.log(app);
console.log(`> Running on http://localhost:`+PORT);
});
var gun = Gun({
file: "data",
//web:app.server //server
web: server
});
gun.on("hi", peer => {
//peer connect
//console.log('connect peer to',peer);
//console.log("peer connect!");
});
gun.on("bye", peer => {
// peer disconnect
//console.log('disconnected from', peer);
//console.log("disconnected from peer!");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment