aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorshpuld <shp@cock.li>2019-02-02 22:29:10 +0200
committershpuld <shp@cock.li>2019-02-02 22:29:10 +0200
commitdbb16d56e29b8a32fb7c1a7af56a7953f571cdb4 (patch)
treecb7f2e2adc5fe9db8cafb8faa1554cfa81c0c26f /src
parent8ce513ed09124fa0183943c359950f49ebcf2f89 (diff)
follows/followers pagination ready for review
Diffstat (limited to 'src')
-rw-r--r--src/components/followers_list/followers_list.js61
-rw-r--r--src/components/followers_list/followers_list.vue12
-rw-r--r--src/components/friends_list/friends_list.js61
-rw-r--r--src/components/friends_list/friends_list.vue12
-rw-r--r--src/components/user_card/user_card.vue4
-rw-r--r--src/components/user_profile/user_profile.js76
-rw-r--r--src/components/user_profile/user_profile.vue9
-rw-r--r--src/modules/users.js66
-rw-r--r--src/services/user_profile_link_generator/user_profile_link_generator.js2
9 files changed, 228 insertions, 75 deletions
diff --git a/src/components/followers_list/followers_list.js b/src/components/followers_list/followers_list.js
new file mode 100644
index 00000000..13aace18
--- /dev/null
+++ b/src/components/followers_list/followers_list.js
@@ -0,0 +1,61 @@
+import UserCard from '../user_card/user_card.vue'
+
+const FollowersList = {
+ data () {
+ return {
+ loading: false,
+ bottomedOut: false,
+ error: false
+ }
+ },
+ props: ['userId'],
+ created () {
+ window.addEventListener('scroll', this.scrollLoad)
+ if (this.user.followers.length === 0) {
+ this.fetchFollowers()
+ }
+ },
+ destroyed () {
+ window.removeEventListener('scroll', this.scrollLoad)
+ this.$store.dispatch('clearFriendsAndFollowers', this.userId)
+ },
+ computed: {
+ user () {
+ return this.$store.getters.userById(this.userId)
+ },
+ followers () {
+ return this.user.followers
+ }
+ },
+ methods: {
+ fetchFollowers () {
+ if (!this.loading) {
+ this.loading = true
+ this.$store.dispatch('addFollowers', this.userId).then(followers => {
+ this.error = false
+ this.loading = false
+ this.bottomedOut = followers.length === 0
+ }).catch(() => {
+ this.error = true
+ this.loading = false
+ })
+ }
+ },
+ scrollLoad (e) {
+ const bodyBRect = document.body.getBoundingClientRect()
+ const height = Math.max(bodyBRect.height, -(bodyBRect.y))
+ if (this.loading === false &&
+ this.bottomedOut === false &&
+ this.$el.offsetHeight > 0 &&
+ (window.innerHeight + window.pageYOffset) >= (height - 750)
+ ) {
+ this.fetchFollowers()
+ }
+ }
+ },
+ components: {
+ UserCard
+ }
+}
+
+export default FollowersList
diff --git a/src/components/followers_list/followers_list.vue b/src/components/followers_list/followers_list.vue
new file mode 100644
index 00000000..b6bd35e1
--- /dev/null
+++ b/src/components/followers_list/followers_list.vue
@@ -0,0 +1,12 @@
+<template>
+ <div>
+ <user-card v-for="follower in followers" :key="follower.id" :user="follower" :showFollows="true"></user-card>
+ <div @click="fetchFollowers" class="new-status-notification text-center panel-footer">
+ <span v-if="error" class="alert error">Error loading followers</span>
+ <i v-else-if="loading" class="icon-spin3 animate-spin"/>
+ <span v-else-if="bottomedOut"></span>
+ </div>
+ </div>
+</template>
+
+<script src="./followers_list.js" />
diff --git a/src/components/friends_list/friends_list.js b/src/components/friends_list/friends_list.js
new file mode 100644
index 00000000..d5c1837a
--- /dev/null
+++ b/src/components/friends_list/friends_list.js
@@ -0,0 +1,61 @@
+import UserCard from '../user_card/user_card.vue'
+
+const FriendsList = {
+ data () {
+ return {
+ loading: false,
+ bottomedOut: false,
+ error: false
+ }
+ },
+ props: ['userId'],
+ created () {
+ window.addEventListener('scroll', this.scrollLoad)
+ if (this.user.followers.length === 0) {
+ this.fetchFriends()
+ }
+ },
+ destroyed () {
+ window.removeEventListener('scroll', this.scrollLoad)
+ this.$store.dispatch('clearFriendsAndFollowers', this.userId)
+ },
+ computed: {
+ user () {
+ return this.$store.getters.userById(this.userId)
+ },
+ friends () {
+ return this.user.friends
+ }
+ },
+ methods: {
+ fetchFriends () {
+ if (!this.loading) {
+ this.loading = true
+ this.$store.dispatch('addFriends', this.userId).then(friends => {
+ this.error = false
+ this.loading = false
+ this.bottomedOut = friends.length === 0
+ }).catch(() => {
+ this.error = true
+ this.loading = false
+ })
+ }
+ },
+ scrollLoad (e) {
+ const bodyBRect = document.body.getBoundingClientRect()
+ const height = Math.max(bodyBRect.height, -(bodyBRect.y))
+ if (this.loading === false &&
+ this.bottomedOut === false &&
+ this.$el.offsetHeight > 0 &&
+ (window.innerHeight + window.pageYOffset) >= (height - 750)
+ ) {
+ this.fetchFriends()
+ }
+ }
+ },
+ components: {
+ UserCard
+ }
+}
+
+export default FriendsList
diff --git a/src/components/friends_list/friends_list.vue b/src/components/friends_list/friends_list.vue
new file mode 100644
index 00000000..75657cc8
--- /dev/null
+++ b/src/components/friends_list/friends_list.vue
@@ -0,0 +1,12 @@
+<template>
+ <div>
+ <user-card v-for="friend in friends" :key="friend.id" :user="friend" :showFollows="true"></user-card>
+ <div @click="fetchFriends" class="new-status-notification text-center panel-footer">
+ <span v-if="error" class="alert error">Error loading follows</span>
+ <i v-else-if="loading" class="icon-spin3 animate-spin"/>
+ <span v-else-if="bottomedOut"></span>
+ </div>
+ </div>
+</template>
+
+<script src="./friends_list.js" />
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index cf69606d..2a590889 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -1,7 +1,7 @@
<template>
<div class="card">
<a href="#">
- <StillImage @click.prevent="toggleUserExpanded" class="avatar" :src="user.profile_image_url"/>
+ <StillImage @click.prevent.native="toggleUserExpanded" class="avatar" :src="user.profile_image_url"/>
</a>
<div class="usercard" v-if="userExpanded">
<user-card-content :user="user" :switcher="false"></user-card-content>
@@ -79,7 +79,7 @@
.usercard {
width: fill-available;
- margin: 0.2em 0 0.7em 0;
+ margin: 0.2em 0 0 0.7em;
border-radius: $fallback--panelRadius;
border-radius: var(--panelRadius, $fallback--panelRadius);
border-style: solid;
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 4f09c9e7..0361d253 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -1,6 +1,8 @@
import UserCardContent from '../user_card_content/user_card_content.vue'
import UserCard from '../user_card/user_card.vue'
import Timeline from '../timeline/timeline.vue'
+import FriendsList from '../friends_list/friends_list.vue'
+import FollowersList from '../followers_list/followers_list.vue'
const UserProfile = {
created () {
@@ -15,15 +17,7 @@ const UserProfile = {
}
},
destroyed () {
- this.$store.dispatch('stopFetching', 'user')
- this.$store.dispatch('stopFetching', 'favorites')
- this.$store.dispatch('stopFetching', 'media')
- },
- data () {
- return {
- followsPage: 0,
- followersPage: 0
- }
+ this.cleanUp(this.userId)
},
computed: {
timeline () {
@@ -45,12 +39,6 @@ const UserProfile = {
return this.userId && this.$store.state.users.currentUser.id &&
this.userId === this.$store.state.users.currentUser.id
},
- friends () {
- return this.user.friends
- },
- followers () {
- return this.user.followers
- },
userInStore () {
if (this.isExternal) {
return this.$store.getters.userById(this.userId)
@@ -74,66 +62,48 @@ const UserProfile = {
}
},
methods: {
- fetchFollowers () {
- const id = this.userId
- this.$store.dispatch('addFollowers', { id })
- },
- fetchFriends () {
- const id = this.userId
- this.$store.dispatch('addFriends', { id })
- },
startFetchFavorites () {
if (this.isUs) {
this.$store.dispatch('startFetching', ['favorites', this.fetchBy])
}
},
- nextFollowsPage () {
- this.followsPage += 1
- this.$store.dispatch('addFriends', { id: this.userId, page: this.followsPage })
- console.log(this.user.friends)
- }
- },
- watch: {
- // TODO get rid of this copypasta
- userName () {
- if (this.isExternal) {
- return
- }
- this.$store.dispatch('stopFetching', 'user')
- this.$store.dispatch('stopFetching', 'favorites')
- this.$store.dispatch('stopFetching', 'media')
- this.$store.commit('clearTimeline', { timeline: 'user' })
- this.$store.commit('clearTimeline', { timeline: 'favorites' })
- this.$store.commit('clearTimeline', { timeline: 'media' })
+ startUp () {
this.$store.dispatch('startFetching', ['user', this.fetchBy])
this.$store.dispatch('startFetching', ['media', this.fetchBy])
+
this.startFetchFavorites()
},
- userId () {
- if (!this.isExternal) {
- return
- }
+ cleanUp () {
this.$store.dispatch('stopFetching', 'user')
this.$store.dispatch('stopFetching', 'favorites')
this.$store.dispatch('stopFetching', 'media')
this.$store.commit('clearTimeline', { timeline: 'user' })
this.$store.commit('clearTimeline', { timeline: 'favorites' })
this.$store.commit('clearTimeline', { timeline: 'media' })
- this.$store.dispatch('startFetching', ['user', this.fetchBy])
- this.$store.dispatch('startFetching', ['media', this.fetchBy])
- this.startFetchFavorites()
+ }
+ },
+ watch: {
+ userName () {
+ if (this.isExternal) {
+ return
+ }
+ this.cleanUp()
+ this.startUp()
},
- user () {
- if (this.user.id && !this.user.followers) {
- this.fetchFollowers()
- this.fetchFriends()
+ userId () {
+ if (!this.isExternal) {
+ return
}
+ this.cleanUp()
+ this.startUp()
}
},
components: {
UserCardContent,
UserCard,
- Timeline
+ Timeline,
+ FriendsList,
+ FollowersList
}
}
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index baf6aef6..4ba09dc3 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -5,18 +5,13 @@
<tab-switcher>
<Timeline :label="$t('user_card.statuses')" :embedded="true" :title="$t('user_profile.timeline_title')" :timeline="timeline" :timeline-name="'user'" :user-id="fetchBy"/>
<div :label="$t('user_card.followees')">
- <div v-if="friends">
- <user-card v-for="friend in friends" :key="friend.id" :user="friend" :showFollows="true"></user-card>
- <div @click="nextFollowsPage" class="panel-footer">MORE</div>
- </div>
+ <FriendsList v-if="user.friends_count > 0" :userId="userId" />
<div class="userlist-placeholder" v-else>
<i class="icon-spin3 animate-spin"></i>
</div>
</div>
<div :label="$t('user_card.followers')">
- <div v-if="followers">
- <user-card v-for="follower in followers" :key="follower.id" :user="follower" :showFollows="false"></user-card>
- </div>
+ <FollowersList v-if="user.followers_count > 0" :userId="userId" />
<div class="userlist-placeholder" v-else>
<i class="icon-spin3 animate-spin"></i>
</div>
diff --git a/src/modules/users.js b/src/modules/users.js
index ca8e5606..ca2e0f31 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -1,5 +1,5 @@
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
-import { compact, map, each, merge, concat } from 'lodash'
+import { compact, map, each, merge, find } from 'lodash'
import { set } from 'vue'
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
import oauthApi from '../services/new_api/oauth'
@@ -52,14 +52,35 @@ export const mutations = {
state.loggingIn = false
},
// TODO Clean after ourselves?
- addFriends (state, { id, friends }) {
+ addFriends (state, { id, friends, page }) {
const user = state.usersObject[id]
- console.log(user.friends)
- user.friends = concat(user.friends, friends)
+ each(friends, friend => {
+ if (!find(user.friends, { id: friend.id })) {
+ user.friends.push(friend)
+ }
+ })
+ user.friendsPage = page + 1
},
- addFollowers (state, { id, followers }) {
+ addFollowers (state, { id, followers, page }) {
const user = state.usersObject[id]
- user.followers = followers
+ each(followers, follower => {
+ if (!find(user.followers, { id: follower.id })) {
+ user.followers.push(follower)
+ }
+ })
+ user.followersPage = page + 1
+ },
+ // Because frontend doesn't have a reason to keep these stuff in memory
+ // outside of viewing someones user profile.
+ clearFriendsAndFollowers (state, userKey) {
+ const user = state.usersObject[userKey]
+ if (!user) {
+ return
+ }
+ user.friends = []
+ user.followers = []
+ user.friendsPage = 0
+ user.followersPage = 0
},
addNewUsers (state, users) {
each(users, (user) => mergeOrAdd(state.users, state.usersObject, user))
@@ -116,13 +137,34 @@ const users = {
store.rootState.api.backendInteractor.fetchUser({ id })
.then((user) => store.commit('addNewUsers', [user]))
},
- addFriends ({ rootState, commit }, { id, page }) {
- rootState.api.backendInteractor.fetchFriends({ id, page })
- .then((friends) => commit('addFriends', { id, friends }))
+ addFriends ({ rootState, commit }, fetchBy) {
+ return new Promise((resolve, reject) => {
+ const user = rootState.users.usersObject[fetchBy]
+ const page = user.friendsPage || 1
+ rootState.api.backendInteractor.fetchFriends({ id: user.id, page })
+ .then((friends) => {
+ commit('addFriends', { id: user.id, friends, page })
+ resolve(friends)
+ }).catch(() => {
+ reject()
+ })
+ })
+ },
+ addFollowers ({ rootState, commit }, fetchBy) {
+ return new Promise((resolve, reject) => {
+ const user = rootState.users.usersObject[fetchBy]
+ const page = user.followersPage || 1
+ rootState.api.backendInteractor.fetchFollowers({ id: user.id, page })
+ .then((followers) => {
+ commit('addFollowers', { id: user.id, followers, page })
+ resolve(followers)
+ }).catch(() => {
+ reject()
+ })
+ })
},
- addFollowers ({ rootState, commit }, { id, page }) {
- rootState.api.backendInteractor.fetchFollowers({ id, page })
- .then((followers) => commit('addFollowers', { id, followers }))
+ clearFriendsAndFollowers ({ commit }, userKey) {
+ commit('clearFriendsAndFollowers', userKey)
},
registerPushNotifications (store) {
const token = store.state.currentUser.credentials
diff --git a/src/services/user_profile_link_generator/user_profile_link_generator.js b/src/services/user_profile_link_generator/user_profile_link_generator.js
index bca2c9cd..a214ca48 100644
--- a/src/services/user_profile_link_generator/user_profile_link_generator.js
+++ b/src/services/user_profile_link_generator/user_profile_link_generator.js
@@ -8,6 +8,6 @@ const generateProfileLink = (id, screenName, restrictedNicknames) => {
}
}
-const isExternal = screenName => screenName.includes('@')
+const isExternal = screenName => screenName && screenName.includes('@')
export default generateProfileLink