diff --git a/src/client/app/desktop/views/components/ui.sidebar.vue b/src/client/app/desktop/views/components/ui.sidebar.vue index 8e586eb259..a411080734 100644 --- a/src/client/app/desktop/views/components/ui.sidebar.vue +++ b/src/client/app/desktop/views/components/ui.sidebar.vue @@ -23,28 +23,43 @@ <div> <a @click="drive">%fa:cloud%</a> </div> - <div> - <router-link to="/i/favorites">%fa:star%</router-link> - </div> - <div v-if="($store.state.i.isLocked || $store.state.i.carefulBot)"> - <a @click="followRequests">%fa:envelope R%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></a> + <div ref="notificationsButton" :class="{ active: showNotifications }" style="z-index:1;"> + <a @click="notifications">%fa:R bell%</a> </div> <div> <a @click="settings">%fa:cog%</a> </div> - <div> - <a @click="dark"><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></a> - </div> - <div class="signout"> - <a @click="signout">%fa:power-off%</a> - </div> </div> <div class="account"> <router-link :to="`/@${ $store.state.i.username }`"> <mk-avatar class="avatar" :user="$store.state.i"/> </router-link> + + <div class="nav menu"> + <div class="signout"> + <a @click="signout">%fa:power-off%</a> + </div> + <div> + <router-link to="/i/favorites">%fa:star%</router-link> + </div> + <div v-if="($store.state.i.isLocked || $store.state.i.carefulBot)"> + <a @click="followRequests">%fa:envelope R%<i v-if="$store.state.i.pendingReceivedFollowRequestsCount">{{ $store.state.i.pendingReceivedFollowRequestsCount }}</i></a> + </div> + </div> </div> + + <div class="nav dark"> + <div> + <a @click="dark"><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></a> + </div> + </div> + + <transition name="slide"> + <div class="notifications" v-if="showNotifications" ref="notifications"> + <mk-notifications/> + </div> + </transition> </div> </template> @@ -56,12 +71,14 @@ import MkSettingsWindow from './settings-window.vue'; import MkDriveWindow from './drive-window.vue'; import MkMessagingWindow from './messaging-window.vue'; import MkGameWindow from './game-window.vue'; +import contains from '../../../common/scripts/contains'; export default Vue.extend({ data() { return { hasGameInvitations: false, - connection: null + connection: null, + showNotifications: false }; }, @@ -130,6 +147,37 @@ export default Vue.extend({ (this as any).os.signout(); }, + notifications() { + this.showNotifications ? this.closeNotifications() : this.openNotifications(); + }, + + openNotifications() { + this.showNotifications = true; + Array.from(document.querySelectorAll('body *')).forEach(el => { + el.addEventListener('mousedown', this.onMousedown); + }); + }, + + closeNotifications() { + this.showNotifications = false; + Array.from(document.querySelectorAll('body *')).forEach(el => { + el.removeEventListener('mousedown', this.onMousedown); + }); + }, + + onMousedown(e) { + e.preventDefault(); + if ( + !contains(this.$refs.notifications, e.target) && + this.$refs.notifications != e.target && + !contains(this.$refs.notificationsButton, e.target) && + this.$refs.notificationsButton != e.target + ) { + this.closeNotifications(); + } + return false; + }, + dark() { this.$store.commit('device/set', { key: 'darkmode', @@ -157,7 +205,7 @@ export default Vue.extend({ width $width height 100% background var(--desktopHeaderBg) - box-shadow var(--shadow) + box-shadow var(--shadowRight) &.left left 0 @@ -165,28 +213,6 @@ export default Vue.extend({ &.right right 0 - > .nav - > * - &.active - box-shadow -4px 0 var(--primary) inset - - > * - display block - width $width - line-height 52px - text-align center - font-size 18px - color var(--desktopHeaderFg) - - &:hover - color var(--desktopHeaderHoverFg) - text-decoration none - - > .nav.bottom - position absolute - bottom 64px - left 0 - > .post width $width height $width @@ -219,15 +245,52 @@ export default Vue.extend({ background var(--primaryDarken10) !important transition background 0s ease + .nav + > * + &.active + box-shadow -4px 0 var(--primary) inset + + > * + display block + width $width + line-height 52px + text-align center + font-size 18px + color var(--desktopHeaderFg) + + &:hover + background rgba(0, 0, 0, 0.05) + color var(--desktopHeaderHoverFg) + text-decoration none + + &:active + background rgba(0, 0, 0, 0.1) + + > .nav.bottom + position absolute + bottom 128px + left 0 + > .account position absolute - bottom 0 + bottom 64px left 0 width $width height $width padding 14px - > * + > .menu + display none + position absolute + bottom 64px + left 0 + background var(--desktopHeaderBg) + + &:hover + > .menu + display block + + > *:not(.menu) display block width 100% height 100% @@ -237,4 +300,30 @@ export default Vue.extend({ width 100% height 100% + > .dark + position absolute + bottom 0 + left 0 + width $width + height $width + + > .notifications + position fixed + top 0 + left $width + width 350px + height 100% + overflow auto + background var(--face) + box-shadow var(--shadowRight) + +.slide-enter-active, +.slide-leave-active { + transition: all 0.2s ease; +} + +.slide-enter, .slide-leave-to { + transform: translateX(-16px); + opacity: 0; +} </style> diff --git a/src/client/app/init.ts b/src/client/app/init.ts index ad9909374a..d5faf4557c 100644 --- a/src/client/app/init.ts +++ b/src/client/app/init.ts @@ -124,11 +124,14 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API) //#region shadow const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)'; + const shadowRight = '4px 0 4px rgba(0, 0, 0, 0.1)'; if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow); + if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadowRight', shadowRight); os.store.watch(s => { return s.settings.useShadow; }, v => { document.documentElement.style.setProperty('--shadow', v ? shadow : 'none'); + document.documentElement.style.setProperty('--shadowRight', v ? shadowRight : 'none'); }); //#endregion