commit: 2bc261afbaf9377450999e49a5fe46dcbcc8b180
parent: 5a1ad8409244ca7deb224b172ceb2b4acf7b8614
Author: Shpuld Shpuldson <shpuld@gmail.com>
Date: Wed, 23 Aug 2017 12:40:39 -0400
Merge branch 'feature/follow-lists' into 'develop'
Feature/follow lists
See merge request !106
Diffstat:
11 files changed, 226 insertions(+), 26 deletions(-)
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
@@ -40,7 +40,7 @@
</div>
<div class="media-body">
<div class="base05 base05=border usercard" v-if="userExpanded">
- <user-card-content :user="status.user"></user-card-content>
+ <user-card-content :user="status.user" :switcher="false"></user-card-content>
</div>
<div class="user-content">
<div class="media-heading">
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
@@ -1,6 +1,7 @@
import Status from '../status/status.vue'
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
import StatusOrConversation from '../status_or_conversation/status_or_conversation.vue'
+import UserCard from '../user_card/user_card.vue'
const Timeline = {
props: [
@@ -10,11 +11,21 @@ const Timeline = {
'userId'
],
computed: {
- timelineError () { return this.$store.state.statuses.error }
+ timelineError () { return this.$store.state.statuses.error },
+ followers () {
+ return this.timeline.followers
+ },
+ friends () {
+ return this.timeline.friends
+ },
+ viewing () {
+ return this.timeline.viewing
+ }
},
components: {
Status,
- StatusOrConversation
+ StatusOrConversation,
+ UserCard
},
created () {
const store = this.$store
@@ -30,6 +41,12 @@ const Timeline = {
showImmediately,
userId: this.userId
})
+
+ // don't fetch followers for public, friend, twkn
+ if (this.timelineName === 'user') {
+ this.fetchFriends()
+ this.fetchFollowers()
+ }
},
methods: {
showNewStatuses () {
@@ -48,6 +65,16 @@ const Timeline = {
userId: this.userId
}).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false }))
},
+ fetchFollowers () {
+ const id = this.userId
+ this.$store.state.api.backendInteractor.fetchFollowers({ id })
+ .then((followers) => this.$store.dispatch('addFollowers', { followers }))
+ },
+ fetchFriends () {
+ const id = this.userId
+ this.$store.state.api.backendInteractor.fetchFriends({ id })
+ .then((friends) => this.$store.dispatch('addFriends', { friends }))
+ },
scrollLoad (e) {
let height = Math.max(document.body.offsetHeight, document.body.scrollHeight)
if (this.timeline.loading === false && this.$store.state.config.autoLoad && (window.innerHeight + window.pageYOffset) >= (height - 750)) {
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
@@ -1,5 +1,5 @@
<template>
- <div class="timeline panel panel-default">
+ <div class="timeline panel panel-default" v-if="viewing == 'statuses'">
<div class="panel-heading timeline-heading base01-background base04">
<div class="title">
{{title}}
@@ -24,6 +24,30 @@
</div>
</div>
</div>
+ <div class="timeline panel panel-default" v-else-if="viewing == 'followers'">
+ <div class="panel-heading timeline-heading base01-background base04">
+ <div class="title">
+ Followers
+ </div>
+ </div>
+ <div class="panel-body">
+ <div class="timeline">
+ <user-card v-for="follower in followers" :user="follower" :showFollows="false"></user-card>
+ </div>
+ </div>
+ </div>
+ <div class="timeline panel panel-default" v-else-if="viewing == 'friends'">
+ <div class="panel-heading timeline-heading base01-background base04">
+ <div class="title">
+ Following
+ </div>
+ </div>
+ <div class="panel-body">
+ <div class="timeline">
+ <user-card v-for="friend in friends" :user="friend" :showFollows="true"></user-card>
+ </div>
+ </div>
+ </div>
</template>
<script src="./timeline.js"></script>
@@ -65,6 +89,13 @@
}
}
+ .avatar {
+ padding-top: 0.3em;
+ width:32px;
+ height: 32px;
+ border-radius: 50%;
+ }
+
.new-status-notification {
position:relative;
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
@@ -0,0 +1,23 @@
+import UserCardContent from '../user_card_content/user_card_content.vue'
+
+const UserCard = {
+ props: [
+ 'user',
+ 'showFollows'
+ ],
+ data () {
+ return {
+ userExpanded: false
+ }
+ },
+ components: {
+ UserCardContent
+ },
+ methods: {
+ toggleUserExpanded () {
+ this.userExpanded = !this.userExpanded
+ }
+ }
+}
+
+export default UserCard
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
@@ -0,0 +1,61 @@
+<template>
+ <div class="card base00-background base03-border">
+ <a href="#">
+ <img @click.prevent="toggleUserExpanded" class="avatar" :src="user.profile_image_url">
+ </a>
+ <div class="base05 base05=border usercard" v-if="userExpanded">
+ <user-card-content :user="user" :switcher="false"></user-card-content>
+ </div>
+ <div class="name-and-screen-name" v-else>
+ <div class="user-name">{{ user.name }}</div>
+ <a href="user.statusnet_profile_url"><div class="user-screen-name">@{{ user.screen_name }}</div></a>
+ </div>
+ <span class="follows-you" v-if="!userExpanded && showFollows">
+ <div class="follows" v-if="user.follows_you">
+ Follows you!
+ </div>
+ </span>
+ </div>
+</template>
+
+<script src="./user_card.js"></script>
+
+<style lang="scss">
+ .name-and-screen-name {
+ margin-left: 0.7em;
+ min-width: 16em;
+ display:block;
+ margin-top:0.0em;
+ margin-right: 2em;
+ text-align: left;
+ }
+
+ .follows-you {
+ margin-left: 2em;
+ width:-webkit-fill-available;
+ width: -moz-webkit-fill-available;
+ }
+
+ .follows {
+ float: right;
+ }
+
+ .card {
+ display: flex;
+ flex: 1 0;
+ padding-top: 0.6em;
+ padding-right: 1em;
+ padding-bottom: 0.6em;
+ padding-left: 1em;
+ border-bottom: 1px solid;
+ margin: 0;
+ border-bottom-color: inherit;
+ }
+
+ .usercard {
+ width: -webkit-fill-available;
+ width: -moz-webkit-fill-available;
+ stretch: fill;
+ margin-left: 0.7em;
+ }
+</style>
diff --git a/src/components/user_card_content/user_card_content.vue b/src/components/user_card_content/user_card_content.vue
@@ -47,17 +47,20 @@
</div>
<div class="panel-body profile-panel-body" :style="bodyStyle">
<div class="user-counts">
- <div class="user-count">
- <h5>Statuses</h5>
- <span>{{user.statuses_count}} <br><span class="dailyAvg">{{dailyAvg}} per day</span></span>
+ <div class="user-count base04">
+ <a href="#" v-on:click.prevent="setProfileView('statuses')" v-if="switcher"><h5 class="base05">Statuses</h5></a>
+ <h5 v-else>Statuses</h5>
+ <span class="base05">{{user.statuses_count}} <br><span class="dailyAvg">{{dailyAvg}} per day</span></span>
</div>
<div class="user-count">
- <h5>Following</h5>
- <span>{{user.friends_count}}</span>
+ <a href="#" v-on:click.prevent="setProfileView('friends')" v-if="switcher"><h5 class="base05">Following</h5></a>
+ <h5 v-else>Following</h5>
+ <span class="base05">{{user.friends_count}}</span>
</div>
<div class="user-count">
- <h5>Followers</h5>
- <span>{{user.followers_count}}</span>
+ <a href="#" v-on:click.prevent="setProfileView('followers')" v-if="switcher"><h5 class="base05">Followers</h5></a>
+ <h5 v-else>Followers</h5>
+ <span class="base05">{{user.followers_count}}</span>
</div>
</div>
<p>{{user.description}}</p>
@@ -67,7 +70,7 @@
<script>
export default {
- props: [ 'user' ],
+ props: [ 'user', 'switcher' ],
computed: {
headingStyle () {
let color = this.$store.state.config.colors['base00']
@@ -110,13 +113,18 @@
const store = this.$store
store.commit('setMuted', {user: this.user, muted: !this.user.muted})
store.state.api.backendInteractor.setUserMute(this.user)
+ },
+ setProfileView (v) {
+ const store = this.$store
+ store.commit('setProfileView', { v })
}
}
}
</script>
<style lang="scss">
-
+@import '../../_variables.scss';
+
.profile-panel-background {
background-size: cover;
border-radius: 10px;
@@ -242,6 +250,9 @@
font-weight: bolder;
margin: 0 0 0.25em;
}
+ a {
+ text-decoration: none;
+ }
}
.dailyAvg {
diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue
@@ -2,7 +2,7 @@
<div class="user-panel">
<div v-if='user' class="panel panel-default">
- <user-card-content :user="user"></user-card-content>
+ <user-card-content :user="user" :switcher="false"></user-card-content>
<div class="panel-footer base00-background">
<post-status-form v-if='user'></post-status-form>
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
@@ -1,7 +1,7 @@
<template>
<div>
<div v-if="user" class="user-profile panel panel-default base00-background">
- <user-card-content :user="user"></user-card-content>
+ <user-card-content :user="user" :switcher="true"></user-card-content>
</div>
<Timeline :title="'User Timeline'" v-bind:timeline="timeline" v-bind:timeline-name="'user'" :user-id="userId"/>
</div>
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
@@ -19,7 +19,10 @@ export const defaultState = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
},
public: {
statuses: [],
@@ -30,7 +33,10 @@ export const defaultState = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
},
user: {
statuses: [],
@@ -41,7 +47,10 @@ export const defaultState = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
},
publicAndExternal: {
statuses: [],
@@ -52,7 +61,10 @@ export const defaultState = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
},
friends: {
statuses: [],
@@ -63,7 +75,10 @@ export const defaultState = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
}
}
}
@@ -320,7 +335,10 @@ export const mutations = {
newStatusCount: 0,
maxId: 0,
minVisibleId: 0,
- loading: false
+ loading: false,
+ followers: [],
+ friends: [],
+ viewing: 'statuses'
}
state.timelines[timeline] = emptyTimeline
@@ -347,6 +365,16 @@ export const mutations = {
setError (state, { value }) {
state.error = value
},
+ setProfileView (state, { v }) {
+ // load followers / friends only when needed
+ state.timelines['user'].viewing = v
+ },
+ addFriends (state, { friends }) {
+ state.timelines['user'].friends = friends
+ },
+ addFollowers (state, { followers }) {
+ state.timelines['user'].followers = followers
+ },
markNotificationsAsSeen (state, notifications) {
each(notifications, (notification) => {
notification.seen = true
@@ -363,6 +391,12 @@ const statuses = {
setError ({ rootState, commit }, { value }) {
commit('setError', { value })
},
+ addFriends ({ rootState, commit }, { friends }) {
+ commit('addFriends', { friends })
+ },
+ addFollowers ({ rootState, commit }, { followers }) {
+ commit('addFollowers', { followers })
+ },
deleteStatus ({ rootState, commit }, status) {
commit('setDeleted', { status })
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
@@ -13,6 +13,7 @@ const STATUS_URL = '/api/statuses/show'
const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload'
const CONVERSATION_URL = '/api/statusnet/conversation'
const MENTIONS_URL = '/api/statuses/mentions.json'
+const FOLLOWERS_URL = '/api/statuses/followers.json'
const FRIENDS_URL = '/api/statuses/friends.json'
const FOLLOWING_URL = '/api/friendships/create.json'
const UNFOLLOWING_URL = '/api/friendships/destroy.json'
@@ -179,8 +180,15 @@ const unfollowUser = ({id, credentials}) => {
}).then((data) => data.json())
}
-const fetchFriends = ({credentials}) => {
- return fetch(FRIENDS_URL, { headers: authHeaders(credentials) })
+const fetchFriends = ({id, credentials}) => {
+ let url = `${FRIENDS_URL}?user_id=${id}`
+ return fetch(url, { headers: authHeaders(credentials) })
+ .then((data) => data.json())
+}
+
+const fetchFollowers = ({id, credentials}) => {
+ let url = `${FOLLOWERS_URL}?user_id=${id}`
+ return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json())
}
@@ -234,7 +242,6 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
if (since) {
params.push(['since_id', since])
}
-
if (until) {
params.push(['max_id', until])
}
@@ -326,6 +333,7 @@ const apiService = {
fetchConversation,
fetchStatus,
fetchFriends,
+ fetchFollowers,
followUser,
unfollowUser,
favorite,
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -10,8 +10,12 @@ const backendInteractorService = (credentials) => {
return apiService.fetchConversation({id, credentials})
}
- const fetchFriends = () => {
- return apiService.fetchFriends({credentials})
+ const fetchFriends = ({id}) => {
+ return apiService.fetchFriends({id, credentials})
+ }
+
+ const fetchFollowers = ({id}) => {
+ return apiService.fetchFollowers({id, credentials})
}
const fetchAllFollowing = ({username}) => {
@@ -48,6 +52,7 @@ const backendInteractorService = (credentials) => {
fetchStatus,
fetchConversation,
fetchFriends,
+ fetchFollowers,
followUser,
unfollowUser,
fetchAllFollowing,