Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#18 Preload statuses from repo #19

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/pages/home.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Status } from '#/db'
import { html } from '../lib/view'
import { html } from '#/lib/view'
import { shell } from './shell'

const TODAY = new Date().toDateString()
Expand Down Expand Up @@ -35,8 +35,8 @@ const STATUS_OPTIONS = [
]

type Props = {
statuses: Status[]
didHandleMap: Record<string, string>
statuses?: Status[]
didHandleMap?: Record<string, string>
profile?: { displayName?: string }
myStatus?: Status
}
Expand Down Expand Up @@ -88,8 +88,8 @@ function content({ statuses, didHandleMap, profile, myStatus }: Props) {
</button>`
)}
</form>
${statuses.map((status, i) => {
const handle = didHandleMap[status.authorDid] || status.authorDid
${(statuses || []).map((status, i) => {
const handle = didHandleMap?.[status.authorDid] || status.authorDid
const date = ts(status)
return html`
<div class=${i === 0 ? 'status-line no-line' : 'status-line'}>
Expand Down
87 changes: 73 additions & 14 deletions src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const createRouter = (ctx: AppContext) => {
ctx.logger.error({ err }, 'oauth callback failed')
return res.redirect('/?error')
}
return res.redirect('/')
return res.redirect('/statuses')
})
)

Expand Down Expand Up @@ -148,33 +148,27 @@ export const createRouter = (ctx: AppContext) => {
handler(async (req, res) => {
// If the user is signed in, get an agent which communicates with their server
const agent = await getSessionAgent(req, res, ctx)
if (!agent) {
// Serve the logged-out view
return res.type('html').send(page(home({})))
}

// Fetch data stored in our SQLite
const statuses = await ctx.db
.selectFrom('status')
.selectAll()
.where('authorDid', '=', agent.assertDid)
.orderBy('indexedAt', 'desc')
.limit(10)
.execute()
const myStatus = agent
? await ctx.db
.selectFrom('status')
.selectAll()
.where('authorDid', '=', agent.assertDid)
.orderBy('indexedAt', 'desc')
.executeTakeFirst()
: undefined

const myStatus = statuses[0]

// Map user DIDs to their domain-name handles
const didHandleMap = await ctx.resolver.resolveDidsToHandles(
statuses.map((s) => s.authorDid)
)

if (!agent) {
// Serve the logged-out view
return res.type('html').send(page(home({ statuses, didHandleMap })))
}

// Fetch additional information about the logged-in user
const { data: profileRecord } = await agent.com.atproto.repo.getRecord({
repo: agent.assertDid,
Expand All @@ -201,6 +195,71 @@ export const createRouter = (ctx: AppContext) => {
})
)

// "Fetch statuses" handler
router.get(
'/statuses',
handler(async (req, res) => {
// If the user is signed in, get an agent which communicates with their server
const agent = await getSessionAgent(req, res, ctx)
if (!agent) {
return res
.status(401)
.type('html')
.send('<h1>Error: Session required</h1>')
}

// Fetch data stored in the repo
const { data: repoStatusesData } = await agent.com.atproto.repo.listRecords({
repo: agent.assertDid,
collection: 'xyz.statusphere.status',
limit: 10,
})

// Map the records to our SQLite schema
const repoStatuses = repoStatusesData.records
.map((record) => {
const value = record.value as { status: string; createdAt: string }
return {
uri: record.uri,
authorDid: agent.assertDid,
status: value.status,
createdAt: value.createdAt,
indexedAt: value.createdAt,
}
})
.filter((record) => Status.validateRecord(record).success)

if (repoStatuses.length !== 0) {
// Fetch existing statuses from SQLite
const dbStatuses = await ctx.db
.selectFrom('status')
.selectAll()
.where('uri', 'in', repoStatuses.map((s) => s.uri))
.limit(10)
.execute()

// Persist to SQLite only if there are not existing in SQLite
const statuses = dbStatuses.length === 0
? repoStatuses
: repoStatuses.filter((s) => !dbStatuses.some((dbS) => dbS.uri === s.uri))

if (statuses.length !== 0) {
try {
// Optimistically update our SQLite
await ctx.db
.insertInto('status')
.values(statuses)
.execute()
} catch (err) {
ctx.logger.warn({err}, 'failed to update computed view; ignoring as it should be caught by the firehose')
}
}
}

return res.redirect('/')
})
)

// "Set status" handler
router.post(
'/status',
Expand Down