aboutsummaryrefslogtreecommitdiff
path: root/src/components/timeline
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/timeline')
-rw-r--r--src/components/timeline/timeline.js35
-rw-r--r--src/components/timeline/timeline.scss31
-rw-r--r--src/components/timeline/timeline.vue91
-rw-r--r--src/components/timeline/timeline_quick_settings.js67
-rw-r--r--src/components/timeline/timeline_quick_settings.vue109
5 files changed, 136 insertions, 197 deletions
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index c575e876..b7414610 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -1,15 +1,21 @@
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'
-import TimelineQuickSettings from './timeline_quick_settings.vue'
+import QuickFilterSettings from '../quick_filter_settings/quick_filter_settings.vue'
+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 = {
@@ -18,6 +24,7 @@ const Timeline = {
'timelineName',
'title',
'userId',
+ 'listId',
'tag',
'embedded',
'count',
@@ -27,6 +34,7 @@ const Timeline = {
],
data () {
return {
+ showScrollTop: false,
paused: false,
unfocused: false,
bottomedOut: false,
@@ -38,7 +46,8 @@ const Timeline = {
Status,
Conversation,
TimelineMenu,
- TimelineQuickSettings
+ QuickFilterSettings,
+ QuickViewSettings
},
computed: {
filteredVisibleStatuses () {
@@ -60,6 +69,13 @@ const Timeline = {
return `${this.$t('timeline.show_new')} (${this.newStatusCount})`
}
},
+ mobileLoadButtonString () {
+ if (this.timeline.flushMarker !== 0) {
+ return '+'
+ } else {
+ return this.newStatusCount > 99 ? '∞' : this.newStatusCount
+ }
+ },
classes () {
let rootClasses = !this.embedded ? ['panel', 'panel-default'] : ['-nonpanel']
if (this.blockingClicks) rootClasses = rootClasses.concat(['-blocked', '_misclick-prevention'])
@@ -84,7 +100,10 @@ const Timeline = {
},
virtualScrollingEnabled () {
return this.$store.getters.mergedConfig.virtualScrolling
- }
+ },
+ ...mapState({
+ mobileLayout: state => state.interface.layoutType === 'mobile'
+ })
},
created () {
const store = this.$store
@@ -101,6 +120,7 @@ const Timeline = {
timeline: this.timelineName,
showImmediately,
userId: this.userId,
+ listId: this.listId,
tag: this.tag
})
},
@@ -119,6 +139,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),
@@ -156,6 +179,7 @@ const Timeline = {
older: true,
showImmediately: true,
userId: this.userId,
+ listId: this.listId,
tag: this.tag
}).then(({ statuses }) => {
if (statuses && statuses.length === 0) {
@@ -217,6 +241,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..c6fb1ca7 100644
--- a/src/components/timeline/timeline.scss
+++ b/src/components/timeline/timeline.scss
@@ -1,8 +1,35 @@
@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(--badgeNeutral);
+ }
+
+ .alert-badge {
+ font-size: 0.75em;
+ line-height: 1;
+ text-align: right;
+ border-radius: var(--tooltipRadius);
+ position: absolute;
+ left: calc(50% - 0.5em);
+ top: calc(50% - 0.4em);
+ padding: 0.2em;
+ margin-left: 0.7em;
+ margin-top: -1em;
+ background-color: var(--badgeNeutral);
+ color: var(--badgeNeutralText);
+ }
+
+ .loadmore-button {
+ position: relative;
}
&.-blocked {
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index f65881b6..877a0cc0 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -1,31 +1,90 @@
<template>
<div :class="['Timeline', classes.root]">
<div :class="classes.header">
- <TimelineMenu v-if="!embedded" />
- <button
- v-if="showLoadButton"
- class="button-default loadmore-button"
- @click.prevent="showNewStatuses"
- >
- {{ loadButtonString }}
- </button>
+ <TimelineMenu
+ v-if="!embedded"
+ :timeline-name="timelineName"
+ />
<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"
+ :title="$t('general.scroll_to_top')"
+ @click="scrollToTop"
+ >
+ <FALayers class="fa-scale-110 fa-old-padding-layer">
+ <FAIcon icon="arrow-up" />
+ <FAIcon
+ icon="minus"
+ transform="up-7"
+ />
+ </FALayers>
+ </button>
</div>
- <TimelineQuickSettings v-if="!embedded" />
+ <template v-if="mobileLayout && !embedded">
+ <div
+ class="rightside-button"
+ v-if="showLoadButton"
+ >
+ <button
+ class="button-unstyled loadmore-button"
+ :title="loadButtonString"
+ @click.prevent="showNewStatuses"
+ >
+ <FAIcon
+ fixed-width
+ icon="circle-plus"
+ />
+ <div class="alert-badge">
+ {{ mobileLoadButtonString }}
+ </div>
+ </button>
+ </div>
+ <div
+ v-else-if="!embedded"
+ class="loadmore-text faint veryfaint rightside-icon"
+ :title="$t('timeline.up_to_date')"
+ :aria-disabled="true"
+ @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
ref="timeline"
class="timeline"
+ role="feed"
>
<conversation
v-for="statusId in filteredPinnedStatusIds"
:key="statusId + '-pinned'"
+ role="listitem"
class="status-fadein"
:status-id="statusId"
:collapsable="true"
@@ -36,6 +95,7 @@
<conversation
v-for="status in filteredVisibleStatuses"
:key="status.id"
+ role="listitem"
class="status-fadein"
:status-id="status.id"
:collapsable="true"
@@ -46,7 +106,10 @@
</div>
</div>
<div :class="classes.footer">
- <teleport :to="footerSlipgate" :disabled="!embedded || !footerSlipgate">
+ <teleport
+ :to="footerSlipgate"
+ :disabled="!embedded || !footerSlipgate"
+ >
<div
v-if="count===0"
class="new-status-notification text-center faint"
diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js
deleted file mode 100644
index 92d5ac14..00000000
--- a/src/components/timeline/timeline_quick_settings.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import Popover from '../popover/popover.vue'
-import { mapGetters } from 'vuex'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
-
-library.add(
- faFilter,
- faFont,
- faWrench
-)
-
-const TimelineQuickSettings = {
- components: {
- Popover
- },
- methods: {
- setReplyVisibility (visibility) {
- this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility })
- this.$store.dispatch('queueFlushAll')
- },
- openTab (tab) {
- this.$store.dispatch('openSettingsModalTab', tab)
- }
- },
- computed: {
- ...mapGetters(['mergedConfig']),
- loggedIn () {
- return !!this.$store.state.users.currentUser
- },
- replyVisibilitySelf: {
- get () { return this.mergedConfig.replyVisibility === 'self' },
- set () { this.setReplyVisibility('self') }
- },
- replyVisibilityFollowing: {
- get () { return this.mergedConfig.replyVisibility === 'following' },
- set () { this.setReplyVisibility('following') }
- },
- replyVisibilityAll: {
- get () { return this.mergedConfig.replyVisibility === 'all' },
- set () { this.setReplyVisibility('all') }
- },
- hideMedia: {
- get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv },
- set () {
- const value = !this.hideMedia
- this.$store.dispatch('setOption', { name: 'hideAttachments', value })
- this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
- }
- },
- hideMutedPosts: {
- get () { return this.mergedConfig.hideFilteredStatuses },
- set () {
- const value = !this.hideMutedPosts
- this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
- }
- },
- muteBotStatuses: {
- get () { return this.mergedConfig.muteBotStatuses },
- set () {
- const value = !this.muteBotStatuses
- this.$store.dispatch('setOption', { name: 'muteBotStatuses', value })
- }
- }
- }
-}
-
-export default TimelineQuickSettings
diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue
deleted file mode 100644
index 98fab926..00000000
--- a/src/components/timeline/timeline_quick_settings.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<template>
- <Popover
- trigger="click"
- class="TimelineQuickSettings"
- :bound-to="{ x: 'container' }"
- >
- <template v-slot:content>
- <div class="dropdown-menu">
- <div v-if="loggedIn">
- <button
- class="button-default dropdown-item"
- @click="replyVisibilityAll = true"
- >
- <span
- class="menu-checkbox -radio"
- :class="{ 'menu-checkbox-checked': replyVisibilityAll }"
- />{{ $t('settings.reply_visibility_all') }}
- </button>
- <button
- class="button-default dropdown-item"
- @click="replyVisibilityFollowing = true"
- >
- <span
- class="menu-checkbox -radio"
- :class="{ 'menu-checkbox-checked': replyVisibilityFollowing }"
- />{{ $t('settings.reply_visibility_following_short') }}
- </button>
- <button
- class="button-default dropdown-item"
- @click="replyVisibilitySelf = true"
- >
- <span
- class="menu-checkbox -radio"
- :class="{ 'menu-checkbox-checked': replyVisibilitySelf }"
- />{{ $t('settings.reply_visibility_self_short') }}
- </button>
- <div
- role="separator"
- class="dropdown-divider"
- />
- </div>
- <button
- class="button-default dropdown-item"
- @click="muteBotStatuses = !muteBotStatuses"
- >
- <span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-checked': muteBotStatuses }"
- />{{ $t('settings.mute_bot_posts') }}
- </button>
- <button
- class="button-default dropdown-item"
- @click="hideMedia = !hideMedia"
- >
- <span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-checked': hideMedia }"
- />{{ $t('settings.hide_media_previews') }}
- </button>
- <button
- class="button-default dropdown-item"
- @click="hideMutedPosts = !hideMutedPosts"
- >
- <span
- class="menu-checkbox"
- :class="{ 'menu-checkbox-checked': hideMutedPosts }"
- />{{ $t('settings.hide_all_muted_posts') }}
- </button>
- <button
- class="button-default dropdown-item dropdown-item-icon"
- @click="openTab('filtering')"
- >
- <FAIcon icon="font" />{{ $t('settings.word_filter') }}
- </button>
- <button
- class="button-default dropdown-item dropdown-item-icon"
- @click="openTab('general')"
- >
- <FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
- </button>
- </div>
- </template>
- <template v-slot:trigger>
- <button class="button-unstyled">
- <FAIcon icon="filter" />
- </button>
- </template>
- </Popover>
-</template>
-
-<script src="./timeline_quick_settings.js"></script>
-
-<style lang="scss">
-
-.TimelineQuickSettings {
-
- > button {
- line-height: 100%;
- height: 100%;
- width: var(--__panel-heading-height-inner);
- text-align: center;
-
- svg {
- font-size: 1.2em;
- }
- }
-}
-
-</style>