add reblogging/unreblogging
This commit is contained in:
parent
a82cc57f83
commit
00ccf35777
|
@ -5,7 +5,7 @@ import { toast } from '../_utils/toast'
|
||||||
|
|
||||||
export async function setFavorited (statusId, favorited) {
|
export async function setFavorited (statusId, favorited) {
|
||||||
if (!store.get('online')) {
|
if (!store.get('online')) {
|
||||||
toast.say('You cannot favorite or unfavorite while offline.')
|
toast.say(`You cannot ${favorited ? 'favorite' : 'unfavorite'} while offline.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
|
@ -22,6 +22,6 @@ export async function setFavorited (statusId, favorited) {
|
||||||
store.set({statusModifications: statusModifications})
|
store.set({statusModifications: statusModifications})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say('Failed to favorite or unfavorite. ' + (e.message || ''))
|
toast.say(`Failed to ${favorited ? 'favorite' : 'unfavorite'}. ` + (e.message || ''))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
routes/_actions/reblog.js
Normal file
27
routes/_actions/reblog.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { store } from '../_store/store'
|
||||||
|
import { database } from '../_database/database'
|
||||||
|
import { toast } from '../_utils/toast'
|
||||||
|
import { reblogStatus, unreblogStatus } from '../_api/reblog'
|
||||||
|
|
||||||
|
export async function setReblogged (statusId, reblogged) {
|
||||||
|
if (!store.get('online')) {
|
||||||
|
toast.say(`You cannot ${reblogged ? 'boost' : 'unboost'} while offline.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let instanceName = store.get('currentInstance')
|
||||||
|
let accessToken = store.get('accessToken')
|
||||||
|
try {
|
||||||
|
await (reblogged
|
||||||
|
? reblogStatus(instanceName, accessToken, statusId)
|
||||||
|
: unreblogStatus(instanceName, accessToken, statusId))
|
||||||
|
await database.setStatusReblogged(instanceName, statusId, reblogged)
|
||||||
|
let statusModifications = store.get('statusModifications')
|
||||||
|
let currentStatusModifications = statusModifications[instanceName] =
|
||||||
|
(statusModifications[instanceName] || {favorites: {}, reblogs: {}})
|
||||||
|
currentStatusModifications.reblogs[statusId] = reblogged
|
||||||
|
store.set({statusModifications: statusModifications})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
toast.say(`Failed to ${reblogged ? 'boost' : 'unboost'}. ` + (e.message || ''))
|
||||||
|
}
|
||||||
|
}
|
12
routes/_api/reblog.js
Normal file
12
routes/_api/reblog.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { post } from '../_utils/ajax'
|
||||||
|
import { basename, auth } from './utils'
|
||||||
|
|
||||||
|
export async function reblogStatus (instanceName, accessToken, statusId) {
|
||||||
|
let url = `${basename(instanceName)}/api/v1/statuses/${statusId}/reblog`
|
||||||
|
return post(url, null, auth(accessToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unreblogStatus (instanceName, accessToken, statusId) {
|
||||||
|
let url = `${basename(instanceName)}/api/v1/statuses/${statusId}/unreblog`
|
||||||
|
return post(url, null, auth(accessToken))
|
||||||
|
}
|
|
@ -4,11 +4,13 @@
|
||||||
href="#fa-reply"
|
href="#fa-reply"
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
label="{{boostLabel}}"
|
label="{{reblogLabel}}"
|
||||||
pressable="{{!boostDisabled}}"
|
pressable="{{!reblogDisabled}}"
|
||||||
pressed="{{status.reblogged}}"
|
pressed="{{reblogged}}"
|
||||||
disabled="{{boostDisabled}}"
|
disabled="{{reblogDisabled}}"
|
||||||
href="{{boostIcon}}"
|
href="{{reblogIcon}}"
|
||||||
|
delegateKey="{{reblogKey}}"
|
||||||
|
ref:reblogNode
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
label="Favorite"
|
label="Favorite"
|
||||||
|
@ -38,17 +40,19 @@
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { registerClickDelegate, unregisterClickDelegate } from '../../_utils/delegate'
|
import { registerClickDelegate, unregisterClickDelegate } from '../../_utils/delegate'
|
||||||
import { setFavorited } from '../../_actions/favorite'
|
import { setFavorited } from '../../_actions/favorite'
|
||||||
|
import { setReblogged } from '../../_actions/reblog'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
oncreate() {
|
||||||
this.onFavoriteClick = this.onFavoriteClick.bind(this)
|
this.onFavoriteClick = this.onFavoriteClick.bind(this)
|
||||||
|
this.onReblogClick = this.onReblogClick.bind(this)
|
||||||
|
|
||||||
let favoriteKey = this.get('favoriteKey')
|
registerClickDelegate(this.get('favoriteKey'), this.onFavoriteClick)
|
||||||
registerClickDelegate(favoriteKey, this.onFavoriteClick)
|
registerClickDelegate(this.get('reblogKey'), this.onReblogClick)
|
||||||
},
|
},
|
||||||
ondestroy() {
|
ondestroy() {
|
||||||
let favoriteKey = this.get('favoriteKey')
|
unregisterClickDelegate(this.get('favoriteKey'))
|
||||||
unregisterClickDelegate(favoriteKey)
|
unregisterClickDelegate(this.get('reblogKey'))
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
IconButton
|
IconButton
|
||||||
|
@ -59,11 +63,16 @@
|
||||||
let statusId = this.get('statusId')
|
let statusId = this.get('statusId')
|
||||||
let favorited = this.get('favorited')
|
let favorited = this.get('favorited')
|
||||||
/* no await */ setFavorited(statusId, !favorited)
|
/* no await */ setFavorited(statusId, !favorited)
|
||||||
|
},
|
||||||
|
onReblogClick() {
|
||||||
|
let statusId = this.get('statusId')
|
||||||
|
let reblogged = this.get('reblogged')
|
||||||
|
/* no await */ setReblogged(statusId, !reblogged)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
visibility: (status) => status.visibility,
|
visibility: (status) => status.visibility,
|
||||||
boostLabel: (visibility) => {
|
reblogLabel: (visibility) => {
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
case 'private':
|
case 'private':
|
||||||
return 'Cannot be boosted because this is followers-only'
|
return 'Cannot be boosted because this is followers-only'
|
||||||
|
@ -73,7 +82,7 @@
|
||||||
return 'Boost'
|
return 'Boost'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
boostIcon: (visibility) => {
|
reblogIcon: (visibility) => {
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
case 'private':
|
case 'private':
|
||||||
return '#fa-lock'
|
return '#fa-lock'
|
||||||
|
@ -83,9 +92,15 @@
|
||||||
return '#fa-retweet'
|
return '#fa-retweet'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
boostDisabled: (visibility) => {
|
reblogDisabled: (visibility) => {
|
||||||
return visibility === 'private' || visibility === 'direct'
|
return visibility === 'private' || visibility === 'direct'
|
||||||
},
|
},
|
||||||
|
reblogged: (status, $currentStatusModifications) => {
|
||||||
|
if ($currentStatusModifications && status.id in $currentStatusModifications.reblogs) {
|
||||||
|
return $currentStatusModifications.reblogs[status.id]
|
||||||
|
}
|
||||||
|
return status.reblogged
|
||||||
|
},
|
||||||
favorited: (status, $currentStatusModifications) => {
|
favorited: (status, $currentStatusModifications) => {
|
||||||
if ($currentStatusModifications && status.id in $currentStatusModifications.favorites) {
|
if ($currentStatusModifications && status.id in $currentStatusModifications.favorites) {
|
||||||
return $currentStatusModifications.favorites[status.id]
|
return $currentStatusModifications.favorites[status.id]
|
||||||
|
@ -93,7 +108,8 @@
|
||||||
return status.favourited
|
return status.favourited
|
||||||
},
|
},
|
||||||
statusId: (status) => status.id,
|
statusId: (status) => status.id,
|
||||||
favoriteKey: (statusId, timelineType, timelineValue) => `fav-${timelineType}-${timelineValue}-${statusId}`
|
favoriteKey: (statusId, timelineType, timelineValue) => `fav-${timelineType}-${timelineValue}-${statusId}`,
|
||||||
|
reblogKey: (statusId, timelineType, timelineValue) => `reblog-${timelineType}-${timelineValue}-${statusId}`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -416,3 +416,11 @@ export async function setStatusFavorited (instanceName, statusId, favorited) {
|
||||||
status.favourites_count = (status.favourites_count || 0) + delta
|
status.favourites_count = (status.favourites_count || 0) + delta
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setStatusReblogged (instanceName, statusId, reblogged) {
|
||||||
|
return updateStatus(instanceName, statusId, status => {
|
||||||
|
let delta = (reblogged ? 1 : 0) - (status.reblogged ? 1 : 0)
|
||||||
|
status.reblogged = reblogged
|
||||||
|
status.reblogs_count = (status.reblogs_count || 0) + delta
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ function fetchWithTimeout (url, options) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function throwErrorIfInvalidResponse(response) {
|
async function throwErrorIfInvalidResponse (response) {
|
||||||
let json = await response.json()
|
let json = await response.json()
|
||||||
if (response.status >= 200 && response.status < 300) {
|
if (response.status >= 200 && response.status < 300) {
|
||||||
return json
|
return json
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { foobarRole } from '../roles'
|
import { foobarRole } from '../roles'
|
||||||
|
|
||||||
fixture`12-favorite-unfavorite.js`
|
fixture`30-favorite-unfavorite.js`
|
||||||
.page`http://localhost:4002`
|
.page`http://localhost:4002`
|
||||||
|
|
||||||
test('favorites a status', async t => {
|
test('favorites a status', async t => {
|
||||||
|
@ -54,7 +54,7 @@ test('unfavorites a status', async t => {
|
||||||
.expect(getNthFavorited(1)).eql('true')
|
.expect(getNthFavorited(1)).eql('true')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Keeps the correct count', async t => {
|
test('Keeps the correct favorites count', async t => {
|
||||||
await t.useRole(foobarRole)
|
await t.useRole(foobarRole)
|
||||||
.hover(getNthStatus(4))
|
.hover(getNthStatus(4))
|
||||||
.click(getNthFavoriteButton(4))
|
.click(getNthFavoriteButton(4))
|
78
tests/spec/31-reblog-unreblog.js
Normal file
78
tests/spec/31-reblog-unreblog.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
getNthReblogButton, getNthReblogged, getNthStatus, getReblogsCount, getUrl, homeNavButton,
|
||||||
|
notificationsNavButton,
|
||||||
|
scrollToBottomOfTimeline, scrollToTopOfTimeline
|
||||||
|
} from '../utils'
|
||||||
|
import { foobarRole } from '../roles'
|
||||||
|
|
||||||
|
fixture`31-reblog-unreblog.js`
|
||||||
|
.page`http://localhost:4002`
|
||||||
|
|
||||||
|
test('reblogs a status', async t => {
|
||||||
|
await t.useRole(foobarRole)
|
||||||
|
.hover(getNthStatus(0))
|
||||||
|
.expect(getNthReblogged(0)).eql('false')
|
||||||
|
.click(getNthReblogButton(0))
|
||||||
|
.expect(getNthReblogged(0)).eql('true')
|
||||||
|
|
||||||
|
// scroll down and back up to force an unrender
|
||||||
|
await scrollToBottomOfTimeline(t)
|
||||||
|
await scrollToTopOfTimeline(t)
|
||||||
|
await t
|
||||||
|
.hover(getNthStatus(0))
|
||||||
|
.expect(getNthReblogged(0)).eql('true')
|
||||||
|
.click(notificationsNavButton)
|
||||||
|
.click(homeNavButton)
|
||||||
|
.expect(getNthReblogged(0)).eql('true')
|
||||||
|
.click(notificationsNavButton)
|
||||||
|
.expect(getUrl()).contains('/notifications')
|
||||||
|
.click(homeNavButton)
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/')
|
||||||
|
.expect(getNthReblogged(0)).eql('true')
|
||||||
|
.click(getNthReblogButton(0))
|
||||||
|
.expect(getNthReblogged(0)).eql('false')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('unreblogs a status', async t => {
|
||||||
|
await t.useRole(foobarRole)
|
||||||
|
.hover(getNthStatus(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
.click(getNthReblogButton(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('true')
|
||||||
|
.click(getNthReblogButton(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
|
||||||
|
// scroll down and back up to force an unrender
|
||||||
|
await scrollToBottomOfTimeline(t)
|
||||||
|
await scrollToTopOfTimeline(t)
|
||||||
|
await t
|
||||||
|
.hover(getNthStatus(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
.click(notificationsNavButton)
|
||||||
|
.click(homeNavButton)
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
.click(notificationsNavButton)
|
||||||
|
.navigateTo('/')
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
.click(getNthReblogButton(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('true')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Keeps the correct reblogs count', async t => {
|
||||||
|
await t.useRole(foobarRole)
|
||||||
|
.hover(getNthStatus(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('true')
|
||||||
|
.click(getNthStatus(4))
|
||||||
|
.expect(getUrl()).contains('/status')
|
||||||
|
.expect(getNthReblogged(0)).eql('true')
|
||||||
|
.expect(getReblogsCount()).eql(2)
|
||||||
|
.click(homeNavButton)
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/')
|
||||||
|
.hover(getNthStatus(4))
|
||||||
|
.click(getNthReblogButton(4))
|
||||||
|
.expect(getNthReblogged(4)).eql('false')
|
||||||
|
.click(getNthStatus(4))
|
||||||
|
.expect(getUrl()).contains('/status')
|
||||||
|
.expect(getNthReblogged(0)).eql('false')
|
||||||
|
.expect(getReblogsCount()).eql(1)
|
||||||
|
})
|
|
@ -51,6 +51,14 @@ export function getFavoritesCount () {
|
||||||
return favoritesCountElement.innerCount
|
return favoritesCountElement.innerCount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNthReblogButton (n) {
|
||||||
|
return getNthStatus(n).find('.status-toolbar button:nth-child(2)')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNthReblogged (n) {
|
||||||
|
return getNthReblogButton(n).getAttribute('aria-pressed')
|
||||||
|
}
|
||||||
|
|
||||||
export function getReblogsCount () {
|
export function getReblogsCount () {
|
||||||
return reblogsCountElement.innerCount
|
return reblogsCountElement.innerCount
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue