refs #872 Update favourited toot in all timelines

This commit is contained in:
AkiraFukushima 2019-05-18 22:28:53 +09:00
parent 891877519d
commit a97c5feae6
4 changed files with 276 additions and 240 deletions

View File

@ -2,7 +2,23 @@
<div <div
class="status" class="status"
tabIndex="0" tabIndex="0"
v-shortkey="shortcutEnabled ? {next: ['j'], prev: ['k'], right: ['l'], left: ['h'], reply: ['r'], boost: ['b'], fav: ['f'], open: ['o'], profile: ['p'], image: ['i'], cw: ['x']} : {}" v-shortkey="
shortcutEnabled
? {
next: ['j'],
prev: ['k'],
right: ['l'],
left: ['h'],
reply: ['r'],
boost: ['b'],
fav: ['f'],
open: ['o'],
profile: ['p'],
image: ['i'],
cw: ['x']
}
: {}
"
@shortkey="handleTootControl" @shortkey="handleTootControl"
ref="status" ref="status"
@click="$emit('selectToot')" @click="$emit('selectToot')"
@ -50,7 +66,13 @@
{{ $t('cards.toot.sensitive') }} {{ $t('cards.toot.sensitive') }}
</el-button> </el-button>
<div v-show="isShowAttachments"> <div v-show="isShowAttachments">
<el-button v-show="sensitive && isShowAttachments" class="hide-sensitive" type="text" @click="showAttachments = false" :title="$t('cards.toot.hide')"> <el-button
v-show="sensitive && isShowAttachments"
class="hide-sensitive"
type="text"
@click="showAttachments = false"
:title="$t('cards.toot.hide')"
>
<icon name="eye" class="hide"></icon> <icon name="eye" class="hide"></icon>
</el-button> </el-button>
<div class="media" v-bind:key="media.preview_url" v-for="media in mediaAttachments"> <div class="media" v-bind:key="media.preview_url" v-for="media in mediaAttachments">
@ -64,11 +86,14 @@
<div class="reblogger" v-show="message.reblog !== null"> <div class="reblogger" v-show="message.reblog !== null">
<icon name="retweet"></icon> <icon name="retweet"></icon>
<span class="reblogger-icon" @click="openUser(message.account)" role="presentation"> <span class="reblogger-icon" @click="openUser(message.account)" role="presentation">
<FailoverImg <FailoverImg :src="message.account.avatar" :alt="`Avatar of ${message.account.username}`" />
:src="message.account.avatar"
:alt="`Avatar of ${message.account.username}`" />
</span> </span>
<span class="reblogger-name" @click="openUser(message.account)" :title="`Reblogged by ${message.account.username}`" :aria-label="`Reblogged by ${message.account.username}`"> <span
class="reblogger-name"
@click="openUser(message.account)"
:title="`Reblogged by ${message.account.username}`"
:aria-label="`Reblogged by ${message.account.username}`"
>
<bdi v-html="username(message.account)"></bdi> <bdi v-html="username(message.account)"></bdi>
</span> </span>
</div> </div>
@ -82,13 +107,25 @@
<el-button v-show="directed" type="text" class="directed"> <el-button v-show="directed" type="text" class="directed">
<icon name="envelope" scale="0.9"></icon> <icon name="envelope" scale="0.9"></icon>
</el-button> </el-button>
<el-button v-show="!locked&&!directed" type="text" @click="changeReblog(originalMessage)" :class="originalMessage.reblogged ? 'reblogged' : 'reblog'" :title="$t('cards.toot.reblog')"> <el-button
v-show="!locked && !directed"
type="text"
@click="changeReblog(originalMessage)"
:class="originalMessage.reblogged ? 'reblogged' : 'reblog'"
:title="$t('cards.toot.reblog')"
>
<icon name="retweet" scale="0.9"></icon> <icon name="retweet" scale="0.9"></icon>
</el-button> </el-button>
<span class="count"> <span class="count">
{{ reblogsCount }} {{ reblogsCount }}
</span> </span>
<el-button type="text" @click="changeFavourite(originalMessage)" :class="originalMessage.favourited ? 'favourited animated bounceIn' : 'favourite'" :title="$t('cards.toot.fav')" :aria-label="$t('cards.toot.fav')"> <el-button
type="text"
@click="changeFavourite(originalMessage)"
:class="originalMessage.favourited ? 'favourited animated bounceIn' : 'favourite'"
:title="$t('cards.toot.fav')"
:aria-label="$t('cards.toot.fav')"
>
<icon name="star" scale="0.9"></icon> <icon name="star" scale="0.9"></icon>
</el-button> </el-button>
<span class="count"> <span class="count">
@ -227,8 +264,7 @@ export default {
}, },
application: function() { application: function() {
let msg = this.originalMessage let msg = this.originalMessage
if (msg.application !== undefined && if (msg.application !== undefined && msg.application !== null) {
msg.application !== null) {
return msg.application.name return msg.application.name
} }
return null return null
@ -326,12 +362,13 @@ export default {
const parsedAccount = findAccount(e.target, 'toot') const parsedAccount = findAccount(e.target, 'toot')
if (parsedAccount !== null) { if (parsedAccount !== null) {
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true) this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true)
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/searchAccount', parsedAccount) this.$store
.then((account) => { .dispatch('TimelineSpace/Contents/SideBar/AccountProfile/searchAccount', parsedAccount)
.then(account => {
this.$store.dispatch('TimelineSpace/Contents/SideBar/openAccountComponent') this.$store.dispatch('TimelineSpace/Contents/SideBar/openAccountComponent')
this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/changeAccount', account) this.$store.dispatch('TimelineSpace/Contents/SideBar/AccountProfile/changeAccount', account)
}) })
.catch((err) => { .catch(err => {
console.error(err) console.error(err)
this.openLink(e) this.openLink(e)
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', false) this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', false)
@ -378,22 +415,26 @@ export default {
}, },
changeReblog(message) { changeReblog(message) {
if (message.reblogged) { if (message.reblogged) {
this.$store.dispatch('molecules/Toot/unreblog', message) this.$store
.then((data) => { .dispatch('molecules/Toot/unreblog', message)
.then(data => {
this.$emit('update', data) this.$emit('update', data)
}) })
.catch(() => { .catch(err => {
console.error(err)
this.$message({ this.$message({
message: this.$t('message.unreblog_error'), message: this.$t('message.unreblog_error'),
type: 'error' type: 'error'
}) })
}) })
} else { } else {
this.$store.dispatch('molecules/Toot/reblog', message) this.$store
.then((data) => { .dispatch('molecules/Toot/reblog', message)
.then(data => {
this.$emit('update', data) this.$emit('update', data)
}) })
.catch(() => { .catch(err => {
console.error(err)
this.$message({ this.$message({
message: this.$t('message.reblog_error'), message: this.$t('message.reblog_error'),
type: 'error' type: 'error'
@ -403,22 +444,26 @@ export default {
}, },
changeFavourite(message) { changeFavourite(message) {
if (message.favourited) { if (message.favourited) {
this.$store.dispatch('molecules/Toot/removeFavourite', message) this.$store
.then((data) => { .dispatch('molecules/Toot/removeFavourite', message)
.then(data => {
this.$emit('update', data) this.$emit('update', data)
}) })
.catch(() => { .catche(err => {
console.error(err)
this.$message({ this.$message({
message: this.$t('message.unfavourite_error'), message: this.$t('message.unfavourite_error'),
type: 'error' type: 'error'
}) })
}) })
} else { } else {
this.$store.dispatch('molecules/Toot/addFavourite', message) this.$store
.then((data) => { .dispatch('molecules/Toot/addFavourite', message)
.then(data => {
this.$emit('update', data) this.$emit('update', data)
}) })
.catch(() => { .catch(err => {
console.error(err)
this.$message({ this.$message({
message: this.$t('message.favourite_error'), message: this.$t('message.favourite_error'),
type: 'error' type: 'error'
@ -427,13 +472,11 @@ export default {
} }
}, },
openImage(url, rawMediaList) { openImage(url, rawMediaList) {
const mediaList = rawMediaList.map((media) => { const mediaList = rawMediaList.map(media => {
return media.url return media.url
}) })
const currentIndex = mediaList.indexOf(url) const currentIndex = mediaList.indexOf(url)
this.$store.dispatch( this.$store.dispatch('TimelineSpace/Modals/ImageViewer/openModal', {
'TimelineSpace/Modals/ImageViewer/openModal',
{
currentIndex: currentIndex, currentIndex: currentIndex,
mediaList: rawMediaList mediaList: rawMediaList
}) })
@ -444,8 +487,9 @@ export default {
this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true) this.$store.commit('TimelineSpace/Contents/SideBar/changeOpenSideBar', true)
}, },
deleteToot(message) { deleteToot(message) {
this.$store.dispatch('molecules/Toot/deleteToot', message) this.$store
.then((message) => { .dispatch('molecules/Toot/deleteToot', message)
.then(message => {
this.$emit('delete', message) this.$emit('delete', message)
}) })
.catch(() => { .catch(() => {
@ -510,7 +554,6 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.toot { .toot {
padding: 8px 0 0 16px; padding: 8px 0 0 16px;

View File

@ -441,6 +441,21 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}, },
stopDirectMessagesStreaming: () => { stopDirectMessagesStreaming: () => {
ipcRenderer.send('stop-directmessages-streaming') ipcRenderer.send('stop-directmessages-streaming')
},
addFavouriteToot: ({ commit, state }, status: Status): boolean => {
commit('TimelineSpace/Contents/Home/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Notifications/updateToot', status, { root: true })
commit('TimelineSpace/Contents/Mentions/updateToot', status, { root: true })
if (state.unreadNotification.direct) {
commit('TimelineSpace/Contents/DirectMessages/updateToot', status, { root: true })
}
if (state.unreadNotification.local) {
commit('TimelineSpace/Contents/Local/updateToot', state, { root: true })
}
if (state.unreadNotification.public) {
commit('TimelineSpace/Contents/Public/updateToot', state, { root: true })
}
return true
} }
} }

View File

@ -4,10 +4,10 @@ import { Module, MutationTree, ActionTree } from 'vuex'
import { RootState } from '@/store' import { RootState } from '@/store'
export interface NotificationsState { export interface NotificationsState {
lazyLoading: boolean, lazyLoading: boolean
heading: boolean, heading: boolean
notifications: Array<Notification>, notifications: Array<Notification>
unreadNotifications: Array<Notification>, unreadNotifications: Array<Notification>
filter: string filter: string
} }
@ -49,7 +49,7 @@ const mutations: MutationTree<NotificationsState> = {
[MUTATION_TYPES.UPDATE_NOTIFICATIONS]: (state, notifications: Array<Notification>) => { [MUTATION_TYPES.UPDATE_NOTIFICATIONS]: (state, notifications: Array<Notification>) => {
state.notifications = notifications state.notifications = notifications
}, },
[MUTATION_TYPES.MERGE_NOTIFICATIONS]: (state) => { [MUTATION_TYPES.MERGE_NOTIFICATIONS]: state => {
state.notifications = state.unreadNotifications.slice(0, 80).concat(state.notifications) state.notifications = state.unreadNotifications.slice(0, 80).concat(state.notifications)
state.unreadNotifications = [] state.unreadNotifications = []
}, },
@ -57,10 +57,10 @@ const mutations: MutationTree<NotificationsState> = {
state.notifications = state.notifications.concat(notifications) state.notifications = state.notifications.concat(notifications)
}, },
[MUTATION_TYPES.UPDATE_TOOT]: (state, message: Status) => { [MUTATION_TYPES.UPDATE_TOOT]: (state, message: Status) => {
state.notifications = state.notifications.map((notification) => { state.notifications = state.notifications.map(notification => {
// I want to update toot only mention. // I want to update toot only mention.
// Because Toot component don't use status information when other patterns. // Because Toot component don't use status information when other patterns.
if (notification.status !== null && notification.status.id === message.id) { if (notification.type === 'mention' && notification.status && notification.status.id === message.id) {
const status = { const status = {
status: message status: message
} }
@ -70,10 +70,10 @@ const mutations: MutationTree<NotificationsState> = {
} }
}) })
}, },
[MUTATION_TYPES.CLEAR_NOTIFICATIONS]: (state) => { [MUTATION_TYPES.CLEAR_NOTIFICATIONS]: state => {
state.notifications = [] state.notifications = []
}, },
[MUTATION_TYPES.ARCHIVE_NOTIFICATIONS]: (state) => { [MUTATION_TYPES.ARCHIVE_NOTIFICATIONS]: state => {
state.notifications = state.notifications.slice(0, 30) state.notifications = state.notifications.slice(0, 30)
}, },
[MUTATION_TYPES.CHANGE_FILTER]: (state, filter: string) => { [MUTATION_TYPES.CHANGE_FILTER]: (state, filter: string) => {
@ -83,10 +83,7 @@ const mutations: MutationTree<NotificationsState> = {
const actions: ActionTree<NotificationsState, RootState> = { const actions: ActionTree<NotificationsState, RootState> = {
fetchNotifications: async ({ commit, rootState }): Promise<Array<Notification>> => { fetchNotifications: async ({ commit, rootState }): Promise<Array<Notification>> => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Array<Notification>> = await client.get<Array<Notification>>('/notifications', { limit: 30 }) const res: Response<Array<Notification>> = await client.get<Array<Notification>>('/notifications', { limit: 30 })
commit(MUTATION_TYPES.UPDATE_NOTIFICATIONS, res.data) commit(MUTATION_TYPES.UPDATE_NOTIFICATIONS, res.data)
@ -97,11 +94,9 @@ const actions: ActionTree<NotificationsState, RootState> = {
return Promise.resolve(null) return Promise.resolve(null)
} }
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true) commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!, return client
rootState.TimelineSpace.account.baseURL + '/api/v1' .get<Array<Notification>>('/notifications', { max_id: lastNotification.id, limit: 30 })
)
return client.get<Array<Notification>>('/notifications', { max_id: lastNotification.id, limit: 30 })
.then(res => { .then(res => {
commit(MUTATION_TYPES.INSERT_NOTIFICATIONS, res.data) commit(MUTATION_TYPES.INSERT_NOTIFICATIONS, res.data)
return res.data return res.data

View File

@ -9,10 +9,7 @@ const state = (): TootState => ({})
const actions: ActionTree<TootState, RootState> = { const actions: ActionTree<TootState, RootState> = {
reblog: async ({ rootState }, message: Status) => { reblog: async ({ rootState }, message: Status) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/reblog`) const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/reblog`)
// API returns new status when reblog. // API returns new status when reblog.
// Reblog target status is in the data.reblog. // Reblog target status is in the data.reblog.
@ -21,43 +18,29 @@ const actions: ActionTree<TootState, RootState> = {
return res.data.reblog return res.data.reblog
}, },
unreblog: async ({ rootState }, message: Status) => { unreblog: async ({ rootState }, message: Status) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unreblog`) const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unreblog`)
return res.data return res.data
}, },
addFavourite: async ({ rootState }, message: Status) => { addFavourite: async ({ rootState, dispatch }, message: Status) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/favourite`) const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/favourite`)
ipcRenderer.send('fav-rt-action-sound') ipcRenderer.send('fav-rt-action-sound')
dispatch('TimelineSpace/addFavouriteToot', res.data, { root: true })
return res.data return res.data
}, },
removeFavourite: async ({ rootState }, message: Status) => { removeFavourite: async ({ rootState }, message: Status) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unfavourite`) const res: Response<Status> = await client.post<Status>(`/statuses/${message.id}/unfavourite`)
return res.data return res.data
}, },
deleteToot: async ({ rootState }, message: Status) => { deleteToot: async ({ rootState }, message: Status) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
await client.del(`/statuses/${message.id}`) await client.del(`/statuses/${message.id}`)
return message return message
}, },
block: async ({ rootState }, account: Account) => { block: async ({ rootState }, account: Account) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
return client.post(`/accounts/${account.id}/block`) return client.post(`/accounts/${account.id}/block`)
} }
} }