about summary refs log tree commit diff
path: root/src/screens/Onboarding/util.ts
blob: 1a0b8d21bcd79280b835072fb3aaa68f8aa73451 (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {AppBskyGraphFollow, AppBskyGraphGetFollows} from '@atproto/api'

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

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 => {
      // suggestions from server
      const rawSuggestions = map[i]

      // safeguard against a missing interest->suggestion mapping
      if (!rawSuggestions || !rawSuggestions.length) {
        return []
      }

      const suggestions = shuffle(rawSuggestions)

      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((a, b) => {
    if (a === PRIMARY_FEEDS[0].uri) {
      return -1
    }
    if (b === PRIMARY_FEEDS[0].uri) {
      return 1
    }
    if (a === PRIMARY_FEEDS[1].uri) {
      return -1
    }
    if (b === PRIMARY_FEEDS[1].uri) {
      return 1
    }
    return a.localeCompare(b)
  })
}