Skip to content

Commit 0a706d1

Browse files
committed
Add custom flash library that works as expected
1 parent bab2081 commit 0a706d1

File tree

13 files changed

+233
-37
lines changed

13 files changed

+233
-37
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
"vue-template-compiler": "^2.5.16",
3939
"vueify-insert-css": "^1.0.0",
4040
"vuex": "^3.0.1",
41-
"vuex-flash": "^1.0.0",
4241
"vuex-i18n": "^1.10.5",
4342
"webpack": "^4.4.1",
4443
"webpack-stream": "^4.0.3",

resources/assets/scripts/app.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import Vue from 'vue';
22
import Vuex from 'vuex';
33
import vuexI18n from 'vuex-i18n';
4-
import VuexFlash from 'vuex-flash';
54
import { createFlashStore } from 'vuex-flash';
65
import VueRouter from 'vue-router';
76

87
// Helpers
98
import { Ziggy } from './helpers/ziggy';
109
import Locales from './../../../resources/lang/locales';
10+
import { flash } from './mixins/flash';
1111

1212
// Base Vuejs Templates
1313
import Login from './components/auth/Login';
1414
import ResetPassword from './components/auth/ResetPassword';
1515

16-
// Used for the route() helper.
16+
window.events = new Vue;
1717
window.Ziggy = Ziggy;
1818

1919
Vue.use(Vuex);
@@ -23,10 +23,10 @@ const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default;
2323

2424
Vue.config.productionTip = false;
2525
Vue.mixin({ methods: { route } });
26+
Vue.mixin(flash);
2627

2728
Vue.use(VueRouter);
2829
Vue.use(vuexI18n.plugin, store);
29-
Vue.use(VuexFlash, { mixin: true, template: require('./components/errors/Flash.template') });
3030

3131
Vue.i18n.add('en', Locales.en);
3232
Vue.i18n.set('en');
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<template>
2+
<div v-if="notifications.length > 0">
3+
<transition-group tag="div" class="mb-2" name="fade">
4+
<div :class="item.class" class="lg:inline-flex mb-2" role="alert" :key="index" v-for="(item, index) in notifications">
5+
<span class="title" v-html="item.title" v-if="item.title.length > 0"></span>
6+
<span class="message" v-html="item.message"></span>
7+
</div>
8+
</transition-group>
9+
</div>
10+
</template>
11+
12+
<script>
13+
export default {
14+
name: 'flash',
15+
props: {
16+
timeout: {
17+
type: Number,
18+
default: 0,
19+
},
20+
types: {
21+
type: Object,
22+
default: function () {
23+
return {
24+
base: 'alert',
25+
success: 'alert success',
26+
info: 'alert info',
27+
warning: 'alert warning',
28+
error: 'alert error',
29+
}
30+
}
31+
}
32+
},
33+
34+
data: function () {
35+
return {
36+
notifications: [],
37+
};
38+
},
39+
40+
/**
41+
* Listen for flash events.
42+
*/
43+
created: function () {
44+
const self = this;
45+
window.events.$on('flash', function (data) {
46+
self.flash(data.message, data.title, data.severity);
47+
});
48+
49+
window.events.$on('clear-flashes', function () {
50+
self.clear();
51+
});
52+
},
53+
54+
methods: {
55+
/**
56+
* Flash a message to the screen when a flash event is emitted over
57+
* the global event stream.
58+
*
59+
* @param {string} message
60+
* @param {string} title
61+
* @param {string} severity
62+
*/
63+
flash: function (message, title, severity) {
64+
this.notifications.push({
65+
message, severity, title, class: this.types[severity] || this.types.base,
66+
});
67+
68+
if (this.timeout > 0) {
69+
setTimeout(this.hide, this.timeout);
70+
}
71+
},
72+
73+
/**
74+
* Clear all of the flash messages from the screen.
75+
*/
76+
clear: function () {
77+
this.notifications = [];
78+
window.events.$emit('flashes-cleared');
79+
},
80+
81+
/**
82+
* Hide a notification after a given amount of time.
83+
*
84+
* @param {int} item
85+
*/
86+
hide: function (item = this.notifications[0]) {
87+
let key = this.notifications.indexOf(item);
88+
this.notifications.splice(key, 1);
89+
},
90+
}
91+
};
92+
</script>

resources/assets/scripts/components/auth/ForgotPassword.vue

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
<template>
22
<div>
3-
<div class="pb-4" v-for="error in errors">
4-
<div class="p-2 bg-red-dark border-red-darker border items-center text-red-lightest leading-normal rounded flex lg:inline-flex w-full text-sm"
5-
role="alert">
6-
<span class="flex rounded-full bg-red uppercase px-2 py-1 text-xs font-bold mr-3 leading-none">Error</span>
7-
<span class="mr-2 text-left flex-auto">{{ error }}</span>
8-
</div>
9-
</div>
103
<form class="bg-white shadow-lg rounded-lg pt-10 px-8 pb-6 mb-4 animate fadein" method="post"
114
v-on:submit.prevent="submitForm"
125
>
@@ -70,13 +63,14 @@
7063
this.$data.showSpinner = true;
7164
this.$data.errors = [];
7265
66+
this.clearFlashes();
7367
window.axios.post(this.route('auth.forgot-password'), {
7468
email: this.$props.email,
7569
})
7670
.then(function (response) {
7771
self.$data.submitDisabled = false;
7872
self.$data.showSpinner = false;
79-
self.flash({message: response.data.status, variant: 'success'});
73+
self.success(response.data.status);
8074
self.$router.push({name: 'login'});
8175
})
8276
.catch(function (err) {
@@ -87,7 +81,9 @@
8781
8882
const response = err.response;
8983
if (response.data && _.isObject(response.data.errors)) {
90-
self.$data.errors.push(response.data.errors[0].detail);
84+
response.data.errors.forEach(function (error) {
85+
self.error(error.detail);
86+
});
9187
}
9288
});
9389
}

resources/assets/scripts/components/auth/Login.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<template>
22
<div>
3+
<flash/>
34
<login-form
45
v-if="this.$route.name === 'login'"
56
v-bind:user="user"
@@ -15,8 +16,9 @@
1516
</template>
1617

1718
<script>
18-
import LoginForm from "./LoginForm";
19+
import Flash from '../Flash';
1920
import ForgotPassword from "./ForgotPassword";
21+
import LoginForm from "./LoginForm";
2022
import TwoFactorForm from "./TwoFactorForm";
2123
2224
export default {
@@ -34,6 +36,7 @@
3436
},
3537
},
3638
components: {
39+
Flash,
3740
TwoFactorForm,
3841
ForgotPassword,
3942
LoginForm,

resources/assets/scripts/components/auth/LoginForm.vue

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
<template>
22
<div>
3-
<flash-message variant="danger" />
4-
<flash-message variant="success" />
5-
<div class="pb-4" v-for="error in errors">
6-
<div class="p-2 bg-red-dark border-red-darker border items-center text-red-lightest leading-normal rounded flex lg:inline-flex w-full text-sm"
7-
role="alert">
8-
<span class="flex rounded-full bg-red uppercase px-2 py-1 text-xs font-bold mr-3 leading-none">Error</span>
9-
<span class="mr-2 text-left flex-auto">{{ error }}</span>
10-
</div>
11-
</div>
123
<form class="bg-white shadow-lg rounded-lg pt-10 px-8 pb-6 mb-4 animate fadein" method="post"
134
v-on:submit.prevent="submitForm"
145
>
@@ -84,6 +75,7 @@
8475
const self = this;
8576
this.$data.showSpinner = true;
8677
78+
this.clearFlashes();
8779
axios.post(this.route('auth.login'), {
8880
user: this.$props.user.email,
8981
password: this.$props.user.password,
@@ -106,7 +98,9 @@
10698
10799
const response = err.response;
108100
if (response.data && _.isObject(response.data.errors)) {
109-
self.$data.errors = [response.data.errors[0].detail];
101+
response.data.errors.forEach(function (error) {
102+
self.error(error.detail);
103+
});
110104
self.$refs.password.focus();
111105
}
112106
});

resources/assets/scripts/components/auth/ResetPassword.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
<template>
22
<div>
3-
<div class="pb-4" v-for="error in errors">
4-
<div class="p-2 bg-red-dark border-red-darker border items-center text-red-lightest leading-normal rounded flex lg:inline-flex w-full text-sm"
5-
role="alert">
6-
<span class="flex rounded-full bg-red uppercase px-2 py-1 text-xs font-bold mr-3 leading-none">Error</span>
7-
<span class="mr-2 text-left flex-auto">{{ error }}</span>
8-
</div>
9-
</div>
103
<form class="bg-white shadow-lg rounded-lg pt-10 px-8 pb-6 mb-4 animate fadein" method="post"
114
v-on:submit.prevent="submitForm"
125
>
@@ -90,6 +83,7 @@
9083
const self = this;
9184
this.$data.showSpinner = true;
9285
86+
this.clearFlashes();
9387
window.axios.post(this.route('auth.reset-password'), {
9488
email: this.$props.email,
9589
password: this.$data.password,
@@ -107,7 +101,9 @@
107101
108102
const response = err.response;
109103
if (response.data && _.isObject(response.data.errors)) {
110-
self.$data.errors = [response.data.errors[0].detail];
104+
response.data.errors.forEach(function (error) {
105+
self.error(error.detail);
106+
});
111107
self.$refs.password.focus();
112108
}
113109
});

resources/assets/scripts/components/auth/TwoFactorForm.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
submitToken: function () {
4444
const self = this;
4545
46+
self.clearFlashes();
4647
axios.post(this.route('auth.login-checkpoint'), {
4748
confirmation_token: this.$route.query.token,
4849
authentication_code: this.$data.code,
@@ -57,7 +58,9 @@
5758
5859
const response = err.response;
5960
if (response.data && _.isObject(response.data.errors)) {
60-
self.flash({message: response.data.errors[0].detail, variant: 'danger'});
61+
response.data.errors.forEach(function (error) {
62+
self.error(error.detail);
63+
});
6164
self.$router.push({ name: 'login' });
6265
}
6366
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
export const flash = {
2+
methods: {
3+
/**
4+
* Flash a message to the event stream in the browser.
5+
*
6+
* @param {string} message
7+
* @param {string} title
8+
* @param {string} severity
9+
*/
10+
flash: function (message, title, severity = 'info') {
11+
severity = severity || 'info';
12+
if (['danger', 'fatal', 'error'].includes(severity)) {
13+
severity = 'error';
14+
}
15+
16+
window.events.$emit('flash', { message, title, severity });
17+
},
18+
19+
/**
20+
* Clear all of the flash messages from the screen.
21+
*/
22+
clearFlashes: function () {
23+
window.events.$emit('clear-flashes');
24+
},
25+
26+
/**
27+
* Helper function to flash a normal success message to the user.
28+
*
29+
* @param {string} message
30+
*/
31+
success: function (message) {
32+
this.flash(message, 'Success', 'success');
33+
},
34+
35+
/**
36+
* Helper function to flash a normal info message to the user.
37+
*
38+
* @param {string} message
39+
*/
40+
info: function (message) {
41+
this.flash(message, 'Info', 'info');
42+
},
43+
44+
/**
45+
* Helper function to flash a normal warning message to the user.
46+
*
47+
* @param {string} message
48+
*/
49+
warning: function (message) {
50+
this.flash(message, 'Warning', 'warning');
51+
},
52+
53+
/**
54+
* Helper function to flash a normal error message to the user.
55+
*
56+
* @param {string} message
57+
*/
58+
error: function (message) {
59+
this.flash(message, 'Error', 'danger');
60+
},
61+
}
62+
};

resources/assets/styles/components/animations.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
}
55
}
66

7+
.fade-enter-active {
8+
animation: fadein 500ms;
9+
}
10+
11+
.fade-leave-active {
12+
animation: fadein 500ms reverse;
13+
}
14+
715
@keyframes fadein {
816
from { opacity: 0; }
917
to { opacity: 1; }

0 commit comments

Comments
 (0)