summaryrefslogtreecommitdiff
path: root/src/server/web/app/desktop/views/components/ui.header.notifications.vue
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/web/app/desktop/views/components/ui.header.notifications.vue')
-rw-r--r--src/server/web/app/desktop/views/components/ui.header.notifications.vue158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/server/web/app/desktop/views/components/ui.header.notifications.vue b/src/server/web/app/desktop/views/components/ui.header.notifications.vue
new file mode 100644
index 0000000000..e829418d18
--- /dev/null
+++ b/src/server/web/app/desktop/views/components/ui.header.notifications.vue
@@ -0,0 +1,158 @@
+<template>
+<div class="notifications">
+ <button :data-active="isOpen" @click="toggle" title="%i18n:desktop.tags.mk-ui-header-notifications.title%">
+ %fa:R bell%<template v-if="hasUnreadNotifications">%fa:circle%</template>
+ </button>
+ <div class="pop" v-if="isOpen">
+ <mk-notifications/>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import contains from '../../../common/scripts/contains';
+
+export default Vue.extend({
+ data() {
+ return {
+ isOpen: false,
+ hasUnreadNotifications: false,
+ connection: null,
+ connectionId: null
+ };
+ },
+ mounted() {
+ if ((this as any).os.isSignedIn) {
+ this.connection = (this as any).os.stream.getConnection();
+ this.connectionId = (this as any).os.stream.use();
+
+ this.connection.on('read_all_notifications', this.onReadAllNotifications);
+ this.connection.on('unread_notification', this.onUnreadNotification);
+
+ // Fetch count of unread notifications
+ (this as any).api('notifications/get_unread_count').then(res => {
+ if (res.count > 0) {
+ this.hasUnreadNotifications = true;
+ }
+ });
+ }
+ },
+ beforeDestroy() {
+ if ((this as any).os.isSignedIn) {
+ this.connection.off('read_all_notifications', this.onReadAllNotifications);
+ this.connection.off('unread_notification', this.onUnreadNotification);
+ (this as any).os.stream.dispose(this.connectionId);
+ }
+ },
+ methods: {
+ onReadAllNotifications() {
+ this.hasUnreadNotifications = false;
+ },
+
+ onUnreadNotification() {
+ this.hasUnreadNotifications = true;
+ },
+
+ toggle() {
+ this.isOpen ? this.close() : this.open();
+ },
+
+ open() {
+ this.isOpen = true;
+ Array.from(document.querySelectorAll('body *')).forEach(el => {
+ el.addEventListener('mousedown', this.onMousedown);
+ });
+ },
+
+ close() {
+ this.isOpen = false;
+ Array.from(document.querySelectorAll('body *')).forEach(el => {
+ el.removeEventListener('mousedown', this.onMousedown);
+ });
+ },
+
+ onMousedown(e) {
+ e.preventDefault();
+ if (!contains(this.$el, e.target) && this.$el != e.target) this.close();
+ return false;
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.notifications
+
+ > button
+ display block
+ margin 0
+ padding 0
+ width 32px
+ color #9eaba8
+ border none
+ background transparent
+ cursor pointer
+
+ *
+ pointer-events none
+
+ &:hover
+ &[data-active='true']
+ color darken(#9eaba8, 20%)
+
+ &:active
+ color darken(#9eaba8, 30%)
+
+ > [data-fa].bell
+ font-size 1.2em
+ line-height 48px
+
+ > [data-fa].circle
+ margin-left -5px
+ vertical-align super
+ font-size 10px
+ color $theme-color
+
+ > .pop
+ display block
+ position absolute
+ top 56px
+ right -72px
+ width 300px
+ background #fff
+ border-radius 4px
+ box-shadow 0 1px 4px rgba(0, 0, 0, 0.25)
+
+ &:before
+ content ""
+ pointer-events none
+ display block
+ position absolute
+ top -28px
+ right 74px
+ border-top solid 14px transparent
+ border-right solid 14px transparent
+ border-bottom solid 14px rgba(0, 0, 0, 0.1)
+ border-left solid 14px transparent
+
+ &:after
+ content ""
+ pointer-events none
+ display block
+ position absolute
+ top -27px
+ right 74px
+ border-top solid 14px transparent
+ border-right solid 14px transparent
+ border-bottom solid 14px #fff
+ border-left solid 14px transparent
+
+ > .mk-notifications
+ max-height 350px
+ font-size 1rem
+ overflow auto
+
+</style>