master
1import { Badge, Evidence, ID, List, Presenter } from './badges.js'
2import { parseMask } from './utils.js'
3import { Data } from './data.js'
4
5export const presentBadges = <P extends Presenter<List>>(
6 presenters: P[],
7 data: Data,
8 userBadges: Badge[],
9 pickBadges: string[],
10 omitBadges: string[],
11 compact: boolean,
12): Badge[] => {
13 const newlyAddedBadges = new Set<ID>()
14
15 for (const presenter of presenters) {
16 const newBadges: Badge[] = []
17 const grant = badgeCollection(newBadges)
18
19 // @ts-ignore As `id: string` is not assignable to `id: ID`.
20 presenter.present(data, grant)
21
22 if (newBadges.length === 0) {
23 continue
24 }
25
26 // Add new badges to the list of badges.
27 for (const b of newBadges) {
28 newlyAddedBadges.add(b.id)
29 }
30
31 // Enhance badges with image URLs.
32 for (const b of newBadges) {
33 b.image = `https://my-badges.github.io/my-badges/${b.id}.png`
34 }
35
36 const badgeFromPresenter = (x: Badge) => presenter.badges.includes(x.id)
37
38 // Merge existing userBadges with newBadges.
39 if (compact && presenter.tiers) {
40 const newHighestTierBadge = newBadges.reduce((prev, curr) => {
41 return prev.tier > curr.tier ? prev : curr
42 })
43
44 const existingBadgeIndex = userBadges.findIndex(badgeFromPresenter)
45 if (existingBadgeIndex === -1) {
46 userBadges.push(newHighestTierBadge)
47 } else if (
48 newHighestTierBadge.tier >= userBadges[existingBadgeIndex].tier
49 ) {
50 userBadges[existingBadgeIndex] = newHighestTierBadge
51
52 // Drop all other badges from the same presenter.
53 userBadges = userBadges.filter(
54 (x, i) => i === existingBadgeIndex || !badgeFromPresenter(x),
55 )
56 }
57 } else {
58 for (const badge of newBadges) {
59 const index = userBadges.findIndex((x) => x.id === badge.id)
60 if (index === -1) {
61 userBadges.push(badge)
62 } else {
63 userBadges[index] = badge
64 }
65 }
66 }
67 }
68
69 if (pickBadges.length > 0) {
70 userBadges = userBadges.filter((x) =>
71 pickBadges.map(parseMask).some((r) => r.test(x.id)),
72 )
73 }
74
75 if (omitBadges.length > 0) {
76 userBadges = userBadges.filter((x) =>
77 omitBadges.map(parseMask).every((r) => !r.test(x.id)),
78 )
79 }
80
81 // Filter out old badges, keep only granted badges.
82 userBadges = userBadges.filter((b) => newlyAddedBadges.has(b.id))
83
84 return userBadges
85}
86
87export function badgeCollection(newBadges: Badge[]) {
88 return function grant(id: ID, desc: string) {
89 const badge: Badge = {
90 id,
91 tier: 0,
92 desc,
93 body: '',
94 image: '',
95 }
96 if (!newBadges.some((x) => x.id === id)) {
97 newBadges.push(badge)
98 }
99 return new Evidence(badge)
100 }
101}