feat: Adds a basic conversations timeline (#1137)

fixes #639
This commit is contained in:
Steve Genoud 2019-04-02 12:05:27 +02:00 committed by Nolan Lawson
parent 37a50dd8ea
commit 622dbde258
11 changed files with 89 additions and 7 deletions

View File

@ -16,6 +16,14 @@ function processMessage (instanceName, timelineName, message) {
case 'notification':
addStatusOrNotification(instanceName, 'notifications', JSON.parse(payload))
break
case 'conversation':
// This is a hack in order to mostly fit the conversation model into
// a timeline of statuses. To have a clean implementation we would need to
// reproduce what is done for statuses for the conversation.
//
// It will add new DMs as new conversations intead of updating existing threads
addStatusOrNotification(instanceName, timelineName, JSON.parse(payload).last_status)
break
}
stop('processMessage')
}
@ -24,7 +32,12 @@ export function createStream (streamingApi, instanceName, accessToken,
timelineName, onOpenStream) {
return new TimelineStream(streamingApi, accessToken, timelineName, {
onMessage (msg) {
if (msg.event !== 'update' && msg.event !== 'delete' && msg.event !== 'notification') {
if (
msg.event !== 'update' &&
msg.event !== 'delete' &&
msg.event !== 'notification' &&
msg.event !== 'conversation'
) {
console.error("don't know how to handle event", msg)
return
}

View File

@ -12,6 +12,8 @@ function getStreamName (timeline) {
return 'user'
case 'notifications':
return 'user:notification'
case 'conversations':
return 'direct'
}
if (timeline.startsWith('tag/')) {
return 'hashtag'

View File

@ -12,6 +12,8 @@ function getTimelineUrlPath (timeline) {
return 'notifications'
case 'favorites':
return 'favourites'
case 'conversations':
return 'conversations'
}
if (timeline.startsWith('tag/')) {
return 'timelines/tag'
@ -61,5 +63,8 @@ export function getTimeline (instanceName, accessToken, timeline, maxId, since,
url += '?' + paramsString(params)
return get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
const timelineRequest = get(url, auth(accessToken), { timeout: DEFAULT_TIMEOUT })
if (timeline !== 'conversations') return timelineRequest
return timelineRequest.then(items => items.map(item => item.last_status))
}

View File

@ -4,6 +4,7 @@
<Shortcut key="g h" on:pressed="goto('/')"/>
<Shortcut key="g n" on:pressed="goto('/notifications')"/>
<Shortcut key="g c" on:pressed="goto('/community')"/>
<Shortcut key="g d" on:pressed="goto('/conversations')"/>
<Shortcut key="s" on:pressed="goto('/search')"/>
<Shortcut key="h|?" on:pressed="showShortcutHelpDialog()"/>
<Shortcut key="c" on:pressed="showComposeDialog()"/>

View File

@ -13,6 +13,7 @@
<li><kbd>g</kbd> + <kbd>l</kbd> to go to the local timeline</li>
<li><kbd>g</kbd> + <kbd>t</kbd> to go to the federated timeline</li>
<li><kbd>g</kbd> + <kbd>c</kbd> to go to the community page</li>
<li><kbd>g</kbd> + <kbd>d</kbd> to go to the conversations page</li>
<li><kbd>h</kbd> or <kbd>?</kbd> to toggle the help dialog</li>
<li><kbd>Backspace</kbd> to go back, close dialogs</li>
</ul>

View File

@ -73,10 +73,6 @@
border-bottom: 1px solid var(--main-border);
}
.status-article.status-direct {
background-color: var(--status-direct-background);
}
.status-article:focus {
outline: none; /* focus is on the parent instead */
}
@ -288,7 +284,6 @@
className: ({ visibility, timelineType, isStatusInOwnThread, $underlineLinks, $disableTapOnStatus }) => (classname(
'status-article',
'shortcut-list-item',
visibility === 'direct' && 'status-direct',
timelineType !== 'search' && 'status-in-timeline',
isStatusInOwnThread && 'status-in-own-thread',
$underlineLinks && 'underline-links',

View File

@ -21,6 +21,11 @@
icon="#fa-star"
pinnable="true"
/>
<PageListItem href="/conversations"
label="Conversations"
icon="#fa-envelope"
pinnable="true"
/>
</PageList>
{#if $lists.length}

View File

@ -0,0 +1,32 @@
{#if $isUserLoggedIn}
<TimelinePage timeline="conversations">
{#if $pinnedPage !== '/conversations'}
<DynamicPageBanner title="Conversations" icon="#fa-envelope"/>
{/if}
</TimelinePage>
{:else}
<HiddenFromSSR>
<FreeTextLayout>
<h1>Conversations</h1>
<p>Your conversations will appear here when logged in.</p>
</FreeTextLayout>
</HiddenFromSSR>
{/if}
<script>
import TimelinePage from '../_components/TimelinePage.html'
import FreeTextLayout from '../_components/FreeTextLayout.html'
import { store } from '../_store/store.js'
import HiddenFromSSR from '../_components/HiddenFromSSR'
import DynamicPageBanner from '../_components/DynamicPageBanner.html'
export default {
store: () => store,
components: {
TimelinePage,
FreeTextLayout,
HiddenFromSSR,
DynamicPageBanner
}
}
</script>

View File

@ -24,6 +24,13 @@ export function navComputations (store) {
svg: '#fa-globe',
label: 'Federated'
}
} else if (pinnedPage === '/conversations') {
pinnedPageObject = {
name: 'conversations',
href: '/conversations',
svg: '#fa-envelope',
label: 'Conversations'
}
} else if (pinnedPage === '/favorites') {
pinnedPageObject = {
name: 'favorites',

View File

@ -26,6 +26,7 @@ export function timelineObservers () {
!(
timeline !== 'local' &&
timeline !== 'federated' &&
timeline !== 'conversations' &&
!timeline.startsWith('list/') &&
!timeline.startsWith('tag/')
)

View File

@ -0,0 +1,20 @@
<Title name="Favorites" />
<LazyPage {pageComponent} {params} />
<script>
import Title from './_components/Title.html'
import LazyPage from './_components/LazyPage.html'
import pageComponent from './_pages/conversations.html'
export default {
components: {
Title,
LazyPage
},
data: () => ({
pageComponent
})
}
</script>