2024-04-18 19:37:59 +00:00
window . Umi = { UI : { } } ;
2024-01-18 19:50:37 +00:00
2025-02-20 20:49:09 +00:00
# include array . js
# include html . js
# include uniqstr . js
# include xhr . js
2024-01-18 19:50:37 +00:00
# include animate . js
2024-06-24 23:33:25 +00:00
# include args . js
2024-06-24 21:36:07 +00:00
# include awaitable . js
2024-01-18 19:50:37 +00:00
# include common . js
2024-02-29 23:57:36 +00:00
# include compat . js
2024-03-02 01:31:26 +00:00
# include conman . js
2024-01-18 19:50:37 +00:00
# include context . js
# include emotes . js
2024-04-17 15:42:50 +00:00
# include events . js
2024-05-10 01:16:06 +00:00
# include mobile . js
2024-01-18 19:50:37 +00:00
# include mszauth . js
2024-07-01 21:38:07 +00:00
# include parsing . js
2024-06-26 14:06:01 +00:00
# include themes . js
2024-02-10 17:23:47 +00:00
# include txtrigs . js
2024-06-24 21:36:07 +00:00
# include users . js
2024-01-22 21:45:39 +00:00
# include weeb . js
2024-02-09 22:38:57 +00:00
# include audio / autoplay . js
2024-07-01 21:38:07 +00:00
# include chatform / form . jsx
# include colpick / picker . jsx
2024-04-19 22:39:21 +00:00
# include controls / msgbox . jsx
2024-06-24 21:42:00 +00:00
# include controls / ping . jsx
2024-02-10 17:23:47 +00:00
# include controls / views . js
2024-01-22 21:45:39 +00:00
# include eeprom / eeprom . js
2025-01-14 01:43:49 +00:00
# include eeprom / eepromv2 . js
2024-06-26 23:04:12 +00:00
# include emotes / picker . jsx
2024-06-24 21:42:00 +00:00
# include notices / baka . jsx
# include notices / youare . jsx
2024-01-22 21:45:39 +00:00
# include settings / backup . js
# include settings / settings . js
2024-06-24 21:36:07 +00:00
# include sidebar / act - clear - backlog . jsx
# include sidebar / act - collapse - all . jsx
# include sidebar / act - ping . jsx
# include s idebar / act - scroll . jsx
# include sidebar / act - sound . jsx
# include sidebar / act - toggle . jsx
# include sidebar / pan - channels . jsx
# include sidebar / pan - settings . jsx
# include sidebar / pan - uploads . jsx
# include sidebar / pan - users . jsx
# include sidebar / sidebar . jsx
2024-04-17 15:42:50 +00:00
# include sockchat / client . js
# include sockchat / handlers . js
2024-02-10 17:23:47 +00:00
# include sound / context . js
2024-01-22 21:45:39 +00:00
# include sound / osukeys . js
2024-06-24 21:36:07 +00:00
# include sound / sndtest . jsx
2024-01-18 19:50:37 +00:00
# include ui / chat - layout . js
2024-06-24 21:36:07 +00:00
# include ui / emotes . js
2024-01-18 19:50:37 +00:00
# include ui / loading - overlay . jsx
2024-06-24 23:33:25 +00:00
const MamiInit = async args => {
args = MamiArgs ( 'args' , args , define => {
define ( 'parent' ) . default ( document . body ) . constraint ( value => value instanceof Element ) . done ( ) ;
define ( 'eventTarget' ) . required ( ) . constraint ( MamiIsEventTarget ) . done ( ) ;
define ( 'settingsPrefix' ) . default ( 'umi-' ) . done ( ) ;
} ) ;
2024-04-17 15:42:50 +00:00
2024-06-24 23:33:25 +00:00
const ctx = new MamiContext ( args . eventTarget ) ;
2024-04-17 15:42:50 +00:00
2024-06-24 23:33:25 +00:00
// remove this later and replace with the one commented out way below
if ( ! ( 'mami' in window ) )
Object . defineProperty ( window , 'mami' , { enumerable : true , value : ctx } ) ;
ctx . views = new MamiViewsControl ( { body : args . parent } ) ;
ctx . msgbox = new MamiMessageBoxControl ( { parent : args . parent } ) ;
2024-04-17 15:42:50 +00:00
2024-02-10 17:23:47 +00:00
const loadingOverlay = new Umi . UI . LoadingOverlay ( 'spinner' , 'Loading...' ) ;
2024-04-19 22:39:21 +00:00
await ctx . views . push ( loadingOverlay ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 23:33:25 +00:00
if ( ! ( 'futami' in window ) ) {
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Loading environment...' ;
2024-06-24 23:33:25 +00:00
try {
window . futami = await FutamiCommon . load ( ) ;
} catch ( ex ) {
console . error ( 'Failed to load common settings.' , ex ) ;
2024-06-26 00:53:12 +00:00
loadingOverlay . icon = 'cross' ;
loadingOverlay . header = 'Failed!' ;
loadingOverlay . message = 'Failed to load common settings.' ;
2024-06-24 23:33:25 +00:00
return ;
}
2024-01-18 19:50:37 +00:00
}
2024-06-24 23:33:25 +00:00
if ( ! MamiMisuzuAuth . hasInfo ( ) ) {
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Fetching credentials...' ;
2024-06-24 23:33:25 +00:00
try {
const auth = await MamiMisuzuAuth . update ( ) ;
if ( ! auth . ok )
throw 'Authentication failed.' ;
} catch ( ex ) {
console . error ( ex ) ;
location . assign ( futami . get ( 'login' ) ) ;
return ;
}
2024-01-18 19:50:37 +00:00
2024-06-24 23:33:25 +00:00
setInterval ( ( ) => {
MamiMisuzuAuth . update ( )
. then ( auth => {
if ( ! auth . ok )
location . assign ( futami . get ( 'login' ) ) ;
} )
} , 600000 ) ;
}
2024-01-18 19:50:37 +00:00
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Loading settings...' ;
2024-01-22 21:45:39 +00:00
2024-06-24 23:33:25 +00:00
const settings = new MamiSettings ( args . settingsPrefix , ctx . events . scopeTo ( 'settings' ) ) ;
2024-04-17 15:42:50 +00:00
ctx . settings = settings ;
2024-01-22 21:45:39 +00:00
2024-04-17 17:26:33 +00:00
settings . define ( 'style' ) . default ( 'dark' ) . create ( ) ;
settings . define ( 'compactView' ) . default ( false ) . create ( ) ;
settings . define ( 'autoScroll' ) . default ( true ) . create ( ) ;
settings . define ( 'closeTabConfirm' ) . default ( false ) . create ( ) ;
settings . define ( 'autoCloseUserContext' ) . default ( true ) . create ( ) ;
settings . define ( 'preventOverflow' ) . default ( false ) . create ( ) ;
settings . define ( 'expandTextBox' ) . default ( false ) . create ( ) ;
settings . define ( 'eepromAutoInsert' ) . default ( true ) . create ( ) ;
settings . define ( 'autoEmbedV1' ) . default ( false ) . create ( ) ;
2024-05-16 20:16:32 +00:00
settings . define ( 'autoEmbedPlay' ) . default ( false ) . create ( ) ;
2024-04-17 17:26:33 +00:00
settings . define ( 'soundEnable' ) . default ( true ) . critical ( ) . create ( ) ;
2024-04-17 19:16:27 +00:00
settings . define ( 'soundPack' ) . default ( '' ) . create ( ) ;
2024-04-21 18:58:18 +00:00
settings . define ( 'soundVolume' ) . default ( 80 ) . min ( 0 ) . max ( 100 ) . create ( ) ;
2024-04-17 17:26:33 +00:00
settings . define ( 'soundEnableJoin' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnableLeave' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnableError' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnableServer' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnableIncoming' ) . default ( true ) . create ( ) ;
settings . define ( 'onlySoundOnMention' ) . default ( false ) . create ( ) ;
settings . define ( 'soundEnableOutgoing' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnablePrivate' ) . default ( true ) . create ( ) ;
settings . define ( 'soundEnableForceLeave' ) . default ( true ) . create ( ) ;
settings . define ( 'minecraft' ) . type ( [ 'no' , 'yes' , 'old' ] ) . default ( 'no' ) . create ( ) ;
settings . define ( 'windowsLiveMessenger' ) . default ( false ) . create ( ) ;
settings . define ( 'seinfeld' ) . default ( false ) . create ( ) ;
settings . define ( 'flashTitle' ) . default ( true ) . create ( ) ;
2024-05-10 01:16:06 +00:00
settings . define ( 'onlyConnectWhenVisible2' ) . default ( MamiIsMobileDevice ( ) ) . create ( ) ;
2024-04-17 17:26:33 +00:00
settings . define ( 'playJokeSounds' ) . default ( true ) . create ( ) ;
settings . define ( 'weeaboo' ) . default ( false ) . create ( ) ;
settings . define ( 'motivationalImages' ) . default ( false ) . create ( ) ;
settings . define ( 'motivationalVideos' ) . default ( false ) . create ( ) ;
settings . define ( 'osuKeys' ) . default ( false ) . create ( ) ;
settings . define ( 'osuKeysV2' ) . type ( [ 'no' , 'yes' , 'rng' ] ) . default ( 'no' ) . create ( ) ;
2024-04-21 18:58:18 +00:00
settings . define ( 'explosionRadius' ) . default ( 20 ) . min ( 0 ) . create ( ) ;
2024-04-17 17:26:33 +00:00
settings . define ( 'dumpPackets' ) . default ( FUTAMI _DEBUG ) . create ( ) ;
settings . define ( 'dumpEvents' ) . default ( FUTAMI _DEBUG ) . create ( ) ;
settings . define ( 'marqueeAllNames' ) . default ( false ) . create ( ) ;
2024-04-21 19:15:38 +00:00
settings . define ( 'dbgAnimDurationMulti' ) . default ( 1 ) . min ( 0 ) . max ( 10 ) . create ( ) ;
2024-06-24 22:10:00 +00:00
settings . define ( 'newLineOnEnter' ) . default ( false ) . create ( ) ;
2024-06-26 23:04:12 +00:00
settings . define ( 'keepEmotePickerOpen' ) . default ( true ) . create ( ) ;
2024-06-27 16:01:28 +00:00
settings . define ( 'doNotMarkLinksAsVisited' ) . default ( false ) . create ( ) ;
2024-07-01 21:38:07 +00:00
settings . define ( 'showMarkupSelector' ) . type ( [ 'always' , 'focus' , 'never' ] ) . default ( 'always' ) . create ( ) ;
2025-01-14 01:43:49 +00:00
settings . define ( 'dbgEEPROMv2' ) . default ( false ) . create ( ) ;
2024-01-22 21:45:39 +00:00
const noNotifSupport = ! ( 'Notification' in window ) ;
2024-04-17 17:26:33 +00:00
settings . define ( 'enableNotifications' ) . default ( false ) . immutable ( noNotifSupport ) . critical ( ) . create ( ) ;
settings . define ( 'notificationShowMessage' ) . default ( false ) . immutable ( noNotifSupport ) . create ( ) ;
settings . define ( 'notificationTriggers' ) . default ( '' ) . immutable ( noNotifSupport ) . create ( ) ;
2024-01-22 21:45:39 +00:00
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Loading sounds...' ;
2024-02-10 17:23:47 +00:00
const soundCtx = new MamiSoundContext ;
2024-04-17 15:42:50 +00:00
ctx . sound = soundCtx ;
2024-02-10 17:23:47 +00:00
2024-04-17 19:16:27 +00:00
futami . getJson ( 'sounds2' )
. catch ( ex => { console . error ( 'Failed to load sound library and packs.' , ex ) ; } )
. then ( sounds => {
if ( Array . isArray ( sounds . library ) )
soundCtx . library . register ( sounds . library , true ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
if ( Array . isArray ( sounds . packs ) ) {
soundCtx . packs . register ( sounds . packs , true ) ;
settings . touch ( 'soundPack' , true ) ;
}
} ) ;
MamiDetectAutoPlay ( )
. then ( canAutoPlay => {
if ( canAutoPlay ) return ;
settings . set ( 'soundEnable' , false ) ;
settings . virtualise ( 'soundEnable' ) ;
} ) ;
2024-01-22 21:45:39 +00:00
2024-03-01 19:07:17 +00:00
settings . watch ( 'soundEnable' , ev => {
if ( ev . detail . value ) {
2024-02-10 17:23:47 +00:00
if ( ! soundCtx . ready )
soundCtx . reset ( ) ;
2024-01-22 21:45:39 +00:00
settings . touch ( 'soundVolume' ) ;
2024-04-17 19:16:27 +00:00
settings . touch ( 'soundPack' , true ) ;
2024-01-22 21:45:39 +00:00
2024-04-17 19:16:27 +00:00
// do we need to do this?
if ( ! ev . detail . initial && ! ev . detail . silent && ev . detail . local )
soundCtx . library . play ( soundCtx . pack . getEventSound ( 'server' ) ) ;
2024-01-22 21:45:39 +00:00
}
2024-03-01 19:07:17 +00:00
soundCtx . muted = ! ev . detail . value ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'soundPack' , ev => {
2024-02-10 17:23:47 +00:00
const packs = soundCtx . packs ;
2024-04-17 19:16:27 +00:00
let packName = ev . detail . value ;
if ( packName === '' ) {
2024-06-26 00:53:12 +00:00
const names = packs . names ;
2024-04-17 19:16:27 +00:00
if ( names . length < 1 )
return ;
packName = names [ 0 ] ;
} else if ( ! packs . has ( packName ) )
2024-01-22 21:45:39 +00:00
return ;
2024-04-17 19:16:27 +00:00
soundCtx . pack = packs . get ( packName ) ;
if ( ! ev . detail . initial && ! ev . detail . silent && ev . detail . local )
soundCtx . library . play ( soundCtx . pack . getEventSound ( 'server' ) ) ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'soundVolume' , ev => {
soundCtx . volume = ev . detail . value / 100 ;
2024-01-22 21:45:39 +00:00
} )
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
// loading these asynchronously makes them not show up in the backlog
// revisit when emote reparsing is implemented
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Loading emoticons...' ;
2024-01-18 19:50:37 +00:00
try {
2024-11-14 03:20:36 +00:00
MamiEmotes . load ( await futami . getApiJson ( '/v1/emotes' ) ) ;
2024-01-18 19:50:37 +00:00
} catch ( ex ) {
2024-04-17 19:16:27 +00:00
console . error ( 'Failed to load emoticons.' , ex ) ;
2024-01-18 19:50:37 +00:00
}
const onHashChange = ( ) => {
if ( location . hash === '#reset' ) {
2024-01-22 21:45:39 +00:00
settings . clear ( true ) ;
2024-01-18 19:50:37 +00:00
location . assign ( '/' ) ;
}
} ;
window . addEventListener ( 'hashchange' , onHashChange ) ;
onHashChange ( ) ;
window . addEventListener ( 'keydown' , ev => {
2024-02-29 23:57:36 +00:00
if ( ev . altKey && ev . shiftKey && ( ev . key === 'R' || ev . key === 'r' ) )
2024-01-18 19:50:37 +00:00
location . hash = 'reset' ;
} ) ;
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Preparing UI...' ;
2024-02-10 17:23:47 +00:00
2024-04-17 15:42:50 +00:00
ctx . textTriggers = new MamiTextTriggers ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
const sidebar = new MamiSidebar ;
MamiCompat ( 'Umi.UI.Menus.Add' , {
value : ( baseId , title ) => {
sidebar . createPanel ( {
name : ` compat: ${ baseId } ` ,
text : title ,
createdButton : button => {
button . element . id = ` umi-menu-icons- ${ baseId } ` ;
2025-02-20 20:49:09 +00:00
button . element . append ( $element ( 'div' , { className : ` sidebar__selector-mode-- ${ baseId } ` } ) ) ;
2024-06-24 21:36:07 +00:00
} ,
2025-02-20 20:49:09 +00:00
element : $element ( 'div' , { 'class' : ` sidebar__menu-- ${ baseId } ` , id : ` umi-menus- ${ baseId } ` } ) ,
2024-06-24 21:36:07 +00:00
} ) ;
}
} ) ;
MamiCompat ( 'Umi.UI.Menus.Get' , {
value : ( baseId , icon ) => {
const info = sidebar [ icon ? 'getButton' : 'getPanel' ] ( ` compat: ${ baseId } ` ) ;
if ( info === undefined )
return null ;
return icon ? info . element : info . element . firstElementChild ;
} ,
} ) ;
MamiCompat ( 'Umi.UI.Menus.Attention' , {
value : baseId => {
sidebar . getButton ( ` compat: ${ baseId } ` ) ? . attention ( ) ;
} ,
} ) ;
2024-07-01 21:38:07 +00:00
const chatForm = new MamiChatForm ( ctx . events . scopeTo ( 'form' ) ) ;
window . addEventListener ( 'keydown' , ev => {
if ( ( ev . ctrlKey && ev . key !== 'v' ) || ev . altKey )
return ;
if ( ! ev . target . matches ( 'input, textarea, select, button' ) )
chatForm . focus ( ) ;
} ) ;
settings . watch ( 'showMarkupSelector' , ev => {
chatForm . markup . visible = ev . detail . value !== 'never' ;
Umi . UI . Messages . ScrollIfNeeded ( chatForm . markup . height ) ;
} ) ;
let colourPicker , colourPickerVisible = false ;
for ( const bbCode of UmiBBCodes )
if ( bbCode . text !== undefined )
chatForm . markup . createButton ( {
title : bbCode . text ,
style : bbCode . style ,
onclick : ev => {
if ( bbCode . tag === 'color' ) {
if ( colourPicker === undefined ) {
colourPicker = new MamiColourPicker ( { presets : futami . get ( 'colours' ) } ) ;
2025-02-20 20:49:09 +00:00
layout . element . appendChild ( colourPicker . element ) ;
2024-07-01 21:38:07 +00:00
}
if ( colourPickerVisible ) {
colourPicker . close ( ) ;
} else {
colourPickerVisible = true ;
colourPicker . dialog ( ev )
. then ( colour => { chatForm . input . insertAroundSelection ( ` [ ${ bbCode . tag } = ${ MamiColour . hex ( colour ) } ] ` , ` [/ ${ bbCode . tag } ] ` ) } )
. catch ( ( ) => { } ) // noop so the console stops screaming
. finally ( ( ) => colourPickerVisible = false ) ;
}
} else
chatForm . input . insertAroundSelection ( ` [ ${ bbCode . tag } ${ ( bbCode . arg ? '=' : '' ) } ] ` , ` [/ ${ bbCode . tag } ] ` ) ;
} ,
} ) ;
const layout = new Umi . UI . ChatLayout ( chatForm , sidebar ) ;
2024-04-19 22:39:21 +00:00
await ctx . views . unshift ( layout ) ;
2024-02-09 00:56:27 +00:00
2024-07-01 21:38:07 +00:00
ctx . events . watch ( 'form:resize' , ev => {
Umi . UI . Messages . ScrollIfNeeded ( ev . detail . diffHeight ) ;
} ) ;
2024-01-18 19:50:37 +00:00
2024-06-26 14:06:01 +00:00
settings . watch ( 'style' , ev => {
2025-02-20 20:49:09 +00:00
for ( const className of layout . element . classList )
2024-06-26 14:06:01 +00:00
if ( className . startsWith ( 'umi--' ) )
2025-02-20 20:49:09 +00:00
layout . element . classList . remove ( className ) ;
layout . element . classList . add ( ` umi-- ${ ev . detail . value } ` ) ;
2024-06-26 14:06:01 +00:00
UmiThemeApply ( ev . detail . value ) ;
} ) ;
settings . watch ( 'compactView' , ev => {
2025-02-20 20:49:09 +00:00
layout . element . classList . toggle ( 'chat--compact' , ev . detail . value ) ;
layout . interface . messageList . element . classList . toggle ( 'chat--compact' , ev . detail . value ) ;
2024-06-26 14:06:01 +00:00
} ) ;
2024-06-27 16:01:28 +00:00
settings . watch ( 'preventOverflow' , ev => { args . parent . classList . toggle ( 'prevent-overflow' , ev . detail . value ) ; } ) ;
2025-02-20 20:49:09 +00:00
settings . watch ( 'doNotMarkLinksAsVisited' , ev => { layout . interface . messageList . element . classList . toggle ( 'mami-do-not-mark-links-as-visited' , ev . detail . value ) ; } ) ;
2024-07-01 21:38:07 +00:00
settings . watch ( 'newLineOnEnter' , ev => { chatForm . input . newLineOnEnter = ev . detail . value ; } ) ;
settings . watch ( 'expandTextBox' , ev => { chatForm . input . growInputField = ev . detail . value ; } ) ;
2024-01-22 21:45:39 +00:00
2024-03-01 19:07:17 +00:00
settings . watch ( 'minecraft' , ev => {
if ( ev . detail . initial && ev . detail . value === 'no' )
2024-02-16 00:41:13 +00:00
return ;
soundCtx . library . play ( ( ( ) => {
2024-03-02 16:23:21 +00:00
if ( ev . detail . initial )
2024-02-16 00:41:13 +00:00
return 'minecraft:nether:enter' ;
2024-03-01 19:07:17 +00:00
if ( ev . detail . value === 'yes' )
2024-02-16 00:41:13 +00:00
return 'minecraft:door:open' ;
2024-03-01 19:07:17 +00:00
if ( ev . detail . value === 'old' )
2024-02-16 00:41:13 +00:00
return 'minecraft:door:open-old' ;
2024-02-24 01:10:30 +00:00
return soundCtx . pack . getEventSound ( 'join' ) ;
2024-02-16 00:41:13 +00:00
} ) ( ) ) ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'enableNotifications' , ev => {
if ( ! ev . detail . value || ! ( 'Notification' in window )
2024-01-22 21:45:39 +00:00
|| ( Notification . permission === 'granted' && Notification . permission !== 'denied' ) )
return ;
Notification . requestPermission ( )
. then ( perm => {
if ( perm !== 'granted' )
settings . set ( 'enableNotifications' , false ) ;
} ) ;
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'playJokeSounds' , ev => {
if ( ! ev . detail . value ) return ;
2024-01-22 21:45:39 +00:00
2024-06-26 00:53:12 +00:00
if ( ! ctx . textTriggers . hasTriggers )
2024-04-17 15:42:50 +00:00
futami . getJson ( 'texttriggers' ) . then ( trigInfos => ctx . textTriggers . addTriggers ( trigInfos ) ) ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'weeaboo' , ev => {
if ( ev . detail . value ) Weeaboo . init ( ) ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'osuKeysV2' , ev => {
2024-01-22 21:45:39 +00:00
// migrate old value
2024-03-01 19:07:17 +00:00
if ( ev . detail . initial ) {
2024-01-22 21:45:39 +00:00
if ( settings . has ( 'osuKeys' ) ) {
settings . set ( 'osuKeysV2' , settings . get ( 'osuKeys' ) ? 'yes' : 'no' ) ;
settings . delete ( 'osuKeys' ) ;
return ;
}
}
2024-06-26 00:53:12 +00:00
OsuKeys . enable = ev . detail . value !== 'no' ;
OsuKeys . randomRate = ev . detail . value === 'rng' ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-01-18 19:50:37 +00:00
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Building menus...' ;
2024-01-18 19:50:37 +00:00
2024-02-29 23:57:36 +00:00
MamiCompat ( 'Umi.Parser.SockChatBBcode.EmbedStub' , { value : ( ) => { } } ) ; // intentionally a no-op
2024-07-01 21:38:07 +00:00
MamiCompat ( 'Umi.UI.View.SetText' , { value : text => { chatForm . input . setText ( text ) ; } } ) ;
2024-02-29 23:57:36 +00:00
2024-06-24 21:36:07 +00:00
const sbUsers = new MamiSidebarPanelUsers ;
sidebar . createPanel ( sbUsers ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
sbUsers . addOption ( {
name : 'profile' ,
text : 'View profile' ,
onclick : entry => window . open ( futami . get ( 'profile' ) . replace ( '{user:id}' , entry . id ) , '_blank' ) ,
} ) ;
sbUsers . addOption ( {
name : 'action' ,
text : 'Describe action' ,
condition : entry => Umi . User . getCurrentUser ( ) ? . id === entry . id ,
2024-07-01 21:38:07 +00:00
onclick : entry => { chatForm . input . setText ( '/me ' ) ; } ,
2024-06-24 21:36:07 +00:00
} ) ;
sbUsers . addOption ( {
name : 'nick' ,
text : 'Set nickname' ,
condition : entry => Umi . User . getCurrentUser ( ) ? . id === entry . id && Umi . User . getCurrentUser ( ) . perms . canSetNick ,
2024-07-01 21:38:07 +00:00
onclick : entry => { chatForm . input . setText ( '/nick ' ) ; } ,
2024-06-24 21:36:07 +00:00
} ) ;
sbUsers . addOption ( {
name : 'bans' ,
text : 'View bans' ,
condition : entry => Umi . User . getCurrentUser ( ) ? . id === entry . id && Umi . User . getCurrentUser ( ) . perms . canKick ,
onclick : entry => { Umi . Server . sendMessage ( '/bans' ) ; } ,
} ) ;
sbUsers . addOption ( {
name : 'dm' ,
text : 'Send direct message' ,
condition : entry => Umi . User . getCurrentUser ( ) ? . id !== entry . id ,
2024-07-01 21:38:07 +00:00
onclick : entry => { chatForm . input . setText ( ` /msg ${ entry . name } ` ) ; } ,
2024-06-24 21:36:07 +00:00
} ) ;
sbUsers . addOption ( {
name : 'kick' ,
text : 'Kick from chat' ,
condition : entry => Umi . User . hasCurrentUser ( ) && Umi . User . getCurrentUser ( ) . id !== entry . id && Umi . User . getCurrentUser ( ) . perms . canKick ,
2024-07-01 21:38:07 +00:00
onclick : entry => { chatForm . input . setText ( ` /kick ${ entry . name } ` ) ; } ,
2024-06-24 21:36:07 +00:00
} ) ;
sbUsers . addOption ( {
name : 'ipaddr' ,
text : 'View IP address' ,
condition : entry => Umi . User . hasCurrentUser ( ) && Umi . User . getCurrentUser ( ) . id !== entry . id && Umi . User . getCurrentUser ( ) . perms . canKick ,
onclick : entry => { Umi . Server . sendMessage ( ` /ip ${ entry . name } ` ) ; } ,
} ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
const sbChannels = new MamiSidebarPanelChannels ;
sidebar . createPanel ( sbChannels ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
const sbSettings = new MamiSidebarPanelSettings ( settings ) ;
sidebar . createPanel ( sbSettings ) ;
sbSettings . category ( category => {
category . header ( 'Interface' ) ;
2024-06-26 14:06:01 +00:00
category . setting ( 'style' ) . title ( 'Theme' ) . type ( 'select' ) . options ( ( ) => {
const themes = { } ;
for ( const theme of UmiThemes )
themes [ theme . id ] = theme . name ;
return themes ;
} ) . done ( ) ;
2024-06-24 21:36:07 +00:00
category . setting ( 'compactView' ) . title ( 'Use compact view' ) . done ( ) ;
category . setting ( 'autoScroll' ) . title ( 'Enable auto scroll' ) . done ( ) ;
category . setting ( 'closeTabConfirm' ) . title ( 'Confirm tab close' ) . done ( ) ;
category . setting ( 'autoCloseUserContext' ) . title ( 'Auto-close user menus' ) . done ( ) ;
2024-06-27 16:01:28 +00:00
category . setting ( 'doNotMarkLinksAsVisited' ) . title ( "Don't mark links as visited" ) . done ( ) ;
2024-06-24 21:36:07 +00:00
} ) ;
sbSettings . category ( category => {
category . header ( 'Text' ) ;
category . setting ( 'preventOverflow' ) . title ( 'Prevent overflow' ) . done ( ) ;
category . setting ( 'expandTextBox' ) . title ( 'Grow input box while typing' ) . done ( ) ;
category . setting ( 'eepromAutoInsert' ) . title ( 'Auto-insert uploads' ) . done ( ) ;
category . setting ( 'autoEmbedV1' ) . title ( 'Auto-embed media' ) . done ( ) ;
category . setting ( 'autoEmbedPlay' ) . title ( 'Auto-play embedded media' ) . done ( ) ;
2024-06-26 23:04:12 +00:00
category . setting ( 'keepEmotePickerOpen' ) . title ( 'Keep emoticon picker open' ) . done ( ) ;
2024-06-24 22:10:00 +00:00
category . setting ( 'newLineOnEnter' ) . title ( 'Swap Enter and Shift+Enter behaviour' ) . done ( ) ;
2024-07-01 21:38:07 +00:00
category . setting ( 'showMarkupSelector' ) . title ( 'Show markup buttons' ) . type ( 'checkbox' ) . on ( 'always' ) . off ( 'never' ) . done ( ) ;
2024-06-24 21:36:07 +00:00
} ) ;
sbSettings . category ( category => {
category . header ( 'Notifications' ) ;
category . setting ( 'flashTitle' ) . title ( 'Strobe title on new message' ) . done ( ) ;
category . setting ( 'enableNotifications' ) . title ( 'Show notifications' ) . done ( ) ;
category . setting ( 'notificationShowMessage' ) . title ( 'Show contents of message' ) . done ( ) ;
category . setting ( 'notificationTriggers' ) . title ( 'Triggers' ) . done ( ) ;
} ) ;
sbSettings . category ( category => {
category . header ( 'Sound' ) ;
category . setting ( 'soundEnable' ) . title ( 'Enable sound' ) . done ( ) ;
category . setting ( 'soundPack' ) . title ( 'Sound pack' ) . type ( 'select' ) . options ( ( ) => {
const options = { '' : 'Default' } ;
2024-06-26 00:53:12 +00:00
for ( const name of soundCtx . packs . names )
options [ name ] = soundCtx . packs . info ( name ) . title ;
2024-06-24 21:36:07 +00:00
return options ;
} ) . done ( ) ;
category . setting ( 'soundVolume' ) . title ( 'Sound volume' ) . type ( 'range' ) . done ( ) ;
category . setting ( 'soundEnableJoin' ) . title ( 'Play join sound' ) . done ( ) ;
category . setting ( 'soundEnableLeave' ) . title ( 'Play leave sound' ) . done ( ) ;
category . setting ( 'soundEnableError' ) . title ( 'Play error sound' ) . done ( ) ;
category . setting ( 'soundEnableServer' ) . title ( 'Play server message sound' ) . done ( ) ;
category . setting ( 'soundEnableIncoming' ) . title ( 'Play receive message sound' ) . done ( ) ;
category . setting ( 'onlySoundOnMention' ) . title ( 'Only plays sounds when you are mentioned' ) . done ( ) ;
category . setting ( 'soundEnableOutgoing' ) . title ( 'Play send message sound' ) . done ( ) ;
category . setting ( 'soundEnablePrivate' ) . title ( 'Play private message sound' ) . done ( ) ;
category . setting ( 'soundEnableForceLeave' ) . title ( 'Play kick sound' ) . done ( ) ;
category . setting ( 'minecraft' ) . title ( 'Minecraft' ) . type ( 'select' ) . options ( ( ) => {
return {
'no' : 'No Minecraft' ,
'yes' : 'Yes Minecraft' ,
'old' : 'Old Minecraft' ,
} ;
} ) . done ( ) ;
category . setting ( 'windowsLiveMessenger' ) . title ( 'Windows Live Messenger' ) . done ( ) ;
category . setting ( 'seinfeld' ) . title ( 'Seinfeld' ) . done ( ) ;
} ) ;
sbSettings . category ( category => {
category . header ( 'Misc' ) ;
category . setting ( 'onlyConnectWhenVisible2' ) . title ( 'Only connect when the tab is in the foreground' ) . confirm ( [
'Please only disable this setting if you are using a desktop or laptop computer, this should always remain on on a phone, tablet or other device of that sort.' ,
'Are you sure you want to change this setting? Ignoring this warning may carry consequences.' ,
] ) . done ( ) ;
category . setting ( 'playJokeSounds' ) . title ( 'Run joke triggers' ) . done ( ) ;
category . setting ( 'weeaboo' ) . title ( 'Weeaboo' ) . done ( ) ;
category . setting ( 'motivationalImages' ) . title ( 'Make images motivational' ) . done ( ) ;
category . setting ( 'motivationalVideos' ) . title ( 'Make videos motivational' ) . done ( ) ;
category . setting ( 'osuKeysV2' ) . title ( 'osu! keyboard sounds' ) . type ( 'select' ) . options ( ( ) => {
return {
'no' : 'Off' ,
'yes' : 'On' ,
'rng' : 'On, random pitch' ,
} ;
} ) . done ( ) ;
category . setting ( 'explosionRadius' ) . title ( 'Messages to keep on clear' ) . done ( ) ;
} ) ;
sbSettings . category ( category => {
category . header ( 'Actions' ) ;
category . button ( 'Open compatibility client' , ( ) => { window . open ( window . AMI _URL , '_blank' , 'noopener' ) ; } ) ;
category . button ( 'Manual reconnect' , async button => {
const textOrig = button . textContent ;
let lock = 10 ;
button . disabled = true ;
button . textContent = 'Reconnecting...' ;
try {
await ctx . conMan . start ( ) ;
while ( -- lock > 0 ) {
button . textContent = textOrig + ` ( ${ lock } s) ` ;
await MamiSleep ( 1000 ) ;
}
} finally {
button . textContent = textOrig ;
button . disabled = false ;
}
} ) ;
category . button ( 'Reload emoticons' , async button => {
const textOrig = button . textContent ;
button . disabled = true ;
button . textContent = 'Reloading emoticons...' ;
try {
2024-11-14 03:20:36 +00:00
const emotes = await futami . getApiJson ( '/v1/emotes' , true ) ;
2024-06-24 21:36:07 +00:00
MamiEmotes . clear ( ) ;
2024-11-14 03:20:36 +00:00
MamiEmotes . load ( emotes ) ;
2024-06-24 21:36:07 +00:00
} finally {
button . textContent = textOrig ;
button . disabled = false ;
}
} ) ;
category . button ( 'Reload sound library' , async button => {
const textOrig = button . textContent ;
button . disabled = true ;
button . textContent = 'Reloading sound library...' ;
try {
const sounds = await futami . getJson ( 'sounds2' ) ;
if ( Array . isArray ( sounds . library ) )
soundCtx . library . register ( sounds . library , true ) ;
if ( Array . isArray ( sounds . packs ) ) {
soundCtx . packs . register ( sounds . packs , true ) ;
settings . touch ( 'soundPack' , true ) ;
}
} finally {
button . textContent = textOrig ;
button . disabled = false ;
}
} ) ;
category . button ( 'Reload joke triggers' , async button => {
const textOrig = button . textContent ;
button . disabled = true ;
button . textContent = 'Reloading joke triggers...' ;
try {
const triggers = await futami . getJson ( 'texttriggers' , true ) ;
ctx . textTriggers . clearTriggers ( ) ;
ctx . textTriggers . addTriggers ( triggers )
} finally {
button . textContent = textOrig ;
button . disabled = false ;
}
} ) ;
} ) ;
sbSettings . category ( category => {
category . header ( 'Settings' ) ;
category . button ( 'Import settings' , ( ) => {
2024-06-24 23:33:25 +00:00
( new MamiSettingsBackup ( settings ) ) . importUpload ( args . parent ) ;
2024-06-24 21:36:07 +00:00
} , [ 'Your current settings will be replaced with the ones in the export.' , 'Are you sure you want to continue?' ] ) ;
category . button ( 'Export settings' , ( ) => {
const user = Umi . User . getCurrentUser ( ) ;
let fileName ;
if ( user !== null )
fileName = ` ${ user . name } 's settings.mami ` ;
2024-06-24 23:33:25 +00:00
( new MamiSettingsBackup ( settings ) ) . exportDownload ( args . parent , fileName ) ;
2024-06-24 21:36:07 +00:00
} ) ;
category . button ( 'Reset settings' , ( ) => {
settings . clear ( ) ;
} , [ 'This will reset all your settings to their defaults values.' , 'Are you sure you want to do this?' ] ) ;
} ) ;
sbSettings . category ( category => {
category . header ( 'Debug' ) ;
category . collapse ( ) ;
category . warning ( "Only touch these settings if you're ABSOLUTELY sure you know what you're doing, you're on your own if you break something." ) ;
category . setting ( 'dumpPackets' ) . title ( 'Dump packets to console' ) . done ( ) ;
category . setting ( 'dumpEvents' ) . title ( 'Dump events to console' ) . done ( ) ;
2025-01-14 01:43:49 +00:00
category . setting ( 'dbgEEPROMv2' ) . title ( 'Use EEPROM v2 API (requires reload)' ) . done ( ) ;
category . setting ( 'marqueeAllNames' ) . title ( 'Apply marquee on everyone (requires reload)' ) . done ( ) ;
2024-06-24 21:36:07 +00:00
category . setting ( 'dbgAnimDurationMulti' ) . title ( 'Animation multiplier' ) . type ( 'range' ) . done ( ) ;
category . button ( 'Test kick/ban notice' , async button => {
button . disabled = true ;
await ctx . views . push ( new MamiForceDisconnectNotice ( { perma : true , type : 'ban' } ) ) ;
await MamiSleep ( 5000 ) ;
await ctx . views . pop ( ) ;
button . disabled = false ;
} ) ;
category . button ( 'You are an idiot!' , async button => {
button . disabled = true ;
await ctx . views . push ( new MamiYouAreAnIdiot ( soundCtx . library , ctx . views ) ) ;
button . disabled = false ;
} ) ;
category . button ( 'Sound test' , async ( button , ev ) => {
button . disabled = true ;
await ctx . views . push ( new MamiSoundTest (
settings ,
soundCtx . audio ,
soundCtx . manager ,
soundCtx . library ,
[ ev . clientX , ev . clientY ] ,
) ) ;
button . disabled = false ;
} ) ;
category . button ( 'Reset audio context' , ( ) => {
soundCtx . reset ( ) ;
} ) ;
2024-01-18 19:50:37 +00:00
} ) ;
2024-06-24 21:36:07 +00:00
const sbUploads = new MamiSidebarPanelUploads ;
sidebar . createPanel ( sbUploads ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
const sbActToggle = new MamiSidebarActionToggle ( sidebar ) ;
sidebar . createAction ( sbActToggle ) ;
if ( window . innerWidth < 800 )
sbActToggle . click ( ) ;
2024-01-18 19:50:37 +00:00
2024-06-24 21:36:07 +00:00
sidebar . createAction ( new MamiSidebarActionScroll ( settings ) ) ;
sidebar . createAction ( new MamiSidebarActionSound ( settings ) ) ;
sidebar . createAction ( new MamiSidebarActionCollapseAll ) ;
sidebar . createAction ( new MamiSidebarActionClearBacklog ( settings , soundCtx . library , ctx . msgbox ) ) ;
2024-01-18 19:50:37 +00:00
2024-02-29 21:43:00 +00:00
const pingIndicator = new MamiPingIndicator ;
2024-06-24 21:36:07 +00:00
const sbActPing = new MamiSidebarActionPing ( pingIndicator , ctx . msgbox ) ;
sidebar . createAction ( sbActPing ) ;
2024-02-29 21:43:00 +00:00
2024-06-26 23:04:12 +00:00
let emotePicker , emotePickerVisible = false ;
2024-07-01 21:38:07 +00:00
chatForm . input . createButton ( {
title : 'Emoticons' ,
icon : 'fas fa-smile' ,
onclick : ( ev , button ) => {
if ( emotePicker === undefined ) {
emotePicker = new MamiEmotePicker ( {
onClose : ( ) => { emotePickerVisible = false ; } ,
onPick : emote => {
chatForm . input . insertAtCursor ( ` : ${ emote . strings [ 0 ] } : ` ) ;
} ,
getEmotes : ( ) => MamiEmotes . all ( Umi . User . getCurrentUser ( ) . perms . rank ) ,
setKeepOpenOnPick : value => { settings . set ( 'keepEmotePickerOpen' , value ) ; } ,
} ) ;
2025-02-20 20:49:09 +00:00
layout . element . appendChild ( emotePicker . element ) ;
2024-07-01 21:38:07 +00:00
settings . watch ( 'keepEmotePickerOpen' , ev => { emotePicker . keepOpenOnPick = ev . detail . value ; } ) ;
}
2024-06-26 23:04:12 +00:00
2024-07-01 21:38:07 +00:00
if ( emotePickerVisible ) {
emotePicker . close ( ) ;
} else {
emotePickerVisible = true ;
emotePicker . dialog ( button ) ;
}
} ,
2024-06-26 23:04:12 +00:00
} ) ;
2024-01-18 19:50:37 +00:00
2024-07-01 21:38:07 +00:00
2025-01-14 01:43:49 +00:00
ctx . eeprom = settings . get ( 'dbgEEPROMv2' )
? new MamiEEPROMv2 ( futami . get ( 'api' ) , MamiMisuzuAuth . getLine , 'chat' )
: new MamiEEPROM ( futami . get ( 'eeprom2' ) , MamiMisuzuAuth . getLine ) ;
2024-06-24 21:36:07 +00:00
2025-01-04 01:35:56 +00:00
sbUploads . addOption ( {
name : 'view' ,
text : 'View upload' ,
condition : entry => entry . uploadInfo !== undefined ,
onclick : entry => window . open ( entry . uploadInfo . url ) ,
} ) ;
sbUploads . addOption ( {
name : 'insert' ,
text : 'Insert into message' ,
condition : entry => entry . uploadInfo !== undefined ,
onclick : entry => {
const upload = entry . uploadInfo ;
let text ;
2025-01-14 01:43:49 +00:00
let url = upload . url ;
2025-01-04 01:35:56 +00:00
if ( upload . isImage ( ) ) {
2025-01-14 01:43:49 +00:00
text = ` [img] ${ url } [/img] ` ;
2025-01-04 01:35:56 +00:00
} else if ( upload . isAudio ( ) ) {
2025-01-14 01:43:49 +00:00
text = ` [audio] ${ url } [/audio] ` ;
2025-01-04 01:35:56 +00:00
} else if ( upload . isVideo ( ) ) {
2025-01-14 01:43:49 +00:00
text = ` [video] ${ url } [/video] ` ;
} else {
if ( url . startsWith ( '//' ) )
url = location . protocol + url ;
text = url ;
}
2025-01-04 01:35:56 +00:00
chatForm . input . insertAtCursor ( text ) ;
} ,
} ) ;
sbUploads . addOption ( {
name : 'delete' ,
text : 'Delete upload' ,
condition : entry => entry . uploadInfo !== undefined ,
onclick : async entry => {
try {
await ctx . eeprom . delete ( entry . uploadInfo ) ;
sbUploads . deleteEntry ( entry ) ;
} catch ( ex ) {
console . error ( ex ) ;
await ctx . msgbox . show ( { body : [ 'An error occurred while trying to delete an uploaded file:' , ex ] } ) ;
}
} ,
} ) ;
2024-04-17 19:16:27 +00:00
2025-01-04 01:35:56 +00:00
const doUpload = async file => {
const entry = sbUploads . createEntry ( file ) ;
2024-04-17 19:16:27 +00:00
2025-01-04 01:35:56 +00:00
const task = ctx . eeprom . create ( file ) ;
entry . addOption ( {
name : 'cancel' ,
text : 'Cancel upload' ,
onclick : ( ) => { task . abort ( ) ; } ,
} ) ;
2024-04-17 19:16:27 +00:00
2025-01-04 01:35:56 +00:00
try {
2025-01-17 16:04:07 +00:00
const fileInfo = await task . start ( prog => { entry . progress = prog . progress ; } ) ;
2024-06-24 21:36:07 +00:00
2025-01-04 01:35:56 +00:00
entry . optionsVisible = false ;
entry . uploadInfo = fileInfo ;
entry . removeOption ( 'cancel' ) ;
entry . nukeProgress ( ) ;
sbUploads . reloadOptionsFor ( entry ) ;
2024-04-17 19:16:27 +00:00
2025-01-04 01:35:56 +00:00
if ( settings . get ( 'eepromAutoInsert' ) )
entry . clickOption ( 'insert' ) ;
} catch ( ex ) {
2025-01-04 02:29:04 +00:00
if ( ex !== '' ) {
2025-01-04 01:35:56 +00:00
console . error ( ex ) ;
ctx . msgbox . show ( { body : [ 'An error occurred while trying to upload a file:' , ex ] } ) ;
}
2024-01-18 19:50:37 +00:00
2025-01-04 01:35:56 +00:00
sbUploads . deleteEntry ( entry ) ;
}
} ;
2024-04-17 19:16:27 +00:00
2025-02-20 20:49:09 +00:00
const uploadForm = $element ( 'input' , {
type : 'file' ,
multiple : true ,
style : { display : 'none' } ,
onchange : ev => {
for ( const file of ev . target . files )
doUpload ( file ) ;
2025-01-04 01:35:56 +00:00
} ,
} ) ;
args . parent . appendChild ( uploadForm ) ;
2024-01-18 19:50:37 +00:00
2025-01-04 01:35:56 +00:00
chatForm . input . createButton ( {
title : 'Upload' ,
icon : 'fas fa-file-upload' ,
onclick : ( ) => { uploadForm . click ( ) ; } ,
} ) ;
2024-04-17 19:16:27 +00:00
2025-01-04 01:35:56 +00:00
ctx . events . watch ( 'form:upload' , ev => {
console . info ( ev ) ;
for ( const file of ev . detail . files )
doUpload ( file ) ;
} ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
// figure out how to display a UI for this someday
2024-06-24 23:33:25 +00:00
//args.parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
//args.parent.addEventListener('dragleave', ev => { console.info('dragleave', ev); });
args . parent . addEventListener ( 'dragover' , ev => { ev . preventDefault ( ) ; } ) ;
args . parent . addEventListener ( 'drop' , ev => {
2024-04-17 19:16:27 +00:00
if ( ev . dataTransfer === undefined || ev . dataTransfer === null || ev . dataTransfer . files . length < 1 )
return ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
ev . preventDefault ( ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
for ( const file of ev . dataTransfer . files ) {
2024-04-19 22:39:21 +00:00
if ( file . name . slice ( - 5 ) === '.mami' ) {
ctx . msgbox . show ( {
body : [
'This file appears to be a settings export.' ,
'Do you want to import it? This will overwrite your existing settings!' ,
] ,
yes : true ,
no : true ,
} )
. then ( ( ) => {
( new MamiSettingsBackup ( settings ) ) . importFile ( file ) ;
} )
. catch ( ( ) => {
if ( doUpload !== undefined )
doUpload ( file ) ;
} ) ;
} else if ( doUpload !== undefined )
2024-04-17 19:16:27 +00:00
doUpload ( file ) ;
}
} ) ;
2024-01-18 19:50:37 +00:00
window . addEventListener ( 'beforeunload' , function ( ev ) {
2024-01-22 21:45:39 +00:00
if ( settings . get ( 'closeTabConfirm' ) ) {
2024-01-18 19:50:37 +00:00
ev . preventDefault ( ) ;
return ev . returnValue = 'Are you sure you want to close the tab?' ;
}
2024-03-02 01:31:26 +00:00
2024-04-17 15:42:50 +00:00
ctx . isUnloading = true ;
2024-01-18 19:50:37 +00:00
} ) ;
2024-06-26 00:53:12 +00:00
loadingOverlay . message = 'Connecting...' ;
2024-02-24 01:10:30 +00:00
2024-04-17 15:42:50 +00:00
const setLoadingOverlay = async ( icon , header , message , optional ) => {
2024-06-26 00:53:12 +00:00
const currentView = ctx . views . current ;
2024-02-24 01:10:30 +00:00
2024-06-26 00:53:12 +00:00
if ( 'icon' in currentView ) {
currentView . icon = icon ;
currentView . header = header ;
currentView . message = message ;
2024-02-24 01:10:30 +00:00
return currentView ;
}
2024-04-17 15:42:50 +00:00
if ( ! optional ) {
const loading = new Umi . UI . LoadingOverlay ( icon , header , message ) ;
2024-04-19 22:39:21 +00:00
await ctx . views . push ( loading ) ;
2024-04-17 15:42:50 +00:00
}
2024-02-24 20:26:40 +00:00
} ;
2024-02-24 01:10:30 +00:00
2024-08-28 21:36:25 +00:00
const sockChat = new MamiSockChat ( ctx . events . scopeTo ( 'sockchat' ) ) ;
2024-04-17 15:42:50 +00:00
const conMan = new MamiConnectionManager ( sockChat , settings , futami . get ( 'servers' ) , ctx . events . scopeTo ( 'conn' ) ) ;
ctx . conMan = conMan ;
2024-02-29 23:57:36 +00:00
2024-07-01 21:38:07 +00:00
ctx . events . watch ( 'form:send' , ev => {
sockChat . client . sendMessage ( ev . detail . text ) ;
} ) ;
2024-06-24 21:36:07 +00:00
sbChannels . onClickEntry = async info => {
await sockChat . client . switchChannel ( info ) ;
// hack for DM channels
if ( info . isUserChannel ) {
sbChannels . setActiveEntry ( info . name ) ;
Umi . UI . Messages . SwitchChannel ( info ) ;
}
} ;
2024-04-17 15:42:50 +00:00
let sockChatRestarting ;
2024-02-24 01:10:30 +00:00
2024-04-17 15:42:50 +00:00
const sockChatReconnect = ( ) => {
if ( conMan . isActive )
2024-03-02 01:31:26 +00:00
return ;
2024-02-24 01:10:30 +00:00
2024-06-24 21:36:07 +00:00
sbActPing . pingMs = - 1 ;
2024-06-26 00:53:12 +00:00
pingIndicator . strength = - 1 ;
2024-02-29 23:19:36 +00:00
2024-04-17 15:42:50 +00:00
const reconManAttempt = ev => {
if ( sockChatRestarting || ev . detail . delay > 2000 )
setLoadingOverlay ( 'spinner' , 'Connecting...' , 'Connecting to server...' ) ;
2024-03-02 01:31:26 +00:00
} ;
2024-04-17 15:42:50 +00:00
const reconManFail = ev => {
// this is absolutely disgusting but i really don't care right now sorry
if ( sockChatRestarting || ev . detail . delay > 2000 )
setLoadingOverlay ( 'unlink' , sockChatRestarting ? 'Restarting...' : 'Disconnected' , ` Attempting to reconnect in ${ ( ev . detail . delay / 1000 ) . toLocaleString ( ) } seconds...<br><a href="javascript:void(0)" onclick="mami.conMan.force()">Retry now</a> ` ) ;
} ;
const reconManSuccess = ( ) => {
conMan . unwatch ( 'success' , reconManSuccess ) ;
conMan . unwatch ( 'attempt' , reconManAttempt ) ;
conMan . unwatch ( 'fail' , reconManFail ) ;
2024-03-02 01:31:26 +00:00
} ;
2024-04-17 15:42:50 +00:00
conMan . watch ( 'attempt' , reconManAttempt ) ;
conMan . watch ( 'fail' , reconManFail ) ;
conMan . watch ( 'success' , reconManSuccess ) ;
2024-02-24 01:10:30 +00:00
2024-04-17 15:42:50 +00:00
conMan . start ( ) ;
} ;
2024-02-24 01:10:30 +00:00
2024-06-24 21:36:07 +00:00
const sockChatHandlers = new MamiSockChatHandlers (
ctx , sockChat , setLoadingOverlay , sockChatReconnect , pingIndicator ,
sbActPing , sbChannels , sbUsers
) ;
2024-04-17 15:42:50 +00:00
settings . watch ( 'dumpEvents' , ev => sockChatHandlers . setDumpEvents ( ev . detail . value ) ) ;
settings . watch ( 'dumpPackets' , ev => sockChat . setDumpPackets ( ev . detail . value ) ) ;
sockChatHandlers . register ( ) ;
2024-02-24 01:10:30 +00:00
2024-03-02 01:31:26 +00:00
const conManAttempt = ev => {
let message = ev . detail . attempt > 2 ? ` Attempt ${ ev . detail . attempt } ... ` : 'Connecting to server...' ;
2024-04-17 15:42:50 +00:00
setLoadingOverlay ( 'spinner' , 'Connecting...' , message ) ;
2024-03-02 01:31:26 +00:00
} ;
const conManFail = ev => {
2024-04-17 15:42:50 +00:00
setLoadingOverlay ( 'cross' , 'Failed to connect' , ` Retrying in ${ ev . detail . delay / 1000 } seconds... ` ) ;
} ;
const conManSuccess = ( ) => {
conMan . unwatch ( 'success' , conManSuccess ) ;
conMan . unwatch ( 'attempt' , conManAttempt ) ;
conMan . unwatch ( 'fail' , conManFail ) ;
2024-03-02 01:31:26 +00:00
} ;
2024-04-17 15:42:50 +00:00
conMan . watch ( 'success' , conManSuccess ) ;
2024-03-02 01:31:26 +00:00
conMan . watch ( 'attempt' , conManAttempt ) ;
conMan . watch ( 'fail' , conManFail ) ;
2024-08-28 21:36:25 +00:00
await sockChat . create ( ) ;
conMan . client = sockChat ;
await conMan . start ( ) ;
2024-06-24 23:33:25 +00:00
return ctx ;
} ;
( ( ) => {
const eventTarget = new MamiEventTargetWindow ;
Object . defineProperty ( window , 'mamiEventTarget' , { enumerable : true , value : eventTarget } ) ;
MamiInit ( {
eventTarget : eventTarget ,
} ) . then ( mami => {
//Object.defineProperty(window, 'mami', { enumerable: true, value: mami });
} ) ;
2024-01-18 19:50:37 +00:00
} ) ( ) ;
2024-06-24 23:33:25 +00:00
const MamiDbgCreateFloatingInstance = async ( ) => {
if ( ! FUTAMI _DEBUG )
return ;
2025-02-20 20:49:09 +00:00
const prefix = $rngs ( 8 ) ;
const parent = $element ( 'div' , {
style : {
position : 'absolute' ,
bottom : '100px' ,
right : '100px' ,
zIndex : '9001' ,
width : '640px' ,
height : '480px' ,
background : '#0f0' ,
2024-06-24 23:33:25 +00:00
} ,
} ) ;
document . body . appendChild ( parent ) ;
return await MamiInit ( {
parent : parent ,
settingsPrefix : ` dbg: ${ prefix } : ` ,
eventTarget : mamiEventTarget . scopeTo ( prefix ) ,
} ) ;
} ;