about summary refs log tree commit diff
path: root/src/screens/Onboarding/util.ts
blob: 2a709a67b6cb24752679e96b29e47315d10f7403 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import {AppBskyGraphFollow, AppBskyGraphGetFollows} from '@atproto/api'

import {until} from '#/lib/async/until'
import {getAgent} from '#/state/session'

function shuffle(array: any) {
  let currentIndex = array.length,
    randomIndex

  // While there remain elements to shuffle.
  while (currentIndex > 0) {
    // Pick a remaining element.
    randomIndex = Math.floor(Math.random() * currentIndex)
    currentIndex--

    // And swap it with the current element.
    ;[array[currentIndex], array[randomIndex]] = [
      array[randomIndex],
      array[currentIndex],
    ]
  }

  return array
}

export function aggregateInterestItems(
  interests: string[],
  map: {[key: string]: string[]},
  fallbackItems: string[],
) {
  const selected = interests.length
  const all = interests
    .map(i => {
      const suggestions = shuffle(map[i])

      if (selected === 1) {
        return suggestions // return all
      } else if (selected === 2) {
        return suggestions.slice(0, 5) // return 5
      } else {
        return suggestions.slice(0, 3) // return 3
      }
    })
    .flat()
  // dedupe suggestions
  const results = Array.from(new Set(all))

  // backfill
  if (results.length < 20) {
    results.push(...shuffle(fallbackItems))
  }

  // dedupe and return 20
  return Array.from(new Set(results)).slice(0, 20)
}

export async function bulkWriteFollows(dids: string[]) {
  const session = getAgent().session

  if (!session) {
    throw new Error(`bulkWriteFollows failed: no session`)
  }

  const followRecords: AppBskyGraphFollow.Record[] = dids.map(did => {
    return {
      $type: 'app.bsky.graph.follow',
      subject: did,
      createdAt: new Date().toISOString(),
    }
  })
  const followWrites = followRecords.map(r => ({
    $type: 'com.atproto.repo.applyWrites#create',
    collection: 'app.bsky.graph.follow',
    value: r,
  }))

  await getAgent().com.atproto.repo.applyWrites({
    repo: session.did,
    writes: followWrites,
  })
  await whenFollowsIndexed(session.did, res => !!res.data.follows.length)
}

async function whenFollowsIndexed(
  actor: string,
  fn: (res: AppBskyGraphGetFollows.Response) => boolean,
) {
  await until(
    5, // 5 tries
    1e3, // 1s delay between tries
    fn,
    () =>
      getAgent().app.bsky.graph.getFollows({
        actor,
        limit: 1,
      }),
  )
}

/**
 * Kinda hacky, but we want For Your or Discover to appear as the first pinned
 * feed after Following
 */
export function sortPrimaryAlgorithmFeeds(uris: string[]) {
  return uris.sort(uri => {
    return uri.includes('the-algorithm')
      ? -1
      : uri.includes('whats-hot')
      ? 0
      : 1
  })
}