2024-04-18 19:37:59 +00:00
window . Umi = { UI : { } } ;
2024-01-18 19:50:37 +00:00
# include animate . js
# 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-01-18 19:50:37 +00:00
# include messages . js
# include mszauth . js
2024-02-10 17:23:47 +00:00
# include txtrigs . js
2024-01-18 19:50:37 +00:00
# include utility . js
2024-01-22 21:45:39 +00:00
# include weeb . js
2024-04-17 15:42:50 +00:00
# include worker . js
2024-02-09 22:38:57 +00:00
# include audio / autoplay . js
2024-04-19 22:39:21 +00:00
# include controls / msgbox . 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
# include settings / backup . js
# include settings / settings . js
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-01-18 19:50:37 +00:00
# include ui / chat - layout . js
# include ui / hooks . js
# include ui / input - menus . js
# include ui / loading - overlay . jsx
# include ui / markup . js
# include ui / menus . js
2024-02-29 21:43:00 +00:00
# include ui / ping . jsx
2024-01-22 21:45:39 +00:00
# include ui / settings . jsx
2024-01-18 19:50:37 +00:00
# include ui / toggles . js
# include ui / uploads . js
2024-04-17 15:42:50 +00:00
# include ui / view . js
2024-01-18 19:50:37 +00:00
( async ( ) => {
2024-04-17 15:42:50 +00:00
const eventTarget = new MamiEventTargetWindow ;
const ctx = new MamiContext ( eventTarget ) ;
Object . defineProperty ( window , 'mami' , { enumerable : true , value : ctx } ) ;
2024-04-19 22:39:21 +00:00
ctx . views = new MamiViewsControl ( { body : document . body } ) ;
ctx . msgbox = new MamiMessageBoxControl ( { views : ctx . views } ) ;
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-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Loading environment...' ) ;
2024-01-18 19:50:37 +00:00
try {
window . futami = await FutamiCommon . load ( ) ;
} catch ( ex ) {
2024-04-17 19:16:27 +00:00
console . error ( 'Failed to load common settings.' , ex ) ;
2024-02-10 17:23:47 +00:00
loadingOverlay . setIcon ( 'cross' ) ;
loadingOverlay . setHeader ( 'Failed!' ) ;
2024-04-17 19:16:27 +00:00
loadingOverlay . setMessage ( 'Failed to load common settings.' ) ;
2024-02-10 04:24:25 +00:00
return ;
2024-01-18 19:50:37 +00:00
}
2024-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Fetching credentials...' ) ;
2024-01-18 19:50:37 +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 ;
}
setInterval ( ( ) => {
MamiMisuzuAuth . update ( )
. then ( auth => {
if ( ! auth . ok )
location . assign ( futami . get ( 'login' ) ) ;
} )
} , 600000 ) ;
2024-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Loading settings...' ) ;
2024-01-22 21:45:39 +00:00
2024-04-17 15:42:50 +00:00
const settings = new MamiSettings ( 'umi-' , ctx . events . scopeTo ( 'settings' ) ) ;
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 ( 'showChannelList' ) . default ( false ) . create ( ) ;
settings . define ( 'fancyInfo' ) . default ( true ) . create ( ) ;
settings . define ( 'autoCloseUserContext' ) . default ( true ) . create ( ) ;
settings . define ( 'enableParser' ) . default ( true ) . create ( ) ;
settings . define ( 'enableEmoticons' ) . default ( true ) . create ( ) ;
settings . define ( 'autoParseUrls' ) . 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 ( ) ;
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 ( ) ;
settings . define ( 'showServerMsgInTitle' ) . default ( true ) . create ( ) ;
settings . define ( 'onlyConnectWhenVisible' ) . default ( true ) . create ( ) ;
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 ( ) ;
settings . define ( 'tmpDisableOldThemeSys' ) . default ( false ) . critical ( ) . 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-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Loading sounds...' ) ;
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 === '' ) {
const names = packs . names ( ) ;
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-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Loading emoticons...' ) ;
2024-01-18 19:50:37 +00:00
try {
const emotes = await futami . getJson ( 'emotes' ) ;
MamiEmotes . loadLegacy ( emotes ) ;
} catch ( ex ) {
2024-04-17 19:16:27 +00:00
console . error ( 'Failed to load emoticons.' , ex ) ;
} finally {
// this is currently called in the sock chat handlers
// does a permissions check which it can't do at this point
//Umi.UI.Emoticons.Init();
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-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( 'Preparing UI...' ) ;
2024-04-17 15:42:50 +00:00
ctx . textTriggers = new MamiTextTriggers ;
2024-01-18 19:50:37 +00:00
2024-02-09 00:56:27 +00:00
// should be dynamic when possible
const layout = new Umi . UI . ChatLayout ;
2024-04-19 22:39:21 +00:00
await ctx . views . unshift ( layout ) ;
2024-02-09 00:56:27 +00:00
2024-01-18 19:50:37 +00:00
Umi . UI . View . AccentReload ( ) ;
2024-02-09 00:56:27 +00:00
Umi . UI . Hooks . AddHooks ( ) ;
2024-01-18 19:50:37 +00:00
2024-03-01 19:07:17 +00:00
settings . watch ( 'style' , ev => { if ( ! ev . detail . initial ) Umi . UI . View . AccentReload ( ) ; } ) ;
settings . watch ( 'compactView' , ev => { if ( ! ev . detail . initial ) Umi . UI . View . AccentReload ( ) ; } ) ;
settings . watch ( 'preventOverflow' , ev => document . body . classList . toggle ( 'prevent-overflow' , ev . detail . value ) ) ;
settings . watch ( 'tmpDisableOldThemeSys' , ev => { if ( ! ev . detail . initial ) Umi . UI . View . AccentReload ( ) ; } ) ;
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-04-17 15:42:50 +00:00
if ( ! ctx . textTriggers . hasTriggers ( ) )
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-03-01 19:07:17 +00:00
OsuKeys . setEnable ( ev . detail . value !== 'no' ) ;
OsuKeys . setRandomRate ( ev . detail . value === 'rng' ) ;
2024-01-22 21:45:39 +00:00
} ) ;
2024-01-18 19:50:37 +00:00
2024-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( '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
MamiCompat ( 'Umi.UI.View.SetText' , { value : text => console . log ( ` Umi.UI.View.SetText(text: ${ text } ) ` ) } ) ;
MamiCompat ( 'Umi.UI.Menus.Add' , { value : ( baseId , title , initiallyHidden ) => console . log ( ` Umi.UI.Menus.Add(baseId: ${ baseId } , title: ${ title } , initiallyHidden: ${ initiallyHidden } ) ` ) } ) ;
MamiCompat ( 'Umi.UI.Menus.Get' , { value : ( baseId , icon ) => console . log ( ` Umi.UI.Menus.Get(baseId: ${ baseId } , icon: ${ icon } ) ` ) } ) ;
2024-01-18 19:50:37 +00:00
Umi . UI . Menus . Add ( 'users' , 'Users' ) ;
2024-01-22 21:45:39 +00:00
Umi . UI . Menus . Add ( 'channels' , 'Channels' , ! settings . get ( 'showChannelList' ) ) ;
2024-01-18 19:50:37 +00:00
Umi . UI . Menus . Add ( 'settings' , 'Settings' ) ;
let sidebarAnimation = null ;
Umi . UI . Settings . Init ( ) ;
Umi . UI . Toggles . Add ( 'menu-toggle' , {
'click' : function ( ) {
2024-04-17 15:42:50 +00:00
const sidebar = $c ( 'sidebar' ) [ 0 ] ;
const toggle = Umi . UI . Toggles . Get ( 'menu-toggle' ) ;
2024-04-17 18:08:12 +00:00
const isClosed = toggle . classList . contains ( 'sidebar__selector-mode--menu-toggle-closed' ) ;
2024-01-18 19:50:37 +00:00
if ( sidebarAnimation !== null ) {
sidebarAnimation . cancel ( ) ;
sidebarAnimation = null ;
}
2024-04-17 18:08:12 +00:00
toggle . classList . toggle ( 'sidebar__selector-mode--menu-toggle-opened' , isClosed ) ;
toggle . classList . toggle ( 'sidebar__selector-mode--menu-toggle-closed' , ! isClosed ) ;
2024-01-18 19:50:37 +00:00
let update ;
if ( isClosed )
update = function ( t ) {
sidebar . style . width = ( 40 + ( 220 * t ) ) . toString ( ) + 'px' ;
} ;
else
update = function ( t ) {
sidebar . style . width = ( 260 - ( 220 * t ) ) . toString ( ) + 'px' ;
} ;
sidebarAnimation = MamiAnimate ( {
duration : 500 ,
2024-01-23 01:23:33 +00:00
easing : 'outExpo' ,
2024-01-18 19:50:37 +00:00
update : update ,
} ) ;
}
} , 'Toggle Sidebar' ) ;
Umi . UI . Toggles . Get ( 'menu-toggle' ) . classList . add ( 'sidebar__selector-mode--menu-toggle-opened' ) ;
Umi . UI . Toggles . Add ( 'scroll' , {
'click' : function ( ) {
2024-01-22 21:45:39 +00:00
settings . toggle ( 'autoScroll' ) ;
2024-01-18 19:50:37 +00:00
}
} , 'Autoscroll' ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'autoScroll' , ev => {
Umi . UI . Toggles . Get ( 'scroll' ) . classList . toggle ( 'sidebar__selector-mode--scroll-off' , ! ev . detail . value ) ;
2024-01-18 19:50:37 +00:00
} ) ;
if ( window . innerWidth < 768 )
Umi . UI . Toggles . Get ( 'menu-toggle' ) . click ( ) ;
Umi . UI . Toggles . Add ( 'audio' , {
'click' : function ( ) {
2024-01-22 21:45:39 +00:00
settings . toggle ( 'soundEnable' ) ;
2024-01-18 19:50:37 +00:00
}
} , 'Sounds' ) ;
2024-03-01 19:07:17 +00:00
settings . watch ( 'soundEnable' , ev => {
Umi . UI . Toggles . Get ( 'audio' ) . classList . toggle ( 'sidebar__selector-mode--audio-off' , ! ev . detail . value ) ;
2024-01-18 19:50:37 +00:00
} ) ;
Umi . UI . Toggles . Add ( 'unembed' , {
'click' : function ( ) {
const buttons = $qa ( '[data-embed="1"]' ) ;
for ( const button of buttons )
button . click ( ) ;
}
} , 'Unembed any embedded media' ) ;
Umi . UI . Toggles . Add ( 'clear' , {
'click' : function ( ) {
2024-04-19 22:39:21 +00:00
ctx . msgbox . show ( { body : 'ARE YOU SURE ABOUT THAT???' , yes : true , no : true } ) . then ( ( ) => {
2024-01-22 21:45:39 +00:00
const limit = settings . get ( 'explosionRadius' ) ;
2024-01-18 19:50:37 +00:00
const explode = $e ( {
tag : 'img' ,
attrs : {
src : '//static.flash.moe/images/explode.gif' ,
alt : '' ,
style : {
position : 'absolute' ,
zIndex : 9001 ,
bottom : 0 ,
right : 0 ,
pointerEvents : 'none' ,
} ,
onLoad : function ( ) {
setTimeout ( function ( ) {
$r ( explode ) ;
} , 1700 ) ;
2024-02-10 17:23:47 +00:00
soundCtx . library . play ( 'misc:explode' ) ;
2024-01-18 19:50:37 +00:00
} ,
} ,
} ) ;
document . body . appendChild ( explode ) ;
let backLog = Umi . Messages . All ( ) ;
backLog = backLog . slice ( Math . max ( backLog . length - limit , 0 ) ) ;
Umi . Messages . Clear ( ) ;
for ( const blMsg of backLog )
Umi . Messages . Add ( blMsg ) ;
2024-04-19 22:39:21 +00:00
} ) . catch ( ( ) => { } ) ;
2024-01-18 19:50:37 +00:00
}
} , 'Clear Logs' ) ;
2024-02-29 21:43:00 +00:00
const pingIndicator = new MamiPingIndicator ;
2024-02-29 21:55:29 +00:00
const pingToggle = Umi . UI . Toggles . Add ( 'ping' , {
2024-04-19 22:39:21 +00:00
click : ( ) => { ctx . msgbox . show ( { body : ` Your current ping is ${ pingToggle . title } ` } ) ; } ,
2024-02-29 21:58:01 +00:00
} , 'Ready~' ) ;
2024-02-29 21:43:00 +00:00
pingToggle . appendChild ( pingIndicator . getElement ( ) ) ;
2024-04-17 19:16:27 +00:00
Umi . UI . InputMenus . Add ( 'markup' , 'BB Code' ) ;
Umi . UI . InputMenus . Add ( 'emotes' , 'Emoticons' ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
let doUpload ;
2024-04-18 19:37:59 +00:00
ctx . eeprom = new MamiEEPROM ( futami . get ( 'eeprom2' ) , MamiMisuzuAuth . getLine ) ;
ctx . eeprom . init ( )
2024-04-17 19:16:27 +00:00
. catch ( ex => {
console . log ( 'Failed to initialise EEPROM.' , ex ) ;
} )
. then ( ( ) => {
Umi . UI . Menus . Add ( 'uploads' , 'Upload History' , ! FUTAMI _DEBUG ) ;
doUpload = async file => {
const uploadEntry = Umi . UI . Uploads . create ( file . name ) ;
const uploadTask = ctx . eeprom . create ( file ) ;
uploadTask . onProgress ( prog => uploadEntry . setProgress ( prog . progress ) ) ;
uploadEntry . addOption ( 'Cancel' , ( ) => uploadTask . abort ( ) ) ;
try {
const fileInfo = await uploadTask . start ( ) ;
uploadEntry . hideOptions ( ) ;
uploadEntry . clearOptions ( ) ;
uploadEntry . removeProgress ( ) ;
uploadEntry . addOption ( 'Open' , fileInfo . url ) ;
uploadEntry . addOption ( 'Insert' , ( ) => Umi . UI . Markup . InsertRaw ( insertText , '' ) ) ;
uploadEntry . addOption ( 'Delete' , ( ) => {
ctx . eeprom . delete ( fileInfo )
. then ( ( ) => uploadEntry . remove ( ) )
. catch ( ex => {
console . error ( ex ) ;
2024-04-19 22:39:21 +00:00
ctx . msgbox . show ( { body : [ 'An error occurred while trying to delete an uploaded file:' , ex ] } ) ;
2024-04-17 19:16:27 +00:00
} ) ;
} ) ;
let insertText ;
if ( fileInfo . isImage ( ) ) {
insertText = ` [img] ${ fileInfo . url } [/img] ` ;
uploadEntry . setThumbnail ( fileInfo . thumb ) ;
} else if ( fileInfo . isAudio ( ) ) {
insertText = ` [audio] ${ fileInfo . url } [/audio] ` ;
uploadEntry . setThumbnail ( fileInfo . thumb ) ;
} else if ( fileInfo . isVideo ( ) ) {
insertText = ` [video] ${ fileInfo . url } [/video] ` ;
uploadEntry . setThumbnail ( fileInfo . thumb ) ;
} else
insertText = location . protocol + fileInfo . url ;
if ( settings . get ( 'eepromAutoInsert' ) )
Umi . UI . Markup . InsertRaw ( insertText , '' ) ;
} catch ( ex ) {
if ( ! ex . aborted ) {
console . error ( ex ) ;
2024-04-19 22:39:21 +00:00
ctx . msgbox . show ( { body : [ 'An error occurred while trying to upload a file:' , ex ] } ) ;
2024-04-17 19:16:27 +00:00
}
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
uploadEntry . remove ( ) ;
2024-02-02 21:02:29 +00:00
}
2024-04-17 19:16:27 +00:00
} ;
const uploadForm = $e ( {
tag : 'input' ,
attrs : {
type : 'file' ,
multiple : true ,
style : { display : 'none' } ,
onchange : ev => {
for ( const file of ev . target . files )
doUpload ( file ) ;
} ,
} ,
} ) ;
document . body . appendChild ( uploadForm ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 19:16:27 +00:00
Umi . UI . InputMenus . AddButton ( 'upload' , 'Upload' , ( ) => uploadForm . click ( ) , 'markup' ) ;
$i ( 'umi-msg-text' ) . onpaste = ev => {
if ( ev . clipboardData && ev . clipboardData . files . length > 0 )
for ( const file of ev . clipboardData . files )
2024-02-02 21:02:29 +00:00
doUpload ( file ) ;
2024-04-17 19:16:27 +00:00
} ;
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
//document.body.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
//document.body.addEventListener('dragleave', ev => { console.info('dragleave', ev); });
document . body . addEventListener ( 'dragover' , ev => { ev . preventDefault ( ) ; } ) ;
document . body . addEventListener ( 'drop' , ev => {
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-02-10 17:23:47 +00:00
loadingOverlay . setMessage ( '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-04-19 22:39:21 +00:00
const currentView = ctx . views . current ( ) ;
2024-02-24 01:10:30 +00:00
if ( 'setIcon' in currentView ) {
currentView . setIcon ( icon ) ;
currentView . setHeader ( header ) ;
currentView . setMessage ( message ) ;
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-04-17 15:42:50 +00:00
const protoWorker = new MamiWorker ( MAMI _PROTO _JS , ctx . events . scopeTo ( 'worker' ) ) ;
ctx . protoWorker = protoWorker ;
2024-02-29 23:57:36 +00:00
2024-04-17 15:42:50 +00:00
const sockChat = new MamiSockChat ( protoWorker ) ;
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-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-04-17 15:42:50 +00:00
pingToggle . title = 'Reconnecting...' ;
2024-03-02 01:31:26 +00:00
pingIndicator . setStrength ( - 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-04-17 15:42:50 +00:00
const sockChatHandlers = new MamiSockChatHandlers ( ctx , sockChat , setLoadingOverlay , sockChatReconnect , pingIndicator , pingToggle ) ;
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-04-17 15:42:50 +00:00
let workerStarting = false ;
const initWorker = async ( ) => {
if ( workerStarting )
return ;
workerStarting = true ;
if ( FUTAMI _DEBUG )
console . info ( '[proto] initialising worker...' ) ;
try {
await protoWorker . connect ( ) ;
await sockChat . create ( ) ;
conMan . client = sockChat ;
await conMan . start ( ) ;
} finally {
workerStarting = false ;
}
} ;
2024-03-02 01:31:26 +00:00
2024-04-17 15:42:50 +00:00
protoWorker . watch ( ':timeout' , ev => {
console . warn ( 'worker timeout' , ev . detail ) ;
initWorker ( ) ;
} ) ;
2024-03-02 01:31:26 +00:00
2024-04-17 15:42:50 +00:00
window . addEventListener ( 'visibilitychange' , ( ) => {
if ( document . visibilityState === 'visible' ) {
protoWorker . ping ( ) . catch ( ex => {
console . warn ( 'worker died' , ex ) ;
initWorker ( ) ;
} ) ;
}
} ) ;
2024-01-18 19:50:37 +00:00
2024-04-17 15:42:50 +00:00
await initWorker ( ) ;
2024-01-18 19:50:37 +00:00
} ) ( ) ;