Merge branch 'develop' into 'feature/users_timeline'
# Conflicts: # src/modules/users.js
This commit is contained in:
commit
a35e9ce3e0
|
@ -0,0 +1,17 @@
|
||||||
|
const DeleteButton = {
|
||||||
|
props: [ 'status' ],
|
||||||
|
methods: {
|
||||||
|
deleteStatus () {
|
||||||
|
const confirmed = confirm('Do you really want to delete this status?')
|
||||||
|
if (confirmed) {
|
||||||
|
this.$store.dispatch('deleteStatus', { id: this.status.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentUser () { return this.$store.state.users.currentUser },
|
||||||
|
canDelete () { return this.currentUser.rights.delete_others_notice || this.status.user.id == this.currentUser.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DeleteButton
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="canDelete">
|
||||||
|
<a href="#" v-on:click.prevent="deleteStatus()">
|
||||||
|
<i class='fa icon-cancel delete-status'></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./delete_button.js" ></script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.icon-cancel,.delete-status {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,7 @@
|
||||||
import Attachment from '../attachment/attachment.vue'
|
import Attachment from '../attachment/attachment.vue'
|
||||||
import FavoriteButton from '../favorite_button/favorite_button.vue'
|
import FavoriteButton from '../favorite_button/favorite_button.vue'
|
||||||
import RetweetButton from '../retweet_button/retweet_button.vue'
|
import RetweetButton from '../retweet_button/retweet_button.vue'
|
||||||
|
import DeleteButton from '../delete_button/delete_button.vue'
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
|
@ -20,25 +21,18 @@ const Status = {
|
||||||
},
|
},
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!this.$store.state.users.currentUser
|
||||||
},
|
}
|
||||||
deleted () { return this.statusoid.deleted },
|
|
||||||
canDelete () { return this.statusoid.user.id === this.$store.state.users.currentUser.id }
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Attachment,
|
Attachment,
|
||||||
FavoriteButton,
|
FavoriteButton,
|
||||||
RetweetButton,
|
RetweetButton,
|
||||||
|
DeleteButton,
|
||||||
PostStatusForm
|
PostStatusForm
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleReplying () {
|
toggleReplying () {
|
||||||
this.replying = !this.replying
|
this.replying = !this.replying
|
||||||
},
|
|
||||||
deleteStatus () {
|
|
||||||
const confirmed = confirm('Do you really want to delete this status?')
|
|
||||||
if (confirmed) {
|
|
||||||
this.$store.dispatch('deleteStatus', { id: this.status.id })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,7 @@
|
||||||
</div>
|
</div>
|
||||||
<retweet-button :status=status></retweet-button>
|
<retweet-button :status=status></retweet-button>
|
||||||
<favorite-button :status=status></favorite-button>
|
<favorite-button :status=status></favorite-button>
|
||||||
<div v-if="canDelete">
|
<delete-button :status=status></delete-button>
|
||||||
<a href="#" v-on:click.prevent="deleteStatus">
|
|
||||||
<i class='fa icon-cancel delete-status'></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<post-status-form v-if="replying" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying"></post-status-form>
|
<post-status-form v-if="replying" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying"></post-status-form>
|
||||||
|
@ -130,11 +126,4 @@
|
||||||
.status-el:last-child .status {
|
.status-el:last-child .status {
|
||||||
border: none
|
border: none
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-cancel,.delete-status {
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: $red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,6 +6,24 @@
|
||||||
<span class="glyphicon glyphicon-user"></span>
|
<span class="glyphicon glyphicon-user"></span>
|
||||||
<div class='user-name'>{{user.name}}</div>
|
<div class='user-name'>{{user.name}}</div>
|
||||||
<div class='user-screen-name'>@{{user.screen_name}}</div>
|
<div class='user-screen-name'>@{{user.screen_name}}</div>
|
||||||
|
<div v-if="isOtherUser" class="following-info">
|
||||||
|
<div v-if="user.follows_you" class="following">
|
||||||
|
Follows you!
|
||||||
|
</div>
|
||||||
|
<div class="followed">
|
||||||
|
<span v-if="user.following">
|
||||||
|
Following them!
|
||||||
|
<button @click="unfollowUser">
|
||||||
|
Unfollow!
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
<span v-if="!user.following" >
|
||||||
|
<button @click="followUser">
|
||||||
|
Follow!
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
@ -37,6 +55,21 @@
|
||||||
color: `#${this.user.profile_link_color}`,
|
color: `#${this.user.profile_link_color}`,
|
||||||
'background-image': `url(${this.user.cover_photo})`
|
'background-image': `url(${this.user.cover_photo})`
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
isOtherUser () {
|
||||||
|
return this.user !== this.$store.state.users.currentUser
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
followUser () {
|
||||||
|
const store = this.$store
|
||||||
|
store.state.api.backendInteractor.followUser(this.user.id)
|
||||||
|
.then((followedUser) => store.commit('addNewUsers', [followedUser]))
|
||||||
|
},
|
||||||
|
unfollowUser () {
|
||||||
|
const store = this.$store
|
||||||
|
store.state.api.backendInteractor.unfollowUser(this.user.id)
|
||||||
|
.then((unfollowedUser) => store.commit('addNewUsers', [unfollowedUser]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,3 +14,20 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_profile.js"></script>
|
<script src="./user_profile.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.user-profile {
|
||||||
|
flex: 2;
|
||||||
|
flex-basis: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
.following-info {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
div {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -107,7 +107,7 @@ const mergeOrAdd = (arr, item) => {
|
||||||
const sortTimeline = (timeline) => {
|
const sortTimeline = (timeline) => {
|
||||||
timeline.visibleStatuses = sortBy(timeline.visibleStatuses, ({id}) => -id)
|
timeline.visibleStatuses = sortBy(timeline.visibleStatuses, ({id}) => -id)
|
||||||
timeline.statuses = sortBy(timeline.statuses, ({id}) => -id)
|
timeline.statuses = sortBy(timeline.statuses, ({id}) => -id)
|
||||||
timeline.minVisibleId = (last(timeline.statuses) || {}).id
|
timeline.minVisibleId = (last(timeline.visibleStatuses) || {}).id
|
||||||
|
|
||||||
return timeline
|
return timeline
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import timelineFetcher from '../services/timeline_fetcher/timeline_fetcher.service.js'
|
import timelineFetcher from '../services/timeline_fetcher/timeline_fetcher.service.js'
|
||||||
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
|
||||||
import { each, find, merge } from 'lodash'
|
import { compact, map, each, find, merge } from 'lodash'
|
||||||
|
|
||||||
// TODO: Unify with mergeOrAdd in statuses.js
|
// TODO: Unify with mergeOrAdd in statuses.js
|
||||||
export const mergeOrAdd = (arr, item) => {
|
export const mergeOrAdd = (arr, item) => {
|
||||||
|
@ -43,14 +43,10 @@ const users = {
|
||||||
mutations,
|
mutations,
|
||||||
actions: {
|
actions: {
|
||||||
addNewStatuses (store, { statuses }) {
|
addNewStatuses (store, { statuses }) {
|
||||||
const users = statuses.reduce((acc, status) => {
|
const users = map(statuses, 'user')
|
||||||
const addUsers = [status.user]
|
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
|
||||||
if (status.retweeted_status) {
|
|
||||||
addUsers.push(status.retweeted_status.user)
|
|
||||||
}
|
|
||||||
return acc.concat(addUsers)
|
|
||||||
}, [])
|
|
||||||
store.commit('addNewUsers', users)
|
store.commit('addNewUsers', users)
|
||||||
|
store.commit('addNewUsers', retweetedUsers)
|
||||||
},
|
},
|
||||||
loginUser (store, userCredentials) {
|
loginUser (store, userCredentials) {
|
||||||
const commit = store.commit
|
const commit = store.commit
|
||||||
|
@ -85,4 +81,4 @@ const users = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default users
|
export default users
|
|
@ -14,6 +14,9 @@ const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload'
|
||||||
const CONVERSATION_URL = '/api/statusnet/conversation'
|
const CONVERSATION_URL = '/api/statusnet/conversation'
|
||||||
const MENTIONS_URL = '/api/statuses/mentions.json'
|
const MENTIONS_URL = '/api/statuses/mentions.json'
|
||||||
const FRIENDS_URL = '/api/statuses/friends.json'
|
const FRIENDS_URL = '/api/statuses/friends.json'
|
||||||
|
const FOLLOWING_URL = '/api/friendships/create.json'
|
||||||
|
const UNFOLLOWING_URL = '/api/friendships/destroy.json'
|
||||||
|
// const USER_URL = '/api/users/show.json'
|
||||||
|
|
||||||
const oldfetch = window.fetch
|
const oldfetch = window.fetch
|
||||||
|
|
||||||
|
@ -31,6 +34,22 @@ const authHeaders = (user) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const followUser = ({id, credentials}) => {
|
||||||
|
let url = `${FOLLOWING_URL}?user_id=${id}`
|
||||||
|
return fetch(url, {
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
method: 'POST'
|
||||||
|
}).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
const unfollowUser = ({id, credentials}) => {
|
||||||
|
let url = `${UNFOLLOWING_URL}?user_id=${id}`
|
||||||
|
return fetch(url, {
|
||||||
|
headers: authHeaders(credentials),
|
||||||
|
method: 'POST'
|
||||||
|
}).then((data) => data.json())
|
||||||
|
}
|
||||||
|
|
||||||
const fetchFriends = ({credentials}) => {
|
const fetchFriends = ({credentials}) => {
|
||||||
return fetch(FRIENDS_URL, { headers: authHeaders(credentials) })
|
return fetch(FRIENDS_URL, { headers: authHeaders(credentials) })
|
||||||
.then((data) => data.json())
|
.then((data) => data.json())
|
||||||
|
@ -158,6 +177,8 @@ const apiService = {
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
fetchMentions,
|
fetchMentions,
|
||||||
fetchFriends,
|
fetchFriends,
|
||||||
|
followUser,
|
||||||
|
unfollowUser,
|
||||||
favorite,
|
favorite,
|
||||||
unfavorite,
|
unfavorite,
|
||||||
retweet,
|
retweet,
|
||||||
|
|
|
@ -17,11 +17,21 @@ const backendInteractorService = (credentials) => {
|
||||||
return apiService.fetchFriends({credentials})
|
return apiService.fetchFriends({credentials})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const followUser = (id) => {
|
||||||
|
return apiService.followUser({credentials, id})
|
||||||
|
}
|
||||||
|
|
||||||
|
const unfollowUser = (id) => {
|
||||||
|
return apiService.unfollowUser({credentials, id})
|
||||||
|
}
|
||||||
|
|
||||||
const backendInteractorServiceInstance = {
|
const backendInteractorServiceInstance = {
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
fetchConversation,
|
fetchConversation,
|
||||||
fetchMentions,
|
fetchMentions,
|
||||||
fetchFriends,
|
fetchFriends,
|
||||||
|
followUser,
|
||||||
|
unfollowUser,
|
||||||
verifyCredentials: apiService.verifyCredentials
|
verifyCredentials: apiService.verifyCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,18 +136,21 @@ describe('The Statuses module', () => {
|
||||||
|
|
||||||
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
|
it('keeps a descending by id order in timeline.visibleStatuses and timeline.statuses', () => {
|
||||||
const state = cloneDeep(defaultState)
|
const state = cloneDeep(defaultState)
|
||||||
const status = makeMockStatus({id: 2})
|
const nonVisibleStatus = makeMockStatus({id: 1})
|
||||||
const statusTwo = makeMockStatus({id: 1})
|
const status = makeMockStatus({id: 3})
|
||||||
const statusThree = makeMockStatus({id: 3})
|
const statusTwo = makeMockStatus({id: 2})
|
||||||
|
const statusThree = makeMockStatus({id: 4})
|
||||||
|
|
||||||
|
mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' })
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
|
||||||
mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' })
|
mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
expect(state.timelines.public.minVisibleId).to.equal(1)
|
expect(state.timelines.public.minVisibleId).to.equal(2)
|
||||||
|
|
||||||
mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' })
|
mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' })
|
||||||
|
|
||||||
expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo])
|
expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus])
|
||||||
expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo])
|
expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue