add infinite scrolling to local and federated timelines

pull/283/head
Dario Piotrowicz 2023-02-14 11:49:14 +00:00
rodzic d2a5ff122a
commit 91d6bb0503
4 zmienionych plików z 61 dodań i 26 usunięć

Wyświetl plik

@ -24,10 +24,10 @@ export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string },
) )
export default component$(() => { export default component$(() => {
const statuses = statusesLoader.use() const statuses = statusesLoader.use().value
return ( return (
<StatusesPanel <StatusesPanel
initialStatuses={statuses.value} initialStatuses={statuses}
fetchMoreStatuses={$(async (numOfCurrentStatuses: number) => { fetchMoreStatuses={$(async (numOfCurrentStatuses: number) => {
let statuses: MastodonStatus[] = [] let statuses: MastodonStatus[] = []
try { try {

Wyświetl plik

@ -1,11 +1,11 @@
import { component$ } from '@builder.io/qwik' import { $, component$ } from '@builder.io/qwik'
import { MastodonStatus } from '~/types' import { MastodonStatus } from '~/types'
import * as timelines from 'wildebeest/functions/api/v1/timelines/public' import * as timelines from 'wildebeest/functions/api/v1/timelines/public'
import Status from '~/components/Status'
import { DocumentHead, loader$ } from '@builder.io/qwik-city' import { DocumentHead, loader$ } from '@builder.io/qwik-city'
import StickyHeader from '~/components/StickyHeader/StickyHeader' import StickyHeader from '~/components/StickyHeader/StickyHeader'
import { getDocumentHead } from '~/utils/getDocumentHead' import { getDocumentHead } from '~/utils/getDocumentHead'
import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler' import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler'
import { StatusesPanel } from '~/components/StatusesPanel/StatusesPanel'
export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string }, Promise<MastodonStatus[]>>( export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string }, Promise<MastodonStatus[]>>(
async ({ platform, html }) => { async ({ platform, html }) => {
@ -22,7 +22,7 @@ export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string },
) )
export default component$(() => { export default component$(() => {
const statuses = statusesLoader.use() const statuses = statusesLoader.use().value
return ( return (
<> <>
@ -32,13 +32,22 @@ export default component$(() => {
<span>Federated timeline</span> <span>Federated timeline</span>
</div> </div>
</StickyHeader> </StickyHeader>
{statuses.value.length > 0 ? ( <StatusesPanel
statuses.value.map((status) => <Status status={status} />) initialStatuses={statuses}
) : ( fetchMoreStatuses={$(async (numOfCurrentStatuses: number) => {
<div class="flex-1 grid place-items-center bg-wildebeest-600 text-center"> let statuses: MastodonStatus[] = []
<p>Nothing to see right now. Check back later!</p> try {
</div> const response = await fetch(`/api/v1/timelines/public?offset=${numOfCurrentStatuses}`)
)} if (response.ok) {
const results = await response.text()
statuses = JSON.parse(results)
}
} catch {
/* empty */
}
return statuses
})}
/>
</> </>
) )
}) })

Wyświetl plik

@ -1,11 +1,11 @@
import { component$ } from '@builder.io/qwik' import { $, component$ } from '@builder.io/qwik'
import { MastodonStatus } from '~/types' import { MastodonStatus } from '~/types'
import * as timelines from 'wildebeest/functions/api/v1/timelines/public' import * as timelines from 'wildebeest/functions/api/v1/timelines/public'
import Status from '~/components/Status'
import { DocumentHead, loader$ } from '@builder.io/qwik-city' import { DocumentHead, loader$ } from '@builder.io/qwik-city'
import StickyHeader from '~/components/StickyHeader/StickyHeader' import StickyHeader from '~/components/StickyHeader/StickyHeader'
import { getDocumentHead } from '~/utils/getDocumentHead' import { getDocumentHead } from '~/utils/getDocumentHead'
import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler' import { RequestContext } from '@builder.io/qwik-city/middleware/request-handler'
import { StatusesPanel } from '~/components/StatusesPanel/StatusesPanel'
export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string }, Promise<MastodonStatus[]>>( export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string }, Promise<MastodonStatus[]>>(
async ({ platform, html }) => { async ({ platform, html }) => {
@ -22,7 +22,7 @@ export const statusesLoader = loader$<{ DATABASE: D1Database; domain: string },
) )
export default component$(() => { export default component$(() => {
const statuses = statusesLoader.use() const statuses = statusesLoader.use().value
return ( return (
<> <>
<StickyHeader> <StickyHeader>
@ -31,13 +31,22 @@ export default component$(() => {
<span>Local timeline</span> <span>Local timeline</span>
</div> </div>
</StickyHeader> </StickyHeader>
{statuses.value.length > 0 ? ( <StatusesPanel
statuses.value.map((status) => <Status status={status} />) initialStatuses={statuses}
) : ( fetchMoreStatuses={$(async (numOfCurrentStatuses: number) => {
<div class="flex-1 grid place-items-center bg-wildebeest-600 text-center"> let statuses: MastodonStatus[] = []
<p>Nothing to see right now. Check back later!</p> try {
</div> const response = await fetch(`/api/v1/timelines/public?local=true&offset=${numOfCurrentStatuses}`)
)} if (response.ok) {
const results = await response.text()
statuses = JSON.parse(results)
}
} catch {
/* empty */
}
return statuses
})}
/>
</> </>
) )
}) })

Wyświetl plik

@ -1,4 +1,4 @@
import { test, expect, Page } from '@playwright/test' import { test, expect, Page, Request } from '@playwright/test'
import type { Account, MastodonStatus } from 'wildebeest/frontend/src/types' import type { Account, MastodonStatus } from 'wildebeest/frontend/src/types'
test.describe('Infinite (statuses) scrolling', () => { test.describe('Infinite (statuses) scrolling', () => {
@ -8,6 +8,20 @@ test.describe('Infinite (statuses) scrolling', () => {
goToPageFn: async (page: Page) => await page.goto('http://127.0.0.1:8788/explore'), goToPageFn: async (page: Page) => await page.goto('http://127.0.0.1:8788/explore'),
fetchUrl: 'http://127.0.0.1:8788/api/v1/timelines/public?*', fetchUrl: 'http://127.0.0.1:8788/api/v1/timelines/public?*',
}, },
{
description: 'in local page',
goToPageFn: async (page: Page) => await page.goto('http://127.0.0.1:8788/public/local'),
fetchUrl: 'http://127.0.0.1:8788/api/v1/timelines/public?*',
isRequestValid: (request: Request) => {
const searchParams = new URL(request.url()).searchParams
return searchParams.get('local') === 'true'
},
},
{
description: 'in federated page',
goToPageFn: async (page: Page) => await page.goto('http://127.0.0.1:8788/public'),
fetchUrl: 'http://127.0.0.1:8788/api/v1/timelines/public?*',
},
{ {
description: 'in account page', description: 'in account page',
goToPageFn: async (page: Page) => { goToPageFn: async (page: Page) => {
@ -21,7 +35,7 @@ test.describe('Infinite (statuses) scrolling', () => {
}, },
] ]
tests.forEach(({ description, fetchUrl, goToPageFn }) => tests.forEach(({ description, fetchUrl, goToPageFn, isRequestValid }) =>
test(description, async ({ page, browserName }) => { test(description, async ({ page, browserName }) => {
test.skip(browserName !== 'chromium', 'Only chromium tests infinite scrolling well') test.skip(browserName !== 'chromium', 'Only chromium tests infinite scrolling well')
@ -29,8 +43,11 @@ test.describe('Infinite (statuses) scrolling', () => {
await page.waitForLoadState('networkidle') await page.waitForLoadState('networkidle')
const generateFakeStatus = getMockStatusFn() const generateFakeStatus = getMockStatusFn()
await page.route(fetchUrl, async (route) => { await page.route(fetchUrl, async (route, request) => {
const newStatuses = new Array(5).fill(null).map(generateFakeStatus) let newStatuses: MastodonStatus[] = []
if (!isRequestValid || isRequestValid(request)) {
newStatuses = new Array(5).fill(null).map(generateFakeStatus)
}
await route.fulfill({ body: JSON.stringify(newStatuses) }) await route.fulfill({ body: JSON.stringify(newStatuses) })
}) })