diff --git a/routes/_actions/favorite.js b/routes/_actions/favorite.js
index 60959e3..c809159 100644
--- a/routes/_actions/favorite.js
+++ b/routes/_actions/favorite.js
@@ -5,7 +5,7 @@ import { toast } from '../_utils/toast'
export async function setFavorited (statusId, favorited) {
if (!store.get('online')) {
- toast.say('You cannot favorite or unfavorite while offline.')
+ toast.say(`You cannot ${favorited ? 'favorite' : 'unfavorite'} while offline.`)
return
}
let instanceName = store.get('currentInstance')
@@ -22,6 +22,6 @@ export async function setFavorited (statusId, favorited) {
store.set({statusModifications: statusModifications})
} catch (e) {
console.error(e)
- toast.say('Failed to favorite or unfavorite. ' + (e.message || ''))
+ toast.say(`Failed to ${favorited ? 'favorite' : 'unfavorite'}. ` + (e.message || ''))
}
}
diff --git a/routes/_actions/reblog.js b/routes/_actions/reblog.js
new file mode 100644
index 0000000..0069dac
--- /dev/null
+++ b/routes/_actions/reblog.js
@@ -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 || ''))
+ }
+}
diff --git a/routes/_api/reblog.js b/routes/_api/reblog.js
new file mode 100644
index 0000000..31dcac6
--- /dev/null
+++ b/routes/_api/reblog.js
@@ -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))
+}
diff --git a/routes/_components/status/StatusToolbar.html b/routes/_components/status/StatusToolbar.html
index 436fb23..bbda219 100644
--- a/routes/_components/status/StatusToolbar.html
+++ b/routes/_components/status/StatusToolbar.html
@@ -4,11 +4,13 @@
href="#fa-reply"
/>
status.visibility,
- boostLabel: (visibility) => {
+ reblogLabel: (visibility) => {
switch (visibility) {
case 'private':
return 'Cannot be boosted because this is followers-only'
@@ -73,7 +82,7 @@
return 'Boost'
}
},
- boostIcon: (visibility) => {
+ reblogIcon: (visibility) => {
switch (visibility) {
case 'private':
return '#fa-lock'
@@ -83,9 +92,15 @@
return '#fa-retweet'
}
},
- boostDisabled: (visibility) => {
+ reblogDisabled: (visibility) => {
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) => {
if ($currentStatusModifications && status.id in $currentStatusModifications.favorites) {
return $currentStatusModifications.favorites[status.id]
@@ -93,7 +108,8 @@
return status.favourited
},
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}`,
}
}
\ No newline at end of file
diff --git a/routes/_database/timelines.js b/routes/_database/timelines.js
index f4294c9..12bd476 100644
--- a/routes/_database/timelines.js
+++ b/routes/_database/timelines.js
@@ -416,3 +416,11 @@ export async function setStatusFavorited (instanceName, statusId, favorited) {
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
+ })
+}
diff --git a/routes/_utils/ajax.js b/routes/_utils/ajax.js
index 162f586..533430f 100644
--- a/routes/_utils/ajax.js
+++ b/routes/_utils/ajax.js
@@ -7,7 +7,7 @@ function fetchWithTimeout (url, options) {
})
}
-async function throwErrorIfInvalidResponse(response) {
+async function throwErrorIfInvalidResponse (response) {
let json = await response.json()
if (response.status >= 200 && response.status < 300) {
return json
diff --git a/tests/spec/12-favorite-unfavorite.js b/tests/spec/30-favorite-unfavorite.js
similarity index 96%
rename from tests/spec/12-favorite-unfavorite.js
rename to tests/spec/30-favorite-unfavorite.js
index 0fae5b3..b52f537 100644
--- a/tests/spec/12-favorite-unfavorite.js
+++ b/tests/spec/30-favorite-unfavorite.js
@@ -5,7 +5,7 @@ import {
} from '../utils'
import { foobarRole } from '../roles'
-fixture`12-favorite-unfavorite.js`
+fixture`30-favorite-unfavorite.js`
.page`http://localhost:4002`
test('favorites a status', async t => {
@@ -54,7 +54,7 @@ test('unfavorites a status', async t => {
.expect(getNthFavorited(1)).eql('true')
})
-test('Keeps the correct count', async t => {
+test('Keeps the correct favorites count', async t => {
await t.useRole(foobarRole)
.hover(getNthStatus(4))
.click(getNthFavoriteButton(4))
diff --git a/tests/spec/31-reblog-unreblog.js b/tests/spec/31-reblog-unreblog.js
new file mode 100644
index 0000000..3dee8be
--- /dev/null
+++ b/tests/spec/31-reblog-unreblog.js
@@ -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)
+})
diff --git a/tests/utils.js b/tests/utils.js
index bdfc704..a573cd7 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -51,6 +51,14 @@ export function getFavoritesCount () {
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 () {
return reblogsCountElement.innerCount
}