Use EventTarget instead of the custom watchers bag.

This commit is contained in:
flash 2024-03-01 19:07:17 +00:00
parent 53258703e1
commit bc859a042d
6 changed files with 213 additions and 269 deletions

View file

@ -0,0 +1,22 @@
const MamiEventTarget = function(prefix) {
prefix = typeof prefix === 'string' ? `${prefix}:` : '';
const eventTarget = new EventTarget;
const createEvent = (name, detail) => new CustomEvent(prefix + name, (typeof detail === 'object' && detail !== null && 'detail' in detail ? detail : { detail: detail }));
return {
create: createEvent,
addEventListener: eventTarget.addEventListener.bind(eventTarget),
removeEventListener: eventTarget.removeEventListener.bind(eventTarget),
dispatchEvent: eventTarget.dispatchEvent.bind(eventTarget),
watch: (name, ...args) => {
eventTarget.addEventListener(prefix + name, ...args);
},
unwatch: (name, ...args) => {
eventTarget.removeEventListener(prefix + name, ...args);
},
dispatch: (...args) => eventTarget.dispatchEvent(createEvent(...args)),
};
};

View file

@ -150,8 +150,9 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
settings.virtualise('soundEnable'); settings.virtualise('soundEnable');
} }
settings.watch('soundEnable', (v, n, i) => { settings.watch('soundEnable', ev => {
if(v) { console.log(ev);
if(ev.detail.value) {
if(!soundCtx.ready) if(!soundCtx.ready)
soundCtx.reset(); soundCtx.reset();
@ -161,22 +162,22 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
soundCtx.library.play(soundCtx.pack.getEventSound('server')); soundCtx.library.play(soundCtx.pack.getEventSound('server'));
} }
soundCtx.muted = !v; soundCtx.muted = !ev.detail.value;
}); });
settings.watch('soundPack', (v, n, i) => { settings.watch('soundPack', ev => {
const packs = soundCtx.packs; const packs = soundCtx.packs;
if(!packs.has(v)) { if(!packs.has(ev.detail.value)) {
settings.delete(n); settings.delete(ev.detail.name);
return; return;
} }
soundCtx.pack = packs.get(v); soundCtx.pack = packs.get(ev.detail.value);
if(!i) soundCtx.library.play(soundCtx.pack.getEventSound('server')); if(!ev.detail.initial) soundCtx.library.play(soundCtx.pack.getEventSound('server'));
}); });
settings.watch('soundVolume', v => { settings.watch('soundVolume', ev => {
soundCtx.volume = v / 100; soundCtx.volume = ev.detail.value / 100;
}) })
@ -238,28 +239,28 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
Umi.UI.View.AccentReload(); Umi.UI.View.AccentReload();
Umi.UI.Hooks.AddHooks(); Umi.UI.Hooks.AddHooks();
settings.watch('style', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); }); settings.watch('style', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('compactView', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); }); settings.watch('compactView', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('preventOverflow', v => document.body.classList.toggle('prevent-overflow', v)); settings.watch('preventOverflow', ev => document.body.classList.toggle('prevent-overflow', ev.detail.value));
settings.watch('tmpDisableOldThemeSys', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); }); settings.watch('tmpDisableOldThemeSys', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('minecraft', (v, n, i) => { settings.watch('minecraft', ev => {
if(i && v === 'no') if(ev.detail.initial && ev.detail.value === 'no')
return; return;
soundCtx.library.play((() => { soundCtx.library.play((() => {
if(i) if(i)
return 'minecraft:nether:enter'; return 'minecraft:nether:enter';
if(v === 'yes') if(ev.detail.value === 'yes')
return 'minecraft:door:open'; return 'minecraft:door:open';
if(v === 'old') if(ev.detail.value === 'old')
return 'minecraft:door:open-old'; return 'minecraft:door:open-old';
return soundCtx.pack.getEventSound('join'); return soundCtx.pack.getEventSound('join');
})()); })());
}); });
settings.watch('enableNotifications', v => { settings.watch('enableNotifications', ev => {
if(!v || !('Notification' in window) if(!ev.detail.value || !('Notification' in window)
|| (Notification.permission === 'granted' && Notification.permission !== 'denied')) || (Notification.permission === 'granted' && Notification.permission !== 'denied'))
return; return;
@ -270,20 +271,20 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
}); });
}); });
settings.watch('playJokeSounds', v => { settings.watch('playJokeSounds', ev => {
if(!v) return; if(!ev.detail.value) return;
if(!textTriggers.hasTriggers()) if(!textTriggers.hasTriggers())
futami.getJson('texttriggers').then(trigInfos => textTriggers.addTriggers(trigInfos)); futami.getJson('texttriggers').then(trigInfos => textTriggers.addTriggers(trigInfos));
}); });
settings.watch('weeaboo', v => { settings.watch('weeaboo', ev => {
if(v) Weeaboo.init(); if(ev.detail.value) Weeaboo.init();
}); });
settings.watch('osuKeysV2', (v, n, i) => { settings.watch('osuKeysV2', ev => {
// migrate old value // migrate old value
if(i) { if(ev.detail.initial) {
if(settings.has('osuKeys')) { if(settings.has('osuKeys')) {
settings.set('osuKeysV2', settings.get('osuKeys') ? 'yes' : 'no'); settings.set('osuKeysV2', settings.get('osuKeys') ? 'yes' : 'no');
settings.delete('osuKeys'); settings.delete('osuKeys');
@ -291,8 +292,8 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
} }
} }
OsuKeys.setEnable(v !== 'no'); OsuKeys.setEnable(ev.detail.value !== 'no');
OsuKeys.setRandomRate(v === 'rng'); OsuKeys.setRandomRate(ev.detail.value === 'rng');
}); });
@ -356,8 +357,8 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
settings.toggle('autoScroll'); settings.toggle('autoScroll');
} }
}, 'Autoscroll'); }, 'Autoscroll');
settings.watch('autoScroll', function(value) { settings.watch('autoScroll', ev => {
Umi.UI.Toggles.Get('scroll').classList[value ? 'remove' : 'add']('sidebar__selector-mode--scroll-off'); Umi.UI.Toggles.Get('scroll').classList.toggle('sidebar__selector-mode--scroll-off', !ev.detail.value);
}); });
if(window.innerWidth < 768) if(window.innerWidth < 768)
@ -368,8 +369,8 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
settings.toggle('soundEnable'); settings.toggle('soundEnable');
} }
}, 'Sounds'); }, 'Sounds');
settings.watch('soundEnable', function(value) { settings.watch('soundEnable', ev => {
Umi.UI.Toggles.Get('audio').classList[value ? 'remove' : 'add']('sidebar__selector-mode--audio-off'); Umi.UI.Toggles.Get('audio').classList.toggle('sidebar__selector-mode--audio-off', !ev.detail.value);
}); });
Umi.UI.Toggles.Add('unembed', { Umi.UI.Toggles.Add('unembed', {
@ -587,8 +588,8 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
MamiCompat('Umi.Server', { get: () => sockChat, configurable: true }); MamiCompat('Umi.Server', { get: () => sockChat, configurable: true });
let dumpEvents = false; let dumpEvents = false;
settings.watch('dumpEvents', value => dumpEvents = value); settings.watch('dumpEvents', ev => dumpEvents = ev.detail.value);
settings.watch('dumpPackets', value => sockChat.setDumpPackets(value)); settings.watch('dumpPackets', ev => sockChat.setDumpPackets(ev.detail.value));
Umi.UI.Hooks.SetCallbacks(sockChat.sendMessage, sockChat.switchChannel); Umi.UI.Hooks.SetCallbacks(sockChat.sendMessage, sockChat.switchChannel);
@ -596,60 +597,60 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
MamiCompat('Umi.Protocol.SockChat.Protocol.Instance.SendMessage', { value: text => sockChat.sendMessage(text), configurable: true }); MamiCompat('Umi.Protocol.SockChat.Protocol.Instance.SendMessage', { value: text => sockChat.sendMessage(text), configurable: true });
MamiCompat('Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage', { value: text => sockChat.sendMessage(text), configurable: true }); MamiCompat('Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage', { value: text => sockChat.sendMessage(text), configurable: true });
sockChat.watch('conn:init', init => { sockChat.watch('conn:init', ev => {
if(dumpEvents) console.log('conn:init', init); if(dumpEvents) console.log('conn:init', ev);
let message = 'Connecting to server...'; let message = 'Connecting to server...';
if(init.attempt > 2) if(ev.detail.attempt > 2)
message += ` (Attempt ${connectAttempts})`; message += ` (Attempt ${connectAttempts})`;
getLoadingOverlay('spinner', 'Loading...', message); getLoadingOverlay('spinner', 'Loading...', message);
}); });
sockChat.watch('conn:ready', ready => { sockChat.watch('conn:ready', ev => {
if(dumpEvents) console.log('conn:ready', ready); if(dumpEvents) console.log('conn:ready', ev);
getLoadingOverlay('spinner', 'Loading...', 'Authenticating...'); getLoadingOverlay('spinner', 'Loading...', 'Authenticating...');
const authInfo = MamiMisuzuAuth.getInfo(); const authInfo = MamiMisuzuAuth.getInfo();
sockChat.sendAuth(authInfo.method, authInfo.token); sockChat.sendAuth(authInfo.method, authInfo.token);
}); });
sockChat.watch('conn:lost', lost => { sockChat.watch('conn:lost', ev => {
if(dumpEvents) console.log('conn:lost', lost); if(dumpEvents) console.log('conn:lost', ev);
getLoadingOverlay( getLoadingOverlay(
'unlink', 'Disconnected!', 'unlink', 'Disconnected!',
wsCloseReasons[`_${lost.code}`] ?? `Something caused an unexpected connection loss. (${lost.code})` wsCloseReasons[`_${ev.detail.code}`] ?? `Something caused an unexpected connection loss. (${ev.detail.code})`
); );
}); });
sockChat.watch('conn:error', error => { sockChat.watch('conn:error', ev => {
console.error('conn:error', error); console.error('conn:error', ev);
}); });
sockChat.watch('ping:send', send => { sockChat.watch('ping:send', ev => {
if(dumpEvents) console.log('ping:send', send); if(dumpEvents) console.log('ping:send', ev);
}); });
sockChat.watch('ping:long', long => { sockChat.watch('ping:long', ev => {
if(dumpEvents) console.log('ping:long', long); if(dumpEvents) console.log('ping:long', ev);
pingToggle.title = '+2000ms'; pingToggle.title = '+2000ms';
pingIndicator.setStrength(0); pingIndicator.setStrength(0);
}); });
sockChat.watch('ping:recv', recv => { sockChat.watch('ping:recv', ev => {
if(dumpEvents) console.log('ping:recv', recv); if(dumpEvents) console.log('ping:recv', ev);
let strength = 3; let strength = 3;
if(recv.diff >= 1000) --strength; if(ev.detail.diff >= 1000) --strength;
if(recv.diff >= 250) --strength; if(ev.detail.diff >= 250) --strength;
pingToggle.title = `${recv.diff.toLocaleString()}ms`; pingToggle.title = `${ev.detail.diff.toLocaleString()}ms`;
pingIndicator.setStrength(strength); pingIndicator.setStrength(strength);
}); });
sockChat.watch('session:start', start => { sockChat.watch('session:start', ev => {
if(dumpEvents) console.log('session:start', start); if(dumpEvents) console.log('session:start', ev);
const userInfo = new Umi.User(start.user.id, start.user.name, start.user.colour, start.user.permsRaw); const userInfo = new Umi.User(ev.detail.user.id, ev.detail.user.name, ev.detail.user.colour, ev.detail.user.permsRaw);
Umi.User.setCurrentUser(userInfo); Umi.User.setCurrentUser(userInfo);
Umi.Users.Add(userInfo); Umi.Users.Add(userInfo);
@ -675,84 +676,84 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
}, },
})); }));
}); });
sockChat.watch('session:fail', fail => { sockChat.watch('session:fail', ev => {
if(dumpEvents) console.log('session:fail', fail); if(dumpEvents) console.log('session:fail', ev);
if(fail.baka !== undefined) { if(ev.detail.baka !== undefined) {
new MamiForceDisconnectNotice(fail.baka).pushOn(views); new MamiForceDisconnectNotice(ev.detail.baka).pushOn(views);
return; return;
} }
getLoadingOverlay( getLoadingOverlay(
'cross', 'Failed!', 'cross', 'Failed!',
sessFailReasons[fail.session.reason] ?? `Unknown reason: ${fail.session.reason}` sessFailReasons[ev.detail.session.reason] ?? `Unknown reason: ${ev.detail.session.reason}`
); );
if(fail.session.needsAuth) if(ev.detail.session.needsAuth)
setTimeout(() => location.assign(futami.get('login')), 1000); setTimeout(() => location.assign(futami.get('login')), 1000);
}); });
sockChat.watch('session:term', term => { sockChat.watch('session:term', ev => {
if(dumpEvents) console.log('session:term', term); if(dumpEvents) console.log('session:term', ev);
new MamiForceDisconnectNotice(term.baka).pushOn(views); new MamiForceDisconnectNotice(ev.detail.baka).pushOn(views);
}); });
sockChat.watch('user:add', add => { sockChat.watch('user:add', ev => {
if(dumpEvents) console.log('user:add', add); if(dumpEvents) console.log('user:add', ev);
if(add.user.self) if(ev.detail.user.self)
return; return;
const userInfo = new Umi.User(add.user.id, add.user.name, add.user.colour, add.user.permsRaw); const userInfo = new Umi.User(ev.detail.user.id, ev.detail.user.name, ev.detail.user.colour, ev.detail.user.permsRaw);
Umi.Users.Add(userInfo); Umi.Users.Add(userInfo);
if(add.msg !== undefined) if(ev.detail.msg !== undefined)
Umi.Messages.Add(new Umi.Message( Umi.Messages.Add(new Umi.Message(
add.msg.id, add.msg.time, undefined, '', add.msg.channel, false, ev.detail.msg.id, ev.detail.msg.time, undefined, '', ev.detail.msg.channel, false,
{ {
isError: false, isError: false,
type: add.msg.botInfo.type, type: ev.detail.msg.botInfo.type,
args: add.msg.botInfo.args, args: ev.detail.msg.botInfo.args,
target: userInfo, target: userInfo,
} }
)); ));
}); });
sockChat.watch('user:remove', remove => { sockChat.watch('user:remove', ev => {
if(dumpEvents) console.log('user:remove', remove); if(dumpEvents) console.log('user:remove', ev);
const userInfo = Umi.Users.Get(remove.user.id); const userInfo = Umi.Users.Get(ev.detail.user.id);
if(userInfo === null) if(userInfo === null)
return; return;
if(remove.msg !== undefined) if(ev.detail.msg !== undefined)
Umi.Messages.Add(new Umi.Message( Umi.Messages.Add(new Umi.Message(
remove.msg.id, ev.detail.msg.id,
remove.msg.time, ev.detail.msg.time,
undefined, undefined,
'', '',
remove.msg.channel, ev.detail.msg.channel,
false, false,
{ {
isError: false, isError: false,
type: remove.msg.botInfo.type, type: ev.detail.msg.botInfo.type,
args: remove.msg.botInfo.args, args: ev.detail.msg.botInfo.args,
target: userInfo, target: userInfo,
}, },
)); ));
Umi.Users.Remove(userInfo); Umi.Users.Remove(userInfo);
}); });
sockChat.watch('user:update', update => { sockChat.watch('user:update', ev => {
if(dumpEvents) console.log('user:update', update); if(dumpEvents) console.log('user:update', ev);
const userInfo = Umi.Users.Get(update.user.id); const userInfo = Umi.Users.Get(ev.detail.user.id);
userInfo.setName(update.user.name); userInfo.setName(ev.detail.user.name);
userInfo.setColour(update.user.colour); userInfo.setColour(ev.detail.user.colour);
userInfo.setPermissions(update.user.permsRaw); userInfo.setPermissions(ev.detail.user.permsRaw);
Umi.Users.Update(userInfo.getId(), userInfo); Umi.Users.Update(userInfo.getId(), userInfo);
}); });
sockChat.watch('user:clear', () => { sockChat.watch('user:clear', ev => {
if(dumpEvents) console.log('user:clear'); if(dumpEvents) console.log('user:clear', ev);
const self = Umi.User.currentUser; const self = Umi.User.currentUser;
Umi.Users.Clear(); Umi.Users.Clear();
@ -760,48 +761,48 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
Umi.Users.Add(self); Umi.Users.Add(self);
}); });
sockChat.watch('chan:add', add => { sockChat.watch('chan:add', ev => {
if(dumpEvents) console.log('chan:add', add); if(dumpEvents) console.log('chan:add', ev);
Umi.Channels.Add(new Umi.Channel( Umi.Channels.Add(new Umi.Channel(
add.channel.name, ev.detail.channel.name,
add.channel.hasPassword, ev.detail.channel.hasPassword,
add.channel.isTemporary, ev.detail.channel.isTemporary,
)); ));
}); });
sockChat.watch('chan:remove', remove => { sockChat.watch('chan:remove', ev => {
if(dumpEvents) console.log('chan:remove', remove); if(dumpEvents) console.log('chan:remove', ev);
Umi.Channels.Remove(Umi.Channels.Get(remove.channel.name)); Umi.Channels.Remove(Umi.Channels.Get(ev.detail.channel.name));
}); });
sockChat.watch('chan:update', update => { sockChat.watch('chan:update', ev => {
if(dumpEvents) console.log('chan:update', update); if(dumpEvents) console.log('chan:update', ev);
const chanInfo = Umi.Channels.Get(update.channel.previousName); const chanInfo = Umi.Channels.Get(ev.detail.channel.previousName);
chanInfo.setName(update.channel.name); chanInfo.setName(ev.detail.channel.name);
chanInfo.setHasPassword(update.channel.hasPassword); chanInfo.setHasPassword(ev.detail.channel.hasPassword);
chanInfo.setTemporary(update.channel.isTemporary); chanInfo.setTemporary(ev.detail.channel.isTemporary);
Umi.Channels.Update(update.channel.previousName, chanInfo); Umi.Channels.Update(ev.detail.channel.previousName, chanInfo);
}); });
sockChat.watch('chan:clear', () => { sockChat.watch('chan:clear', ev => {
if(dumpEvents) console.log('chan:clear'); if(dumpEvents) console.log('chan:clear', ev);
Umi.Channels.Clear(); Umi.Channels.Clear();
}); });
sockChat.watch('chan:focus', focus => { sockChat.watch('chan:focus', ev => {
if(dumpEvents) console.log('chan:focus', focus); if(dumpEvents) console.log('chan:focus', ev);
Umi.Channels.Switch(Umi.Channels.Get(focus.channel.name)); Umi.Channels.Switch(Umi.Channels.Get(ev.detail.channel.name));
}); });
sockChat.watch('chan:join', join => { sockChat.watch('chan:join', ev => {
if(dumpEvents) console.log('chan:join', join); if(dumpEvents) console.log('chan:join', ev);
const userInfo = new Umi.User(join.user.id, join.user.name, join.user.colour, join.user.permsRaw); const userInfo = new Umi.User(ev.detail.user.id, ev.detail.user.name, ev.detail.user.colour, ev.detail.user.permsRaw);
Umi.Users.Add(userInfo); Umi.Users.Add(userInfo);
if(join.msg !== undefined) if(ev.detail.msg !== undefined)
Umi.Messages.Add(new Umi.Message( Umi.Messages.Add(new Umi.Message(
join.msg.id, null, undefined, '', join.msg.channel, false, ev.detail.msg.id, null, undefined, '', ev.detail.msg.channel, false,
{ {
isError: false, isError: false,
type: leave.msg.botInfo.type, type: leave.msg.botInfo.type,
@ -810,22 +811,22 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
}, },
)); ));
}); });
sockChat.watch('chan:leave', leave => { sockChat.watch('chan:leave', ev => {
if(dumpEvents) console.log('chan:leave', leave); if(dumpEvents) console.log('chan:leave', ev);
if(leave.user.self) if(ev.detail.user.self)
return; return;
const userInfo = Umi.Users.Get(leave.user.id); const userInfo = Umi.Users.Get(ev.detail.user.id);
if(userInfo === null) if(userInfo === null)
return; return;
if(leave.msg !== undefined) if(ev.detail.msg !== undefined)
Umi.Messages.Add(new Umi.Message( Umi.Messages.Add(new Umi.Message(
leave.msg.id, null, undefined, '', leave.msg.channel, false, ev.detail.msg.id, null, undefined, '', ev.detail.msg.channel, false,
{ {
isError: false, isError: false,
type: leave.msg.botInfo.type, type: ev.detail.msg.botInfo.type,
args: [ userInfo.getName() ], args: [ userInfo.getName() ],
target: userInfo, target: userInfo,
}, },
@ -834,16 +835,16 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
Umi.Users.Remove(userInfo); Umi.Users.Remove(userInfo);
}); });
sockChat.watch('msg:add', add => { sockChat.watch('msg:add', ev => {
if(dumpEvents) console.log('msg:add', add); if(dumpEvents) console.log('msg:add', ev);
const senderInfo = add.msg.sender; const senderInfo = ev.detail.msg.sender;
const userInfo = senderInfo.name === undefined const userInfo = senderInfo.name === undefined
? Umi.Users.Get(senderInfo.id) ? Umi.Users.Get(senderInfo.id)
: new Umi.User(senderInfo.id, senderInfo.name, senderInfo.colour, senderInfo.permsRaw); : new Umi.User(senderInfo.id, senderInfo.name, senderInfo.colour, senderInfo.permsRaw);
// hack // hack
let channelName = add.msg.channel; let channelName = ev.detail.msg.channel;
if(channelName !== undefined && channelName.startsWith('@~')) { if(channelName !== undefined && channelName.startsWith('@~')) {
const chanUserInfo = Umi.Users.Get(channelName.substring(2)); const chanUserInfo = Umi.Users.Get(channelName.substring(2));
if(chanUserInfo !== null) if(chanUserInfo !== null)
@ -851,7 +852,7 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
} }
// also hack // also hack
if(add.msg.flags.isPM) { if(ev.detail.msg.flags.isPM) {
if(Umi.Channels.Get(channelName) === null) if(Umi.Channels.Get(channelName) === null)
Umi.Channels.Add(new Umi.Channel(channelName, false, true, true)); Umi.Channels.Add(new Umi.Channel(channelName, false, true, true));
@ -860,24 +861,24 @@ window.Umi = { UI: {}, Protocol: { SockChat: { Protocol: {} } } };
} }
Umi.Messages.Add(new Umi.Message( Umi.Messages.Add(new Umi.Message(
add.msg.id, ev.detail.msg.id,
add.msg.time, ev.detail.msg.time,
userInfo, userInfo,
add.msg.text, ev.detail.msg.text,
channelName, channelName,
false, false,
add.msg.botInfo, ev.detail.msg.botInfo,
add.msg.flags.isAction, ev.detail.msg.flags.isAction,
add.msg.silent, ev.detail.msg.silent,
)); ));
}); });
sockChat.watch('msg:remove', remove => { sockChat.watch('msg:remove', ev => {
if(dumpEvents) console.log('msg:remove', remove); if(dumpEvents) console.log('msg:remove', ev);
Umi.Messages.Remove(Umi.Messages.Get(remove.msg.id)); Umi.Messages.Remove(Umi.Messages.Get(ev.detail.msg.id));
}); });
sockChat.watch('msg:clear', () => { sockChat.watch('msg:clear', ev => {
if(dumpEvents) console.log('msg:clear'); if(dumpEvents) console.log('msg:clear', ev);
Umi.UI.Messages.RemoveAll(); Umi.UI.Messages.RemoveAll();
}); });

View file

@ -1,4 +1,4 @@
#include watcher.js #include eventtarget.js
#include settings/scoped.js #include settings/scoped.js
#include settings/virtual.js #include settings/virtual.js
#include settings/webstorage.js #include settings/webstorage.js
@ -15,9 +15,16 @@ const MamiSettings = function(storageOrPrefix) {
throw 'required methods do not exist in storageOrPrefix object'; throw 'required methods do not exist in storageOrPrefix object';
const storage = new MamiSettingsVirtualStorage(storageOrPrefix); const storage = new MamiSettingsVirtualStorage(storageOrPrefix);
const watchers = new MamiWatchers;
const settings = new Map; const settings = new Map;
const eventTarget = new MamiEventTarget('mami:setting');
const createUpdateEvent = (name, value, initial) => eventTarget.create(name, {
name: name,
value: value,
initial: !!initial,
});
const dispatchUpdate = (name, value) => eventTarget.dispatchEvent(createUpdateEvent(name, value));
const broadcast = new BroadcastChannel(`${MAMI_JS}:settings:${storage.name()}`); const broadcast = new BroadcastChannel(`${MAMI_JS}:settings:${storage.name()}`);
const broadcastUpdate = (name, value) => { const broadcastUpdate = (name, value) => {
setTimeout(() => broadcast.postMessage({ act: 'update', name: name, value: value }), 0); setTimeout(() => broadcast.postMessage({ act: 'update', name: name, value: value }), 0);
@ -43,7 +50,7 @@ const MamiSettings = function(storageOrPrefix) {
return; return;
storage.delete(setting.name); storage.delete(setting.name);
watchers.call(setting.name, setting.fallback, setting.name); dispatchUpdate(setting.name, setting.fallback);
broadcastUpdate(setting.name, setting.fallback); broadcastUpdate(setting.name, setting.fallback);
}; };
@ -89,7 +96,7 @@ const MamiSettings = function(storageOrPrefix) {
} else } else
storage.set(setting.name, value); storage.set(setting.name, value);
watchers.call(setting.name, value, setting.name); dispatchUpdate(setting.name, value);
broadcastUpdate(setting.name, value); broadcastUpdate(setting.name, value);
}; };
@ -98,7 +105,7 @@ const MamiSettings = function(storageOrPrefix) {
return; return;
if(ev.data.act === 'update' && typeof ev.data.name === 'string') { if(ev.data.act === 'update' && typeof ev.data.name === 'string') {
watchers.call(ev.data.name, ev.data.value, ev.data.name); dispatchUpdate(ev.data.name, ev.data.value);
return; return;
} }
}; };
@ -122,8 +129,6 @@ const MamiSettings = function(storageOrPrefix) {
if(virtual === true) if(virtual === true)
storage.virtualise(name); storage.virtualise(name);
watchers.define(name);
}, },
info: name => getSetting(name), info: name => getSetting(name),
names: () => Array.from(settings.keys()), names: () => Array.from(settings.keys()),
@ -143,7 +148,7 @@ const MamiSettings = function(storageOrPrefix) {
}, },
touch: name => { touch: name => {
const setting = getSetting(name); const setting = getSetting(name);
watchers.call(setting.name, getValue(setting), setting.name); dispatchUpdate(setting.name, getValue(setting));
}, },
clear: (criticalOnly, prefix) => { clear: (criticalOnly, prefix) => {
for(const setting of settings.values()) for(const setting of settings.values())
@ -152,10 +157,11 @@ const MamiSettings = function(storageOrPrefix) {
}, },
watch: (name, handler) => { watch: (name, handler) => {
const setting = getSetting(name); const setting = getSetting(name);
watchers.watch(setting.name, handler, getValue(setting), setting.name); eventTarget.watch(setting.name, handler);
handler(createUpdateEvent(setting.name, getValue(setting), true));
}, },
unwatch: (name, handler) => { unwatch: (name, handler) => {
watchers.unwatch(getSetting(name).name, handler); eventTarget.unwatch(name, handler);
}, },
virtualise: name => storage.virtualise(getSetting(name).name), virtualise: name => storage.virtualise(getSetting(name).name),
scope: name => new MamiSettingsScoped(pub, name), scope: name => new MamiSettingsScoped(pub, name),

View file

@ -1,18 +1,10 @@
#include common.js #include common.js
#include eventtarget.js
#include servers.js #include servers.js
#include watcher.js
#include websock.js #include websock.js
Umi.Protocol.SockChat.Protocol = function() { Umi.Protocol.SockChat.Protocol = function() {
const watchers = new MamiWatchers(false); const eventTarget = new MamiEventTarget('mami:proto');
watchers.define([
'conn:init', 'conn:ready', 'conn:lost', 'conn:error',
'ping:send', 'ping:long', 'ping:recv',
'session:start', 'session:fail', 'session:term',
'user:add', 'user:remove', 'user:update', 'user:clear',
'chan:add', 'chan:remove', 'chan:update', 'chan:clear', 'chan:focus', 'chan:join', 'chan:leave',
'msg:add', 'msg:remove', 'msg:clear',
]);
const parseUserColour = str => { const parseUserColour = str => {
// todo // todo
@ -61,7 +53,7 @@ Umi.Protocol.SockChat.Protocol = function() {
stopPingWatcher(); stopPingWatcher();
if(lastPong === undefined) if(lastPong === undefined)
watchers.call('ping:long'); eventTarget.dispatch('ping:long');
}, 2000); }, 2000);
}; };
@ -81,7 +73,7 @@ Umi.Protocol.SockChat.Protocol = function() {
if(selfUserId === undefined) if(selfUserId === undefined)
return; return;
watchers.call('ping:send'); eventTarget.dispatch('ping:send');
startPingWatcher(); startPingWatcher();
lastPong = undefined; lastPong = undefined;
@ -111,10 +103,10 @@ Umi.Protocol.SockChat.Protocol = function() {
isRestarting = false; isRestarting = false;
// see if these are neccesary // see if these are neccesary
watchers.call('user:clear'); eventTarget.dispatch('user:clear');
watchers.call('chan:clear'); eventTarget.dispatch('chan:clear');
watchers.call('conn:ready', { wasConnected: wasConnected }); eventTarget.dispatch('conn:ready', { wasConnected: wasConnected });
}; };
const onClose = ev => { const onClose = ev => {
@ -136,7 +128,7 @@ Umi.Protocol.SockChat.Protocol = function() {
} else if(code === 1012) } else if(code === 1012)
isRestarting = true; isRestarting = true;
watchers.call('conn:lost', { eventTarget.dispatch('conn:lost', {
wasConnected: wasConnected, wasConnected: wasConnected,
isRestarting: isRestarting, isRestarting: isRestarting,
code: code, code: code,
@ -148,7 +140,7 @@ Umi.Protocol.SockChat.Protocol = function() {
}; };
const onError = ex => { const onError = ex => {
watchers.call('conn:error', ex); eventTarget.dispatch('conn:error', ex);
}; };
const unfuckText = text => { const unfuckText = text => {
@ -181,7 +173,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// pong handler // pong handler
handlers['0'] = () => { handlers['0'] = () => {
lastPong = Date.now(); lastPong = Date.now();
watchers.call('ping:recv', { eventTarget.dispatch('ping:recv', {
ping: lastPing, ping: lastPing,
pong: lastPong, pong: lastPong,
diff: lastPong - lastPing, diff: lastPong - lastPing,
@ -194,7 +186,7 @@ Umi.Protocol.SockChat.Protocol = function() {
selfUserId = userIdOrReason; selfUserId = userIdOrReason;
selfChannelName = chanNameOrMsgId; selfChannelName = chanNameOrMsgId;
watchers.call('session:start', { eventTarget.dispatch('session:start', {
wasConnected: wasConnected, wasConnected: wasConnected,
session: { success: true }, session: { success: true },
ctx: { ctx: {
@ -235,11 +227,11 @@ Umi.Protocol.SockChat.Protocol = function() {
until: userNameOrExpiry === '-1' ? undefined : new Date(parseInt(userNameOrExpiry) * 1000), until: userNameOrExpiry === '-1' ? undefined : new Date(parseInt(userNameOrExpiry) * 1000),
}; };
watchers.call('session:fail', failInfo); eventTarget.dispatch('session:fail', failInfo);
return; return;
} }
watchers.call('user:add', { eventTarget.dispatch('user:add', {
msg: { msg: {
id: chanNameOrMsgId, id: chanNameOrMsgId,
time: new Date(parseInt(successOrTimeStamp) * 1000), time: new Date(parseInt(successOrTimeStamp) * 1000),
@ -300,12 +292,12 @@ Umi.Protocol.SockChat.Protocol = function() {
}; };
} }
watchers.call('msg:add', msgInfo); eventTarget.dispatch('msg:add', msgInfo);
}; };
// user leave // user leave
handlers['3'] = (userId, userName, reason, timeStamp, msgId) => { handlers['3'] = (userId, userName, reason, timeStamp, msgId) => {
watchers.call('user:remove', { eventTarget.dispatch('user:remove', {
leave: { type: reason }, leave: { type: reason },
msg: { msg: {
id: msgId, id: msgId,
@ -329,7 +321,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// channel add // channel add
handlers['4']['0'] = (name, hasPass, isTemp) => { handlers['4']['0'] = (name, hasPass, isTemp) => {
watchers.call('chan:add', { eventTarget.dispatch('chan:add', {
channel: { channel: {
name: name, name: name,
hasPassword: hasPass !== '0', hasPassword: hasPass !== '0',
@ -340,7 +332,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// channel update // channel update
handlers['4']['1'] = (prevName, name, hasPass, isTemp) => { handlers['4']['1'] = (prevName, name, hasPass, isTemp) => {
watchers.call('chan:update', { eventTarget.dispatch('chan:update', {
channel: { channel: {
previousName: prevName, previousName: prevName,
name: name, name: name,
@ -352,7 +344,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// channel remove // channel remove
handlers['4']['2'] = name => { handlers['4']['2'] = name => {
watchers.call('chan:remove', { eventTarget.dispatch('chan:remove', {
channel: { name: name }, channel: { name: name },
}); });
}; };
@ -362,7 +354,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// user join channel // user join channel
handlers['5']['0'] = (userId, userName, userColour, userPerms, msgId) => { handlers['5']['0'] = (userId, userName, userColour, userPerms, msgId) => {
watchers.call('chan:join', { eventTarget.dispatch('chan:join', {
user: { user: {
id: userId, id: userId,
self: userId === selfUserId, self: userId === selfUserId,
@ -384,7 +376,7 @@ Umi.Protocol.SockChat.Protocol = function() {
// user leave channel // user leave channel
handlers['5']['1'] = (userId, msgId) => { handlers['5']['1'] = (userId, msgId) => {
watchers.call('chan:leave', { eventTarget.dispatch('chan:leave', {
user: { user: {
id: userId, id: userId,
self: userId === selfUserId, self: userId === selfUserId,
@ -404,14 +396,14 @@ Umi.Protocol.SockChat.Protocol = function() {
handlers['5']['2'] = name => { handlers['5']['2'] = name => {
selfChannelName = name; selfChannelName = name;
watchers.call('chan:focus', { eventTarget.dispatch('chan:focus', {
channel: { name: selfChannelName }, channel: { name: selfChannelName },
}); });
}; };
// message delete // message delete
handlers['6'] = msgId => { handlers['6'] = msgId => {
watchers.call('msg:remove', { eventTarget.dispatch('msg:remove', {
msg: { msg: {
id: msgId, id: msgId,
channel: selfChannelName, channel: selfChannelName,
@ -429,7 +421,7 @@ Umi.Protocol.SockChat.Protocol = function() {
for(let i = 0; i < count; ++i) { for(let i = 0; i < count; ++i) {
const offset = 5 * i; const offset = 5 * i;
watchers.call('user:add', { eventTarget.dispatch('user:add', {
user: { user: {
id: args[offset], id: args[offset],
self: args[offset] === selfUserId, self: args[offset] === selfUserId,
@ -479,7 +471,7 @@ Umi.Protocol.SockChat.Protocol = function() {
}; };
} }
watchers.call('msg:add', info); eventTarget.dispatch('msg:add', info);
}; };
// existing channels // existing channels
@ -489,7 +481,7 @@ Umi.Protocol.SockChat.Protocol = function() {
for(let i = 0; i < count; ++i) { for(let i = 0; i < count; ++i) {
const offset = 3 * i; const offset = 3 * i;
watchers.call('chan:add', { eventTarget.dispatch('chan:add', {
channel: { channel: {
name: args[offset], name: args[offset],
hasPassword: args[offset + 1] !== '0', hasPassword: args[offset + 1] !== '0',
@ -499,7 +491,7 @@ Umi.Protocol.SockChat.Protocol = function() {
}); });
} }
watchers.call('chan:focus', { eventTarget.dispatch('chan:focus', {
channel: { name: selfChannelName }, channel: { name: selfChannelName },
}); });
}; };
@ -507,13 +499,13 @@ Umi.Protocol.SockChat.Protocol = function() {
// context clear // context clear
handlers['8'] = mode => { handlers['8'] = mode => {
if(mode === '0' || mode === '3' || mode === '4') if(mode === '0' || mode === '3' || mode === '4')
watchers.call('msg:clear'); eventTarget.dispatch('msg:clear');
if(mode === '1' || mode === '3' || mode === '4') if(mode === '1' || mode === '3' || mode === '4')
watchers.call('user:clear'); eventTarget.dispatch('user:clear');
if(mode === '2' || mode === '4') if(mode === '2' || mode === '4')
watchers.call('chan:clear'); eventTarget.dispatch('chan:clear');
}; };
// baka (ban/kick) // baka (ban/kick)
@ -533,12 +525,12 @@ Umi.Protocol.SockChat.Protocol = function() {
bakaInfo.baka.until = expiry === '-1' ? undefined : new Date(parseInt(expiry) * 1000); bakaInfo.baka.until = expiry === '-1' ? undefined : new Date(parseInt(expiry) * 1000);
} }
watchers.call('session:term', bakaInfo); eventTarget.dispatch('session:term', bakaInfo);
}; };
// user update // user update
handlers['10'] = (userId, userName, userColour, userPerms) => { handlers['10'] = (userId, userName, userColour, userPerms) => {
watchers.call('user:update', { eventTarget.dispatch('user:update', {
user: { user: {
id: userId, id: userId,
self: userId === selfUserId, self: userId === selfUserId,
@ -558,7 +550,7 @@ Umi.Protocol.SockChat.Protocol = function() {
return; return;
UmiServers.getServer(server => { UmiServers.getServer(server => {
watchers.call('conn:init', { eventTarget.dispatch('conn:init', {
server: server, server: server,
wasConnected: wasConnected, wasConnected: wasConnected,
attempt: ++connectAttempts, attempt: ++connectAttempts,
@ -604,8 +596,8 @@ Umi.Protocol.SockChat.Protocol = function() {
noReconnect = true; noReconnect = true;
sock?.close(); sock?.close();
}, },
watch: (name, handler) => watchers.watch(name, handler), watch: (name, handler) => eventTarget.watch(name, handler),
unwatch: (name, handler) => watchers.unwatch(name, handler), unwatch: (name, handler) => eventTarget.unwatch(name, handler),
setDumpPackets: state => dumpPackets = !!state, setDumpPackets: state => dumpPackets = !!state,
switchChannel: channelInfo => { switchChannel: channelInfo => {
if(selfUserId === undefined) if(selfUserId === undefined)

View file

@ -454,7 +454,7 @@ Umi.UI.Settings = (function() {
input.disabled = true; input.disabled = true;
if(display.type === 'checkbox') { if(display.type === 'checkbox') {
mami.settings.watch(setting.name, v => input.checked = v); mami.settings.watch(setting.name, ev => input.checked = ev.detail.value);
input.addEventListener('change', () => { input.addEventListener('change', () => {
if(display.confirm !== undefined && input.checked !== setting.fallback && !confirm(display.confirm)) { if(display.confirm !== undefined && input.checked !== setting.fallback && !confirm(display.confirm)) {
input.checked = setting.fallback; input.checked = setting.fallback;
@ -464,7 +464,7 @@ Umi.UI.Settings = (function() {
mami.settings.toggle(setting.name); mami.settings.toggle(setting.name);
}); });
} else { } else {
mami.settings.watch(setting.name, v => input.value = v); mami.settings.watch(setting.name, ev => input.value = ev.detail.value);
input.addEventListener('change', () => mami.settings.set(setting.name, input.value)); input.addEventListener('change', () => mami.settings.set(setting.name, input.value));
} }
} }

View file

@ -1,77 +0,0 @@
#include utility.js
const MamiWatcher = function(initCall) {
if(typeof initCall !== 'boolean')
initCall = true;
const handlers = [];
const watch = (handler, ...args) => {
if(typeof handler !== 'function')
throw 'handler must be a function';
if(handlers.includes(handler))
throw 'handler already registered';
handlers.push(handler);
if(initCall) {
args.push(true);
handler(...args);
}
};
const unwatch = handler => {
$ari(handlers, handler);
};
return {
watch: watch,
unwatch: unwatch,
call: (...args) => {
if(initCall)
args.push(false);
for(const handler of handlers)
handler(...args);
},
};
};
const MamiWatchers = function(initCall) {
if(typeof initCall !== 'boolean')
initCall = true;
const watchers = new Map;
const getWatcher = name => {
const watcher = watchers.get(name);
if(watcher === undefined)
throw 'undefined watcher name';
return watcher;
};
const watch = (name, handler, ...args) => {
getWatcher(name).watch(handler, ...args);
};
const unwatch = (name, handler) => {
getWatcher(name).unwatch(handler);
};
return {
watch: watch,
unwatch: unwatch,
define: names => {
if(typeof names === 'string')
watchers.set(names, new MamiWatcher(initCall));
else if(Array.isArray(names))
for(const name of names)
watchers.set(name, new MamiWatcher(initCall));
else
throw 'names must be an array of names or a single name';
},
call: (name, ...args) => {
getWatcher(name).call(...args);
},
};
};