2024-07-07 13:43:18 +02:00
(() => {
2024-07-09 15:15:17 +02:00
// node_modules/udn-frontend/index.ts
var UDNFrontend = class {
ws ;
// HANDLERS
connectionHandler = () => {
};
disconnectionHandler = () => {
};
messageHandler = ( data ) => {
};
2024-07-19 18:00:01 +02:00
mailboxHandler = ( mailboxId ) => {
2024-07-09 15:15:17 +02:00
};
2024-07-19 18:00:01 +02:00
mailboxConnectionHandler = ( mailboxId ) => {
2024-07-09 15:15:17 +02:00
};
2024-07-19 18:00:01 +02:00
mailboxDeleteHandler = ( mailboxId ) => {
2024-07-09 15:15:17 +02:00
};
// 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
2024-07-19 18:17:00 +02:00
connect ( address ) {
2024-07-09 15:15:17 +02:00
try {
this . disconnect ();
2024-07-19 18:17:00 +02:00
this . ws = new WebSocket ( address );
2024-07-09 15:15:17 +02:00
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 );
}
});
} catch ( error ) {
console . error ( error );
}
}
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 );
}
2024-07-19 18:00:01 +02:00
connectMailbox ( mailboxId ) {
2024-07-09 15:15:17 +02:00
const messageObject = {
2024-07-19 18:00:01 +02:00
requestedMailbox : mailboxId
2024-07-09 15:15:17 +02:00
};
return this . send ( messageObject );
}
2024-07-19 18:00:01 +02:00
deleteMailbox ( mailboxId ) {
2024-07-09 15:15:17 +02:00
const messageObject = {
2024-07-19 18:00:01 +02:00
deletingMailbox : mailboxId
2024-07-09 15:15:17 +02:00
};
return this . send ( messageObject );
}
};
2024-07-19 23:45:07 +02:00
// src/Model/Utility/utility.ts
2024-07-19 18:00:01 +02:00
function stringify ( data ) {
return JSON . stringify ( data );
2024-07-12 14:30:30 +02:00
}
2024-07-19 19:23:48 +02:00
function parse ( string ) {
return JSON . parse ( string );
}
2024-07-12 14:30:30 +02:00
2024-07-19 18:00:01 +02:00
// src/Model/connectionModel.ts
var ConnectionModel = class {
// basic
udn ;
// data
get isConnected () {
return this . udn . ws != void 0 && this . udn . ws . readyState == 1 ;
2024-07-13 17:13:59 +02:00
}
2024-07-19 18:02:23 +02:00
get address () {
2024-07-19 18:00:01 +02:00
return this . udn . ws ? . url ;
2024-07-13 17:13:59 +02:00
}
2024-07-19 18:00:01 +02:00
// connection
2024-07-19 18:17:00 +02:00
connect = ( address ) => {
this . udn . connect ( address );
2024-07-19 18:02:23 +02:00
};
disconnect = () => {
2024-07-19 18:00:01 +02:00
this . udn . disconnect ();
2024-07-19 18:02:23 +02:00
};
handleConnectionChange = () => {
console . log ( "connection status:" , this . isConnected , this . address );
};
2024-07-19 18:00:01 +02:00
// messaging
2024-07-19 18:02:23 +02:00
sendChatMessage = ( chatMessage ) => {
2024-07-19 18:00:01 +02:00
const stringifiedBody = stringify ( chatMessage );
return this . udn . sendMessage ( chatMessage . channel , stringifiedBody );
2024-07-19 18:02:23 +02:00
};
2024-07-19 18:00:01 +02:00
// setup
2024-07-19 18:17:00 +02:00
constructor ( configuration ) {
2024-07-19 18:00:01 +02:00
this . udn = new UDNFrontend ();
2024-07-19 18:17:00 +02:00
this . udn . onmessage = ( data ) => {
configuration . messageHandler ( data );
};
this . udn . onconnect = () => {
this . handleConnectionChange ();
configuration . connectionChangeHandler ();
};
this . udn . ondisconnect = () => {
this . handleConnectionChange ();
configuration . connectionChangeHandler ();
};
2024-07-09 17:23:29 +02:00
}
};
2024-07-09 17:27:15 +02:00
2024-07-19 23:45:07 +02:00
// node_modules/bloatless-react/index.ts
2024-07-20 02:00:00 +02:00
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 );
}
subscribeSilent ( fn ) {
this . _bindings . add ( fn );
}
// stringify
toString () {
return JSON . stringify ( this . _value );
}
};
function createProxyState ( statesToSubscibe , fn ) {
const proxyState = new State ( fn ());
statesToSubscibe . forEach (
( state ) => state . subscribe (() => proxyState . value = fn ())
);
return proxyState ;
}
2024-07-19 23:45:07 +02:00
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" : {
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 ;
}
case "children" : {
switch ( directiveValue ) {
case "set" : {
const state = value ;
state . subscribe (( newValue ) => {
element . innerHTML = "" ;
element . append (...[ newValue ]. flat ());
});
break ;
}
case "append" :
case "appendandscroll" :
case "prepend" : {
element . style . scrollBehavior = "smooth" ;
try {
const [ listState , toElement ] = value ;
listState . handleAddition (( newItem ) => {
const child = toElement ( newItem );
listState . handleRemoval (
newItem ,
() => child . remove ()
);
if ( directiveValue == "append" ) {
element . append ( child );
} else if ( directiveValue == "prepend" ) {
element . prepend ( child );
}
});
} catch {
throw `error: cannot process subscribe:children directive because StateItemConverter is not defined. Usage: "subscribe:children={[list, converter]}"; you can find a more detailed example in the documentation` ;
}
}
}
}
default :
element . setAttribute ( attributename , value );
}
});
children . filter (( x ) => x ). forEach (( child ) => element . append ( child ));
return element ;
}
2024-07-20 02:00:00 +02:00
// src/View/Components/option.tsx
function Option ( text , value , selectedOnCreate ) {
return /* @__PURE__ */ createElement ( "option" , { value , "toggle:selected" : selectedOnCreate }, text );
}
2024-07-19 23:45:07 +02:00
// src/View/translations.ts
var englishTranslations = {
general : {
closeButton : "close"
2024-07-19 18:17:00 +02:00
},
2024-07-20 01:14:21 +02:00
regional : {
weekdays : {
full : [
"Sunday" ,
"Monday" ,
"Tuesday" ,
"Wednesday" ,
"Thursday" ,
"Friday" ,
"Saturday"
],
abbreviated : [
"Sun" ,
"Mon" ,
"Tue" ,
"Wed" ,
"Thu" ,
"Fri" ,
"Sat"
]
}
},
2024-07-19 23:45:07 +02:00
homePage : {
appName : "Comms" ,
///
overviewHeadline : "Overview" ,
2024-07-20 01:14:21 +02:00
serverAddress : "Server address" ,
connectAudioLabel : "connect to server" ,
disconnectAudioLabel : "disconnect from server" ,
manageConnectionsAudioLabel : "manage connections" ,
mailboxHeadline : "Server Mailbox" ,
mailboxDisabled : "Mailbox disabled. You will miss out on messages sent while you're away" ,
mailboxEnabled : "Mailbox enabled. If you disconnect, the server will keep your messages temporarily" ,
outboxHeadline : "Outbox" ,
outboxAllItemsSent : "All items sent" ,
yourNameLabel : "Your name" ,
setNameButton : "Set" ,
firstDayOfWeekLabel : "First day of week" ,
2024-07-20 00:15:05 +02:00
scrollToChatButton : "Chats" ,
2024-07-19 23:45:07 +02:00
///
2024-07-20 00:15:05 +02:00
backToOverviewAudioLabel : "go back to overview" ,
2024-07-19 23:45:07 +02:00
chatsHeadline : "Chats" ,
addChatAudioLabel : "name of new chat" ,
addChatPlaceholder : "Add chat" ,
addChatButton : "Add chat"
},
settings : {
settingsHeadline : "Settings"
///
2024-07-19 18:17:00 +02:00
}
2024-07-19 23:45:07 +02:00
};
var allTranslations = {
en : englishTranslations
};
var language = navigator . language . substring ( 0 , 2 );
var translations = allTranslations [ language ] || allTranslations . en ;
// src/View/homePage.tsx
2024-07-20 02:00:00 +02:00
function HomePage ( settingsViewModel2 ) {
2024-07-20 01:14:21 +02:00
const overviewSection = /* @__PURE__ */ createElement ( "div" , { id : "overview-section" }, /* @__PURE__ */ createElement ( "h2" , null , translations . homePage . overviewHeadline ), /* @__PURE__ */ createElement ( "label" , { class : "tile flex-no" }, /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "cell_tower" ), /* @__PURE__ */ createElement ( "div" , null , /* @__PURE__ */ createElement ( "span" , null , translations . homePage . serverAddress ), /* @__PURE__ */ createElement ( "input" , null ))), /* @__PURE__ */ createElement ( "div" , { class : "flex-row" }, /* @__PURE__ */ createElement (
"button" ,
{
class : "danger flex justify-center" ,
"aria-label" : translations . homePage . disconnectAudioLabel
},
/* @__PURE__ */ createElement ( "span" , { class : "icon" }, "link_off" )
), /* @__PURE__ */ createElement (
"button" ,
{
class : "flex justify-center" ,
"aria-label" : translations . homePage . manageConnectionsAudioLabel
},
/* @__PURE__ */ createElement ( "span" , { class : "icon" }, "build" )
), /* @__PURE__ */ createElement (
"button" ,
{
class : "primary flex justify-center" ,
"aria-label" : translations . homePage . connectAudioLabel
},
/* @__PURE__ */ createElement ( "span" , { class : "icon" }, "link" )
2024-07-20 02:00:00 +02:00
)), /* @__PURE__ */ createElement ( "hr" , null ), /* @__PURE__ */ createElement ( "div" , { class : "tile flex-no" }, /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "inbox" ), /* @__PURE__ */ createElement ( "div" , null , /* @__PURE__ */ createElement ( "b" , null , translations . homePage . mailboxHeadline ), /* @__PURE__ */ createElement ( "span" , { class : "error" }, translations . homePage . mailboxDisabled ))), /* @__PURE__ */ createElement ( "div" , { class : "tile flex-no" }, /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "outbox" ), /* @__PURE__ */ createElement ( "div" , null , /* @__PURE__ */ createElement ( "b" , null , translations . homePage . outboxHeadline ), /* @__PURE__ */ createElement ( "span" , { class : "success" }, translations . homePage . outboxAllItemsSent ))), /* @__PURE__ */ createElement ( "hr" , null ), /* @__PURE__ */ createElement ( "label" , { class : "tile flex-no" }, /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "account_circle" ), /* @__PURE__ */ createElement ( "div" , null , /* @__PURE__ */ createElement ( "span" , null , translations . homePage . yourNameLabel ), /* @__PURE__ */ createElement (
"input" ,
{
"bind:value" : settingsViewModel2 . nameInput ,
"on:enter" : settingsViewModel2 . setName
}
))), /* @__PURE__ */ createElement ( "div" , { class : "flex-row justify-end" }, /* @__PURE__ */ createElement (
"button" ,
{
class : "width-50" ,
"on:click" : settingsViewModel2 . setName ,
"toggle:disabled" : settingsViewModel2 . cannotSetName
},
translations . homePage . setNameButton ,
/* @__PURE__ */ createElement ( "span" , { class : "icon" }, "check" )
)), /* @__PURE__ */ createElement ( "hr" , null ), /* @__PURE__ */ createElement ( "label" , { class : "tile flex-no" }, /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "calendar_month" ), /* @__PURE__ */ createElement ( "div" , null , /* @__PURE__ */ createElement ( "span" , null , translations . homePage . firstDayOfWeekLabel ), /* @__PURE__ */ createElement ( "select" , { "bind:value" : settingsViewModel2 . firstDayOfWeekInput }, ... translations . regional . weekdays . full . map (
( weekdayName , i ) => Option (
weekdayName ,
i . toString (),
i == settingsViewModel2 . firstDayOfWeekInput . value
)
)), /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "arrow_drop_down" ))), /* @__PURE__ */ createElement ( "div" , { class : "mobile-only" }, /* @__PURE__ */ createElement ( "hr" , null ), /* @__PURE__ */ createElement ( "div" , { class : "flex-row justify-end" }, /* @__PURE__ */ createElement ( "button" , { class : "ghost width-50" , "on:click" : scrollToChat }, translations . homePage . scrollToChatButton , /* @__PURE__ */ createElement ( "span" , { class : "icon" }, "arrow_forward" )))));
2024-07-20 00:15:05 +02:00
const chatSection = /* @__PURE__ */ createElement ( "div" , { id : "chat-section" }, /* @__PURE__ */ createElement ( "h2" , null , translations . homePage . chatsHeadline ), /* @__PURE__ */ createElement ( "div" , { class : "flex-row width-input" }, /* @__PURE__ */ createElement (
2024-07-19 23:45:07 +02:00
"input" ,
{
placeholder : translations . homePage . addChatPlaceholder ,
"aria-label" : translations . homePage . addChatAudioLabel
}
), /* @__PURE__ */ createElement (
"button" ,
{
class : "primary" ,
"aria-label" : translations . homePage . addChatButton
},
/* @__PURE__ */ createElement ( "span" , { class : "icon" }, "add" )
2024-07-20 00:15:05 +02:00
)));
function scrollToChat () {
chatSection . scrollIntoView ();
}
return /* @__PURE__ */ createElement ( "article" , { id : "home-page" }, /* @__PURE__ */ createElement ( "header" , null , /* @__PURE__ */ createElement ( "span" , null , translations . homePage . appName )), /* @__PURE__ */ createElement ( "div" , null , overviewSection , chatSection ));
2024-07-19 23:45:07 +02:00
}
// src/Model/Utility/typeSafety.ts
2024-07-19 21:10:31 +02:00
var DATA_VERSION = "v2" ;
2024-07-19 19:23:48 +02:00
// src/Model/storageModel.ts
2024-07-19 21:10:31 +02:00
var StorageModel = class _StorageModel {
2024-07-19 19:23:48 +02:00
storageEntryTree = {};
// basic
2024-07-19 21:10:31 +02:00
store = ( key , value ) => {
localStorage . setItem ( key , value );
const pathComponents = _StorageModel . keyToPathComponents ( key );
this . updateTree (... pathComponents );
2024-07-19 19:23:48 +02:00
};
2024-07-19 21:10:31 +02:00
restore = ( key ) => {
return localStorage . getItem ( key );
2024-07-19 19:23:48 +02:00
};
2024-07-19 21:10:31 +02:00
remove = ( key ) => {
localStorage . removeItem ( key );
2024-07-19 19:23:48 +02:00
};
2024-07-19 21:10:31 +02:00
list = ( key ) => {
const pathComponents = _StorageModel . keyToPathComponents ( key );
2024-07-19 19:23:48 +02:00
let currentParent = this . storageEntryTree ;
2024-07-19 21:10:31 +02:00
for ( const component of pathComponents ) {
const nextParent = currentParent [ component ];
if ( nextParent == void 0 ) return [];
currentParent = nextParent ;
2024-07-19 19:23:48 +02:00
}
return [... Object . keys ( currentParent )];
};
2024-07-19 21:10:31 +02:00
// stringifiable
storeStringifiable = ( key , value ) => {
2024-07-19 19:23:48 +02:00
const valueString = stringify ( value );
2024-07-19 21:10:31 +02:00
this . store ( key , valueString );
2024-07-19 19:23:48 +02:00
};
2024-07-19 21:10:31 +02:00
restoreStringifiable = ( key ) => {
const valueString = this . restore ( key );
2024-07-19 19:23:48 +02:00
if ( ! valueString ) return null ;
2024-07-19 21:10:31 +02:00
return parse ( valueString );
2024-07-19 19:23:48 +02:00
};
// init
constructor () {
for ( const key of Object . keys ( localStorage )) {
2024-07-19 21:10:31 +02:00
const components = _StorageModel . keyToPathComponents ( key );
this . updateTree (... components );
2024-07-19 19:23:48 +02:00
}
}
// utility
2024-07-19 21:10:31 +02:00
updateTree = (... pathComponents ) => {
2024-07-19 19:23:48 +02:00
let currentParent = this . storageEntryTree ;
2024-07-19 21:10:31 +02:00
for ( const pathPart of pathComponents ) {
2024-07-19 19:23:48 +02:00
if ( ! currentParent [ pathPart ]) {
currentParent [ pathPart ] = {};
}
currentParent = currentParent [ pathPart ];
}
};
2024-07-19 21:10:31 +02:00
static pathComponentsToKey = (... pathComponents ) => {
return pathComponents . join ( "/" );
2024-07-19 19:23:48 +02:00
};
2024-07-19 21:10:31 +02:00
static keyToPathComponents = ( key ) => {
2024-07-19 19:23:48 +02:00
return key . split ( "/" );
};
2024-07-19 21:10:31 +02:00
static join = (... items ) => {
let allComponents = [];
for ( const item of items ) {
const parts = this . keyToPathComponents ( item );
allComponents . push (... parts );
}
return _StorageModel . pathComponentsToKey (... allComponents );
};
};
var storageKeys = {
// connection
socketAddress : ` ${ DATA_VERSION } /connection/socket-address` ,
// settings
2024-07-20 01:14:21 +02:00
username : ` ${ DATA_VERSION } /settings/user-name` ,
2024-07-19 21:10:31 +02:00
firstDayOfWeek : ` ${ DATA_VERSION } /settings/first-day-of-week` ,
// history
previousAddresses : ` ${ DATA_VERSION } /history/previous-addresses` ,
previousObjectCategories : ` ${ DATA_VERSION } /history/object-categories` ,
previousObjectStatuses : ` ${ DATA_VERSION } /history/object-statuses` ,
previousObjectFilters : ` ${ DATA_VERSION } /history/object-filters` ,
// chat etc
chatInfo : ( id ) => ` ${ DATA_VERSION } /chat/ ${ id } /info` ,
chatMessages : ( id ) => ` ${ DATA_VERSION } /chat/ ${ id } /messages` ,
chatObjects : ( id ) => ` ${ DATA_VERSION } /chat/ ${ id } /objects` ,
chatOutbox : ( id ) => ` ${ DATA_VERSION } /chat/ ${ id } /outbox`
2024-07-19 19:23:48 +02:00
};
2024-07-20 02:00:00 +02:00
// src/Model/settingsModel.ts
var SettingsModel = class {
storageModel ;
username ;
firstDayOfWeek ;
// set
setName ( newValue ) {
this . username = newValue ;
const path = storageKeys . username ;
this . storageModel . store ( path , newValue );
}
setFirstDayOfWeek ( newValue ) {
this . firstDayOfWeek = newValue ;
const path = storageKeys . firstDayOfWeek ;
this . storageModel . storeStringifiable ( path , newValue );
}
// restore
restoreUsername () {
const path = storageKeys . username ;
const content = this . storageModel . restore ( path );
this . username = content ?? "" ;
}
restoreFirstDayofWeek () {
const path = storageKeys . firstDayOfWeek ;
const content = this . storageModel . restoreStringifiable ( path );
this . firstDayOfWeek = content ?? 0 ;
}
// init
constructor ( storageModel2 ) {
this . storageModel = storageModel2 ;
this . restoreUsername ();
this . restoreFirstDayofWeek ();
}
};
// src/ViewModel/settingsViewModel.ts
var SettingsViewModel = class {
settingsModel ;
// state
nameInput = new State ( "" );
firstDayOfWeekInput = new State ( 0 );
// toggles
cannotSetName = createProxyState (
[ this . nameInput ],
() => this . nameInput . value == "" || this . nameInput . value == this . settingsModel . username
);
// set
setName = () => {
this . settingsModel . setName ( this . nameInput . value );
this . nameInput . callSubscriptions ();
};
setFirstDayofWeek = () => {
this . settingsModel . setFirstDayOfWeek ( this . firstDayOfWeekInput . value );
};
// init
constructor ( settingsModel2 ) {
this . settingsModel = settingsModel2 ;
this . nameInput . value = settingsModel2 . username ;
this . firstDayOfWeekInput . value = settingsModel2 . firstDayOfWeek ;
this . firstDayOfWeekInput . subscribe ( this . setFirstDayofWeek );
}
};
2024-07-19 19:23:48 +02:00
// src/index.tsx
var storageModel = new StorageModel ();
2024-07-20 02:00:00 +02:00
var settingsModel = new SettingsModel ( storageModel );
2024-07-19 23:45:07 +02:00
var connectionModel = new ConnectionModel ({
connectionChangeHandler () {
},
messageHandler ( data ) {
}
});
2024-07-20 02:00:00 +02:00
var settingsViewModel = new SettingsViewModel ( settingsModel );
document . querySelector ( "main" ). append ( HomePage ( settingsViewModel ));
2024-07-07 13:43:18 +02:00
})();