Skip to content

Commit

Permalink
adding some basic views
Browse files Browse the repository at this point in the history
  • Loading branch information
dholms committed Dec 7, 2023
1 parent ce5eecd commit d828187
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 44 deletions.
6 changes: 6 additions & 0 deletions packages/bsky/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { LabelCache } from './label-cache'
import { NotificationServer } from './notifications'
import { DataPlaneClient } from './data-plane/client'
import { Hydrator } from './hydration/hydrator'
import { Views } from './views'

export class AppContext {
public moderationPushAgent: AtpAgent | undefined
Expand All @@ -26,6 +27,7 @@ export class AppContext {
services: Services
dataplane: DataPlaneClient
hydrator: Hydrator
views: Views
signingKey: Keypair
idResolver: IdResolver
didCache: DidSqlCache
Expand Down Expand Up @@ -70,6 +72,10 @@ export class AppContext {
return this.opts.hydrator
}

get views(): Views {
return this.opts.views
}

get signingKey(): Keypair {
return this.opts.signingKey
}
Expand Down
3 changes: 3 additions & 0 deletions packages/bsky/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { AtpAgent } from '@atproto/api'
import { Keypair } from '@atproto/crypto'
import { createDataPlaneClient } from './data-plane/client'
import { Hydrator } from './hydration/hydrator'
import { Views } from './views'

export * from './data-plane'
export type { ServerConfigValues } from './config'
Expand Down Expand Up @@ -120,13 +121,15 @@ export class BskyAppView {

const dataplane = createDataPlaneClient(config.dataplaneUrl, '1.1')
const hydrator = new Hydrator(dataplane)
const views = new Views(imgUriBuilder)

const ctx = new AppContext({
db,
cfg: config,
services,
dataplane,
hydrator,
views,
imgUriBuilder,
signingKey,
idResolver,
Expand Down
66 changes: 66 additions & 0 deletions packages/bsky/src/views/graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { AtUri } from '@atproto/syntax'
import { HydrationState } from '../hydration/hydrator'
import { ImageUriBuilder } from '../image/uri'
import { ListView, ListViewBasic } from '../lexicon/types/app/bsky/graph/defs'
import { compositeTime } from './util'
import { ActorViews } from './actor'

export class GraphViews {
constructor(
public imgUriBuilder: ImageUriBuilder,
public actor: ActorViews,
) {}

list(uri: string, state: HydrationState): ListView | undefined {
const creatorDid = new AtUri(uri).hostname
const list = state.lists?.get(uri)
if (!list) return
const creator = this.actor.profileBasic(creatorDid, state)
if (!creator) return
const basicView = this.listBasic(uri, state)
if (!basicView) return

return {
...basicView,
creator,
description: list.record.description,
descriptionFacets: list.record.descriptionFacets,
indexedAt: compositeTime(
list.record.createdAt,
list.indexedAt?.toISOString(),
),
}
}

listBasic(uri: string, state: HydrationState): ListViewBasic | undefined {
const list = state.lists?.get(uri)
if (!list) {
return undefined
}
const listViewer = state.listViewers?.get(uri)
const creator = new AtUri(uri).hostname
return {
uri,
cid: list.cid.toString(),
name: list.record.name,
purpose: list.record.purpose,
avatar: list.record.avatar
? this.imgUriBuilder.getPresetUri(
'avatar',
creator,
list.record.avatar.ref,
)
: undefined,
indexedAt: compositeTime(
list.record.createdAt,
list.indexedAt?.toISOString(),
),
viewer: listViewer
? {
muted: !!listViewer.viewerMuted,
blocked: listViewer.viewerListBlockUri,
}
: undefined,
}
}
}
157 changes: 157 additions & 0 deletions packages/bsky/src/views/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { AtUri, INVALID_HANDLE } from '@atproto/syntax'
import { ImageUriBuilder } from '../image/uri'
import { HydrationState } from '../hydration/hydrator'
import { ids } from '../lexicon/lexicons'
import {
ProfileViewDetailed,
ProfileView,
ProfileViewBasic,
} from '../lexicon/types/app/bsky/actor/defs'
import { ListView, ListViewBasic } from '../lexicon/types/app/bsky/graph/defs'
import { compositeTime } from './util'

export class Views {
constructor(public imgUriBuilder: ImageUriBuilder) {}

// Actor
// ------------

profileDetailed(
did: string,
state: HydrationState,
): ProfileViewDetailed | undefined {
const actor = state.actors?.get(did)
if (!actor) return
const baseView = this.profile(did, state)
if (!baseView) return
const profileAggs = state.profileAggs?.get(did)
return {
...baseView,
banner: actor.profile?.banner
? this.imgUriBuilder.getPresetUri(
'banner',
did,
actor.profile.banner.ref,
)
: undefined,
followersCount: profileAggs?.followers,
followsCount: profileAggs?.follows,
postsCount: profileAggs?.posts,
}
}

profile(did: string, state: HydrationState): ProfileView | undefined {
const actor = state.actors?.get(did)
if (!actor) return
const basicView = this.profileBasic(did, state)
if (!basicView) return
return {
...basicView,
description: actor.profile?.description,
indexedAt: actor.indexedAt?.toISOString(),
}
}

profileBasic(
did: string,
state: HydrationState,
): ProfileViewBasic | undefined {
const actor = state.actors?.get(did)
if (!actor) return
const viewer = state.profileViewers?.get(did)
const profileUri = AtUri.make(
did,
ids.AppBskyActorProfile,
'self',
).toString()
const labels = [
...(state.labels?.get(did) ?? []),
...(state.labels?.get(profileUri) ?? []),
]
return {
did,
handle: actor.handle ?? INVALID_HANDLE,
displayName: actor.profile?.displayName,
avatar: actor.profile?.avatar
? this.imgUriBuilder.getPresetUri(
'avatar',
did,
actor.profile.avatar.ref,
)
: undefined,
viewer: viewer
? {
muted: viewer.muted,
mutedByList: viewer.mutedByList
? this.listBasic(viewer.mutedByList, state)
: undefined,
blockedBy: !!viewer.blockedBy,
blocking: viewer.blocking,
// @TODO blockedByList?
blockingByList: viewer.blockingByList
? this.listBasic(viewer.blockingByList, state)
: undefined,
following: viewer.following,
followedBy: viewer.followedBy,
}
: undefined,
labels,
}
}

// Graph
// ------------

list(uri: string, state: HydrationState): ListView | undefined {
const creatorDid = new AtUri(uri).hostname
const list = state.lists?.get(uri)
if (!list) return
const creator = this.profileBasic(creatorDid, state)
if (!creator) return
const basicView = this.listBasic(uri, state)
if (!basicView) return

return {
...basicView,
creator,
description: list.record.description,
descriptionFacets: list.record.descriptionFacets,
indexedAt: compositeTime(
list.record.createdAt,
list.indexedAt?.toISOString(),
),
}
}

listBasic(uri: string, state: HydrationState): ListViewBasic | undefined {
const list = state.lists?.get(uri)
if (!list) {
return undefined
}
const listViewer = state.listViewers?.get(uri)
const creator = new AtUri(uri).hostname
return {
uri,
cid: list.cid.toString(),
name: list.record.name,
purpose: list.record.purpose,
avatar: list.record.avatar
? this.imgUriBuilder.getPresetUri(
'avatar',
creator,
list.record.avatar.ref,
)
: undefined,
indexedAt: compositeTime(
list.record.createdAt,
list.indexedAt?.toISOString(),
),
viewer: listViewer
? {
muted: !!listViewer.viewerMuted,
blocked: listViewer.viewerListBlockUri,
}
: undefined,
}
}
}
44 changes: 0 additions & 44 deletions packages/bsky/src/views/lists.ts

This file was deleted.

7 changes: 7 additions & 0 deletions packages/bsky/src/views/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const now = () => {
return new Date().toISOString()
}

export const compositeTime = (createdAt = now(), indexedAt = now()): string => {
return createdAt < indexedAt ? createdAt : indexedAt
}

0 comments on commit d828187

Please sign in to comment.