XL v1.3.0
This commit is contained in:
parent
59f46f2b3b
commit
9a6bc32ff7
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
@else@*/
|
@else@*/
|
||||||
/*
|
/*
|
||||||
* Copyright© 2019-2020, _Lighty_
|
* Copyright © 2019-2020, _Lighty_
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
* Code may not be redistributed, modified or otherwise taken without explicit permission.
|
* Code may not be redistributed, modified or otherwise taken without explicit permission.
|
||||||
*/
|
*/
|
||||||
|
@ -41,16 +41,33 @@ var XenoLib = (() => {
|
||||||
twitter_username: ''
|
twitter_username: ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
version: '1.2.3',
|
version: '1.3.0',
|
||||||
description: 'Simple library to complement plugins with shared code without lowering performance.',
|
description: 'Simple library to complement plugins with shared code without lowering performance.',
|
||||||
github: 'https://github.com/1Lighty',
|
github: 'https://github.com/1Lighty',
|
||||||
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js'
|
github_raw: 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/1XenoLib.plugin.js'
|
||||||
},
|
},
|
||||||
changelog: [
|
changelog: [
|
||||||
{
|
{
|
||||||
title: 'Hopefully fixed',
|
title: 'Boring changes',
|
||||||
type: 'fixed',
|
type: 'Added',
|
||||||
items: ['Fixed BDFDB causing issues with patching context menus']
|
items: ['User, Channel and Guild context menus now have a state object, setState and forceUpdate functions.', 'Notifications system has been implemented but is still WIP.', 'Now able to stop the context menu from closing when clicking on a context menu item, by passing noClose prop in the third argument of XenoLib.createContextMenuItem.']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
defaultConfig: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
id: 'notifications',
|
||||||
|
name: 'Notification settings',
|
||||||
|
collapsible: true,
|
||||||
|
shown: true,
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
name: 'Notification position',
|
||||||
|
id: 'position',
|
||||||
|
type: 'position',
|
||||||
|
value: 'topLeft'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -68,6 +85,11 @@ var XenoLib = (() => {
|
||||||
Patcher.unpatchAll();
|
Patcher.unpatchAll();
|
||||||
PluginUtilities.removeStyle('XenoLib-CSS');
|
PluginUtilities.removeStyle('XenoLib-CSS');
|
||||||
if (global.BDEvents) BDEvents.off('plugin-unloaded', listener);
|
if (global.BDEvents) BDEvents.off('plugin-unloaded', listener);
|
||||||
|
const notifWrapper = document.querySelector('.xenoLib-notifications');
|
||||||
|
if (notifWrapper) {
|
||||||
|
ReactDOM.unmountComponentAtNode(notifWrapper);
|
||||||
|
notifWrapper.remove();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PluginUtilities.addStyle(
|
PluginUtilities.addStyle(
|
||||||
|
@ -111,6 +133,73 @@ var XenoLib = (() => {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translate3d(-200%,0,0);
|
transform: translate3d(-200%,0,0);
|
||||||
}
|
}
|
||||||
|
.xenoLib-notifications {
|
||||||
|
position: absolute;
|
||||||
|
color: white;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.xenoLib-notification {
|
||||||
|
min-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.xenoLib-notification-content-wrapper {
|
||||||
|
padding: 20px 20px 0 0;
|
||||||
|
}
|
||||||
|
.xenoLib-notification-content {
|
||||||
|
padding: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #474747;
|
||||||
|
pointer-events: all;
|
||||||
|
position: relative;
|
||||||
|
width: 20vw;
|
||||||
|
}
|
||||||
|
.xenoLib-notification-loadbar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0px;
|
||||||
|
width: auto;
|
||||||
|
background-image: linear-gradient(130deg,var(--grad-one),var(--grad-two));
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
.xenoLib-notification-loadbar-striped:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: linear-gradient(
|
||||||
|
-20deg,
|
||||||
|
transparent 35%,
|
||||||
|
var(--bar-color) 35%,
|
||||||
|
var(--bar-color) 70%,
|
||||||
|
transparent 70%
|
||||||
|
);
|
||||||
|
animation: shift 1s linear infinite;
|
||||||
|
background-size: 60px 100%;
|
||||||
|
box-shadow: inset 0 0px 1px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0 -2px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
@keyframes shift {
|
||||||
|
to {
|
||||||
|
background-position: 60px 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.xenoLib-notification-close {
|
||||||
|
float: right;
|
||||||
|
padding: 0;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
.xenLib-notification-counter {
|
||||||
|
float: right;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -228,9 +317,18 @@ var XenoLib = (() => {
|
||||||
} else if (isValid(children)) return children.props.children;
|
} else if (isValid(children)) return children.props.children;
|
||||||
};
|
};
|
||||||
function patchAllContextMenus() {
|
function patchAllContextMenus() {
|
||||||
const handleContextMenu = (_this, ret) => {
|
const handleContextMenu = (_this, ret, noRender) => {
|
||||||
const menuGroups = getContextMenuChild(ret) || ret;
|
const menuGroups = getContextMenuChild(ret) || ret;
|
||||||
if (!menuGroups) return Logger.warn('Failed to get context menu groups!', _this, ret);
|
if (!menuGroups) return Logger.warn('Failed to get context menu groups!', _this, ret);
|
||||||
|
let [value, set] = noRender ? React.useState(false) : [];
|
||||||
|
let [state, setState] = noRender ? React.useState({}) : [];
|
||||||
|
/* emulate a react component */
|
||||||
|
if (noRender) {
|
||||||
|
_this.forceUpdate = () => set(!value);
|
||||||
|
_this.state = state;
|
||||||
|
_this.setState = setState;
|
||||||
|
}
|
||||||
|
if (!_this.state) _this.state = {};
|
||||||
XenoLib.__contextPatches.forEach(e => {
|
XenoLib.__contextPatches.forEach(e => {
|
||||||
try {
|
try {
|
||||||
e(_this, menuGroups);
|
e(_this, menuGroups);
|
||||||
|
@ -259,7 +357,7 @@ var XenoLib = (() => {
|
||||||
if (!menu) return Logger.warn('Special context menu is undefined!');
|
if (!menu) return Logger.warn('Special context menu is undefined!');
|
||||||
const origDef = menu.exports.default;
|
const origDef = menu.exports.default;
|
||||||
const originalFunc = Utilities.getNestedProp(menu, 'exports.BDFDBpatch.default.originalMethod') || menu.exports.default;
|
const originalFunc = Utilities.getNestedProp(menu, 'exports.BDFDBpatch.default.originalMethod') || menu.exports.default;
|
||||||
Patcher.after(menu.exports, 'default', (_, [props], ret) => handleContextMenu({ props }, ret));
|
Patcher.after(menu.exports, 'default', (_, [props], ret) => handleContextMenu({ props }, ret, true));
|
||||||
/* make it friendly to other plugins and libraries that search by string
|
/* make it friendly to other plugins and libraries that search by string
|
||||||
note: removing this makes BDFDB shit itself
|
note: removing this makes BDFDB shit itself
|
||||||
*/
|
*/
|
||||||
|
@ -269,7 +367,7 @@ var XenoLib = (() => {
|
||||||
this function is never called in BDFDB, it's only stored for restore
|
this function is never called in BDFDB, it's only stored for restore
|
||||||
*/
|
*/
|
||||||
if (origDef.isBDFDBpatched && menu.exports.BDFDBpatch && typeof menu.exports.BDFDBpatch.default.originalMethod === 'function') {
|
if (origDef.isBDFDBpatched && menu.exports.BDFDBpatch && typeof menu.exports.BDFDBpatch.default.originalMethod === 'function') {
|
||||||
Patcher.after(menu.exports.BDFDBpatch.default, 'originalMethod', (_, [props], ret) => handleContextMenu({ props }, ret));
|
Patcher.after(menu.exports.BDFDBpatch.default, 'originalMethod', (_, [props], ret) => handleContextMenu({ props }, ret, true));
|
||||||
/* make it friendly to other plugins and libraries that search by string
|
/* make it friendly to other plugins and libraries that search by string
|
||||||
note: removing this makes BDFDB shit itself
|
note: removing this makes BDFDB shit itself
|
||||||
*/
|
*/
|
||||||
|
@ -303,7 +401,7 @@ var XenoLib = (() => {
|
||||||
React.createElement(ContextMenuItem, {
|
React.createElement(ContextMenuItem, {
|
||||||
label,
|
label,
|
||||||
action: () => {
|
action: () => {
|
||||||
ContextMenuActions.closeContextMenu();
|
if (!options.noClose) ContextMenuActions.closeContextMenu();
|
||||||
action();
|
action();
|
||||||
},
|
},
|
||||||
...options
|
...options
|
||||||
|
@ -556,11 +654,12 @@ var XenoLib = (() => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
XenoLib.loadData = (name, key, defaultData) => {
|
XenoLib.loadData = (name, key, defaultData, returnNull) => {
|
||||||
try {
|
try {
|
||||||
return Object.assign(defaultData ? Utilities.deepclone(defaultData) : {}, BdApi.getData(name, key));
|
return Object.assign(defaultData ? Utilities.deepclone(defaultData) : {}, BdApi.getData(name, key));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.err(name, 'Unable to load data: ', err);
|
Logger.err(name, 'Unable to load data: ', err);
|
||||||
|
if (returnNull) return null;
|
||||||
return Utilities.deepclone(defaultData);
|
return Utilities.deepclone(defaultData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -596,6 +695,365 @@ var XenoLib = (() => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* NOTIFICATIONS START */
|
||||||
|
try {
|
||||||
|
const zustand = WebpackModules.getByString('console.warn("Zustand: the 2nd arg');
|
||||||
|
const [useStore, api] = zustand(e => ({ data: [] }));
|
||||||
|
const defaultOptions = {
|
||||||
|
loading: false,
|
||||||
|
progress: -1,
|
||||||
|
channelId: undefined,
|
||||||
|
timeout: 1000,
|
||||||
|
color: '#2196f3'
|
||||||
|
};
|
||||||
|
const utils = {
|
||||||
|
success(content, options = {}) {
|
||||||
|
return this.show(content, Object.assign({ color: '#43b581' }, options));
|
||||||
|
},
|
||||||
|
info(content, options = {}) {
|
||||||
|
return this.show(content, Object.assign({ color: '#4a90e2' }, options));
|
||||||
|
},
|
||||||
|
warning(content, options = {}) {
|
||||||
|
return this.show(content, Object.assign({ color: '#ffa600' }, options));
|
||||||
|
},
|
||||||
|
danger(content, options = { n }) {
|
||||||
|
return this.show(content, Object.assign({ color: '#f04747' }, options));
|
||||||
|
},
|
||||||
|
error(content, options = {}) {
|
||||||
|
return this.danger(content, options);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {string|*} content - Content to display. If it's a string, it'll be formatted with markdown, including URL support [like this](https://google.com/)
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} [options.channelId] Channel ID if content is a string which gets formatted, and you want to mention a role for example.
|
||||||
|
* @param {Number} [options.timeout] Set to 0 to keep it permanently until user closes it, or if you want a progress bar
|
||||||
|
* @param {Boolean} [options.loading] Makes the bar animate differently instead of fading in and out slowly
|
||||||
|
* @param {Number} [options.progress] 0-100, -1 sets it to 100%, setting it to 100% closes the notification automatically
|
||||||
|
* @param {string} [options.color] Bar color
|
||||||
|
* @param {string} [options.allowDuplicates] By default, notifications that are similar get grouped together, use true to disable that
|
||||||
|
* @return {Number} - Notification ID. Store this if you plan on force closing it, changing its content or want to set the progress
|
||||||
|
*/
|
||||||
|
show(content, options = {}) {
|
||||||
|
let id = null;
|
||||||
|
api.setState(state => {
|
||||||
|
if (state.data.length >= 100) return state;
|
||||||
|
if (!options.allowDuplicates) {
|
||||||
|
const notif = state.data.find(n => n.content === content && n.timeout === options.timeout);
|
||||||
|
if (notif) {
|
||||||
|
id = notif.id;
|
||||||
|
Dispatcher.dispatch({ type: 'XL_NOTIFS_DUPLICATE', id: notif.id });
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
id = Math.floor(4294967296 * Math.random());
|
||||||
|
} while (state.data.findIndex(n => n.id === id) !== -1);
|
||||||
|
return { data: [].concat(state.data, [{ content, ...Object.assign(Utilities.deepclone(defaultOptions), options), id }]) };
|
||||||
|
});
|
||||||
|
return id;
|
||||||
|
},
|
||||||
|
remove(id) {
|
||||||
|
Dispatcher.dispatch({ type: 'XL_NOTIFS_REMOVE', id });
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {Number} id Notification ID
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} [options.channelId] Channel ID if content is a string which gets formatted, and you want to mention a role for example.
|
||||||
|
* @param {Boolean} [options.loading] Makes the bar animate differently instead of fading in and out slowly
|
||||||
|
* @param {Number} [options.progress] 0-100, -1 sets it to 100%, setting it to 100% closes the notification automatically
|
||||||
|
* @param {string} [options.color] Bar color
|
||||||
|
*/
|
||||||
|
update(id, options) {
|
||||||
|
delete options.id;
|
||||||
|
api.setState(state => {
|
||||||
|
const idx = state.data.findIndex(n => n.id === id);
|
||||||
|
if (idx === -1) return state;
|
||||||
|
state.data[idx] = Object.assign(state.data[idx], options);
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
Dispatcher.dispatch({ type: 'XL_NOTIFS_UPDATE', id, ...options });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
XenoLib.Notifications = utils;
|
||||||
|
XenoLib.Notifications.__api = api;
|
||||||
|
XenoLib.Notifications.__api._DO_NOT_USE_THIS_IN_YOUR_PLUGIN_OR_YOU_WILL_CRY = 'Because it may be removed at any point in the future';
|
||||||
|
const ReactSpring = WebpackModules.getByProps('useTransition');
|
||||||
|
const BadgesModule = WebpackModules.getByProps('NumberBadge');
|
||||||
|
const ParsersModule = WebpackModules.getByProps('parseAllowLinks', 'parse');
|
||||||
|
const CloseButton = WebpackModules.getByProps('CloseButton').CloseButton;
|
||||||
|
class Notification extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
closeFast: false /* close button pressed, XL_NOTIFS_REMOVE dispatch */,
|
||||||
|
offscreen: false /* don't do anything special if offscreen, not timeout */,
|
||||||
|
counter: 1 /* how many times this notification was shown */,
|
||||||
|
resetBar: false /* reset bar to 0 in the event counter goes up */,
|
||||||
|
hovered: false,
|
||||||
|
leaving: true /* prevent hover events from messing up things */,
|
||||||
|
loading: props.loading /* loading animation, enable progress */,
|
||||||
|
progress: props.progress /* -1 means undetermined */,
|
||||||
|
content: props.content,
|
||||||
|
contentParsed: this.parseContent(props.content, props.channelId),
|
||||||
|
color: props.color
|
||||||
|
};
|
||||||
|
this._contentRef = null;
|
||||||
|
this._ref;
|
||||||
|
this._animationCancel = () => {};
|
||||||
|
this._oldOffsetHeight = 0;
|
||||||
|
this._initialProgress = !this.props.timeout ? (this.state.loading && this.state.progress !== -1 ? this.state.progress : 100) : 0;
|
||||||
|
XenoLib.DiscordUtils.bindAll(this, ['closeNow', 'handleResizeEvent', 'handleDispatch']);
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
this._unsubscribe = api.subscribe(_ => this.checkOffScreen());
|
||||||
|
window.addEventListener('resize', this.handleResizeEvent);
|
||||||
|
Dispatcher.subscribe('XL_NOTIFS_DUPLICATE', this.handleDispatch);
|
||||||
|
Dispatcher.subscribe('XL_NOTIFS_REMOVE', this.handleDispatch);
|
||||||
|
Dispatcher.subscribe('XL_NOTIFS_UPDATE', this.handleDispatch);
|
||||||
|
Dispatcher.subscribe('XL_NOTIFS_ANIMATED', this.handleDispatch);
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._unsubscribe();
|
||||||
|
window.window.removeEventListener('resize', this.handleResizeEvent);
|
||||||
|
Dispatcher.unsubscribe('XL_NOTIFS_DUPLICATE', this.handleDispatch);
|
||||||
|
Dispatcher.unsubscribe('XL_NOTIFS_REMOVE', this.handleDispatch);
|
||||||
|
Dispatcher.unsubscribe('XL_NOTIFS_UPDATE', this.handleDispatch);
|
||||||
|
Dispatcher.unsubscribe('XL_NOTIFS_ANIMATED', this.handleDispatch);
|
||||||
|
}
|
||||||
|
handleDispatch(e) {
|
||||||
|
if (e.type === 'XL_NOTIFS_ANIMATED') this.checkOffScreen();
|
||||||
|
if (e.id !== this.props.id) return;
|
||||||
|
const { content, channelId, loading, progress, color } = e;
|
||||||
|
const { content: curContent, channelId: curChannelId, loading: curLoading, progress: curProgress, color: curColor } = this.state;
|
||||||
|
switch (e.type) {
|
||||||
|
case 'XL_NOTIFS_REMOVE':
|
||||||
|
this.closeNow();
|
||||||
|
break;
|
||||||
|
case 'XL_NOTIFS_DUPLICATE':
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ counter: this.state.counter + 1, resetBar: !!this.props.timeout });
|
||||||
|
break;
|
||||||
|
case 'XL_NOTIFS_UPDATE':
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({
|
||||||
|
content: content || curContent,
|
||||||
|
channelId: channelId || curChannelId,
|
||||||
|
contentParsed: this.parseContent(content || curContent, channelId || curChannelId),
|
||||||
|
loading: typeof loading !== 'undefined' ? loading : curLoading,
|
||||||
|
progress: typeof progress !== 'undefined' ? progress : curProgress,
|
||||||
|
color: color || curColor
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parseContent(content, channelId) {
|
||||||
|
return typeof content === 'string' ? ParsersModule.parseAllowLinks(content, true, { channelId }) : content;
|
||||||
|
}
|
||||||
|
checkOffScreen() {
|
||||||
|
const bcr = this._contentRef.getBoundingClientRect();
|
||||||
|
if (bcr.bottom > Structs.Screen.height) {
|
||||||
|
if (!this.state.offscreen) {
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ offscreen: true });
|
||||||
|
}
|
||||||
|
} else if (this.state.offscreen) {
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ offscreen: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeNow() {
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ closeFast: true });
|
||||||
|
}
|
||||||
|
handleResizeEvent() {
|
||||||
|
if (this._oldOffsetHeight !== this._contentRef.offsetHeight) {
|
||||||
|
this._animationCancel();
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const config = { tension: 125, friction: 20 };
|
||||||
|
if (this._contentRef) this._oldOffsetHeight = this._contentRef.offsetHeight;
|
||||||
|
return React.createElement(
|
||||||
|
ReactSpring.Spring,
|
||||||
|
{
|
||||||
|
native: true,
|
||||||
|
from: { opacity: 0, height: 0, progress: this._initialProgress, loadbrightness: 1 },
|
||||||
|
to: async (next, cancel) => {
|
||||||
|
this.state.leaving = false;
|
||||||
|
this._animationCancel = cancel;
|
||||||
|
if (this.state.offscreen) {
|
||||||
|
if (this.state.closeFast) {
|
||||||
|
this.state.leaving = true;
|
||||||
|
await next({ opacity: 0, height: 0 });
|
||||||
|
api.setState(state => ({ data: state.data.filter(n => n.id !== this.props.id) }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await next({ opacity: 1, height: this._contentRef.offsetHeight, loadbrightness: 1 });
|
||||||
|
if (this.props.timeout) {
|
||||||
|
await next({ progress: 0 });
|
||||||
|
} else {
|
||||||
|
if (this.state.loading && this.state.progress !== -1) {
|
||||||
|
await next({ progress: 0 });
|
||||||
|
} else {
|
||||||
|
await next({ progress: 100 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isSettingHeight = this._ref.offsetHeight !== this._contentRef.offsetHeight;
|
||||||
|
await next({ opacity: 1, height: this._contentRef.offsetHeight });
|
||||||
|
if (isSettingHeight) Dispatcher.dispatch({ type: 'XL_NOTIFS_ANIMATED' });
|
||||||
|
if (this.state.resetBar || this.state.hovered) {
|
||||||
|
await next({ progress: 0 }); /* shit gets reset */
|
||||||
|
this.state.resetBar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.props.timeout && !this.state.closeFast) {
|
||||||
|
if (!this.state.loading) {
|
||||||
|
await next({ progress: 100 });
|
||||||
|
while (!this.state.closeFast) {
|
||||||
|
await next({ loadbrightness: 0.7 });
|
||||||
|
await next({ loadbrightness: 1 });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await next({ loadbrightness: 1 });
|
||||||
|
if (this.state.progress === -1) await next({ progress: 100 });
|
||||||
|
else await next({ progress: this.state.progress });
|
||||||
|
}
|
||||||
|
if (this.state.progress !== 100 || !this.state.loading) return;
|
||||||
|
}
|
||||||
|
if (this.state.hovered && !this.state.closeFast) return;
|
||||||
|
await next({ progress: 100 });
|
||||||
|
this.state.leaving = true;
|
||||||
|
await next({ opacity: 0, height: 0 });
|
||||||
|
api.setState(state => ({ data: state.data.filter(n => n.id !== this.props.id) }));
|
||||||
|
},
|
||||||
|
config: key => {
|
||||||
|
if (key === 'progress') {
|
||||||
|
let duration = this.props.timeout;
|
||||||
|
if (this.state.closeFast || !this.props.timeout || this.state.resetBar || this.state.hovered) duration = 150;
|
||||||
|
if (this.state.offscreen) duration = 0; /* don't animate at all */
|
||||||
|
return { duration };
|
||||||
|
}
|
||||||
|
if (key === 'loadbrightness') return { duration: 750 };
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
return React.createElement(
|
||||||
|
ReactSpring.animated.div,
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
height: e.height,
|
||||||
|
opacity: e.opacity
|
||||||
|
},
|
||||||
|
className: 'xenoLib-notification',
|
||||||
|
ref: e => e && (this._ref = e)
|
||||||
|
},
|
||||||
|
React.createElement(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
className: 'xenoLib-notification-content-wrapper',
|
||||||
|
ref: e => e && (this._contentRef = e),
|
||||||
|
onMouseEnter: e => {
|
||||||
|
if (this.state.leaving || !this.props.timeout) return;
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ hovered: true });
|
||||||
|
},
|
||||||
|
onMouseLeave: e => {
|
||||||
|
if (this.state.leaving || !this.props.timeout) return;
|
||||||
|
this._animationCancel();
|
||||||
|
this.setState({ hovered: false });
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
'--grad-one': this.state.color,
|
||||||
|
'--grad-two': ColorConverter.lightenColor(this.state.color, 20),
|
||||||
|
'--bar-color': ColorConverter.darkenColor(this.state.color, 30)
|
||||||
|
},
|
||||||
|
onClick: e => {
|
||||||
|
if (!this.props.onClick) return;
|
||||||
|
this.props.onClick();
|
||||||
|
this.closeNow();
|
||||||
|
},
|
||||||
|
onContextMenu: e => {
|
||||||
|
if (!this.props.onContext) return;
|
||||||
|
this.props.onContext();
|
||||||
|
this.closeNow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
React.createElement(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
className: 'xenoLib-notification-content'
|
||||||
|
},
|
||||||
|
React.createElement(ReactSpring.animated.div, {
|
||||||
|
className: XenoLib.joinClassNames('xenoLib-notification-loadbar', { 'xenoLib-notification-loadbar-striped': !this.props.timeout && this.state.loading }),
|
||||||
|
style: { right: e.progress.to(e => 100 - e + '%'), filter: e.loadbrightness.to(e => `brightness(${e * 100}%)`) }
|
||||||
|
}),
|
||||||
|
React.createElement(CloseButton, {
|
||||||
|
onClick: e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.closeNow();
|
||||||
|
},
|
||||||
|
onContextMenu: e => {
|
||||||
|
ContextMenuActions.openContextMenu(e, e =>
|
||||||
|
React.createElement(
|
||||||
|
'div',
|
||||||
|
{ className: DiscordClasses.ContextMenu.contextMenu },
|
||||||
|
XenoLib.createContextMenuGroup([
|
||||||
|
XenoLib.createContextMenuItem('Close All', () => {
|
||||||
|
const state = api.getState();
|
||||||
|
state.data.forEach(notif => utils.remove(notif.id));
|
||||||
|
})
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
className: 'xenoLib-notification-close'
|
||||||
|
}),
|
||||||
|
this.state.counter > 1 && BadgesModule.NumberBadge({ count: this.state.counter, className: 'xenLib-notification-counter', color: '#2196f3' }),
|
||||||
|
this.state.contentParsed
|
||||||
|
/* React.createElement('a', { onClick: this.closeNow }, 'close') */
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function NotificationsWrapper(e) {
|
||||||
|
const notifications = useStore(e => {
|
||||||
|
return e.data;
|
||||||
|
});
|
||||||
|
return [
|
||||||
|
/* React.createElement(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
pointerEvents: 'all'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
React.createElement(WebpackModules.getByDisplayName('Backdrop'), {
|
||||||
|
backdropStyle: 'DARK',
|
||||||
|
zIndexBoost: -1000,
|
||||||
|
onClick: e => e.preventDefault()
|
||||||
|
})
|
||||||
|
), */
|
||||||
|
notifications.map(item => React.createElement(Notification, { ...item, key: item.id })).reverse()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
NotificationsWrapper.displayName = 'XenoLibNotifications';
|
||||||
|
const DOMElement = document.createElement('div');
|
||||||
|
DOMElement.className = 'xenoLib-notifications';
|
||||||
|
ReactDOM.render(React.createElement(NotificationsWrapper, {}), DOMElement);
|
||||||
|
document.querySelector('#app-mount').appendChild(DOMElement);
|
||||||
|
} catch (e) {
|
||||||
|
Logger.stacktrace('There has been an error loading the Notifications system', e);
|
||||||
|
}
|
||||||
|
/* NOTIFICATIONS END */
|
||||||
|
|
||||||
global.XenoLib = XenoLib;
|
global.XenoLib = XenoLib;
|
||||||
const listener = e => {
|
const listener = e => {
|
||||||
if (e !== 'XenoLib') return;
|
if (e !== 'XenoLib') return;
|
||||||
|
@ -608,11 +1066,36 @@ var XenoLib = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
XenoLib.changeName(__filename, '1XenoLib'); /* prevent user from changing libs filename */
|
XenoLib.changeName(__filename, '1XenoLib'); /* prevent user from changing libs filename */
|
||||||
|
/*
|
||||||
|
class NotificationPosition extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
return React.createElement('div', {},
|
||||||
|
React.createElement('div', {
|
||||||
|
className: XenoLib.joinClassNames()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
class NotificationPositionField extends Settings.SettingField {
|
||||||
|
constructor(name, note, onChange) {
|
||||||
|
super(name, note, onChange, WebpackModules.getByDisplayName('NotificationSettings'), {
|
||||||
|
position: 'topRight',
|
||||||
|
onChange: (a, b) => console.log(a, b)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return class XenoLib extends Plugin {
|
return class XenoLib extends Plugin {
|
||||||
onStart() {
|
/* buildSetting(data) {
|
||||||
Toasts.info('Starting me does nothing :)');
|
if (data.type === 'position') {
|
||||||
|
return new NotificationPositionField(data.name, data.note, console.log);
|
||||||
}
|
}
|
||||||
|
return super.buildSetting(data);
|
||||||
|
}
|
||||||
|
getSettingsPanel() {
|
||||||
|
return this.buildSettingsPanel().getElement();
|
||||||
|
} */
|
||||||
get name() {
|
get name() {
|
||||||
return config.info.name;
|
return config.info.name;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue