123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- <template>
- <div class='notification_button'>
- <div
- class='notification_button__overlay'
- :class='{ "notification_button__overlay--show" : showMenu}'
- @click='setShowMenu(false)'
- ></div>
- <button
- class='button notification_button__button'
- :class='{ "notification_button__button--shake": shake }'
- @click='setShowMenu(!showMenu)'
- >
- <span>Notifications</span>
- <span
- class='notification_button__button__count'
- :class='{
- "notification_button__button__count--none": !unreadCount,
- "notification_button__button__count--two_figure": unreadCount > 9,
- "notification_button__button__count--three_figure": unreadCount > 99
- }'
- >{{unreadCountText}}</span>
- </button>
- <div
- class='notification_button__menu_group'
- :class='{ "notification_button__menu_group--show" : showMenu}'
- >
- <div class='notification_button__big_triangle'></div>
- <div
- class='notification_button__small_triangle'
- :class='{ "notification_button__small_triangle--empty": !notifications.length}'
- ></div>
- <div class='notification_button__menu'>
- <div
- v-for='(notification, index) in notifications'
- class='notification_button__menu__item'
- :class='{
- "notification_button__menu__item--uninteracted": !notification.interacted,
- "notification_button__menu__item--no_border": index > 2
- }'
- @click='click(notification)'
- >
- <template v-if='notification.type === "mention"'>
- <div class='notification_button__menu__item__header'>
- <span>New mention</span>
- <span>
- <span class='notification_button__menu__item__header__date'>{{notification.createdAt | formatDate }}</span>
- <span
- class='notification_button__menu__item__header__close'
- @click.stop='deleteNotification(notification.id)'
- >×</span>
- </span>
- </div>
- <div>
- <span class='notification_button__menu__item__link'>
- {{notification.PostNotification.User.username}}
- </span>
- wrote
- "{{notification.PostNotification.Post.content | stripTags | truncate(50)}}"
- </div>
- </template>
- <template v-if='notification.type === "reply"'>
- <div class='notification_button__menu__item__header'>
- <span>Reply to your post</span>
- <span>
- <span class='notification_button__menu__item__header__date'>{{notification.createdAt | formatDate }}</span>
- <span
- class='notification_button__menu__item__header__close'
- @click.stop='deleteNotification(notification.id)'
- >×</span>
- </span>
- </div>
- <div>
- <span class='notification_button__menu__item__link'>
- {{notification.PostNotification.User.username}}
- </span>
- replied
- "{{notification.PostNotification.Post.content | stripTags | truncate(50)}}"
- </div>
- </template>
- </div>
- <div class='notification_button__menu__empty' v-if='!notifications.length'>
- <span>{{emojis[emojiIndex % 6]}}</span>
- No notifications
- </div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import AjaxErrorHandler from '../assets/js/errorHandler'
- export default {
- name: 'NotificationButton',
- data () {
- return {
- unreadCount: 0,
- notifications: [],
- showMenu: false,
- shake: false,
- emojis: ['😢', '🤷', '😘', '😒', '😔', '💩'],
- emojiIndex: Math.round(Math.random()*5)
- }
- },
- computed: {
- unreadCountText () {
- if(this.unreadCount > 99) {
- return '99+'
- } else {
- return this.unreadCount
- }
- }
- },
- methods: {
- setShowMenu (val) {
- this.showMenu = val
- if(val) {
- this.resetUnreadCount()
- } else {
- setTimeout(_ => {
- this.emojiIndex++
- }, 200)
- }
- },
- getIndexById (id) {
- let index
- this.notifications.forEach((notification, i) => {
- if(notification.id === id) {
- index = i
- }
- })
- return index
- },
- getNotifications () {
- this.axios
- .get('/api/v1/notification')
- .then(res => {
- this.notifications = res.data.Notifications
- this.unreadCount = res.data.unreadCount
- })
- .catch(AjaxErrorHandler(this.$store))
- },
- resetUnreadCount () {
- this.axios
- .put('/api/v1/notification')
- .then(res => {
- this.unreadCount = 0
- })
- .catch(AjaxErrorHandler(this.$store))
- },
- deleteNotification (id) {
- let index = this.getIndexById(id)
- this.axios
- .delete('/api/v1/notification/' + id)
- .then(res => {
- this.notifications.splice(index, 1)
- })
- .catch(AjaxErrorHandler(this.$store))
- },
- setInteracted (id) {
- let index = this.getIndexById(id)
- let item = this.notifications[index]
-
- this.axios
- .put('/api/v1/notification/' + id)
- .then(res => {
- this.$set(
- this.notifications,
- index,
- Object.assign(item, { interacted: true })
- )
- })
- .catch(AjaxErrorHandler(this.$store))
- },
- click (notification) {
- if(!notification.interacted) {
- this.setInteracted(notification.id)
- }
- if(notification.type === 'mention' || notification.type === 'reply') {
- this.$router.push('/p/' + notification.PostNotification.Post.id)
- } else if(notification.type === 'reply') {
- this.$router.push('/p/' + notification.PostNotification.Post.id)
- }
- this.setShowMenu(false)
- }
- },
- created () {
- if(this.$store.state.username) this.getNotifications()
- socket.on('notification', notification => {
- this.unreadCount++
- this.notifications.unshift(notification)
- this.shake = true
- setTimeout(_ => {
- this.shake = false
- }, 1000)
- })
- },
- watch: {
- '$store.state.username': 'getNotifications'
- }
- }
- </script>
- <style lang='scss' scoped>
- @import '../assets/scss/variables.scss';
- @keyframes shake {
- 0% {
- position: relative;
- left: 0;
- }
- 25% {
- position: relative;
- left: -1rem;
- }
- 75% {
- position: relative;
- left: 1rem;
- }
- 100% {
- left: 0rem;
- }
- }
- .notification_button {
- position: relative;
- @at-root #{&}__overlay {
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- position: fixed;
- z-index: 5;
- pointer-events: none;
- @at-root #{&}--show {
- pointer-events: all;
- }
- }
- @at-root #{&}__menu_group {
- position: relative;
- top: -3rem;
- pointer-events: none;
- opacity: 0;
- transition: opacity 0.2s, top 0.2s;
- @at-root #{&}--show {
- pointer-events: all;
- opacity: 1;
- top: -2.5rem;
- }
- }
- @at-root #{&}__big_triangle {
- width: 1rem;
- height: 1rem;
- background-color: #fafafa;
- transform: rotate(45deg);
- position: absolute;
- box-shadow: 5px 5px 10px 0px rgba(0, 0, 0, 0.75);
- top: 2.4rem;
- border-radius: 0.125rem 0 0 0;
- border: 0.125rem solid $color__gray--primary;
- left: calc(50% - 1.414rem /2);
- z-index: 6;
- }
- @at-root #{&}__small_triangle {
- width: 0;
- left: calc(50% - 1.414rem / 2 - .125rem);
- height: 0;
- border-left: 0.625rem solid transparent;
- top: 2.4rem;
- border-right: 0.625rem solid transparent;
- border-bottom: 0.625rem solid #fafafa;
- position: absolute;
- z-index: 8;
- }
- @at-root #{&}__menu {
- left: calc(-50% - 1.25rem);
- position: absolute;
- top: 2.9rem;
- background-color: #fafafa;
- width: 20rem;
- border-radius: 0.25rem;
- border: 0.125rem solid $color__gray--darker;
- box-shadow: 0 7px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
- min-height: 8rem;
- max-height: 15rem;
- overflow-y: auto;
- z-index: 7;
- @at-root #{&}__empty {
- background-color: #fafafa;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 2rem;
- height: 8rem;
- justify-content: center;
- font-size: 1rem;
- user-select: none;
- cursor: default;
- transition: none;
- color: $color__gray--darkest;
- span {
- font-size: 2rem;
- color: $color__gray--darker;
- margin-bottom: 0.5rem;
- }
- }
- @at-root #{&}__item {
- @at-root #{&}--no_border &:last-child {
- border: none;
- }
- padding: 0.5rem;
- border-bottom: thin solid $color__gray--primary;
- cursor: default;
- background-color: #fff;
- transition: background-color 0.2s;
- &:hover {
- background-color: $color__lightgray--primary;
- }
- @at-root #{&}--uninteracted {
- background-color: rgba(13, 71, 161, 0.1);
- border-bottom-color: $color__gray--darkest;
- &:hover {
- background-color: rgba(13, 71, 161, 0.2);
- }
- }
-
- @at-root #{&}__link {
- font-weight: 400;
- cursor: pointer;
- }
- @at-root #{&}__header {
- display: flex;
- justify-content: space-between;
- font-size: 0.9rem;
- @at-root #{&}__date {
- color: $color__text--secondary;
- }
- @at-root #{&}__close {
- background-color: $color__gray--darkest;
- height: 0.9rem;
- width: 0.9rem;
- cursor: pointer;
- display: inline-flex;
- border-radius: 100%;
- margin-left: 0.25rem;
- align-items: center;
- justify-content: center;
- padding: 0;
- color: #fff;
- position: relative;
- top: 0.0625rem;
- line-height: 1;
- transition: all 0.2s;
- &:hover {
- filter: brightness(0.9);
- }
- }
- }
- }
- }
- @at-root #{&}__button {
- position: relative;
- padding-right: 2.5rem;
- @at-root #{&}--shake {
- animation-name: shake;
- animation-iteration-count: 4;
- animation-duration: 0.25s;
- animation-timing-function: ease-in-out;
- }
- @at-root #{&}__count {
- position: absolute;
- background-color: $color__blue--primary;
- line-height: 1;
- margin-left: 0.25rem;
- color: #fff;
- top: 0.35rem;
- right: 0.5rem;
- border-radius: 100%;
- height: 1rem;
- width: 1rem;
- display: inline-flex;
- align-items: center;
- padding: 0.75rem;
- font-size: 0.9rem;
- justify-content: center;
- transition: all 0.2s;
- @at-root #{&}--none {
- background-color: rgba(white, 0.75);
- font-weight: 300;
- color: initial;
- border: 0.0125rem solid transparent;
- background-color: $color__gray--primary;
- padding: calc(0.75rem - 4*0.0125rem);
- }
- @at-root #{&}--two_figure {
- font-size: 0.8rem;
- }
- @at-root #{&}--three_figure {
- font-size: 0.7rem;
- }
- }
- }
- }
- </style>
|