Commit fedcc716 authored by HJ's avatar HJ 🐼

Merge branch '468-pin-status' into 'develop'

Add ability to pin posts

Closes #468

See merge request pleroma/pleroma-fe!770
parents 9eac3558 2ce01863
Pipeline #11861 passed with stages
in 4 minutes and 14 seconds
...@@ -41,7 +41,8 @@ const conversation = { ...@@ -41,7 +41,8 @@ const conversation = {
props: [ props: [
'statusoid', 'statusoid',
'collapsable', 'collapsable',
'isPage' 'isPage',
'showPinned'
], ],
created () { created () {
if (this.isPage) { if (this.isPage) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
:inlineExpanded="collapsable && isExpanded" :inlineExpanded="collapsable && isExpanded"
:statusoid="status" :statusoid="status"
:expandable='!isExpanded' :expandable='!isExpanded'
:showPinned="showPinned"
:focused="focused(status.id)" :focused="focused(status.id)"
:inConversation="isExpanded" :inConversation="isExpanded"
:highlight="getHighlight()" :highlight="getHighlight()"
......
const DeleteButton = {
props: [ 'status' ],
methods: {
deleteStatus () {
const confirmed = window.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 () {
if (!this.currentUser) { return }
const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
return superuser || this.status.user.id === this.currentUser.id
}
}
}
export default DeleteButton
<template>
<div v-if="canDelete">
<a href="#" v-on:click.prevent="deleteStatus()">
<i class='button-icon 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: $fallback--cRed;
color: var(--cRed, $fallback--cRed);
}
}
</style>
import Popper from 'vue-popperjs/src/component/popper.js.vue'
const ExtraButtons = {
props: [ 'status' ],
components: {
Popper
},
data () {
return {
showDropDown: false,
showPopper: true
}
},
methods: {
deleteStatus () {
this.refreshPopper()
const confirmed = window.confirm(this.$t('status.delete_confirm'))
if (confirmed) {
this.$store.dispatch('deleteStatus', { id: this.status.id })
}
},
toggleMenu () {
this.showDropDown = !this.showDropDown
},
pinStatus () {
this.refreshPopper()
this.$store.dispatch('pinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
unpinStatus () {
this.refreshPopper()
this.$store.dispatch('unpinStatus', this.status.id)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
},
refreshPopper () {
this.showPopper = false
this.showDropDown = false
setTimeout(() => {
this.showPopper = true
})
}
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
canDelete () {
if (!this.currentUser) { return }
const superuser = this.currentUser.rights.moderator || this.currentUser.rights.admin
return superuser || this.status.user.id === this.currentUser.id
},
ownStatus () {
return this.status.user.id === this.currentUser.id
},
canPin () {
return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted')
}
}
}
export default ExtraButtons
<template>
<Popper
trigger="click"
@hide='showDropDown = false'
append-to-body
v-if="showPopper"
:options="{
placement: 'top',
modifiers: {
arrow: { enabled: true },
offset: { offset: '0, 5px' },
}
}"
>
<div class="popper-wrapper">
<div class="dropdown-menu">
<button class="dropdown-item dropdown-item-icon" @click.prevent="pinStatus" v-if="!status.pinned && canPin">
<i class="icon-pin"></i><span>{{$t("status.pin")}}</span>
</button>
<button class="dropdown-item dropdown-item-icon" @click.prevent="unpinStatus" v-if="status.pinned && canPin">
<i class="icon-pin"></i><span>{{$t("status.unpin")}}</span>
</button>
<button class="dropdown-item dropdown-item-icon" @click.prevent="deleteStatus" v-if="canDelete">
<i class="icon-cancel"></i><span>{{$t("status.delete")}}</span>
</button>
</div>
</div>
<div class="button-icon" slot="reference" @click="toggleMenu">
<i class='icon-ellipsis' :class="{'icon-clicked': showDropDown}"></i>
</div>
</Popper>
</template>
<script src="./extra_buttons.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
.icon-ellipsis {
cursor: pointer;
&:hover, &.icon-clicked {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
</style>
...@@ -127,6 +127,14 @@ ...@@ -127,6 +127,14 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
}
}
&:hover { &:hover {
// TODO: improve the look on breeze themes // TODO: improve the look on breeze themes
background-color: $fallback--fg; background-color: $fallback--fg;
......
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 ExtraButtons from '../extra_buttons/extra_buttons.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue' import PostStatusForm from '../post_status_form/post_status_form.vue'
import UserCard from '../user_card/user_card.vue' import UserCard from '../user_card/user_card.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
...@@ -26,7 +26,8 @@ const Status = { ...@@ -26,7 +26,8 @@ const Status = {
'replies', 'replies',
'isPreview', 'isPreview',
'noHeading', 'noHeading',
'inlineExpanded' 'inlineExpanded',
'showPinned'
], ],
data () { data () {
return { return {
...@@ -37,6 +38,7 @@ const Status = { ...@@ -37,6 +38,7 @@ const Status = {
showPreview: false, showPreview: false,
showingTall: this.inConversation && this.focused, showingTall: this.inConversation && this.focused,
showingLongSubject: false, showingLongSubject: false,
error: null,
expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined' expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
? !this.$store.state.instance.collapseMessageWithSubject ? !this.$store.state.instance.collapseMessageWithSubject
: !this.$store.state.config.collapseMessageWithSubject, : !this.$store.state.config.collapseMessageWithSubject,
...@@ -269,13 +271,16 @@ const Status = { ...@@ -269,13 +271,16 @@ const Status = {
this.statusFromGlobalRepository.rebloggedBy this.statusFromGlobalRepository.rebloggedBy
) )
return uniqBy(combinedUsers, 'id') return uniqBy(combinedUsers, 'id')
},
ownStatus () {
return this.status.user.id === this.$store.state.users.currentUser.id
} }
}, },
components: { components: {
Attachment, Attachment,
FavoriteButton, FavoriteButton,
RetweetButton, RetweetButton,
DeleteButton, ExtraButtons,
PostStatusForm, PostStatusForm,
UserCard, UserCard,
UserAvatar, UserAvatar,
...@@ -296,6 +301,12 @@ const Status = { ...@@ -296,6 +301,12 @@ const Status = {
return 'icon-globe' return 'icon-globe'
} }
}, },
showError (error) {
this.error = error
},
clearError () {
this.error = undefined
},
linkClicked (event) { linkClicked (event) {
let { target } = event let { target } = event
if (target.tagName === 'SPAN') { if (target.tagName === 'SPAN') {
......
<template> <template>
<div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]"> <div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
<div v-if="error" class="alert error">
{{error}}
<i class="button-icon icon-cancel" @click="clearError"></i>
</div>
<template v-if="muted && !isPreview"> <template v-if="muted && !isPreview">
<div class="media status container muted"> <div class="media status container muted">
<small> <small>
...@@ -12,6 +16,10 @@ ...@@ -12,6 +16,10 @@
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div v-if="showPinned && statusoid.pinned" class="status-pin">
<i class="fa icon-pin faint"></i>
<span class="faint">{{$t('status.pinned')}}</span>
</div>
<div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> <div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
<UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :user="statusoid.user"/> <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :user="statusoid.user"/>
<div class="media-body faint"> <div class="media-body faint">
...@@ -95,7 +103,7 @@ ...@@ -95,7 +103,7 @@
v-if="preview" v-if="preview"
:isPreview="true" :isPreview="true"
:statusoid="preview" :statusoid="preview"
:compact=true :compact="true"
/> />
<div v-else class="status-preview status-preview-loading"> <div v-else class="status-preview status-preview-loading">
<i class="icon-spin4 animate-spin"></i> <i class="icon-spin4 animate-spin"></i>
...@@ -164,7 +172,7 @@ ...@@ -164,7 +172,7 @@
</div> </div>
<retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button> <retweet-button :visibility='status.visibility' :loggedIn='loggedIn' :status='status'></retweet-button>
<favorite-button :loggedIn='loggedIn' :status='status'></favorite-button> <favorite-button :loggedIn='loggedIn' :status='status'></favorite-button>
<delete-button :status='status'></delete-button> <extra-buttons :status="status" @onError="showError" @onSuccess="clearError"></extra-buttons>
</div> </div>
</div> </div>
</div> </div>
...@@ -199,6 +207,13 @@ $status-margin: 0.75em; ...@@ -199,6 +207,13 @@ $status-margin: 0.75em;
max-width: 100%; max-width: 100%;
} }
.status-pin {
padding: $status-margin $status-margin 0;
display: flex;
align-items: center;
justify-content: flex-end;
}
.status-preview { .status-preview {
position: absolute; position: absolute;
max-width: 95%; max-width: 95%;
......
...@@ -2,6 +2,7 @@ import get from 'lodash/get' ...@@ -2,6 +2,7 @@ import get from 'lodash/get'
import UserCard from '../user_card/user_card.vue' import UserCard from '../user_card/user_card.vue'
import FollowCard from '../follow_card/follow_card.vue' import FollowCard from '../follow_card/follow_card.vue'
import Timeline from '../timeline/timeline.vue' import Timeline from '../timeline/timeline.vue'
import Conversation from '../conversation/conversation.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue'
import List from '../list/list.vue' import List from '../list/list.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more' import withLoadMore from '../../hocs/with_load_more/with_load_more'
...@@ -95,6 +96,8 @@ const UserProfile = { ...@@ -95,6 +96,8 @@ const UserProfile = {
if (this.isUs) { if (this.isUs) {
this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId }) this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId })
} }
// Fetch all pinned statuses immediately
this.$store.dispatch('fetchPinnedStatuses', userId)
}, },
cleanUp () { cleanUp () {
this.$store.dispatch('stopFetching', 'user') this.$store.dispatch('stopFetching', 'user')
...@@ -128,7 +131,8 @@ const UserProfile = { ...@@ -128,7 +131,8 @@ const UserProfile = {
FollowerList, FollowerList,
FriendList, FriendList,
ModerationTools, ModerationTools,
FollowCard FollowCard,
Conversation
} }
} }
......
...@@ -3,16 +3,28 @@ ...@@ -3,16 +3,28 @@
<div v-if="user" class="user-profile panel panel-default"> <div v-if="user" class="user-profile panel panel-default">
<UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/> <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
<tab-switcher :renderOnlyFocused="true" ref="tabSwitcher"> <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
<Timeline <div :label="$t('user_card.statuses')" :disabled="!user.statuses_count">
:label="$t('user_card.statuses')" <div class="timeline">
:disabled="!user.statuses_count" <template v-for="statusId in user.pinnedStatuseIds">
:count="user.statuses_count" <Conversation
:embedded="true" v-if="timeline.statusesObject[statusId]"
:title="$t('user_profile.timeline_title')" class="status-fadein"
:timeline="timeline" :key="statusId"
:timeline-name="'user'" :statusoid="timeline.statusesObject[statusId]"
:user-id="userId" :collapsable="true"
/> :showPinned="true"
/>
</template>
</div>
<Timeline
:count="user.statuses_count"
:embedded="true"
:title="$t('user_profile.timeline_title')"
:timeline="timeline"
:timeline-name="'user'"
:user-id="userId"
/>
</div>
<div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count"> <div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count">
<FriendList :userId="userId"> <FriendList :userId="userId">
<template slot="item" slot-scope="{item}"> <template slot="item" slot-scope="{item}">
......
...@@ -402,6 +402,11 @@ ...@@ -402,6 +402,11 @@
"status": { "status": {
"favorites": "Favorites", "favorites": "Favorites",
"repeats": "Repeats", "repeats": "Repeats",
"delete": "Delete status",
"pin": "Pin on profile",
"unpin": "Unpin from profile",
"pinned": "Pinned",
"delete_confirm": "Do you really want to delete this status?",
"reply_to": "Reply to", "reply_to": "Reply to",
"replies_list": "Replies:" "replies_list": "Replies:"
}, },
......
...@@ -424,6 +424,10 @@ export const mutations = { ...@@ -424,6 +424,10 @@ export const mutations = {
newStatus.favoritedBy.push(user) newStatus.favoritedBy.push(user)
} }
}, },
setPinned (state, status) {
const newStatus = state.allStatusesObject[status.id]
newStatus.pinned = status.pinned
},
setRetweeted (state, { status, value }) { setRetweeted (state, { status, value }) {
const newStatus = state.allStatusesObject[status.id] const newStatus = state.allStatusesObject[status.id]
...@@ -533,6 +537,18 @@ const statuses = { ...@@ -533,6 +537,18 @@ const statuses = {
rootState.api.backendInteractor.unfavorite(status.id) rootState.api.backendInteractor.unfavorite(status.id)
.then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser })) .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
}, },
fetchPinnedStatuses ({ rootState, dispatch }, userId) {
rootState.api.backendInteractor.fetchPinnedStatuses(userId)
.then(statuses => dispatch('addNewStatuses', { statuses, timeline: 'user', userId, showImmediately: true }))
},
pinStatus ({ rootState, commit }, statusId) {
return rootState.api.backendInteractor.pinOwnStatus(statusId)
.then((status) => commit('setPinned', status))
},
unpinStatus ({ rootState, commit }, statusId) {
rootState.api.backendInteractor.unpinOwnStatus(statusId)
.then((status) => commit('setPinned', status))
},
retweet ({ rootState, commit }, status) { retweet ({ rootState, commit }, status) {
// Optimistic retweeting... // Optimistic retweeting...
commit('setRetweeted', { status, value: true }) commit('setRetweeted', { status, value: true })
......
...@@ -165,6 +165,15 @@ export const mutations = { ...@@ -165,6 +165,15 @@ export const mutations = {
state.currentUser.muteIds.push(muteId) state.currentUser.muteIds.push(muteId)
} }
}, },
setPinned (state, status) {
const user = state.usersObject[status.user.id]
const index = user.pinnedStatuseIds.indexOf(status.id)
if (status.pinned && index === -1) {
user.pinnedStatuseIds.push(status.id)
} else if (!status.pinned && index !== -1) {
user.pinnedStatuseIds.splice(index, 1)
}
},
setUserForStatus (state, status) { setUserForStatus (state, status) {
status.user = state.usersObject[status.user.id] status.user = state.usersObject[status.user.id]
}, },
...@@ -318,13 +327,17 @@ const users = { ...@@ -318,13 +327,17 @@ const users = {
store.commit('addNewUsers', users) store.commit('addNewUsers', users)
store.commit('addNewUsers', retweetedUsers) store.commit('addNewUsers', retweetedUsers)
// Reconnect users to statuses
each(statuses, (status) => { each(statuses, (status) => {
// Reconnect users to statuses
store.commit('setUserForStatus', status) store.commit('setUserForStatus', status)
// Set pinned statuses to user
store.commit('setPinned', status)
}) })
// Reconnect users to retweets
each(compact(map(statuses, 'retweeted_status')), (status) => { each(compact(map(statuses, 'retweeted_status')), (status) => {
// Reconnect users to retweets
store.commit('setUserForStatus', status) store.commit('setUserForStatus', status)
// Set pinned retweets to user
store.commit('setPinned', status)
}) })
}, },
addNewNotifications (store, { notifications }) { addNewNotifications (store, { notifications }) {
......
...@@ -51,6 +51,8 @@ const MASTODON_STATUS_FAVORITEDBY_URL = id => `/api/v1/statuses/${id}/favourited ...@@ -51,6 +51,8 @@ const MASTODON_STATUS_FAVORITEDBY_URL = id => `/api/v1/statuses/${id}/favourited
const MASTODON_STATUS_REBLOGGEDBY_URL = id => `/api/v1/statuses/${id}/reblogged_by` const MASTODON_STATUS_REBLOGGEDBY_URL = id => `/api/v1/statuses/${id}/reblogged_by`
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials' const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
const MASTODON_REPORT_USER_URL = '/api/v1/reports' const MASTODON_REPORT_USER_URL = '/api/v1/reports'
const MASTODON_PIN_OWN_STATUS = id => `/api/v1/statuses/${id}/pin`
const MASTODON_UNPIN_OWN_STATUS = id => `/api/v1/statuses/${id}/unpin`
import { each, map, concat, last } from 'lodash' import { each, map, concat, last } from 'lodash'
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js' import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
...@@ -210,6 +212,16 @@ const unfollowUser = ({id, credentials}) => { ...@@ -210,6 +212,16 @@ const unfollowUser = ({id, credentials}) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const pinOwnStatus = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_PIN_OWN_STATUS(id), credentials, method: 'POST' })
.then((data) => parseStatus(data))
}
const unpinOwnStatus = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_UNPIN_OWN_STATUS(id), credentials, method: 'POST' })
.then((data) => parseStatus(data))
}
const blockUser = ({id, credentials}) => { const blockUser = ({id, credentials}) => {
return fetch(MASTODON_BLOCK_USER_URL(id), { return fetch(MASTODON_BLOCK_USER_URL(id), {
headers: authHeaders(credentials), headers: authHeaders(credentials),
...@@ -488,6 +500,12 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use ...@@ -488,6 +500,12 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
.then((data) => data.map(isNotifications ? parseNotification : parseStatus)) .then((data) => data.map(isNotifications ? parseNotification : parseStatus))
} }
const fetchPinnedStatuses = ({ id, credentials }) => {
const url = MASTODON_USER_TIMELINE_URL(id) + '?pinned=true'
return promisedRequest({ url, credentials })
.then((data) => data.map(parseStatus))
}
const verifyCredentials = (user) => { const verifyCredentials = (user) => {
return fetch(LOGIN_URL, { return fetch(LOGIN_URL, {
method: 'POST', method: 'POST',
...@@ -708,6 +726,7 @@ const reportUser = ({credentials, userId, statusIds, comment, forward}) => { ...@@ -708,6 +726,7 @@ const reportUser = ({credentials, userId, statusIds, comment, forward}) => {
const apiService = { const apiService = {
verifyCredentials, verifyCredentials,
fetchTimeline, fetchTimeline,
fetchPinnedStatuses,
fetchConversation, fetchConversation,
fetchStatus, fetchStatus,
fetchFriends, fetchFriends,
...@@ -715,6 +734,8 @@ const apiService = { ...@@ -715,6 +734,8 @@ const apiService = {
fetchFollowers, fetchFollowers,
followUser, followUser,
unfollowUser, unfollowUser,
pinOwnStatus,
unpinOwnStatus,
blockUser, blockUser,
unblockUser, unblockUser,
fetchUser, fetchUser,
......
...@@ -98,6 +98,9 @@ const backendInteractorService = (credentials) => { ...@@ -98,6 +98,9 @@ const backendInteractorService = (credentials) => {
const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials}) const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials})
const fetchOAuthTokens = () => apiService.fetchOAuthTokens({credentials}) const fetchOAuthTokens = () => apiService.fetchOAuthTokens({credentials})
const revokeOAuthToken = (id) => apiService.revokeOAuthToken({id, credentials}) const revokeOAuthToken = (id) => apiService.revokeOAuthToken({id, credentials})
const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({credentials, id})
const pinOwnStatus = (id) => apiService.pinOwnStatus({credentials, id})
const unpinOwnStatus = (id) => apiService.unpinOwnStatus({credentials, id})
const getCaptcha = () => apiService.getCaptcha() const getCaptcha = () => apiService.getCaptcha()
const register = (params) => apiService.register(params) const register = (params) => apiService.register(params)
...@@ -144,6 +147,9 @@ const backendInteractorService = (credentials) => { ...@@ -144,6 +147,9 @@ const backendInteractorService = (credentials) => {
fetchBlocks, fetchBlocks,
fetchOAuthTokens, fetchOAuthTokens,
revokeOAuthToken, revokeOAuthToken,
fetchPinnedStatuses,
pinOwnStatus,
unpinOwnStatus,
tagUser, tagUser,
untagUser, untagUser,
addRight, addRight,
......
...@@ -131,6 +131,8 @@ export const parseUser = (data) => { ...@@ -131,6 +131,8 @@ export const parseUser = (data) => {
output.statuses_count = data.statuses_count output.statuses_count = data.statuses_count
output.friendIds = [] output.friendIds = []
output.followerIds = [] output.followerIds = []
output.pinnedStatuseIds = []
if (data.pleroma) { if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count output.follow_request_count = data.pleroma.follow_request_count
} }
...@@ -141,6 +143,7 @@ export const parseUser = (data) => { ...@@ -141,6 +143,7 @@ export const parseUser = (data) => {
} }
output.tags = output.tags || [] output.tags = output.tags || []
output.rights = output.rights || {}
return output return output
} }
...@@ -211,6 +214,7 @@ export const parseStatus = (data) => { ...@@ -211,6 +214,7 @@ export const parseStatus = (data) => {
output.summary_html = addEmojis(data.spoiler_text, data.emojis) output.summary_html = addEmojis(data.spoiler_text, data.emojis)
output.external_url = data.url output.external_url = data.url
output.pinned = data.pinned
} else { } else {
output.favorited = data.favorited output.favorited = data.favorited
output.fave_num = data.fave_num output.fave_num = data.fave_num
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755