2024-07-04 16:11:02 +02:00
(() => {
2026-03-31 22:02:13 +00:00
// node_modules/bloatless-react/index.ts
var State = class {
_value ;
_bindings = /* @__PURE__ */ new Set ();
// init
constructor ( initialValue ) {
this . _value = initialValue ;
}
// value
get value () {
return this . _value ;
}
set value ( newValue ) {
if ( this . _value == newValue ) return ;
this . _value = newValue ;
this . callSubscriptions ();
}
// subscriptions
callSubscriptions () {
this . _bindings . forEach (( fn ) => fn ( this . _value ));
}
subscribe ( fn ) {
this . _bindings . add ( fn );
fn ( this . _value );
}
// stringify
toString () {
return JSON . stringify ( this . _value );
}
};
var ListState = class extends State {
additionHandlers = /* @__PURE__ */ new Set ();
removalHandlers = /* @__PURE__ */ new Map ();
// init
constructor ( initialItems ) {
super ( new Set ( initialItems ));
}
// list
add (... items ) {
items . forEach (( item ) => {
this . value . add ( item );
this . additionHandlers . forEach (( handler ) => handler ( item ));
});
this . callSubscriptions ();
}
remove (... items ) {
items . forEach (( item ) => {
this . value . delete ( item );
if ( ! this . removalHandlers . has ( item )) return ;
this . removalHandlers . get ( item )( item );
this . removalHandlers . delete ( item );
});
this . callSubscriptions ();
}
clear () {
this . remove (... this . value . values ());
}
// handlers
handleAddition ( handler ) {
this . additionHandlers . add ( handler );
[... this . value . values ()]. forEach ( handler );
}
handleRemoval ( item , handler ) {
this . removalHandlers . set ( item , handler );
}
// stringification
toString () {
const array = [... this . value ];
const json = JSON . stringify ( array );
return json ;
}
};
function createProxyState ( statesToSubscibe , fn ) {
const proxyState = new State ( fn ());
statesToSubscibe . forEach (( state ) =>
state . subscribe (() => ( proxyState . value = fn ())),
);
return proxyState ;
}
function persistState ( localStorageKey , state ) {
state . subscribe (() => {
const stringifiedValue = state . toString ();
localStorage . setItem ( localStorageKey , stringifiedValue );
});
2024-07-08 13:24:21 +02:00
}
2026-03-31 22:02:13 +00:00
function restoreState ( localStorageKey , initialStateValue ) {
const storedString =
localStorage . getItem ( localStorageKey ) ??
JSON . stringify ( initialStateValue );
const convertedValue = JSON . parse ( storedString );
const state = new State ( convertedValue );
persistState ( localStorageKey , state );
return state ;
}
function createElement ( tagName , attributes = {}, ... children ) {
const element = document . createElement ( tagName );
if ( attributes != null )
Object . entries ( attributes ). forEach (( entry ) => {
const [ attributename , value ] = entry ;
const [ directiveKey , directiveValue ] = attributename . split ( ":" );
switch ( directiveKey ) {
case "on" : {
switch ( directiveValue ) {
case "enter" : {
element . addEventListener ( "keydown" , ( e ) => {
if ( e . key != "Enter" ) return ;
value ();
});
break ;
}
default : {
element . addEventListener ( directiveValue , value );
}
}
break ;
}
case "subscribe" : {
if ( directiveValue == "children" ) {
element . style . scrollBehavior = "smooth" ;
try {
const [ listState , toElement ] = value ;
listState . handleAddition (( newItem ) => {
const child = toElement ( newItem );
listState . handleRemoval ( newItem , () =>
child . remove (),
);
element . append ( child );
element . scrollTop = element . scrollHeight ;
});
} catch {
throw `error: cannot process subscribe:children directive because ListItemConverter is not defined. Usage: "subscribe:children={[list, converter]}"; you can find a more detailed example in the documentation` ;
}
} else {
const state = value ;
state . subscribe (
( newValue ) =>
( element [ directiveValue ] = newValue ),
);
}
break ;
}
case "bind" : {
const state = value ;
state . subscribe (
( newValue ) => ( element [ directiveValue ] = newValue ),
);
element . addEventListener (
"input" ,
() => ( state . value = element [ directiveValue ]),
);
break ;
}
case "toggle" : {
if ( value . subscribe ) {
const state = value ;
state . subscribe (( newValue ) =>
element . toggleAttribute (
directiveValue ,
newValue ,
),
);
} else {
element . toggleAttribute ( directiveValue , value );
}
break ;
}
case "set" : {
const state = value ;
state . subscribe (( newValue ) =>
element . setAttribute ( directiveValue , newValue ),
);
break ;
}
default :
element . setAttribute ( attributename , value );
}
});
children . filter (( x ) => x ). forEach (( child ) => element . append ( child ));
return element ;
2024-07-05 00:07:39 +02:00
}
2026-03-31 22:02:13 +00:00
// node_modules/udn-frontend/index.ts
var UDNFrontend = class {
ws ;
// HANDLERS
connectionHandler = () => {};
disconnectionHandler = () => {};
messageHandler = ( data ) => {};
mailboxHandler = ( mailboxId2 ) => {};
mailboxConnectionHandler = ( mailboxId2 ) => {};
mailboxDeleteHandler = ( mailboxId2 ) => {};
// INIT
set onconnect ( handler ) {
this . connectionHandler = handler ;
}
set ondisconnect ( handler ) {
this . disconnectionHandler = handler ;
}
set onmessage ( handler ) {
this . messageHandler = handler ;
}
set onmailboxcreate ( handler ) {
this . mailboxHandler = handler ;
}
set onmailboxconnect ( handler ) {
this . mailboxConnectionHandler = handler ;
}
set onmailboxdelete ( handler ) {
this . mailboxDeleteHandler = handler ;
}
// UTILITY METHODS
send ( messageObject ) {
if ( this . ws == void 0 ) return false ;
const messageString = JSON . stringify ( messageObject );
this . ws . send ( messageString );
return true ;
}
// PUBLIC METHODS
// connection
connect ( address ) {
try {
this . disconnect ();
this . ws = new WebSocket ( address );
this . ws . addEventListener ( "open" , this . connectionHandler );
this . ws . addEventListener ( "close" , this . disconnectionHandler );
this . ws . addEventListener ( "message" , ( message ) => {
const dataString = message . data . toString ();
const data = JSON . parse ( dataString );
if ( data . assignedMailboxId ) {
return this . mailboxHandler ( data . assignedMailboxId );
} else if ( data . connectedMailboxId ) {
return this . mailboxConnectionHandler (
data . connectedMailboxId ,
);
} else if ( data . deletedMailbox ) {
return this . mailboxDeleteHandler ( data . deletedMailbox );
} else {
this . messageHandler ( data );
}
2024-07-08 13:24:21 +02:00
});
2026-03-31 22:02:13 +00:00
} catch ( error ) {
console . error ( error );
2024-07-08 13:24:21 +02:00
}
2024-07-04 16:11:02 +02:00
}
2026-03-31 22:02:13 +00:00
disconnect () {
this . ws ? . close ();
}
// message
sendMessage ( channel , body ) {
const messageObject = {
messageChannel : channel ,
messageBody : body ,
};
return this . send ( messageObject );
}
// subscription
subscribe ( channel ) {
const messageObject = {
subscribeChannel : channel ,
};
return this . send ( messageObject );
}
unsubscribe ( channel ) {
const messageObject = {
unsubscribeChannel : channel ,
};
return this . send ( messageObject );
}
// mailbox
requestMailbox () {
const messageObject = {
requestingMailboxSetup : true ,
};
return this . send ( messageObject );
}
connectMailbox ( mailboxId2 ) {
const messageObject = {
requestedMailbox : mailboxId2 ,
};
return this . send ( messageObject );
}
deleteMailbox ( mailboxId2 ) {
const messageObject = {
deletingMailbox : mailboxId2 ,
};
return this . send ( messageObject );
}
};
2024-07-04 16:11:02 +02:00
2026-03-31 22:02:13 +00:00
// src/translations.tsx
var staticTextEnglish = {
mailbox : "Mailbox" ,
requestMailbox : "Request" ,
deleteMailbox : "Delete" ,
mailboxConnected : "Mailbox active" ,
channel : "Channel" ,
channel_placeholder : "my-channel" ,
disconnected : "Server disconnected" ,
reconnecting : "Reconnecting..." ,
info : "Info" ,
message : "Message" ,
messages : "Messages" ,
message_placeholder : "Hello, world!" ,
message_placeholder_generic : "Type a message..." ,
noMessagesReceived : "No messages received" ,
messageLastReceived : "Last received message" ,
messagesReceived : "Received Messages" ,
send : "Send" ,
serverInfo : "Server Info" ,
settings : "Settings" ,
subscribeToChannel : "Subscribe to channel" ,
subscribe : "Subscribe" ,
unsubscribe : "Unubscribe" ,
2024-07-06 14:11:48 +02:00
};
2026-03-31 22:02:13 +00:00
var allTranslations = {
en : staticTextEnglish ,
es : {
mailbox : "Buz\xF3n" ,
requestMailbox : "Solicitar" ,
deleteMailbox : "Eliminar" ,
mailboxConnected : "Buz\xF3n activado" ,
channel : "Canal" ,
channel_placeholder : "mi-canal" ,
disconnected : "Sin coneci\xF3n" ,
reconnecting : "Conectando..." ,
info : "Info" ,
message : "Mensaje" ,
messages : "Mensajes" ,
message_placeholder : "Hola!" ,
message_placeholder_generic : "Nuevo mensaje" ,
noMessagesReceived : "Sin mensajes" ,
messageLastReceived : "\xDAltimo mensaje" ,
messagesReceived : "Todos los Mensajes" ,
send : "Enviar" ,
serverInfo : "Informaci\xF3n" ,
settings : "Configuraci\xF3n" ,
subscribeToChannel : "Suscribirse a un canal" ,
subscribe : "Suscribirse" ,
unsubscribe : "Cancelar" ,
},
de : {
mailbox : "Briefkasten" ,
requestMailbox : "Beantragen" ,
deleteMailbox : "L\xF6schen" ,
mailboxConnected : "Briefkasten aktiv" ,
channel : "Kanal" ,
channel_placeholder : "mein-kanal" ,
disconnected : "Verbindung getrennt" ,
reconnecting : "Verbindung wird hergestellt..." ,
info : "Daten" ,
message : "Nachricht" ,
messages : "Nachrichten" ,
message_placeholder : "Hallo!" ,
message_placeholder_generic : "Neue Nachricht" ,
noMessagesReceived : "Keine Nachrichten" ,
messageLastReceived : "Letzte Nachricht" ,
messagesReceived : "Empfangene Nachrichten" ,
send : "Senden" ,
serverInfo : "Serverdaten" ,
settings : "Einstellungen" ,
subscribeToChannel : "Kanal beitreten" ,
subscribe : "Beitreten" ,
unsubscribe : "Verlassen" ,
},
2024-07-06 14:11:48 +02:00
};
2026-03-31 22:02:13 +00:00
var language = navigator . language . substring ( 0 , 2 );
var translations = allTranslations [ language ];
// src/model.tsx
var Message = class {
constructor ( channel , body ) {
this . channel = channel ;
this . body = body ;
}
2024-07-06 14:11:48 +02:00
};
2026-03-31 22:02:13 +00:00
var isDisconnected = new State ( true );
var mailboxId = restoreState ( "mailbox-id" , "" );
var isMailboxDisonnected = new State ( true );
var messages = new ListState ();
var lastReceivedMessage = new State ( translations . noMessagesReceived );
var statHTMLString = new State ( "" );
async function updateStats () {
const response = await fetch (
` ${ window . location . protocol } // ${ window . location . host } /stats` ,
);
const data = await response . json ();
let html = "" ;
data . forEach (( item ) => {
const tile = /* @__PURE__ */ createElement (
"div" ,
{ class : "tile" },
/* @__PURE__ */ createElement (
"span" ,
{ class : "secondary flex width-100" },
item [ 0 ]. toString (),
),
/* @__PURE__ */ createElement ( "b" , null , item [ 1 ]. toString ()),
);
html += tile . outerHTML ;
});
statHTMLString . value = html ;
}
var subscriptionChannel = new State ( "" );
var newMessageChannel = new State ( "" );
var newMessageBody = new State ( "" );
var isMessageEmpty = createProxyState (
[ newMessageChannel , newMessageBody ],
() => newMessageChannel . value == "" || newMessageBody . value == "" ,
);
var UDN = new UDNFrontend ();
UDN . onconnect = () => {
isDisconnected . value = false ;
updateStats ();
if ( mailboxId . value != "" ) {
UDN . connectMailbox ( mailboxId . value );
}
2024-07-08 21:13:56 +02:00
};
2026-03-31 22:02:13 +00:00
UDN . onmessage = ( data ) => {
lastReceivedMessage . value = JSON . stringify ( data , null , 4 );
const { messageChannel , messageBody } = data ;
if ( messageChannel && messageBody ) {
const messageObject = new Message ( messageChannel , messageBody );
if ( messageBody ) messages . add ( messageObject );
}
2024-07-08 23:34:16 +02:00
};
2026-03-31 22:02:13 +00:00
UDN . onmailboxcreate = ( id ) => {
console . log ( id );
mailboxId . value = id ;
UDN . connectMailbox ( id );
2024-07-09 02:36:43 +02:00
};
2026-03-31 22:02:13 +00:00
UDN . onmailboxconnect = () => {
isMailboxDisonnected . value = false ;
};
UDN . onmailboxdelete = () => {
isMailboxDisonnected . value = true ;
};
UDN . ondisconnect = () => {
isDisconnected . value = true ;
isMailboxDisonnected . value = true ;
setTimeout ( connect , 1e3 );
};
function connect () {
UDN . connect (
` ${ window . location . protocol . replace ( "http" , "ws" ) } // ${ window . location . host } ` ,
);
2024-07-08 21:13:56 +02:00
}
2026-03-31 22:02:13 +00:00
connect ();
function subscribe () {
UDN . subscribe ( subscriptionChannel . value );
2024-07-06 14:11:48 +02:00
}
2026-03-31 22:02:13 +00:00
function unsubscribe () {
UDN . unsubscribe ( subscriptionChannel . value );
2024-07-06 14:11:48 +02:00
}
2026-03-31 22:02:13 +00:00
function sendMessage () {
if ( isMessageEmpty . value == true ) return ;
UDN . sendMessage ( newMessageChannel . value , newMessageBody . value );
newMessageBody . value = "" ;
2024-07-06 14:11:48 +02:00
}
2026-03-31 22:02:13 +00:00
function requestMailbox () {
UDN . requestMailbox ();
2024-07-08 21:13:56 +02:00
}
2026-03-31 22:02:13 +00:00
function deleteMailbox () {
console . log ( "test" );
UDN . deleteMailbox ( mailboxId . value );
2024-07-08 23:34:16 +02:00
}
2026-03-31 22:02:13 +00:00
// src/infoScreen.tsx
function InfoScreen () {
return /* @__PURE__ */ createElement (
"article" ,
{ id : "info-screen" },
/* @__PURE__ */ createElement (
"header" ,
null ,
translations . serverInfo ,
/* @__PURE__ */ createElement (
"span" ,
null ,
/* @__PURE__ */ createElement (
"button" ,
{ "on:click" : updateStats },
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"refresh" ,
),
),
),
),
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement ( "div" , {
class : "flex-column gap" ,
"subscribe:innerHTML" : statHTMLString ,
}),
),
);
2024-07-08 21:13:56 +02:00
}
2024-07-06 14:11:48 +02:00
2026-03-31 22:02:13 +00:00
// src/mainScreen.tsx
function MainScreen () {
return /* @__PURE__ */ createElement (
"article" ,
{ id : "main-screen" },
/* @__PURE__ */ createElement (
"header" ,
null ,
translations . settings ,
),
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"div" ,
{ class : "tile width-input" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"b" ,
null ,
translations . messageLastReceived ,
),
/* @__PURE__ */ createElement ( "p" , {
class : "secondary" ,
"subscribe:innerText" : lastReceivedMessage ,
}),
),
),
/* @__PURE__ */ createElement ( "hr" , null ),
/* @__PURE__ */ createElement (
"div" ,
{ class : "tile width-input" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"b" ,
null ,
translations . mailbox ,
),
/* @__PURE__ */ createElement (
"p" ,
{
class : "secondary" ,
"toggle:hidden" : isMailboxDisonnected ,
},
translations . mailboxConnected ,
),
),
),
/* @__PURE__ */ createElement (
"div" ,
{ class : "flex-row width-input justify-end" },
/* @__PURE__ */ createElement (
"button" ,
{
class : "danger width-50" ,
"on:click" : deleteMailbox ,
"toggle:disabled" : isMailboxDisonnected ,
},
translations . deleteMailbox ,
),
/* @__PURE__ */ createElement (
"button" ,
{
class : "primary width-50" ,
"on:click" : requestMailbox ,
"toggle:disabled" : isDisconnected ,
},
translations . requestMailbox ,
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"inbox" ,
),
),
),
/* @__PURE__ */ createElement ( "hr" , null ),
/* @__PURE__ */ createElement (
"label" ,
{ class : "tile" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"span" ,
null ,
translations . subscribeToChannel ,
),
/* @__PURE__ */ createElement ( "input" , {
placeholder : translations . channel_placeholder ,
"bind:value" : subscriptionChannel ,
"on:enter" : subscribe ,
}),
),
),
/* @__PURE__ */ createElement (
"div" ,
{ class : "flex-row width-input justify-end" },
/* @__PURE__ */ createElement (
"button" ,
{ class : "danger width-50" , "on:click" : unsubscribe },
translations . unsubscribe ,
),
/* @__PURE__ */ createElement (
"button" ,
{ class : "primary width-50" , "on:click" : subscribe },
translations . subscribe ,
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"arrow_forward" ,
),
),
),
/* @__PURE__ */ createElement ( "hr" , null ),
/* @__PURE__ */ createElement (
"label" ,
{ class : "tile" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"span" ,
null ,
translations . channel ,
),
/* @__PURE__ */ createElement ( "input" , {
placeholder : translations . channel_placeholder ,
"bind:value" : newMessageChannel ,
"on:enter" : sendMessage ,
}),
),
),
/* @__PURE__ */ createElement (
"label" ,
{ class : "tile" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"span" ,
null ,
translations . message ,
),
/* @__PURE__ */ createElement ( "input" , {
placeholder : translations . message_placeholder ,
"bind:value" : newMessageBody ,
"on:enter" : sendMessage ,
}),
),
),
/* @__PURE__ */ createElement (
"div" ,
{ class : "flex-row width-input justify-end" },
/* @__PURE__ */ createElement (
"button" ,
{
class : "primary width-50" ,
"on:click" : sendMessage ,
"toggle:disabled" : isMessageEmpty ,
},
translations . send ,
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"send" ,
),
),
),
),
);
2024-07-05 00:07:39 +02:00
}
2026-03-31 22:02:13 +00:00
// src/messageScreen.tsx
var convertMessageToElement = ( message ) => {
return /* @__PURE__ */ createElement (
"div" ,
{ class : "tile" , style : "flex: 0" },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"span" ,
{ class : "secondary" },
message . channel ,
),
/* @__PURE__ */ createElement ( "b" , null , message . body ),
),
);
};
function MessageScreen () {
return /* @__PURE__ */ createElement (
"article" ,
{ id : "message-screen" },
/* @__PURE__ */ createElement (
"header" ,
null ,
translations . messagesReceived ,
),
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement ( "div" , {
class : "flex-column gap" ,
"subscribe:children" : [ messages , convertMessageToElement ],
}),
),
/* @__PURE__ */ createElement (
"footer" ,
null ,
/* @__PURE__ */ createElement ( "input" , {
style : "max-width: unset" ,
placeholder : translations . message_placeholder_generic ,
"bind:value" : newMessageBody ,
"on:enter" : sendMessage ,
}),
/* @__PURE__ */ createElement (
"button" ,
{
class : "primary" ,
"on:click" : sendMessage ,
"toggle:disabled" : isMessageEmpty ,
},
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"send" ,
),
),
),
);
2024-07-05 00:07:39 +02:00
}
2026-03-31 22:02:13 +00:00
// src/index.tsx
document . body . prepend (
/* @__PURE__ */ createElement (
"menu" ,
null ,
/* @__PURE__ */ createElement (
"a" ,
{ class : "tab-link" , href : "#info-screen" , active : true },
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"info" ,
),
translations . info ,
),
/* @__PURE__ */ createElement (
"a" ,
{ class : "tab-link" , href : "#main-screen" },
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"settings" ,
),
translations . settings ,
),
/* @__PURE__ */ createElement (
"a" ,
{ class : "tab-link" , href : "#message-screen" },
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon" },
"chat" ,
),
translations . messages ,
),
),
2024-07-05 16:49:08 +02:00
);
2026-03-31 22:02:13 +00:00
document
. querySelector ( "main" )
. append ( InfoScreen (), MainScreen (), MessageScreen ());
document . body . append (
/* @__PURE__ */ createElement (
"div" ,
{ class : "modal" , "toggle:open" : isDisconnected },
/* @__PURE__ */ createElement (
"div" ,
null ,
/* @__PURE__ */ createElement (
"main" ,
null ,
/* @__PURE__ */ createElement (
"div" ,
{
class : "flex-column align-center justify-center width-100 height-100" ,
style : "gap: 1rem" ,
},
/* @__PURE__ */ createElement (
"span" ,
{ class : "icon error" , style : "font-size: 3rem" },
"signal_disconnected" ,
),
/* @__PURE__ */ createElement (
"h1" ,
{ class : "error" },
translations . disconnected ,
),
/* @__PURE__ */ createElement (
"p" ,
{ class : "secondary" },
translations . reconnecting ,
),
),
),
),
),
2024-07-08 23:34:16 +02:00
);
2024-07-04 16:11:02 +02:00
})();