Merge branch '564' into 'develop'

Restore scroll position for user profile when navigate back and forward

Closes #636 and #564

See merge request pleroma/pleroma-fe!914
This commit is contained in:
Shpuld Shpludson 2019-08-17 08:12:21 +00:00
commit e5fe4f420c
7 changed files with 89 additions and 74 deletions

View file

@ -13,8 +13,8 @@ const Interactions = {
} }
}, },
methods: { methods: {
onModeSwitch (index, dataset) { onModeSwitch (key) {
this.filterMode = tabModeDict[dataset.filter] this.filterMode = tabModeDict[key]
} }
}, },
components: { components: {

View file

@ -10,18 +10,15 @@
:on-switch="onModeSwitch" :on-switch="onModeSwitch"
> >
<span <span
data-tab-dummy key="mentions"
data-filter="mentions"
:label="$t('nav.mentions')" :label="$t('nav.mentions')"
/> />
<span <span
data-tab-dummy key="likes+repeats"
data-filter="likes+repeats"
:label="$t('interactions.favs_repeats')" :label="$t('interactions.favs_repeats')"
/> />
<span <span
data-tab-dummy key="follows"
data-filter="follows"
:label="$t('interactions.follows')" :label="$t('interactions.follows')"
/> />
</tab-switcher> </tab-switcher>

View file

@ -75,8 +75,8 @@ const Search = {
const length = this[tabName].length const length = this[tabName].length
return length === 0 ? '' : ` (${length})` return length === 0 ? '' : ` (${length})`
}, },
onResultTabSwitch (_index, dataset) { onResultTabSwitch (key) {
this.currenResultTab = dataset.filter this.currenResultTab = key
}, },
getActiveTab () { getActiveTab () {
if (this.visibleStatuses.length > 0) { if (this.visibleStatuses.length > 0) {

View file

@ -31,21 +31,18 @@
<tab-switcher <tab-switcher
ref="tabSwitcher" ref="tabSwitcher"
:on-switch="onResultTabSwitch" :on-switch="onResultTabSwitch"
:custom-active="currenResultTab" :active-tab="currenResultTab"
> >
<span <span
data-tab-dummy key="statuses"
data-filter="statuses"
:label="$t('user_card.statuses') + resultCount('visibleStatuses')" :label="$t('user_card.statuses') + resultCount('visibleStatuses')"
/> />
<span <span
data-tab-dummy key="people"
data-filter="people"
:label="$t('search.people') + resultCount('users')" :label="$t('search.people') + resultCount('users')"
/> />
<span <span
data-tab-dummy key="hashtags"
data-filter="hashtags"
:label="$t('search.hashtags') + resultCount('hashtags')" :label="$t('search.hashtags') + resultCount('hashtags')"
/> />
</tab-switcher> </tab-switcher>

View file

@ -4,12 +4,22 @@ import './tab_switcher.scss'
export default Vue.component('tab-switcher', { export default Vue.component('tab-switcher', {
name: 'TabSwitcher', name: 'TabSwitcher',
props: ['renderOnlyFocused', 'onSwitch', 'customActive'], props: ['renderOnlyFocused', 'onSwitch', 'activeTab'],
data () { data () {
return { return {
active: this.$slots.default.findIndex(_ => _.tag) active: this.$slots.default.findIndex(_ => _.tag)
} }
}, },
computed: {
activeIndex () {
// In case of controlled component
if (this.activeTab) {
return this.$slots.default.findIndex(slot => this.activeTab === slot.key)
} else {
return this.active
}
}
},
beforeUpdate () { beforeUpdate () {
const currentSlot = this.$slots.default[this.active] const currentSlot = this.$slots.default[this.active]
if (!currentSlot.tag) { if (!currentSlot.tag) {
@ -17,21 +27,13 @@ export default Vue.component('tab-switcher', {
} }
}, },
methods: { methods: {
activateTab (index, dataset) { activateTab (index) {
return () => { return () => {
if (typeof this.onSwitch === 'function') { if (typeof this.onSwitch === 'function') {
this.onSwitch.call(null, index, this.$slots.default[index].elm.dataset) this.onSwitch.call(null, this.$slots.default[index].key)
} }
this.active = index this.active = index
} }
},
isActiveTab (index) {
const customActiveIndex = this.$slots.default.findIndex(slot => {
const dataFilter = slot.data && slot.data.attrs && slot.data.attrs['data-filter']
return this.customActive && this.customActive === dataFilter
})
return customActiveIndex > -1 ? customActiveIndex === index : index === this.active
} }
}, },
render (h) { render (h) {
@ -41,13 +43,13 @@ export default Vue.component('tab-switcher', {
const classesTab = ['tab'] const classesTab = ['tab']
const classesWrapper = ['tab-wrapper'] const classesWrapper = ['tab-wrapper']
if (this.isActiveTab(index)) { if (this.activeIndex === index) {
classesTab.push('active') classesTab.push('active')
classesWrapper.push('active') classesWrapper.push('active')
} }
if (slot.data.attrs.image) { if (slot.data.attrs.image) {
return ( return (
<div class={ classesWrapper.join(' ')}> <div class={classesWrapper.join(' ')}>
<button <button
disabled={slot.data.attrs.disabled} disabled={slot.data.attrs.disabled}
onClick={this.activateTab(index)} onClick={this.activateTab(index)}
@ -59,7 +61,7 @@ export default Vue.component('tab-switcher', {
) )
} }
return ( return (
<div class={ classesWrapper.join(' ')}> <div class={classesWrapper.join(' ')}>
<button <button
disabled={slot.data.attrs.disabled} disabled={slot.data.attrs.disabled}
onClick={this.activateTab(index)} onClick={this.activateTab(index)}
@ -71,7 +73,7 @@ export default Vue.component('tab-switcher', {
const contents = this.$slots.default.map((slot, index) => { const contents = this.$slots.default.map((slot, index) => {
if (!slot.tag) return if (!slot.tag) return
const active = index === this.active const active = this.activeIndex === index
if (this.renderOnlyFocused) { if (this.renderOnlyFocused) {
return active return active
? <div class="active">{slot}</div> ? <div class="active">{slot}</div>

View file

@ -22,21 +22,23 @@ const FriendList = withLoadMore({
additionalPropNames: ['userId'] additionalPropNames: ['userId']
})(List) })(List)
const defaultTabKey = 'statuses'
const UserProfile = { const UserProfile = {
data () { data () {
return { return {
error: false, error: false,
userId: null userId: null,
tab: defaultTabKey
} }
}, },
created () { created () {
// Make sure that timelines used in this page are empty
this.cleanUp()
const routeParams = this.$route.params const routeParams = this.$route.params
this.load(routeParams.name || routeParams.id) this.load(routeParams.name || routeParams.id)
this.tab = get(this.$route, 'query.tab', defaultTabKey)
}, },
destroyed () { destroyed () {
this.cleanUp() this.stopFetching()
}, },
computed: { computed: {
timeline () { timeline () {
@ -67,17 +69,36 @@ const UserProfile = {
}, },
methods: { methods: {
load (userNameOrId) { load (userNameOrId) {
const startFetchingTimeline = (timeline, userId) => {
// Clear timeline only if load another user's profile
if (userId !== this.$store.state.statuses.timelines[timeline].userId) {
this.$store.commit('clearTimeline', { timeline })
}
this.$store.dispatch('startFetchingTimeline', { timeline, userId })
}
const loadById = (userId) => {
this.userId = userId
startFetchingTimeline('user', userId)
startFetchingTimeline('media', userId)
if (this.isUs) {
startFetchingTimeline('favorites', userId)
}
// Fetch all pinned statuses immediately
this.$store.dispatch('fetchPinnedStatuses', userId)
}
// Reset view
this.userId = null
this.error = false
// Check if user data is already loaded in store // Check if user data is already loaded in store
const user = this.$store.getters.findUser(userNameOrId) const user = this.$store.getters.findUser(userNameOrId)
if (user) { if (user) {
this.userId = user.id loadById(user.id)
this.fetchTimelines()
} else { } else {
this.$store.dispatch('fetchUser', userNameOrId) this.$store.dispatch('fetchUser', userNameOrId)
.then(({ id }) => { .then(({ id }) => loadById(id))
this.userId = id
this.fetchTimelines()
})
.catch((reason) => { .catch((reason) => {
const errorMessage = get(reason, 'error.error') const errorMessage = get(reason, 'error.error')
if (errorMessage === 'No user with such user_id') { // Known error if (errorMessage === 'No user with such user_id') { // Known error
@ -90,40 +111,33 @@ const UserProfile = {
}) })
} }
}, },
fetchTimelines () { stopFetching () {
const userId = this.userId
this.$store.dispatch('startFetchingTimeline', { timeline: 'user', userId })
this.$store.dispatch('startFetchingTimeline', { timeline: 'media', userId })
if (this.isUs) {
this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId })
}
// Fetch all pinned statuses immediately
this.$store.dispatch('fetchPinnedStatuses', userId)
},
cleanUp () {
this.$store.dispatch('stopFetching', 'user') this.$store.dispatch('stopFetching', 'user')
this.$store.dispatch('stopFetching', 'favorites') this.$store.dispatch('stopFetching', 'favorites')
this.$store.dispatch('stopFetching', 'media') this.$store.dispatch('stopFetching', 'media')
this.$store.commit('clearTimeline', { timeline: 'user' }) },
this.$store.commit('clearTimeline', { timeline: 'favorites' }) switchUser (userNameOrId) {
this.$store.commit('clearTimeline', { timeline: 'media' }) this.stopFetching()
this.load(userNameOrId)
},
onTabSwitch (tab) {
this.tab = tab
this.$router.replace({ query: { tab } })
} }
}, },
watch: { watch: {
'$route.params.id': function (newVal) { '$route.params.id': function (newVal) {
if (newVal) { if (newVal) {
this.cleanUp() this.switchUser(newVal)
this.load(newVal)
} }
}, },
'$route.params.name': function (newVal) { '$route.params.name': function (newVal) {
if (newVal) { if (newVal) {
this.cleanUp() this.switchUser(newVal)
this.load(newVal)
} }
}, },
$route () { '$route.query': function (newVal) {
this.$refs.tabSwitcher.activateTab(0)() this.tab = newVal.tab || defaultTabKey
} }
}, },
components: { components: {

View file

@ -12,22 +12,24 @@
rounded="top" rounded="top"
/> />
<tab-switcher <tab-switcher
ref="tabSwitcher" :active-tab="tab"
:render-only-focused="true" :render-only-focused="true"
:on-switch="onTabSwitch"
> >
<div :label="$t('user_card.statuses')"> <Timeline
<Timeline key="statuses"
:count="user.statuses_count" :label="$t('user_card.statuses')"
:embedded="true" :count="user.statuses_count"
:title="$t('user_profile.timeline_title')" :embedded="true"
:timeline="timeline" :title="$t('user_profile.timeline_title')"
timeline-name="user" :timeline="timeline"
:user-id="userId" timeline-name="user"
:pinned-status-ids="user.pinnedStatusIds" :user-id="userId"
/> :pinned-status-ids="user.pinnedStatusIds"
</div> />
<div <div
v-if="followsTabVisible" v-if="followsTabVisible"
key="followees"
:label="$t('user_card.followees')" :label="$t('user_card.followees')"
:disabled="!user.friends_count" :disabled="!user.friends_count"
> >
@ -42,6 +44,7 @@
</div> </div>
<div <div
v-if="followersTabVisible" v-if="followersTabVisible"
key="followers"
:label="$t('user_card.followers')" :label="$t('user_card.followers')"
:disabled="!user.followers_count" :disabled="!user.followers_count"
> >
@ -58,6 +61,7 @@
</FollowerList> </FollowerList>
</div> </div>
<Timeline <Timeline
key="media"
:label="$t('user_card.media')" :label="$t('user_card.media')"
:disabled="!media.visibleStatuses.length" :disabled="!media.visibleStatuses.length"
:embedded="true" :embedded="true"
@ -68,6 +72,7 @@
/> />
<Timeline <Timeline
v-if="isUs" v-if="isUs"
key="favorites"
:label="$t('user_card.favorites')" :label="$t('user_card.favorites')"
:disabled="!favorites.visibleStatuses.length" :disabled="!favorites.visibleStatuses.length"
:embedded="true" :embedded="true"