#332 - add follow/not follow button to follow list

This commit is contained in:
dave 2019-02-09 23:05:23 -05:00
parent 4bea3c525f
commit 4b18989fef
6 changed files with 158 additions and 76 deletions

View file

@ -25,7 +25,8 @@ const FollowList = {
}, },
entries () { entries () {
return this.showFollowers ? this.user.followers : this.user.friends return this.showFollowers ? this.user.followers : this.user.friends
} },
showActions () { return this.$store.state.users.currentUser.id === this.userId }
}, },
methods: { methods: {
fetchEntries () { fetchEntries () {

View file

@ -3,7 +3,8 @@
<user-card <user-card
v-for="entry in entries" v-for="entry in entries"
:key="entry.id" :user="entry" :key="entry.id" :user="entry"
:showFollows="true" :showFollows="!showFollowers"
:showActions="showActions"
/> />
<div class="text-center panel-footer"> <div class="text-center panel-footer">
<a v-if="error" @click="fetchEntries" class="alert error"> <a v-if="error" @click="fetchEntries" class="alert error">

View file

@ -1,16 +1,21 @@
import UserCardContent from '../user_card_content/user_card_content.vue' import UserCardContent from '../user_card_content/user_card_content.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
const UserCard = { const UserCard = {
props: [ props: [
'user', 'user',
'showFollows', 'showFollows',
'showApproval' 'showApproval',
'showActions'
], ],
data () { data () {
return { return {
userExpanded: false userExpanded: false,
followRequestInProgress: false,
followRequestSent: false,
updated: false
} }
}, },
components: { components: {
@ -18,7 +23,11 @@ const UserCard = {
UserAvatar UserAvatar
}, },
computed: { computed: {
currentUser () { return this.$store.state.users.currentUser } currentUser () { return this.$store.state.users.currentUser },
following () { return this.updated ? this.updated.following : this.user.following },
showFollow () {
return this.showActions && (!this.showFollows && !this.following || this.updated && !this.updated.following)
}
}, },
methods: { methods: {
toggleUserExpanded () { toggleUserExpanded () {
@ -34,6 +43,21 @@ const UserCard = {
}, },
userProfileLink (user) { userProfileLink (user) {
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
},
followUser () {
this.followRequestInProgress = true
requestFollow(this.user, this.$store).then(({ sent, updated }) => {
this.followRequestInProgress = false
this.followRequestSent = sent
this.updated = updated
})
},
unfollowUser () {
this.followRequestInProgress = true
requestUnfollow(this.user, this.$store).then(({ updated }) => {
this.followRequestInProgress = false
this.updated = updated
})
} }
} }
} }

View file

@ -7,22 +7,43 @@
<user-card-content :user="user" :switcher="false"></user-card-content> <user-card-content :user="user" :switcher="false"></user-card-content>
</div> </div>
<div class="name-and-screen-name" v-else> <div class="name-and-screen-name" v-else>
<div :title="user.name" v-if="user.name_html" class="user-name"> <div :title="user.name" class="user-name">
<span v-html="user.name_html"></span> <span v-if="user.name_html" v-html="user.name_html"></span>
<span v-else>{{ user.name }}</span>
<span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you"> <span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you">
{{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }} {{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }}
</span> </span>
</div> </div>
<div :title="user.name" v-else class="user-name"> <div class="user-link-action">
{{ user.name }} <router-link class='user-screen-name' :to="userProfileLink(user)">
<span class="follows-you" v-if="!userExpanded && showFollows && user.follows_you"> @{{user.screen_name}}
{{ currentUser.id == user.id ? $t('user_card.its_you') : $t('user_card.follows_you') }} </router-link>
</span> <button
v-if="showFollow"
class="btn btn-default"
@click="followUser"
:disabled="followRequestInProgress"
:title="followRequestSent ? $t('user_card.follow_again') : ''"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else-if="followRequestSent">
{{ $t('user_card.follow_sent') }}
</template>
<template v-else>
{{ $t('user_card.follow') }}
</template>
</button>
<button v-if="showActions && showFollows && following" class="btn btn-default" @click="unfollowUser" :disabled="followRequestInProgress">
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else>
{{ $t('user_card.follow_unfollow') }}
</template>
</button>
</div> </div>
<router-link class='user-screen-name' :to="userProfileLink(user)">
@{{user.screen_name}}
</router-link>
</div> </div>
<div class="approval" v-if="showApproval"> <div class="approval" v-if="showApproval">
<button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button> <button class="btn btn-default" @click="approveUser">{{ $t('user_card.approve') }}</button>
@ -42,6 +63,9 @@
text-align: left; text-align: left;
width: 100%; width: 100%;
.user-name { .user-name {
display: flex;
justify-content: space-between;
img { img {
object-fit: contain; object-fit: contain;
height: 16px; height: 16px;
@ -49,11 +73,20 @@
vertical-align: middle; vertical-align: middle;
} }
} }
.user-link-action {
display: flex;
align-items: flex-start;
justify-content: space-between;
button {
margin-top: 3px;
}
}
} }
.follows-you { .follows-you {
margin-left: 2em; margin-left: 2em;
float: right;
} }
.card { .card {

View file

@ -1,5 +1,6 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js' import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
export default { export default {
@ -92,69 +93,17 @@ export default {
}, },
methods: { methods: {
followUser () { followUser () {
const store = this.$store
this.followRequestInProgress = true this.followRequestInProgress = true
store.state.api.backendInteractor.followUser(this.user.id) requestFollow(this.user, this.$store).then(({sent}) => {
.then((followedUser) => store.commit('addNewUsers', [followedUser])) this.followRequestInProgress = false
.then(() => { this.followRequestSent = sent
// For locked users we just mark it that we sent the follow request })
if (this.user.locked) {
this.followRequestInProgress = false
this.followRequestSent = true
return
}
if (this.user.following) {
// If we get result immediately, just stop.
this.followRequestInProgress = false
return
}
// But usually we don't get result immediately, so we ask server
// for updated user profile to confirm if we are following them
// Sometimes it takes several tries. Sometimes we end up not following
// user anyway, probably because they locked themselves and we
// don't know that yet.
// Recursive Promise, it will call itself up to 3 times.
const fetchUser = (attempt) => new Promise((resolve, reject) => {
setTimeout(() => {
store.state.api.backendInteractor.fetchUser({ id: this.user.id })
.then((user) => store.commit('addNewUsers', [user]))
.then(() => resolve([this.user.following, attempt]))
.catch((e) => reject(e))
}, 500)
}).then(([following, attempt]) => {
if (!following && attempt <= 3) {
// If we BE reports that we still not following that user - retry,
// increment attempts by one
return fetchUser(++attempt)
} else {
// If we run out of attempts, just return whatever status is.
return following
}
})
return fetchUser(1)
.then((following) => {
if (following) {
// We confirmed and everything its good.
this.followRequestInProgress = false
} else {
// If after all the tries, just treat it as if user is locked
this.followRequestInProgress = false
this.followRequestSent = true
}
})
})
}, },
unfollowUser () { unfollowUser () {
const store = this.$store
this.followRequestInProgress = true this.followRequestInProgress = true
store.state.api.backendInteractor.unfollowUser(this.user.id) requestUnfollow(this.user, this.$store).then(() => {
.then((unfollowedUser) => store.commit('addNewUsers', [unfollowedUser])) this.followRequestInProgress = false
.then(() => { })
this.followRequestInProgress = false
})
}, },
blockUser () { blockUser () {
const store = this.$store const store = this.$store

View file

@ -0,0 +1,74 @@
const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {
setTimeout(() => {
store.state.api.backendInteractor.fetchUser({ id: user.id })
.then((user) => store.commit('addNewUsers', [user]))
.then(() => resolve([user.following, attempt]))
.catch((e) => reject(e))
}, 500)
}).then(([following, attempt]) => {
if (!following && attempt <= 3) {
// If we BE reports that we still not following that user - retry,
// increment attempts by one
return fetchUser(++attempt, user, store)
} else {
// If we run out of attempts, just return whatever status is.
return following
}
})
export const requestFollow = (user, store) => new Promise((resolve, reject) => {
store.state.api.backendInteractor.followUser(user.id)
.then((updated) => {
store.commit('addNewUsers', [updated])
// For locked users we just mark it that we sent the follow request
if (updated.locked) {
resolve({
sent: true,
updated
})
}
if (updated.following) {
// If we get result immediately, just stop.
resolve({
sent: false,
updated
})
}
// But usually we don't get result immediately, so we ask server
// for updated user profile to confirm if we are following them
// Sometimes it takes several tries. Sometimes we end up not following
// user anyway, probably because they locked themselves and we
// don't know that yet.
// Recursive Promise, it will call itself up to 3 times.
return fetchUser(1, user, store)
.then((following) => {
if (following) {
// We confirmed and everything's good.
resolve({
sent: false,
updated
})
} else {
// If after all the tries, just treat it as if user is locked
resolve({
sent: false,
updated
})
}
})
})
})
export const requestUnfollow = (user, store) => new Promise((resolve, reject) => {
store.state.api.backendInteractor.unfollowUser(user.id)
.then((updated) => {
store.commit('addNewUsers', [updated])
resolve({
updated
})
})
})