about summary refs log tree commit diff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/api/build-suggested-posts.ts120
-rw-r--r--src/lib/constants.ts36
-rw-r--r--src/lib/styles.ts4
3 files changed, 158 insertions, 2 deletions
diff --git a/src/lib/api/build-suggested-posts.ts b/src/lib/api/build-suggested-posts.ts
new file mode 100644
index 000000000..6250f4a9c
--- /dev/null
+++ b/src/lib/api/build-suggested-posts.ts
@@ -0,0 +1,120 @@
+import {RootStoreModel} from 'state/index'
+import {
+  AppBskyFeedFeedViewPost,
+  AppBskyFeedGetAuthorFeed as GetAuthorFeed,
+} from '@atproto/api'
+type ReasonRepost = AppBskyFeedFeedViewPost.ReasonRepost
+
+async function getMultipleAuthorsPosts(
+  rootStore: RootStoreModel,
+  authors: string[],
+  cursor: string | undefined = undefined,
+  limit: number = 10,
+) {
+  const responses = await Promise.all(
+    authors.map((author, index) =>
+      rootStore.api.app.bsky.feed
+        .getAuthorFeed({
+          author,
+          limit,
+          before: cursor ? cursor.split(',')[index] : undefined,
+        })
+        .catch(_err => ({success: false, headers: {}, data: {feed: []}})),
+    ),
+  )
+  return responses
+}
+
+function mergePosts(
+  responses: GetAuthorFeed.Response[],
+  {repostsOnly, bestOfOnly}: {repostsOnly?: boolean; bestOfOnly?: boolean},
+) {
+  let posts: AppBskyFeedFeedViewPost.Main[] = []
+
+  if (bestOfOnly) {
+    for (const res of responses) {
+      if (res.success) {
+        // filter the feed down to the post with the most upvotes
+        res.data.feed = res.data.feed.reduce(
+          (acc: AppBskyFeedFeedViewPost.Main[], v) => {
+            if (!acc?.[0] && !v.reason) {
+              return [v]
+            }
+            if (
+              acc &&
+              !v.reason &&
+              v.post.upvoteCount > acc[0].post.upvoteCount
+            ) {
+              return [v]
+            }
+            return acc
+          },
+          [],
+        )
+      }
+    }
+  }
+
+  // merge into one array
+  for (const res of responses) {
+    if (res.success) {
+      posts = posts.concat(res.data.feed)
+    }
+  }
+
+  // filter down to reposts of other users
+  const uris = new Set()
+  posts = posts.filter(p => {
+    if (repostsOnly && !isARepostOfSomeoneElse(p)) {
+      return false
+    }
+    if (uris.has(p.post.uri)) {
+      return false
+    }
+    uris.add(p.post.uri)
+    return true
+  })
+
+  // sort by index time
+  posts.sort((a, b) => {
+    return (
+      Number(new Date(b.post.indexedAt)) - Number(new Date(a.post.indexedAt))
+    )
+  })
+
+  return posts
+}
+
+function isARepostOfSomeoneElse(post: AppBskyFeedFeedViewPost.Main): boolean {
+  return (
+    post.reason?.$type === 'app.bsky.feed.feedViewPost#reasonRepost' &&
+    post.post.author.did !== (post.reason as ReasonRepost).by.did
+  )
+}
+
+function getCombinedCursors(responses: GetAuthorFeed.Response[]) {
+  let hasCursor = false
+  const cursors = responses.map(r => {
+    if (r.data.cursor) {
+      hasCursor = true
+      return r.data.cursor
+    }
+    return ''
+  })
+  if (!hasCursor) {
+    return undefined
+  }
+  const combinedCursors = cursors.join(',')
+  return combinedCursors
+}
+
+function isCombinedCursor(cursor: string) {
+  return cursor.includes(',')
+}
+
+export {
+  getMultipleAuthorsPosts,
+  mergePosts,
+  getCombinedCursors,
+  isCombinedCursor,
+}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 062fc1aa8..a93301b34 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -4,6 +4,31 @@ export const FEEDBACK_FORM_URL =
 export const MAX_DISPLAY_NAME = 64
 export const MAX_DESCRIPTION = 256
 
+export const PROD_TEAM_HANDLES = [
+  'jay.bsky.social',
+  'paul.bsky.social',
+  'dan.bsky.social',
+  'divy.bsky.social',
+  'why.bsky.social',
+  'iamrosewang.bsky.social',
+]
+export const STAGING_TEAM_HANDLES = [
+  'arcalinea.staging.bsky.dev',
+  'paul.staging.bsky.dev',
+  'paul2.staging.bsky.dev',
+]
+export const DEV_TEAM_HANDLES = ['alice.test', 'bob.test', 'carla.test']
+
+export function TEAM_HANDLES(serviceUrl: string) {
+  if (serviceUrl.includes('localhost')) {
+    return DEV_TEAM_HANDLES
+  } else if (serviceUrl.includes('staging')) {
+    return STAGING_TEAM_HANDLES
+  } else {
+    return PROD_TEAM_HANDLES
+  }
+}
+
 export const PROD_SUGGESTED_FOLLOWS = [
   'john',
   'visakanv',
@@ -55,14 +80,21 @@ export const PROD_SUGGESTED_FOLLOWS = [
   'jay',
   'paul',
 ].map(handle => `${handle}.bsky.social`)
-
 export const STAGING_SUGGESTED_FOLLOWS = ['arcalinea', 'paul', 'paul2'].map(
   handle => `${handle}.staging.bsky.dev`,
 )
-
 export const DEV_SUGGESTED_FOLLOWS = ['alice', 'bob', 'carla'].map(
   handle => `${handle}.test`,
 )
+export function SUGGESTED_FOLLOWS(serviceUrl: string) {
+  if (serviceUrl.includes('localhost')) {
+    return DEV_SUGGESTED_FOLLOWS
+  } else if (serviceUrl.includes('staging')) {
+    return STAGING_SUGGESTED_FOLLOWS
+  } else {
+    return PROD_SUGGESTED_FOLLOWS
+  }
+}
 
 export const POST_IMG_MAX_WIDTH = 2000
 export const POST_IMG_MAX_HEIGHT = 2000
diff --git a/src/lib/styles.ts b/src/lib/styles.ts
index dd3c86910..f6e26d53f 100644
--- a/src/lib/styles.ts
+++ b/src/lib/styles.ts
@@ -62,6 +62,10 @@ export const s = StyleSheet.create({
   footerSpacer: {height: 100},
   contentContainer: {paddingBottom: 200},
   border1: {borderWidth: 1},
+  borderTop1: {borderTopWidth: 1},
+  borderRight1: {borderRightWidth: 1},
+  borderBottom1: {borderBottomWidth: 1},
+  borderLeft1: {borderLeftWidth: 1},
 
   // font weights
   fw600: {fontWeight: '600'},