mami/src/mami.js/ui/settings.jsx

615 lines
22 KiB
JavaScript

#include animate.js
#include awaitable.js
#include common.js
#include emotes.js
#include utility.js
#include settings/backup.js
#include ui/baka.jsx
#include ui/emotes.js
#include ui/menus.js
#include ui/view.js
#include ui/youare.jsx
Umi.UI.Settings = (function() {
const items = [
{
name: 'interface',
title: 'Interface',
items: [
{
name: 'style',
title: 'Style',
type: 'select',
options: () => Umi.UI.View.AccentColours,
},
{
name: 'compactView',
title: 'Compact view',
type: 'checkbox',
},
{
name: 'autoScroll',
title: 'Scroll to latest message',
type: 'checkbox',
},
{
name: 'closeTabConfirm',
title: 'Confirm tab close',
type: 'checkbox',
},
{
name: 'showChannelList',
title: 'Show channel list',
type: 'checkbox',
},
{
name: 'fancyInfo',
title: 'Fancy server messages',
type: 'checkbox',
},
{
name: 'autoCloseUserContext',
title: 'Auto-close user menus',
type: 'checkbox',
},
],
},
{
name: 'text',
title: 'Text',
items: [
{
name: 'enableParser',
title: 'Parse markup',
type: 'checkbox',
},
{
name: 'enableEmoticons',
title: 'Parse emoticons',
type: 'checkbox',
},
{
name: 'autoParseUrls',
title: 'Auto detect links',
type: 'checkbox',
},
{
name: 'preventOverflow',
title: 'Prevent overflow',
type: 'checkbox',
},
{
name: 'expandTextBox',
title: 'Grow input box',
type: 'checkbox',
},
{
name: 'eepromAutoInsert',
title: 'Auto-insert uploads',
type: 'checkbox',
},
{
name: 'autoEmbedV1',
title: 'Auto-embed media',
type: 'checkbox',
},
],
},
{
name: 'notification',
title: 'Notification',
items: [
{
name: 'flashTitle',
title: 'Strobe title on new message',
type: 'checkbox',
},
{
name: 'showServerMsgInTitle',
title: 'Show server message in title',
type: 'checkbox',
},
{
name: 'enableNotifications',
title: 'Show notifications',
type: 'checkbox',
},
{
name: 'notificationShowMessage',
title: 'Show contents of message',
type: 'checkbox',
},
{
name: 'notificationTriggers',
title: 'Triggers',
type: 'text',
},
],
},
{
name: 'sounds',
title: 'Sound',
items: [
{
name: 'soundEnable',
title: 'Enable sound',
type: 'checkbox',
},
{
name: 'soundPack',
title: 'Sound pack',
type: 'select',
options: () => {
const packs = mami.sound.packs;
const options = { '': 'Default' };
for(const name of packs.names())
options[name] = packs.info(name).getTitle();
return options;
},
},
{
name: 'soundVolume',
title: 'Sound volume',
type: 'range',
},
{
name: 'soundEnableJoin',
title: 'Play join sound',
type: 'checkbox',
},
{
name: 'soundEnableLeave',
title: 'Play leave sound',
type: 'checkbox',
},
{
name: 'soundEnableError',
title: 'Play error sound',
type: 'checkbox',
},
{
name: 'soundEnableServer',
title: 'Play server message sound',
type: 'checkbox',
},
{
name: 'soundEnableIncoming',
title: 'Play receive message sound',
type: 'checkbox',
},
{
name: 'onlySoundOnMention',
title: 'Do Not Play Any Sound Effects, Unless the Message Contains the Username of the User Controlling the Current Flashii Chat Session, Including but Not Limited To Joke Triggers, Receive Sounds and Join Sounds, but Excluding Typing Sounds',
type: 'checkbox',
},
{
name: 'soundEnableOutgoing',
title: 'Play send message sound',
type: 'checkbox',
},
{
name: 'soundEnablePrivate',
title: 'Play private message sound',
type: 'checkbox',
},
{
name: 'soundEnableForceLeave',
title: 'Play kick sound',
type: 'checkbox',
},
{
name: 'minecraft',
title: 'Minecraft',
type: 'select',
options: () => {
return {
'no': 'No Minecraft',
'yes': 'Yes Minecraft',
'old': 'Old Minecraft',
};
},
},
{
name: 'windowsLiveMessenger',
title: 'Windows Live Messenger',
type: 'checkbox',
},
{
name: 'seinfeld',
title: 'Seinfeld',
type: 'checkbox',
},
],
},
{
name: 'misc',
title: 'Misc',
items: [
{
name: 'onlyConnectWhenVisible',
title: 'Only connect when the tab is in the foreground',
type: 'checkbox',
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.'],
},
{
name: 'playJokeSounds',
title: 'Run joke triggers',
type: 'checkbox',
},
{
name: 'weeaboo',
title: 'Weeaboo',
type: 'checkbox',
},
{
name: 'motivationalImages',
title: 'Make images motivational',
type: 'checkbox',
},
{
name: 'motivationalVideos',
title: 'Make videos motivational',
type: 'checkbox',
},
{
name: 'osuKeysV2',
title: 'osu! keyboard sounds',
type: 'select',
options: () => {
return {
'no': 'Off',
'yes': 'On',
'rng': 'On, random pitch',
};
},
},
{
name: 'explosionRadius',
title: 'Messages to keep on clear',
type: 'number',
},
{
title: 'Reload emoticons',
type: 'button',
invoke: button => {
const emotes = futami.get('emotes');
setTimeout(() => {
button.disabled = true;
futami.getJson('emotes', true)
.then(emotes => {
MamiEmotes.clear();
MamiEmotes.loadLegacy(emotes);
})
.finally(() => {
Umi.UI.Emoticons.Init();
button.disabled = false;
});
}, 200);
},
},
{
title: 'Reload joke triggers',
type: 'button',
invoke: button => {
button.disabled = true;
const triggers = mami.textTriggers;
triggers.clearTriggers();
if(mami.settings.get('playJokeSounds'))
futami.getJson('texttriggers', true)
.then(trigInfos => triggers.addTriggers(trigInfos))
.finally(() => button.disabled = false);
},
},
{
title: 'Open compatibility client',
type: 'button',
invoke: () => {
const meow = $e('a', { href: window.AMI_URL, target: '_blank', style: { display: 'none' } });
document.body.appendChild(meow);
meow.click();
$r(meow);
},
},
{
title: 'Manual reconnect',
type: 'button',
invoke: async button => {
const textOrig = button.value;
let lock = 10;
button.disabled = true;
button.value = 'Reconnecting...';
try {
await mami.conMan.start();
while(--lock > 0) {
button.value = textOrig + ` (${lock}s)`;
await MamiSleep(1000);
}
} finally {
button.value = textOrig;
button.disabled = false;
}
},
},
],
},
{
name: 'settings',
title: 'Settings',
items: [
{
title: 'Import settings',
type: 'button',
confirm: ['Your current settings will be replaced with the ones in the export.', 'Are you sure you want to continue?'],
invoke: () => {
(new MamiSettingsBackup(mami.settings)).importUpload(document.body);
},
},
{
title: 'Export settings',
type: 'button',
invoke: () => {
const user = Umi.User.getCurrentUser();
let fileName;
if(user !== null)
fileName = `${user.name}'s settings.mami`;
(new MamiSettingsBackup(mami.settings)).exportDownload(document.body, fileName);
},
},
{
title: 'Reset settings',
type: 'button',
confirm: ['This will reset all your settings to their defaults values.', 'Are you sure you want to do this?'],
invoke: () => {
mami.settings.clear();
},
},
],
},
{
name: 'debug',
title: 'Debug',
collapse: true,
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.",
items: [
{
name: 'dumpPackets',
title: 'Dump packets to console',
type: 'checkbox',
},
{
name: 'dumpEvents',
title: 'Dump events to console',
type: 'checkbox',
},
{
name: 'marqueeAllNames',
title: 'Apply marquee on everyone',
type: 'checkbox',
},
{
name: 'tmpDisableOldThemeSys',
title: 'Disable Old Theme System',
type: 'checkbox',
},
{
name: 'dbgAnimDurationMulti',
title: 'Animation multiplier',
type: 'range',
},
{
title: 'Test kick/ban notice',
type: 'button',
invoke: async button => {
button.disabled = true;
const notice = new MamiForceDisconnectNotice({ perma: true, type: 'ban' });
await notice.pushOn(mami.views);
await MamiSleep(5000);
await notice.popOff(mami.views);
button.disabled = false;
},
},
{
title: 'You are an idiot!',
type: 'button',
invoke: async button => {
button.disabled = true;
await (new MamiYouAreAnIdiot()).pushOn(mami.views);
button.disabled = false;
},
},
{
title: 'Reset audio context',
type: 'button',
invoke: async () => {
mami.sound.reset();
},
},
],
}
];
const createCopyright = function() {
return <div class="mami-copyright">
<a href="//patchii.net/flashii/mami" target="_blank">Mami</a> # <a href={`//patchii.net/flashii/mami/commit/${GIT_HASH}`} target="_blank">{GIT_HASH.substring(0, 7)}</a> © <a href="//flash.moe" target="_blank">flash.moe</a><br/>
<a href="//railgun.sh/sockchat" target="_blank">Sock Chat documentation</a><br/>
</div>;
};
const createSetting = function(display) {
let setting;
if('name' in display)
setting = mami.settings.info(display.name);
let input = display.type === 'select'
? <select class="setting__input"/>
: <input type={display.type} class="setting__input"/>;
if(display.disabled === true)
input.disabled = true;
const updateSelectOptions = () => {
const options = display.options();
$rc(input);
for(const name in options)
input.appendChild(<option class={['setting__style', `setting__style--${display.name}-${name}`]} value={name}>
{options[name]}
</option>);
};
if(display.type === 'select') {
updateSelectOptions();
} else if(display.type === 'button') {
input.value = display.title;
input.addEventListener('click', () => {
if(display.confirm !== undefined)
mami.msgbox.show({
body: display.confirm,
yes: { primary: false },
no: { primary: true },
}).then(() => { display.invoke(input); }).catch(() => {});
else
display.invoke(input);
});
}
if(setting !== undefined) {
if(!input.disabled && setting.immutable)
input.disabled = true;
if(setting.min !== undefined)
input.min = setting.min;
if(setting.max !== undefined)
input.max = setting.max;
if(display.type === 'checkbox') {
mami.settings.watch(setting.name, ev => input.checked = ev.detail.value);
input.addEventListener('change', () => {
if(display.confirm !== undefined && input.checked !== setting.fallback) {
mami.msgbox.show({
body: display.confirm,
yes: { primary: false },
no: { primary: true },
}).then(() => {
mami.settings.toggle(setting.name);
}).catch(() => {
input.checked = setting.fallback;
});
} else {
mami.settings.toggle(setting.name);
}
});
} else {
// hack: do this more gracefully when an actual API for the settings sidebar is exposed
// specifically for the sound pack
if(display.type === 'select')
input.addEventListener('click', () => {
if(input.childElementCount > 1)
return;
const value = mami.settings.get(setting.name);
updateSelectOptions();
input.value = value;
});
mami.settings.watch(setting.name, ev => input.value = ev.detail.value);
input.addEventListener('change', () => mami.settings.set(setting.name, input.value));
}
}
let label = input;
if(display.type === 'checkbox') {
label = <label class="setting__label">
{input}
<div>{display.title}</div>
</label>;
} else if(display.type === 'button') {
label = <label class="setting__label">{input}</label>;
} else {
label = <label class="setting__label">
<div>{display.title}</div>
{input}
</label>;
}
return <div class={['setting__container', `setting__container--${display.type}`]}>{label}</div>;
};
const createCategory = function(category) {
const catHeader = <div class={['setting__category-title', `setting__category-title--${category.name}`]} style={{ cursor: 'pointer' }}>{category.title}</div>;
const catBody = <div class={['setting__category', `setting__category--${category.name}`]} style={{ overflow: 'hidden' }}/>;
const catContainer = <div>{catHeader}{catBody}</div>;
let anime, closed = false;
catHeader.onclick = () => {
if(anime !== undefined) {
anime.cancel();
anime = undefined;
}
let start, update, end, height;
if(closed) {
start = () => {
const curHeight = catBody.style.height;
catBody.style.height = null;
height = catBody.clientHeight;
catBody.style.height = curHeight;
};
update = t => catBody.style.height = `${height * t}px`;
end = () => catBody.style.height = null;
} else {
start = () => height = catBody.clientHeight;
update = t => catBody.style.height = `${height - (height * t)}px`;
end = () => catBody.style.height = '0';
}
closed = !closed;
anime = MamiAnimate({
duration: 500,
easing: 'outExpo',
start: start,
update: update,
end: end,
});
};
if(category.warning)
catBody.appendChild(<div style={{ fontSize: '.9em', lineHeight: '1.4em', margin: '5px', padding: '5px', backgroundColor: 'darkred', border: '2px solid red', borderRadius: '5px' }}>{category.warning}</div>);
if(category.items)
for(const item of category.items)
catBody.appendChild(createSetting(item));
return catContainer;
};
return {
Init: function() {
const html = Umi.UI.Menus.Get('settings');
$rc(html);
for(const category of items) {
const elem = createCategory(category);
html.appendChild(elem);
// only a little bit of hacking, stan
if(category.collapse)
elem.firstChild.click();
}
html.appendChild(createCopyright());
},
};
})();