grain.social is a photo sharing platform built on atproto.
1import { Record as GrainFollow } from "$lexicon/types/social/grain/graph/follow.ts";
2import { BffContext, WithBffMeta } from "@bigmoves/bff";
3import { getActorProfile } from "./actor.ts";
4
5export function getFollow(
6 followeeDid: string,
7 followerDid: string,
8 ctx: BffContext,
9) {
10 const {
11 items: [follow],
12 } = ctx.indexService.getRecords<
13 WithBffMeta<GrainFollow>
14 >(
15 "social.grain.graph.follow",
16 {
17 where: {
18 AND: [{
19 field: "did",
20 equals: followerDid,
21 }, {
22 field: "subject",
23 equals: followeeDid,
24 }],
25 },
26 },
27 );
28
29 return follow;
30}
31
32export function getFollowers(
33 followeeDid: string,
34 ctx: BffContext,
35): WithBffMeta<GrainFollow>[] {
36 const { items: followers } = ctx.indexService.getRecords<
37 WithBffMeta<GrainFollow>
38 >(
39 "social.grain.graph.follow",
40 {
41 orderBy: [{ field: "createdAt", direction: "desc" }],
42 where: [{
43 field: "subject",
44 equals: followeeDid,
45 }],
46 },
47 );
48 return followers;
49}
50
51export function getFollowing(
52 followerDid: string,
53 ctx: BffContext,
54): WithBffMeta<GrainFollow>[] {
55 const { items: following } = ctx.indexService.getRecords<
56 WithBffMeta<GrainFollow>
57 >(
58 "social.grain.graph.follow",
59 {
60 orderBy: [{ field: "createdAt", direction: "desc" }],
61 where: [{
62 field: "did",
63 equals: followerDid,
64 }],
65 },
66 );
67 return following;
68}
69
70export function getFollowersWithProfiles(
71 followeeDid: string,
72 ctx: BffContext,
73) {
74 const followers = getFollowers(followeeDid, ctx);
75 return followers
76 .map((follow) => getActorProfile(follow.did, ctx))
77 .filter((profile): profile is NonNullable<typeof profile> =>
78 profile != null
79 );
80}
81
82export function getFollowingWithProfiles(
83 followerDid: string,
84 ctx: BffContext,
85) {
86 const following = getFollowing(followerDid, ctx);
87 return following
88 .map((follow) => getActorProfile(follow.subject, ctx))
89 .filter((profile): profile is NonNullable<typeof profile> =>
90 profile != null
91 );
92}
93
94export function getFollowersCount(
95 followeeDid: string,
96 ctx: BffContext,
97): number {
98 return ctx.indexService.countRecords(
99 "social.grain.graph.follow",
100 {
101 orderBy: [{ field: "createdAt", direction: "desc" }],
102 where: [{
103 field: "subject",
104 equals: followeeDid,
105 }],
106 },
107 );
108}
109
110export function getFollowsCount(
111 followerDid: string,
112 ctx: BffContext,
113): number {
114 return ctx.indexService.countRecords(
115 "social.grain.graph.follow",
116 {
117 orderBy: [{ field: "createdAt", direction: "desc" }],
118 where: [{
119 field: "did",
120 equals: followerDid,
121 }],
122 },
123 );
124}
125
126export async function createFollow(
127 followeeDid: string,
128 ctx: BffContext,
129): Promise<string> {
130 const followUri = await ctx.createRecord<GrainFollow>(
131 "social.grain.graph.follow",
132 {
133 subject: followeeDid,
134 createdAt: new Date().toISOString(),
135 },
136 );
137 return followUri;
138}