Merge branch 'toasts' into discord-api

This commit is contained in:
Samuel Elliott 2018-07-20 12:04:51 +01:00
commit 5301498978
18 changed files with 382 additions and 4 deletions

View File

@ -8,7 +8,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { DOM, BdUI, BdMenu, Modals, Reflection } from 'ui';
import { DOM, BdUI, BdMenu, Modals, Reflection, Toasts } from 'ui';
import BdCss from './styles/index.scss';
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi } from 'modules';
import { ClientLogger as Logger, ClientIPC, Utils, IterableWeakCollections, IterableWeakMap, IterableWeakSet } from 'common';
@ -27,7 +27,7 @@ class BetterDiscord {
Logger.log('main', 'BetterDiscord starting');
this._bd = {
DOM, BdUI, BdMenu, Modals, Reflection,
DOM, BdUI, BdMenu, Modals, Reflection, Toasts,
Events, CssEditor, Globals, Settings, Database, Updater,
ModuleManager, PluginManager, ThemeManager, ExtModuleManager,

View File

@ -10,7 +10,7 @@
import { EmoteModule } from 'builtin';
import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs';
import { BdMenu, Modals, DOM, DOMObserver, Reflection, VueInjector } from 'ui';
import { BdMenu, Modals, DOM, DOMObserver, Reflection, VueInjector, Toasts } from 'ui';
import * as CommonComponents from 'commoncomponents';
import { Utils, Filters, ClientLogger as Logger, ClientIPC, AsyncEventEmitter } from 'common';
import Settings from './settings';
@ -305,6 +305,36 @@ export default class PluginApi {
});
}
/**
* Toasts
*/
showToast(message, options = {}) {
return Toasts.push(message, options);
}
showSuccessToast(message, options = {}) {
return Toasts.success(message, options);
}
showInfoToast(message, options = {}) {
return Toasts.info(message, options);
}
showErrorToast(message, options = {}) {
return Toasts.error(message, options);
}
showWarningToast(message, options = {}) {
return Toasts.warning(message, options);
}
get Toasts() {
return {
push: this.showToast.bind(this),
success: this.showSuccessToast.bind(this),
error: this.showErrorToast.bind(this),
info: this.showInfoToast.bind(this),
warning: this.showWarningToast.bind(this)
};
}
/**
* Emotes
*/

View File

@ -8,6 +8,7 @@
* LICENSE file in the root directory of this source tree.
*/
import { Toasts } from 'ui';
import { EmoteModule } from 'builtin';
import { SettingsSet } from 'structs';
import { FileUtils, ClientLogger as Logger } from 'common';
@ -28,6 +29,7 @@ export default new class Settings {
Logger.log('Settings', [`${set.id}/${category.id}/${setting.id} was changed from`, old_value, 'to', value]);
Events.emit('setting-updated', event);
Events.emit(`setting-updated-${set.id}_${category.id}_${setting.id}`, event);
Toasts.success(`${set.id}/${category.id}/${setting.id} was changed from ${old_value} to ${value}`); // Just for debugging purposes remove in prod
});
set.on('settings-updated', async event => {

View File

@ -49,6 +49,30 @@
}
}
@keyframes bd-toast-up {
0% {
transform: translateY(10px);
opacity: 0;
}
100% {
transform: translateY(0%);
opacity: 1;
}
}
@keyframes bd-toast-down {
0% {
transform: translateY(0%);
opacity: 1;
}
100% {
transform: translateY(10px);
opacity: 0;
}
}
@keyframes bd-fade-out {
0% {
opacity: 1;

View File

@ -13,3 +13,4 @@
@import './helpers.scss';
@import './misc.scss';
@import './emotes.scss';
@import './toasts.scss';

View File

@ -0,0 +1,68 @@
.bd-toasts {
display: flex;
position: fixed;
top: 0;
width: 700px;
left: 50%;
transform: translateX(-50%);
bottom: 100px;
flex-direction: column;
align-items: center;
justify-content: flex-end;
pointer-events: none;
z-index: 4000;
.bd-toast {
position: relative;
animation: bd-toast-up 300ms ease;
background: #36393F;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 0 1px rgba(32,34,37,.6), 0 2px 10px 0 rgba(0,0,0,.2);
font-weight: 500;
color: #fff;
user-select: text;
font-size: 14px;
margin-top: 10px;
&.bd-toast-error {
background: #f04747;
}
&.bd-toast-info {
background: #4a90e2;
}
&.bd-toast-warning {
background: #FFA600;
}
&.bd-toast-success {
background: #43b581;
}
&.bd-toast-has-icon {
padding-left: 30px;
}
}
.bd-toast-icon {
position: absolute;
left: 5px;
top: 50%;
transform: translateY(-50%);
bottom: 0;
height: 20px;
width: 20px;
border-radius: 50%;
overflow: hidden;
svg {
fill: white;
}
}
.bd-toast.bd-toast-closing {
animation: bd-toast-down 300ms ease;
}
}

View File

@ -12,7 +12,7 @@ import { Events, DiscordApi } from 'modules';
import { remote } from 'electron';
import DOM from './dom';
import Vue from './vue';
import { BdSettingsWrapper, BdModals } from './components';
import { BdSettingsWrapper, BdModals, BdToasts } from './components';
export default class {
@ -43,8 +43,15 @@ export default class {
static injectUi() {
DOM.createElement('div', null, 'bd-settings').appendTo(DOM.bdBody);
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
DOM.createElement('div', null, 'bd-toasts').appendTo(DOM.bdToasts);
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
this.toasts = new Vue({
el: '#bd-toasts',
components: { BdToasts },
template: '<BdToasts />'
});
this.modals = new Vue({
el: '#bd-modals',
components: { BdModals },

View File

@ -0,0 +1,32 @@
/**
* BetterDiscord Toasts Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<div class="bd-toasts">
<Toast v-for="(toast, index) in toasts" :message="toast.message" :type="toast.type" :icon="toast.icon" :class="toast.additionalClasses" :closing="toast.closing" :key="toast.id"></Toast>
</div>
</template>
<script>
// Imports
import { Toasts } from 'ui';
import { Toast } from './common';
export default {
components: {
Toast
},
data() {
return {
toasts: Toasts.stack
};
}
}
</script>

View File

@ -14,3 +14,6 @@ export { default as MiExtension } from './materialicons/Extension.vue';
export { default as MiError } from './materialicons/Error.vue';
export { default as MiDiscord } from './materialicons/Discord.vue';
export { default as MiStar } from './materialicons/Star.vue';
export { default as MiInfo } from './materialicons/Info.vue';
export { default as MiWarning } from './materialicons/Warning.vue';
export { default as MiSuccess } from './materialicons/Success.vue';

View File

@ -0,0 +1,46 @@
/**
* BetterDiscord Toast Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
<template>
<div :class="['bd-toast', 'bd-toast-' + type, {'bd-toast-has-icon': type !== 'basic' || icon, 'bd-toast-closing': closing}]">
<div class="bd-toast-icon" v-if="type !== 'basic' || icon">
<img v-if="icon" :src="icon" width="20" height="20" />
<MiSuccess v-else-if="type === 'success'" size="20" />
<MiWarning v-else-if="type === 'warning'" size="20" />
<MiInfo v-else-if="type === 'info'" size="20" />
<MiError v-else-if="type === 'error'" size="20" />
</div>
<div class="bd-toast-text">
{{message}}
</div>
</div>
</template>
<script>
// Imports
import { MiSuccess, MiWarning, MiError, MiInfo } from './MaterialIcon';
export default {
props: {
message: String,
icon: String,
type: {
default: 'basic',
validator(value) {
return ['success', 'warning', 'error', 'info', 'basic'].indexOf(value) !== -1
}
},
closing: {type: Boolean, default: false}
},
components: {
MiSuccess, MiWarning, MiError, MiInfo
}
}
</script>

View File

@ -4,6 +4,7 @@ export { default as FormButton } from './FormButton.vue';
export { default as ButtonGroup } from './ButtonGroup.vue';
export { default as Button } from './Button.vue';
export { default as Modal } from './Modal.vue';
export { default as Toast } from './Toast.vue';
export * from './MaterialIcon';
export { default as RefreshBtn } from './RefreshBtn.vue';

View File

@ -0,0 +1,28 @@
/**
* BetterDiscord Material Design Icon
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Material Design Icons
* Copyright (c) 2014 Google
* Apache 2.0 LICENSE
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
<template>
<span class="bd-material-design-icon">
<svg :width="size || 24" :height="size || 24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
</svg>
</span>
</template>
<script>
export default {
props: ['size']
}
</script>

View File

@ -0,0 +1,28 @@
/**
* BetterDiscord Material Design Icon
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Material Design Icons
* Copyright (c) 2014 Google
* Apache 2.0 LICENSE
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
<template>
<span class="bd-material-design-icon">
<svg :width="size || 24" :height="size || 24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
</span>
</template>
<script>
export default {
props: ['size']
}
</script>

View File

@ -0,0 +1,28 @@
/**
* BetterDiscord Material Design Icon
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Material Design Icons
* Copyright (c) 2014 Google
* Apache 2.0 LICENSE
* https://www.apache.org/licenses/LICENSE-2.0.txt
*/
<template>
<span class="bd-material-design-icon">
<svg :width="size || 24" :height="size || 24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>
</svg>
</span>
</template>
<script>
export default {
props: ['size']
}
</script>

View File

@ -1,3 +1,4 @@
export { default as BdSettingsWrapper } from './BdSettingsWrapper.vue';
export { default as BdSettings } from './BdSettings.vue';
export { default as BdModals } from './BdModals.vue';
export { default as BdToasts } from './BdToasts.vue';

View File

@ -184,6 +184,9 @@ export default class DOM {
static get bdThemes() { return this.getElement('bd-themes') || this.createElement('bd-themes').appendTo(this.bdHead) }
static get bdTooltips() { return this.getElement('bd-tooltips') || this.createElement('bd-tooltips').appendTo(this.bdBody) }
static get bdModals() { return this.getElement('bd-modals') || this.createElement('bd-modals').appendTo(this.bdBody) }
static get bdToasts() {
return this.getElement('bd-toasts') || this.createElement('bd-toasts').appendTo(this.bdBody);
}
static getElement(e) {
if (e instanceof BdNode) return e.element;

75
client/src/ui/toasts.js Normal file
View File

@ -0,0 +1,75 @@
/**
* BetterDiscord Toasts
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
let toasts = 0;
export default class Toasts {
/**
* This shows a popup message at the bottom of the screen similar to Android Toasts. This is useful for small user feedback.
*
* @param {string} message The message to be displayed in the toast
* @param {Object} options Options object. Optional parameter.
* @param {string} options.type Changes the type of the toast stylistically and semantically. Choices: "basic", "info", "success", "error", "warning". Default: "basic"
* @param {string} options.icon URL to custom icon to show in the toast. Having this overrides the default icon for the toast type.
* @param {string|Object|Array} options.additionalClasses Additional classes to add to the toast element. Can be used to style it. Optional.
* @param {number} options.timeout Adjusts the time (in ms) the toast should be shown for before disappearing automatically. Default: 3000
* @returns {Promise} This promise resolves when the toast is removed from the DOM.
*/
static async push(message, options = {}) {
const {type = 'basic', icon, additionalClasses, timeout = 3000} = options;
const toast = {id: toasts++, message, type, icon, additionalClasses, closing: false};
this.stack.push(toast);
await new Promise(resolve => setTimeout(resolve, timeout));
toast.closing = true;
await new Promise(resolve => setTimeout(resolve, 300));
this.stack.splice(this.stack.indexOf(toast), 1);
}
/**
* This is a shortcut for `type = "success"` in {@link Toasts#push}. The parameters and options are the same.
*/
static async success(message, options = {}) {
options.type = 'success';
return this.push(message, options);
}
/**
* This is a shortcut for `type = "error"` in {@link Toasts#push}. The parameters and options are the same.
*/
static async error(message, options = {}) {
options.type = 'error';
return this.push(message, options);
}
/**
* This is a shortcut for `type = "info"` in {@link Toasts#push}. The parameters and options are the same.
*/
static async info(message, options = {}) {
options.type = 'info';
return this.push(message, options);
}
/**
* This is a shortcut for `type = "warning"` in {@link Toasts#push}. The parameters and options are the same.
*/
static async warning(message, options = {}) {
options.type = 'warning';
return this.push(message, options);
}
/**
* An array of active toasts.
*/
static get stack() {
return this._stack || (this._stack = []);
}
}

View File

@ -2,6 +2,7 @@ export { default as DOM, DOMObserver, DOMManip } from './dom';
export { default as BdUI } from './bdui';
export { default as BdMenu, BdMenuItems } from './bdmenu';
export { default as Modals } from './modals';
export { default as Toasts } from './toasts';
export { default as VueInjector } from './vueinjector';
export { default as Reflection } from './reflection';