Skip to content

Commit

Permalink
Begin implementation on feeds page
Browse files Browse the repository at this point in the history
This is still totally WIP, but landing it sooner than later for very early live testing. This page shows a feed and its episodes.  It also lets you edit episodes and the feeds and create a new feed.
  • Loading branch information
bcomnes committed Sep 4, 2022
1 parent d0a5de3 commit 09ef8c5
Show file tree
Hide file tree
Showing 34 changed files with 986 additions and 75 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@fastify/static": "^6.4.0",
"@fastify/swagger": "^7.4.1",
"@nearform/sql": "^1.5.0",
"@siteup/cli": "^2.2.0",
"@siteup/cli": "^2.2.4",
"classnames": "^2.3.1",
"clean-deep": "^3.4.0",
"desm": "^1.2.0",
Expand Down
5 changes: 1 addition & 4 deletions routes/api/bookmarks/_id/get-bookmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ export async function getBookmark (fastify, opts) {
const results = await fastify.pg.query(query)
const bookmark = results.rows[0]
if (!bookmark) {
reply.code(404)
return {
status: 'bookmark id not found'
}
return reply.notFound('bookmark id not found')
}
return {
...bookmark
Expand Down
10 changes: 6 additions & 4 deletions routes/api/bookmarks/get-bookmarks-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const getBookmarksQuery = ({
sensitive,
perPage
}) => {
return SQL`
const bookarmsQuery = SQL`
with bookmark_page as (
select bm.*
from bookmarks bm
Expand Down Expand Up @@ -90,17 +90,19 @@ export const getBookmarksQuery = ({
on bookark_page_episodes_array.bookmark_id = b.id
order by b.created_at desc, b.title desc, b.url desc
`

return bookarmsQuery
}

export const afterToBeforeQuery = ({
export const afterToBeforeBookmarkQuery = ({
perPage,
tag,
ownerId,
after,
sensitive
}) => {
const perPageAfterOffset = perPage + 2
const afterCalcQuery = SQL`
const afterCalcBookarksQuery = SQL`
with page as (
select bm.id, bm.url, bm.title, bm.created_at
from bookmarks bm
Expand Down Expand Up @@ -135,5 +137,5 @@ export const afterToBeforeQuery = ({
from bookmark_with_last_row_date
group by last_created_at`

return afterCalcQuery
return afterCalcBookarksQuery
}
16 changes: 9 additions & 7 deletions routes/api/bookmarks/get-bookmarks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fullBookmarkPropsWithEpisodes } from './mixed-bookmark-props.js'
import { getBookmarksQuery, afterToBeforeQuery } from './get-bookmarks-query.js'
import { getBookmarksQuery, afterToBeforeBookmarkQuery } from './get-bookmarks-query.js'

export async function getBookmarks (fastify, opts) {
fastify.get(
Expand Down Expand Up @@ -71,15 +71,17 @@ export async function getBookmarks (fastify, opts) {
},
// Get Bookmarks
async function getBookmarksHandler (request, reply) {
const id = request.user.id
let {
before,
const userId = request.user.id
const {
after,
per_page: perPage,
url,
tag,
sensitive
} = request.query
let {
before
} = request.query

let top = false
let bottom = false
Expand All @@ -88,10 +90,10 @@ export async function getBookmarks (fastify, opts) {
// We have to fetch the first 2 rows because > is inclusive on timestamps (μS)
// and we need to get the item before the next 'before' set.
const perPageAfterOffset = perPage + 2
const afterCalcQuery = afterToBeforeQuery({
const afterCalcQuery = afterToBeforeBookmarkQuery({
perPage,
tag,
ownerId: id,
ownerId: userId,
after,
sensitive
})
Expand All @@ -115,7 +117,7 @@ export async function getBookmarks (fastify, opts) {

const bookmarkQuery = getBookmarksQuery({
tag,
ownerId: id,
ownerId: userId,
before,
url,
sensitive,
Expand Down
103 changes: 103 additions & 0 deletions routes/api/episodes/get-episode-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import SQL from '@nearform/sql'

export function getEpisodesQuery ({
ownerId,
episodeId,
before,
sensitive,
perPage,
feedId
}) {
const episodesQuery = SQL`
select
ep.id,
ep.podcast_feed_id,
ep.created_at,
ep.updated_at,
ep.url,
ep.type,
ep.medium,
ep.size_in_bytes,
ep.duration_in_seconds,
ep.mime_type,
ep.explicit,
ep.author_name,
ep.filename,
ep.ext,
ep.src_type,
ep.ready,
ep.error,
jsonb_build_object(
'id', bm.id,
'url', bm.url,
'title', bm.title,
'note', bm.note,
'created_at', bm.created_at,
'updated_at', bm.updated_at,
'starred', bm.starred,
'toread', bm.toread,
'sensitive', bm.sensitive
) as bookmark
from episodes ep
join bookmarks bm
on ep.bookmark_id = bm.id
where ep.owner_id = ${ownerId}
and bm.owner_id = ${ownerId}
${feedId ? SQL`and ep.podcast_feed_id = ${feedId}` : SQL``}
${episodeId ? SQL`and ep.id = ${episodeId}` : SQL``}
${before ? SQL`and ep.created_at < ${before}` : SQL``}
${!sensitive ? SQL`and sensitive = false` : SQL``}
order by ep.created_at desc, ep.url desc, bm.title desc
${perPage != null ? SQL`fetch first ${perPage} rows only` : SQL``}
`

return episodesQuery
}

export function afterToBeforeEpisodesQuery ({
perPage,
ownerId,
after,
sensitive,
feedId
}) {
const perPageAfterOffset = perPage + 2

const afterCalcEpisodesQuery = SQL`
with page as (
select ep.id, ep.created_at
from episodes ep
${!sensitive
? SQL`
join bookmarks bm
on ep.bookmark_id = bm.id`
: SQL``}
where ep.owner_id = ${ownerId}
and ep.created_at >= ${after}
${feedId ? SQL`and ep.podcast_feed_id = ${feedId}` : SQL``}
${!sensitive
? SQL`
and bm.owner_id = ${ownerId}
and bm.sensitive = false
`
: SQL``
}
order by ep.created_at ASC, ep.url ASC
fetch first ${perPageAfterOffset} rows only
),
episode_with_last_row_date as (
select last_value(page.created_at) over (
order by page.created_at
range between
UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING
) last_created_at
from page
)
select count(*)::int as episode_count, last_created_at
from episode_with_last_row_date
group by last_created_at
`

return afterCalcEpisodesQuery
}
85 changes: 85 additions & 0 deletions routes/api/episodes/get-episodes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { fullEpisodePropsWithBookmarkAndFeed } from './mixed-episode-props.js'
import { getOrCreateDefaultFeed } from '../feeds/default-feed/get-or-create-default-feed-query.js'
import { getEpisodesQuery, afterToBeforeEpisodesQuery } from './get-episode-query.js'

export async function getEpisodes (fastify, opts) {
fastify.get(
'/',
Expand Down Expand Up @@ -48,14 +51,96 @@ export async function getEpisodes (fastify, opts) {
...fullEpisodePropsWithBookmarkAndFeed
}
}
},
pagination: {
type: 'object',
properties: {
before: { type: 'string', format: 'date-time' },
after: { type: 'string', format: 'date-time' },
top: { type: 'boolean' },
bottom: { type: 'boolean' }
}
}
}
}
}
}
},
async function getEpisodesHandler (request, reply) {
return fastify.pg.transact(async client => {
const userId = request.user.id

const {
after,
per_page: perPage,
sensitive
} = request.query
let {
before
} = request.query

const feedId = request.query.feed_id ?? await getOrCreateDefaultFeed({ client, userId })

let top = false
let bottom = false

if (after) {
// We have to fetch the first 2 rows because > is inclusive on timestamps (μS)
// and we need to get the item before the next 'before' set.
const perPageAfterOffset = perPage + 2
const afterCalcQuery = afterToBeforeEpisodesQuery({
perPage,
ownerId: userId,
after,
sensitive,
feedId
})

const afterToBeforeResults = await fastify.pg.query(afterCalcQuery)

const {
episode_count: episodeCount,
last_created_at: lastCreatedAt
} = afterToBeforeResults.rows.pop()

if (episodeCount !== perPageAfterOffset) {
top = true
before = (new Date()).toISOString()
} else {
before = lastCreatedAt
}
}

if (!before && !after) {
top = true
before = (new Date()).toISOString()
}

const episodeQuery = getEpisodesQuery({
ownerId: userId,
before,
sensitive,
perPage,
feedId
})

const episodeResults = await fastify.pg.query(episodeQuery)

if (episodeResults.rows.length !== perPage) bottom = true

const nextPage = bottom ? null : episodeResults.rows.at(-1).created_at
const prevPage = top ? null : episodeResults.rows[0]?.created_at || before

return {
data: episodeResults.rows,
pagination: {
before: nextPage,
after: prevPage,
top,
bottom
}
}
})
}
)
}
2 changes: 1 addition & 1 deletion routes/api/feeds/_feed/delete-feed.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export async function deleteFeed (fastify, opts) {
fastify.delete(
'/',
{
preHandler: fastify.auth([fastify.basicAuth]),
preHandler: fastify.auth([fastify.verifyJWT]),
schema: {
parms: {
type: 'object',
Expand Down
67 changes: 67 additions & 0 deletions routes/api/feeds/_feed/details/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* eslint-disable camelcase */
import { fullFeedProps } from '../../feed-props.js'
import { getFeedQuery } from '../get-feed-query.js'
import { getFeedUrl } from '../../get-feed-url.js'

export default async function getFeedDetails (fastify, opts) {
fastify.get(
'/',
{
preHandler: fastify.auth([fastify.verifyJWT]),
schema: {
parms: {
type: 'object',
properties: {
feed: {
type: 'string',
format: 'uuid'
}
},
required: ['feed']
},
response: {
200: {
type: 'object',
properties: {
data: {
type: 'array',
items: {
type: 'object',
properties: {
...fullFeedProps
}
}
}
}
}
}
}
},

async function getFeedHandler (request, reply) {
const userId = request.user.id
const { feed: feedId } = request.params

const query = getFeedQuery({
ownerId: userId,
feedId,
perPage: 1
})

const results = await fastify.pg.query(query)
const feed = results.rows[0]
if (!feed) {
return reply.notFound('feed not found')
}
return {
...feed,
feed_url: getFeedUrl({
transport: fastify.config.TRANSPORT,
host: fastify.config.HOST,
userId,
token: feed.token,
feedId: feed.id
})
}
})
}

0 comments on commit 09ef8c5

Please sign in to comment.