forked from cybrespace/pinafore
immediately add replies to threads
This commit is contained in:
parent
6f1903fec5
commit
7813cf99ed
|
@ -20,6 +20,43 @@ async function removeDuplicates (instanceName, timelineName, updates) {
|
|||
return updates.filter(update => !existingItemIds.has(update.id))
|
||||
}
|
||||
|
||||
async function insertUpdatesIntoTimeline (instanceName, timelineName, updates) {
|
||||
updates = await removeDuplicates(instanceName, timelineName, updates)
|
||||
|
||||
await database.insertTimelineItems(instanceName, timelineName, updates)
|
||||
|
||||
let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || []
|
||||
if (updates && updates.length) {
|
||||
itemIdsToAdd = itemIdsToAdd.concat(updates.map(_ => _.id))
|
||||
console.log('adding ', itemIdsToAdd.length, 'items to itemIdsToAdd')
|
||||
store.setForTimeline(instanceName, timelineName, {itemIdsToAdd: itemIdsToAdd})
|
||||
}
|
||||
}
|
||||
|
||||
async function insertUpdatesIntoThreads (instanceName, updates) {
|
||||
if (!updates.length) {
|
||||
return
|
||||
}
|
||||
|
||||
let threads = store.getThreadsForTimeline(instanceName)
|
||||
|
||||
for (let timelineName of Object.keys(threads)) {
|
||||
let thread = threads[timelineName]
|
||||
let updatesForThisThread = updates.filter(status => {
|
||||
return thread.includes(status.in_reply_to_id) && !thread.includes(status.id)
|
||||
})
|
||||
let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || []
|
||||
for (let update of updatesForThisThread) {
|
||||
if (!itemIdsToAdd.includes(update.id)) {
|
||||
itemIdsToAdd.push(update.id)
|
||||
}
|
||||
}
|
||||
console.log('adding ', itemIdsToAdd.length, 'items to itemIdsToAdd for thread', timelineName)
|
||||
store.setForTimeline(instanceName, timelineName, {itemIdsToAdd: itemIdsToAdd})
|
||||
console.log('timelineName', timelineName, 'itemIdsToAdd', itemIdsToAdd)
|
||||
}
|
||||
}
|
||||
|
||||
async function processFreshUpdates (instanceName, timelineName) {
|
||||
mark('processFreshUpdates')
|
||||
let freshUpdates = store.getForTimeline(instanceName, timelineName, 'freshUpdates')
|
||||
|
@ -27,18 +64,10 @@ async function processFreshUpdates (instanceName, timelineName) {
|
|||
let updates = freshUpdates.slice()
|
||||
store.setForTimeline(instanceName, timelineName, {freshUpdates: []})
|
||||
|
||||
updates = await removeDuplicates(instanceName, timelineName, updates)
|
||||
|
||||
await database.insertTimelineItems(instanceName, timelineName, updates)
|
||||
|
||||
let itemIdsToAdd = store.getForTimeline(instanceName, timelineName, 'itemIdsToAdd') || []
|
||||
if (updates && updates.length) {
|
||||
itemIdsToAdd = itemIdsToAdd.concat(updates.map(_ => _.id))
|
||||
console.log('adding ', itemIdsToAdd.length, 'items to itemIdsToAdd')
|
||||
store.setForTimeline(instanceName, timelineName, {itemIdsToAdd: itemIdsToAdd})
|
||||
}
|
||||
stop('processFreshUpdates')
|
||||
await insertUpdatesIntoTimeline(instanceName, timelineName, updates)
|
||||
await insertUpdatesIntoThreads(instanceName, updates.filter(status => status.in_reply_to_id))
|
||||
}
|
||||
stop('processFreshUpdates')
|
||||
}
|
||||
|
||||
const lazilyProcessFreshUpdates = throttle((instanceName, timelineName) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { store } from '../_store/store'
|
||||
import { toast } from '../_utils/toast'
|
||||
import { postStatusToServer } from '../_api/statuses'
|
||||
import { postStatus as postStatusToServer } from '../_api/statuses'
|
||||
import { addStatusOrNotification } from './addStatusOrNotification'
|
||||
import { database } from '../_database/database'
|
||||
|
||||
|
|
|
@ -95,3 +95,18 @@ export async function showMoreItemsForCurrentTimeline () {
|
|||
})
|
||||
stop('showMoreItemsForCurrentTimeline')
|
||||
}
|
||||
|
||||
export async function showMoreItemsForCurrentThread () {
|
||||
mark('showMoreItemsForCurrentThread')
|
||||
let instanceName = store.get('currentInstance')
|
||||
let timelineName = store.get('currentTimeline')
|
||||
let itemIdsToAdd = store.get('itemIdsToAdd')
|
||||
// TODO: update database and do this merge correctly
|
||||
let timelineItemIds = store.getForTimeline(instanceName, timelineName, 'timelineItemIds')
|
||||
timelineItemIds = timelineItemIds.concat(itemIdsToAdd)
|
||||
store.setForTimeline(instanceName, timelineName, {
|
||||
itemIdsToAdd: [],
|
||||
timelineItemIds: timelineItemIds
|
||||
})
|
||||
stop('showMoreItemsForCurrentThread')
|
||||
}
|
||||
|
|
|
@ -66,14 +66,14 @@
|
|||
if (realm !== 'home') {
|
||||
// if this is a reply, populate the handle immediately
|
||||
insertHandleForReply(realm)
|
||||
}
|
||||
|
||||
// if this is a reply, go back immediately after it's posted
|
||||
this.observe('postedStatusForRealm', postedStatusForRealm => {
|
||||
if (postedStatusForRealm === realm) {
|
||||
window.history.back()
|
||||
}
|
||||
}, {init: false})
|
||||
// if this is a reply, go back immediately after it's posted
|
||||
this.observe('postedStatusForRealm', postedStatusForRealm => {
|
||||
if (postedStatusForRealm === realm) {
|
||||
window.history.back()
|
||||
}
|
||||
}, {init: false})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ComposeAuthor,
|
||||
|
|
|
@ -60,7 +60,12 @@
|
|||
import VirtualList from '../virtualList/VirtualList.html'
|
||||
import { timelines } from '../../_static/timelines'
|
||||
import { database } from '../../_database/database'
|
||||
import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
|
||||
import {
|
||||
initializeTimeline,
|
||||
fetchTimelineItemsOnScrollToBottom,
|
||||
setupTimeline,
|
||||
showMoreItemsForCurrentThread
|
||||
} from '../../_actions/timeline'
|
||||
import LoadingPage from '../LoadingPage.html'
|
||||
import { focusWithCapture, blurWithCapture } from '../../_utils/events'
|
||||
import { showMoreItemsForCurrentTimeline } from '../../_actions/timeline'
|
||||
|
@ -195,7 +200,10 @@
|
|||
let scrollTop = this.get('scrollTop')
|
||||
let shouldShowHeader = this.store.get('shouldShowHeader')
|
||||
let showHeader = this.store.get('showHeader')
|
||||
if (scrollTop === 0 && !shouldShowHeader && !showHeader) {
|
||||
if (timelineName.startsWith('status/')) {
|
||||
// this is a thread, just insert the statuses already
|
||||
showMoreItemsForCurrentThread()
|
||||
} else if (scrollTop === 0 && !shouldShowHeader && !showHeader) {
|
||||
// if the user is scrolled to the top and we're not showing the header, then
|
||||
// just insert the statuses. this is "chat room mode"
|
||||
showMoreItemsForCurrentTimeline()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import pickBy from 'lodash/pickBy'
|
||||
|
||||
export function timelineMixins (Store) {
|
||||
Store.prototype.setForTimeline = function (instanceName, timelineName, obj) {
|
||||
let valuesToSet = {}
|
||||
|
@ -23,4 +25,18 @@ export function timelineMixins (Store) {
|
|||
let timelineName = this.get('currentTimeline')
|
||||
this.setForTimeline(instanceName, timelineName, obj)
|
||||
}
|
||||
|
||||
Store.prototype.getThreadsForTimeline = function (instanceName) {
|
||||
let root = this.get('timelineData_timelineItemIds') || {}
|
||||
let instanceData = root[instanceName] = root[instanceName] || {}
|
||||
|
||||
return pickBy(instanceData, (value, key) => {
|
||||
return key.startsWith('status/')
|
||||
})
|
||||
}
|
||||
|
||||
Store.prototype.getThreadsForCurrentTimeline = function () {
|
||||
let instanceName = this.get('currentInstance')
|
||||
return this.getThreadsForTimeline(instanceName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ test('account handle populated correctly for replies', async t => {
|
|||
.expect(composeInput.value).eql('')
|
||||
})
|
||||
|
||||
test('replying to posts wth mentions', async t => {
|
||||
test('replying to posts with mentions', async t => {
|
||||
await t.useRole(foobarRole)
|
||||
.click(getNthReplyButton(1))
|
||||
.expect(getUrl()).contains('/statuses')
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { foobarRole } from '../roles'
|
||||
import {
|
||||
composeInput, getNthReplyButton, getNthStatus, getUrl, homeNavButton, notificationsNavButton,
|
||||
postStatusButton
|
||||
} from '../utils'
|
||||
|
||||
fixture`103-compose.js`
|
||||
.page`http://localhost:4002`
|
||||
|
||||
test('statuses show up in home timeline', async t => {
|
||||
await t.useRole(foobarRole)
|
||||
.typeText(composeInput, 'hello world', {paste: true})
|
||||
.click(postStatusButton)
|
||||
.expect(getNthStatus(0).innerText).contains('hello world')
|
||||
.click(notificationsNavButton)
|
||||
.expect(getUrl()).contains('/notifications')
|
||||
.click(homeNavButton)
|
||||
.expect(getUrl()).eql('http://localhost:4002/')
|
||||
.expect(getNthStatus(0).innerText).contains('hello world')
|
||||
.navigateTo('/')
|
||||
.expect(getNthStatus(0).innerText).contains('hello world')
|
||||
})
|
||||
|
||||
test('statuses in threads show up in right order', async t => {
|
||||
await t.useRole(foobarRole)
|
||||
.navigateTo('/accounts/5')
|
||||
.click(getNthStatus(2))
|
||||
.expect(getUrl()).contains('/statuses')
|
||||
.click(getNthReplyButton(3))
|
||||
.expect(getUrl()).contains('/reply')
|
||||
.typeText(composeInput, 'my reply!', {paste: true})
|
||||
.click(postStatusButton)
|
||||
.expect(getUrl()).match(/statuses\/[^/]+$/)
|
||||
.expect(getNthStatus(5).innerText).contains('@baz my reply!')
|
||||
.navigateTo('/accounts/5')
|
||||
.click(getNthStatus(2))
|
||||
.expect(getNthStatus(5).innerText).contains('@baz my reply!')
|
||||
})
|
|
@ -25,6 +25,7 @@ export const passwordInput = $('input#user_password')
|
|||
export const authorizeInput = $('button[type=submit]:not(.negative)')
|
||||
export const logInToInstanceLink = $('a[href="/settings/instances/add"]')
|
||||
export const searchInput = $('.search-input')
|
||||
export const postStatusButton = $('.compose-box-button')
|
||||
|
||||
export const favoritesCountElement = $('.status-favs-reblogs:nth-child(3)').addCustomDOMProperties({
|
||||
innerCount: el => parseInt(el.innerText, 10)
|
||||
|
|
Loading…
Reference in New Issue