diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/conversation/conversation.vue | 2 | ||||
| -rw-r--r-- | src/components/mobile_nav/mobile_nav.js | 23 | ||||
| -rw-r--r-- | src/components/mobile_nav/mobile_nav.vue | 27 | ||||
| -rw-r--r-- | src/components/notifications/notification_filters.vue | 19 | ||||
| -rw-r--r-- | src/components/notifications/notifications.js | 39 | ||||
| -rw-r--r-- | src/components/notifications/notifications.vue | 22 | ||||
| -rw-r--r-- | src/components/quick_filter_settings/quick_filter_settings.vue | 18 | ||||
| -rw-r--r-- | src/components/quick_view_settings/quick_view_settings.vue | 18 | ||||
| -rw-r--r-- | src/components/timeline/timeline.js | 19 | ||||
| -rw-r--r-- | src/components/timeline/timeline.scss | 16 | ||||
| -rw-r--r-- | src/components/timeline/timeline.vue | 73 | ||||
| -rw-r--r-- | src/panel.scss | 35 |
12 files changed, 224 insertions, 87 deletions
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 61832566..3752e3c1 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -20,10 +20,12 @@ <QuickFilterSettings v-if="!collapsable" :conversation="true" + class="rightside-button" /> <QuickViewSettings v-if="!collapsable" :conversation="true" + class="rightside-button" /> </div> <div class="conversation-body panel-body"> diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index af47f032..fb8ffa30 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -8,13 +8,17 @@ import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, faBell, - faBars + faBars, + faArrowUp, + faMinus } from '@fortawesome/free-solid-svg-icons' library.add( faTimes, faBell, - faBars + faBars, + faArrowUp, + faMinus ) const MobileNav = { @@ -25,12 +29,13 @@ const MobileNav = { }, data: () => ({ notificationsCloseGesture: undefined, - notificationsOpen: false + notificationsOpen: false, + notificationsAtTop: true }), created () { this.notificationsCloseGesture = GestureService.swipeGesture( GestureService.DIRECTION_RIGHT, - this.closeMobileNotifications, + () => this.closeMobileNotifications(true), 50 ) }, @@ -61,12 +66,14 @@ const MobileNav = { openMobileNotifications () { this.notificationsOpen = true }, - closeMobileNotifications () { + closeMobileNotifications (markRead) { 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() + if (markRead) { + this.markNotificationsAsSeen() + } } }, notificationsTouchStart (e) { @@ -78,6 +85,9 @@ const MobileNav = { scrollToTop () { window.scrollTo(0, 0) }, + scrollMobileNotificationsToTop () { + this.$refs.mobileNotifications.scrollTo(0, 0) + }, logout () { this.$router.replace('/main/public') this.$store.dispatch('logout') @@ -87,6 +97,7 @@ const MobileNav = { this.$store.dispatch('markNotificationsAsSeen') }, onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) { + this.notificationsAtTop = scrollTop > 0 if (scrollTop + clientHeight >= scrollHeight) { this.$refs.notifications.fetchOlderNotifications() } diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index 9152879c..264bc713 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -48,19 +48,34 @@ > <div class="mobile-notifications-header"> <span class="title">{{ $t('notifications.notifications') }}</span> - <a - class="mobile-nav-button" - @click.stop.prevent="closeMobileNotifications()" + <span class="spacer"/> + <button + v-if="notificationsAtTop" + class="button-unstyled mobile-nav-button" + @click.stop.prevent="scrollMobileNotificationsToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> + <button + class="button-unstyled mobile-nav-button" + @click.stop.prevent="closeMobileNotifications(true)" > <FAIcon class="fa-scale-110 fa-old-padding" icon="times" /> - </a> + </button> </div> <div id="mobile-notifications" class="mobile-notifications" + ref="mobileNotifications" @scroll="onScroll" /> </div> @@ -165,6 +180,10 @@ box-shadow: 0px 0px 4px rgba(0,0,0,.6); box-shadow: var(--topBarShadow); + .spacer { + flex: 1; + } + .title { font-size: 1.3em; margin-left: 0.6em; diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue index b0213167..1315b51a 100644 --- a/src/components/notifications/notification_filters.vue +++ b/src/components/notifications/notification_filters.vue @@ -109,22 +109,3 @@ export default { } } </script> - -<style lang="scss"> - -.NotificationFilters { - align-self: stretch; - - > button { - line-height: 100%; - height: 100%; - width: var(--__panel-heading-height-inner); - text-align: center; - - svg { - font-size: 1.2em; - } - } -} - -</style> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 0851f407..c3acd9e0 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -10,10 +10,12 @@ import { } from '../../services/notification_utils/notification_utils.js' import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' library.add( - faCircleNotch + faCircleNotch, + faArrowUp, + faMinus ) const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 @@ -34,6 +36,7 @@ const Notifications = { }, data () { return { + showScrollTop: false, bottomedOut: false, // How many seen notifications to display in the list. The more there are, // the heavier the page becomes. This count is increased when loading @@ -90,8 +93,20 @@ const Notifications = { notificationsToDisplay () { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, + noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, ...mapGetters(['unreadChatCount']) }, + mounted () { + this.scrollerRef = this.$refs.root.closest('.column.-scrollable') + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.mobile-notifications') + } + this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) + }, + unmounted () { + if (!this.scrollerRef) return + this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) + }, watch: { unseenCountTitle (count) { if (count > 0) { @@ -101,9 +116,29 @@ const Notifications = { FaviconService.clearFaviconBadge() this.$store.dispatch('setPageTitle', '') } + }, + teleportTarget () { + // handle scroller change + this.$nextTick(() => { + this.scrollerRef.removeEventListener('scroll', this.updateScrollPosition) + this.scrollerRef = this.$refs.root.closest('.column.-scrollable') + if (!this.scrollerRef) { + this.scrollerRef = this.$refs.root.closest('.mobile-notifications') + } + this.scrollerRef.addEventListener('scroll', this.updateScrollPosition) + this.updateScrollPosition() + }) } }, methods: { + scrollToTop () { + const scrollable = this.scrollerRef + scrollable.scrollTo({ top: this.$refs.root.offsetTop }) + // this.$refs.root.scrollIntoView({ behavior: 'smooth', block: 'start' }) + }, + updateScrollPosition () { + this.showScrollTop = this.$refs.root.offsetTop < this.scrollerRef.scrollTop + }, markAsSeen () { this.$store.dispatch('markNotificationsAsSeen') this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index e778e27b..3f4b1b42 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -4,6 +4,7 @@ :to="teleportTarget" > <div + ref="root" :class="{ minimal: minimalMode }" class="Notifications" > @@ -19,14 +20,33 @@ class="badge badge-notification unseen-count" >{{ unseenCount }}</span> </div> + <div + class="rightside-button" + v-if="showScrollTop" + > + <button + class="button-unstyled scroll-to-top-button" + type="button" + @click="scrollToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> + </div> <button v-if="unseenCount" class="button-default read-button" + type="button" @click.prevent="markAsSeen" > {{ $t('notifications.read') }} </button> - <NotificationFilters /> + <NotificationFilters class="rightside-button" /> </div> <div class="panel-body"> <div diff --git a/src/components/quick_filter_settings/quick_filter_settings.vue b/src/components/quick_filter_settings/quick_filter_settings.vue index 982238e7..54ea037e 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.vue +++ b/src/components/quick_filter_settings/quick_filter_settings.vue @@ -87,21 +87,3 @@ </template> <script src="./quick_filter_settings.js"></script> - -<style lang="scss"> - -.QuickFilterSettings { - - > button { - line-height: 100%; - height: 100%; - width: var(--__panel-heading-height-inner); - text-align: center; - - svg { - font-size: 1.2em; - } - } -} - -</style> diff --git a/src/components/quick_view_settings/quick_view_settings.vue b/src/components/quick_view_settings/quick_view_settings.vue index 99b14a66..b0413cac 100644 --- a/src/components/quick_view_settings/quick_view_settings.vue +++ b/src/components/quick_view_settings/quick_view_settings.vue @@ -74,21 +74,3 @@ </template> <script src="./quick_view_settings.js"></script> - -<style lang="scss"> - -.QuickViewSettings { - - > button { - line-height: 100%; - height: 100%; - width: var(--__panel-heading-height-inner); - text-align: center; - - svg { - font-size: 1.2em; - } - } -} - -</style> diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 8f6cae66..f4efc5e8 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -1,4 +1,5 @@ import Status from '../status/status.vue' +import { mapState } from 'vuex' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' @@ -6,11 +7,15 @@ import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings. import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' library.add( faCircleNotch, - faCog + faCog, + faMinus, + faArrowUp, + faCirclePlus, + faCheck ) const Timeline = { @@ -29,6 +34,7 @@ const Timeline = { ], data () { return { + showScrollTop: false, paused: false, unfocused: false, bottomedOut: false, @@ -87,7 +93,10 @@ const Timeline = { }, virtualScrollingEnabled () { return this.$store.getters.mergedConfig.virtualScrolling - } + }, + ...mapState({ + mobileLayout: state => state.interface.layoutType === 'mobile' + }) }, created () { const store = this.$store @@ -123,6 +132,9 @@ const Timeline = { this.$store.commit('setLoading', { timeline: this.timelineName, value: false }) }, methods: { + scrollToTop () { + window.scrollTo({ top: this.$el.offsetTop }) + }, stopBlockingClicks: debounce(function () { this.blockingClicks = false }, 1000), @@ -222,6 +234,7 @@ const Timeline = { } }, handleScroll: throttle(function (e) { + this.showScrollTop = this.$el.offsetTop < window.scrollY this.determineVisibleStatuses() this.scrollLoad(e) }, 200), diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss index 9e009fd3..900bdad3 100644 --- a/src/components/timeline/timeline.scss +++ b/src/components/timeline/timeline.scss @@ -1,8 +1,20 @@ @import '../../_variables.scss'; .Timeline { - .loadmore-text { - opacity: 1; + .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: var(--cGreen); + } + + .loadmore-button { + position: relative } &.-blocked { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index f842240b..77480345 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -5,22 +5,69 @@ v-if="!embedded" :timeline-name="timelineName" /> - <button - v-if="showLoadButton" - class="button-default loadmore-button" - @click.prevent="showNewStatuses" - > - {{ loadButtonString }} - </button> <div - v-else-if="!embedded" - class="loadmore-text faint" - @click.prevent + class="rightside-button" + v-if="showScrollTop && !embedded" > - {{ $t('timeline.up_to_date') }} + <button + class="button-unstyled scroll-to-top-button" + type="button" + @click="scrollToTop" + > + <FALayers class="fa-scale-110 fa-old-padding-layer"> + <FAIcon icon="arrow-up" /> + <FAIcon + icon="minus" + transform="up-7" + /> + </FALayers> + </button> </div> - <QuickFilterSettings v-if="!embedded" /> - <QuickViewSettings v-if="!embedded" /> + <template v-if="mobileLayout && !embedded"> + <div + class="rightside-button" + v-if="showLoadButton" + > + <button + class="button-unstyled loadmore-button" + @click.prevent="showNewStatuses" + > + <FAIcon + fixed-width + icon="circle-plus" + /> + <div class="alert-dot" /> + </button> + </div> + <div + v-else-if="!embedded" + class="loadmore-text faint veryfaint rightside-icon" + @click.prevent + > + <FAIcon + fixed-width + icon="check" + /> + </div> + </template> + <template v-else> + <button + v-if="showLoadButton" + class="button-default loadmore-button" + @click.prevent="showNewStatuses" + > + {{ loadButtonString }} + </button> + <div + v-else-if="!embedded" + class="loadmore-text faint" + @click.prevent + > + {{ $t('timeline.up_to_date') }} + </div> + </template> + <QuickFilterSettings v-if="!embedded" class="rightside-button"/> + <QuickViewSettings v-if="!embedded" class="rightside-button"/> </div> <div :class="classes.body"> <div diff --git a/src/panel.scss b/src/panel.scss index 2e769e27..a53e47c6 100644 --- a/src/panel.scss +++ b/src/panel.scss @@ -45,6 +45,7 @@ .panel-heading, .panel-footer { --panel-heading-height-padding: 0.6em; + --__panel-heading-gap: 0.5em; --__panel-heading-height: 3.2em; --__panel-heading-height-inner: calc(var(--__panel-heading-height) - 2 * var(--panel-heading-height-padding, 0)); @@ -54,7 +55,7 @@ grid-auto-flow: column; grid-template-columns: minmax(50%, 1fr); grid-auto-columns: auto; - grid-column-gap: 0.5em; + grid-column-gap: var(--__panel-heading-gap); flex: none; background-size: cover; padding: var(--panel-heading-height-padding); @@ -195,6 +196,38 @@ } } } + + .rightside-button { + align-self: stretch; + text-align: center; + width: var(--__panel-heading-height); + height: var(--__panel-heading-height); + margin: calc(-1 * var(--panel-heading-height-padding)) 0; + margin-right: calc(-1 * var(--__panel-heading-gap)); + + > button { + box-sizing: border-box; + padding: calc(1 * var(--panel-heading-height-padding)) 0; + height: 100%; + width: 100%; + text-align: center; + + svg { + font-size: 1.2em; + } + } + } + + .rightside-icon { + align-self: stretch; + text-align: center; + width: var(--__panel-heading-height); + margin-right: calc(-1 * var(--__panel-heading-gap)); + + svg { + font-size: 1.2em; + } + } } .panel-footer { |
