Merge remote-tracking branch 'upstream/feature/mobile-improvements-3' into shigusegubu
* upstream/feature/mobile-improvements-3: add gesture to close notifications drawer make notifications close on navigation make mobile notifs drawer more like an actual drawer, make mobile nav buttons bigger to press, fix alert dot remove notifications from sidebar, make notifications appear on login only make nav bar slide, move mobile post status to mobile nav more work with notifications drawer start working on one tap notifications
This commit is contained in:
commit
ea9adb3c16
24 changed files with 284 additions and 77 deletions
26
src/App.js
26
src/App.js
|
|
@ -9,7 +9,7 @@ import ChatPanel from './components/chat_panel/chat_panel.vue'
|
|||
import MediaModal from './components/media_modal/media_modal.vue'
|
||||
import SideDrawer from './components/side_drawer/side_drawer.vue'
|
||||
import MobilePostStatusModal from './components/mobile_post_status_modal/mobile_post_status_modal.vue'
|
||||
import { unseenNotificationsFromStore } from './services/notification_utils/notification_utils'
|
||||
import MobileNav from './components/mobile_nav/mobile_nav.vue'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
|
@ -24,7 +24,8 @@ export default {
|
|||
ChatPanel,
|
||||
MediaModal,
|
||||
SideDrawer,
|
||||
MobilePostStatusModal
|
||||
MobilePostStatusModal,
|
||||
MobileNav
|
||||
},
|
||||
data: () => ({
|
||||
mobileActivePanel: 'timeline',
|
||||
|
|
@ -40,6 +41,10 @@ export default {
|
|||
created () {
|
||||
// Load the locale from the storage
|
||||
this.$i18n.locale = this.$store.state.config.interfaceLanguage
|
||||
window.addEventListener('resize', this.updateMobileState)
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.updateMobileState)
|
||||
},
|
||||
computed: {
|
||||
currentUser () { return this.$store.state.users.currentUser },
|
||||
|
|
@ -82,13 +87,8 @@ export default {
|
|||
chat () { return this.$store.state.chat.channel.state === 'joined' },
|
||||
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
||||
showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
unseenNotifications () {
|
||||
return unseenNotificationsFromStore(this.$store)
|
||||
},
|
||||
unseenNotificationsCount () {
|
||||
return this.unseenNotifications.length
|
||||
},
|
||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }
|
||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
||||
isMobileLayout () { return this.$store.state.interface.mobileLayout }
|
||||
},
|
||||
methods: {
|
||||
scrollToTop () {
|
||||
|
|
@ -101,8 +101,12 @@ export default {
|
|||
onFinderToggled (hidden) {
|
||||
this.finderHidden = hidden
|
||||
},
|
||||
toggleMobileSidebar () {
|
||||
this.$refs.sideDrawer.toggleDrawer()
|
||||
updateMobileState () {
|
||||
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
|
||||
const changed = width <= 800 !== this.isMobileLayout
|
||||
if (changed) {
|
||||
this.$store.dispatch('setMobileLayout', width <= 800)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
src/App.scss
32
src/App.scss
|
|
@ -484,24 +484,6 @@ nav {
|
|||
}
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
display: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alert-dot {
|
||||
border-radius: 100%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
left: calc(50% - 4px);
|
||||
top: calc(50% - 4px);
|
||||
margin-left: 6px;
|
||||
margin-top: -6px;
|
||||
background-color: $fallback--cRed;
|
||||
background-color: var(--badgeNotification, $fallback--cRed);
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .2s
|
||||
}
|
||||
|
|
@ -530,20 +512,6 @@ nav {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.panel-switcher {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
flex: 1;
|
||||
max-height: 32px;
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 800px) {
|
||||
body {
|
||||
overflow-y: scroll;
|
||||
|
|
|
|||
11
src/App.vue
11
src/App.vue
|
|
@ -1,17 +1,14 @@
|
|||
<template>
|
||||
<div id="app" v-bind:style="bgAppStyle">
|
||||
<div class="app-bg-wrapper" v-bind:style="bgStyle"></div>
|
||||
<nav class='nav-bar container' @click="scrollToTop()" id="nav">
|
||||
<MobileNav v-if="isMobileLayout" />
|
||||
<nav v-else class='nav-bar container' @click="scrollToTop()" id="nav">
|
||||
<div class='logo' :style='logoBgStyle'>
|
||||
<div class='mask' :style='logoMaskStyle'></div>
|
||||
<img :src='logo' :style='logoStyle'>
|
||||
</div>
|
||||
<div class='inner-nav'>
|
||||
<div class='item'>
|
||||
<a href="#" class="menu-button" @click.stop.prevent="toggleMobileSidebar()">
|
||||
<i class="button-icon icon-menu"></i>
|
||||
<div class="alert-dot" v-if="unseenNotificationsCount"></div>
|
||||
</a>
|
||||
<router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link>
|
||||
</div>
|
||||
<div class='item right'>
|
||||
|
|
@ -22,8 +19,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
<div v-if="" class="container" id="content">
|
||||
<side-drawer ref="sideDrawer" :logout="logout"></side-drawer>
|
||||
<div class="sidebar-flexer mobile-hidden">
|
||||
<div class="sidebar-flexer mobile-hidden" v-if="!isMobileLayout">
|
||||
<div class="sidebar-bounds">
|
||||
<div class="sidebar-scroller">
|
||||
<div class="sidebar">
|
||||
|
|
@ -50,7 +46,6 @@
|
|||
<media-modal></media-modal>
|
||||
</div>
|
||||
<chat-panel :floating="true" v-if="currentUser && chat" class="floating-chat mobile-hidden"></chat-panel>
|
||||
<MobilePostStatusModal />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -230,6 +230,9 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
|
||||
store.dispatch('setMobileLayout', width <= 800)
|
||||
|
||||
const apiConfig = await getStatusnetConfig({ store })
|
||||
const staticConfig = await getStaticConfig()
|
||||
await setSettings({ store, apiConfig, staticConfig })
|
||||
|
|
|
|||
77
src/components/mobile_nav/mobile_nav.js
Normal file
77
src/components/mobile_nav/mobile_nav.js
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import SideDrawer from '../side_drawer/side_drawer.vue'
|
||||
import Notifications from '../notifications/notifications.vue'
|
||||
import MobilePostStatusModal from '../mobile_post_status_modal/mobile_post_status_modal.vue'
|
||||
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
||||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
|
||||
const MobileNav = {
|
||||
components: {
|
||||
SideDrawer,
|
||||
Notifications,
|
||||
MobilePostStatusModal
|
||||
},
|
||||
data: () => ({
|
||||
notificationsCloseGesture: undefined,
|
||||
notificationsOpen: false
|
||||
}),
|
||||
created () {
|
||||
this.notificationsCloseGesture = GestureService.swipeGesture(
|
||||
GestureService.DIRECTION_RIGHT,
|
||||
this.closeMobileNotifications,
|
||||
50
|
||||
)
|
||||
},
|
||||
computed: {
|
||||
currentUser () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
unseenNotifications () {
|
||||
return unseenNotificationsFromStore(this.$store)
|
||||
},
|
||||
unseenNotificationsCount () {
|
||||
return this.unseenNotifications.length
|
||||
},
|
||||
sitename () { return this.$store.state.instance.name }
|
||||
},
|
||||
methods: {
|
||||
toggleMobileSidebar () {
|
||||
this.$refs.sideDrawer.toggleDrawer()
|
||||
},
|
||||
openMobileNotifications () {
|
||||
this.notificationsOpen = true
|
||||
},
|
||||
closeMobileNotifications () {
|
||||
if (this.notificationsOpen) {
|
||||
// make sure to mark notifs seen only when the notifs were open and not
|
||||
// from close-calls.
|
||||
this.notificationsOpen = false
|
||||
this.markNotificationsAsSeen()
|
||||
}
|
||||
},
|
||||
notificationsTouchStart (e) {
|
||||
GestureService.beginSwipe(e, this.notificationsCloseGesture)
|
||||
},
|
||||
notificationsTouchMove (e) {
|
||||
GestureService.updateSwipe(e, this.notificationsCloseGesture)
|
||||
},
|
||||
scrollToTop () {
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
logout () {
|
||||
this.$router.replace('/main/public')
|
||||
this.$store.dispatch('logout')
|
||||
},
|
||||
markNotificationsAsSeen () {
|
||||
this.$refs.notifications.markAsSeen()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route () {
|
||||
// handles closing notificaitons when you press any router-link on the
|
||||
// notifications.
|
||||
this.closeMobileNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MobileNav
|
||||
140
src/components/mobile_nav/mobile_nav.vue
Normal file
140
src/components/mobile_nav/mobile_nav.vue
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<nav class='nav-bar container' id="nav">
|
||||
<div class='mobile-inner-nav' @click="scrollToTop()">
|
||||
<div class='item'>
|
||||
<a href="#" class="mobile-nav-button" @click.stop.prevent="toggleMobileSidebar()">
|
||||
<i class="button-icon icon-menu"></i>
|
||||
</a>
|
||||
<router-link class="site-name" :to="{ name: 'root' }" active-class="home">{{sitename}}</router-link>
|
||||
</div>
|
||||
<div class='item right'>
|
||||
<a class="mobile-nav-button" v-if="currentUser" href="#" @click.stop.prevent="openMobileNotifications()">
|
||||
<i class="button-icon icon-bell-alt"></i>
|
||||
<div class="alert-dot" v-if="unseenNotificationsCount"></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<SideDrawer ref="sideDrawer" :logout="logout"/>
|
||||
<div v-if="currentUser"
|
||||
class="mobile-notifications-drawer"
|
||||
:class="{ 'closed': !notificationsOpen }"
|
||||
@touchstart="notificationsTouchStart"
|
||||
@touchmove="notificationsTouchMove"
|
||||
>
|
||||
<div class="mobile-notifications-header">
|
||||
<span class="title">{{$t('notifications.notifications')}}</span>
|
||||
<a class="mobile-nav-button" @click.stop.prevent="closeMobileNotifications()">
|
||||
<i class="button-icon icon-cancel"/>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="currentUser" class="mobile-notifications">
|
||||
<Notifications ref="notifications" noHeading="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<MobilePostStatusModal />
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script src="./mobile_nav.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.mobile-inner-nav {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mobile-nav-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.alert-dot {
|
||||
border-radius: 100%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
left: calc(50% - 4px);
|
||||
top: calc(50% - 4px);
|
||||
margin-left: 6px;
|
||||
margin-top: -6px;
|
||||
background-color: $fallback--cRed;
|
||||
background-color: var(--badgeNotification, $fallback--cRed);
|
||||
}
|
||||
|
||||
.mobile-notifications-drawer {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
|
||||
box-shadow: var(--panelShadow);
|
||||
transition-property: transform;
|
||||
transition-duration: 0.25s;
|
||||
transform: translateX(0);
|
||||
|
||||
&.closed {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-notifications-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
position: absolute;
|
||||
color: var(--topBarText);
|
||||
background-color: $fallback--fg;
|
||||
background-color: var(--topBar, $fallback--fg);
|
||||
box-shadow: 0px 0px 4px rgba(0,0,0,.6);
|
||||
box-shadow: var(--topBarShadow);
|
||||
|
||||
.title {
|
||||
font-size: 1.3em;
|
||||
margin-left: 0.6em;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-notifications {
|
||||
margin-top: 50px;
|
||||
width: 100vw;
|
||||
height: calc(100vh - 50px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
background-color: $fallback--bg;
|
||||
background-color: var(--bg, $fallback--bg);
|
||||
|
||||
.notifications {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
.panel {
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.panel:after {
|
||||
border-radius: 0;
|
||||
}
|
||||
.panel .panel-heading {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -7,6 +7,9 @@ import {
|
|||
} from '../../services/notification_utils/notification_utils.js'
|
||||
|
||||
const Notifications = {
|
||||
props: [
|
||||
'noHeading'
|
||||
],
|
||||
created () {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="notifications">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div v-if="!noHeading" class="panel-heading">
|
||||
<div class="title">
|
||||
{{$t('notifications.notifications')}}
|
||||
<span class="badge badge-notification unseen-count" v-if="unseenCount">{{unseenCount}}</span>
|
||||
|
|
|
|||
|
|
@ -21,11 +21,6 @@
|
|||
{{ $t("login.login") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser" @click="toggleDrawer">
|
||||
<router-link :to="{ name: 'notifications', params: { username: currentUser.screen_name } }">
|
||||
{{ $t("notifications.notifications") }} {{ unseenNotificationsCount > 0 ? `(${unseenNotificationsCount})` : '' }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser" @click="toggleDrawer">
|
||||
<router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }">
|
||||
{{ $t("nav.dms") }}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ const defaultState = {
|
|||
window.CSS.supports('filter', 'drop-shadow(0 0)') ||
|
||||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
|
||||
)
|
||||
}
|
||||
},
|
||||
mobileLayout: false
|
||||
}
|
||||
|
||||
const interfaceMod = {
|
||||
|
|
@ -31,6 +32,9 @@ const interfaceMod = {
|
|||
},
|
||||
setNotificationPermission (state, permission) {
|
||||
state.notificationPermission = permission
|
||||
},
|
||||
setMobileLayout (state, value) {
|
||||
state.mobileLayout = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
@ -42,6 +46,10 @@ const interfaceMod = {
|
|||
},
|
||||
setNotificationPermission ({ commit }, permission) {
|
||||
commit('setNotificationPermission', permission)
|
||||
},
|
||||
setMobileLayout ({ commit }, value) {
|
||||
console.log('setMobileLayout called')
|
||||
commit('setMobileLayout', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue