add toasts

This commit is contained in:
Zack Rauen 2018-05-12 23:17:04 -04:00
parent 6b517adbe8
commit 2d784b48f4
18 changed files with 370 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, ReactAutoPatcher, DiscordApi } from 'modules';
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
@ -26,7 +26,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, Reflection } from 'ui';
import { BdMenu, Modals, DOM, Reflection, Toasts } from 'ui';
import { Utils, ClientLogger as Logger, ClientIPC, AsyncEventEmitter } from 'common';
import Settings from './settings';
import ExtModuleManager from './extmodulemanager';
@ -289,6 +289,27 @@ 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);
}
/**
* 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, SettingUpdatedEvent } from 'structs';
import { Utils, 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,69 @@
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

@ -14,7 +14,7 @@ import { remote } from 'electron';
import DOM from './dom';
import Vue from './vue';
import AutoManip from './automanip';
import { BdSettingsWrapper, BdModals } from './components';
import { BdSettingsWrapper, BdModals, BdToasts } from './components';
export default class {
@ -68,6 +68,13 @@ export default class {
DOM.createElement('div', null, 'bd-settings').appendTo(DOM.bdBody);
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
DOM.createElement('bd-toasts').appendTo(DOM.bdBody);
const toasts = new Vue({
el: 'bd-toasts',
components: { BdToasts },
template: '<BdToasts/>'
});
this.modals = new Vue({
el: '#bd-modals',

View File

@ -0,0 +1,31 @@
/**
* BetterDiscord Modals 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>
<bd-toasts>
<Toast v-for="(toast, index) in toasts" :message="toast.message" :type="toast.type" :icon="toast.icon" :closing="toast.closing" :key="`${toast.id}`"></Toast>
</bd-toasts>
</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 Modal 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-has-icon': type != 'basic' || icon}, 'bd-toast-' + type, {'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: function (value) {
return ['success', 'warning', 'error', 'info', 'basic'].indexOf(value) !== -1
}
},
closing: {type: Boolean, default: false}
},
components: {
MiSuccess, MiWarning, MiError, MiInfo
}
}
</script>

View File

@ -4,4 +4,5 @@ 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';

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

@ -130,6 +130,9 @@ export default class DOM {
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;

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

@ -0,0 +1,72 @@
/**
* BetterDiscord Modals
* 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.
*/
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 {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, timeout = 3000} = options;
const toast = {id: Math.random(), message, type, icon, 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

@ -3,5 +3,6 @@ export { default as BdUI } from './bdui';
export { default as VueInjector } from './vueinjector';
export { default as BdMenu, BdMenuItems } from './bdmenu';
export { default as Modals } from './modals';
export { default as Toasts } from './toasts';
export { default as ProfileBadges } from './profilebadges';
export { default as Reflection } from './reflection';