@magmamath/students-features 0.6.6-rc.10 → 0.6.6-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/features/gifCelebrations/constants.js +2 -1
- package/dist/commonjs/features/gifCelebrations/constants.js.map +1 -1
- package/dist/commonjs/features/gifCelebrations/helpers.js +36 -33
- package/dist/commonjs/features/gifCelebrations/helpers.js.map +1 -1
- package/dist/commonjs/features/gifCelebrations/model/GifCelebrations.cache.js +1 -0
- package/dist/commonjs/features/gifCelebrations/model/GifCelebrations.cache.js.map +1 -1
- package/dist/commonjs/features/gifCelebrations/model/GifCelebrations.model.js +70 -52
- package/dist/commonjs/features/gifCelebrations/model/GifCelebrations.model.js.map +1 -1
- package/dist/module/features/gifCelebrations/components/Progress.js.map +1 -1
- package/dist/module/features/gifCelebrations/constants.js +1 -0
- package/dist/module/features/gifCelebrations/constants.js.map +1 -1
- package/dist/module/features/gifCelebrations/helpers.js +33 -31
- package/dist/module/features/gifCelebrations/helpers.js.map +1 -1
- package/dist/module/features/gifCelebrations/model/GifCelebrations.cache.js +1 -0
- package/dist/module/features/gifCelebrations/model/GifCelebrations.cache.js.map +1 -1
- package/dist/module/features/gifCelebrations/model/GifCelebrations.model.js +72 -54
- package/dist/module/features/gifCelebrations/model/GifCelebrations.model.js.map +1 -1
- package/dist/typescript/commonjs/features/gifCelebrations/constants.d.ts +1 -0
- package/dist/typescript/commonjs/features/gifCelebrations/constants.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/gifCelebrations/helpers.d.ts +3 -2
- package/dist/typescript/commonjs/features/gifCelebrations/helpers.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/gifCelebrations/model/GifCelebrations.cache.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/gifCelebrations/model/GifCelebrations.model.d.ts +6 -5
- package/dist/typescript/commonjs/features/gifCelebrations/model/GifCelebrations.model.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/gifCelebrations/types/model.types.d.ts +11 -5
- package/dist/typescript/commonjs/features/gifCelebrations/types/model.types.d.ts.map +1 -1
- package/dist/typescript/module/features/gifCelebrations/constants.d.ts +1 -0
- package/dist/typescript/module/features/gifCelebrations/constants.d.ts.map +1 -1
- package/dist/typescript/module/features/gifCelebrations/helpers.d.ts +3 -2
- package/dist/typescript/module/features/gifCelebrations/helpers.d.ts.map +1 -1
- package/dist/typescript/module/features/gifCelebrations/model/GifCelebrations.cache.d.ts.map +1 -1
- package/dist/typescript/module/features/gifCelebrations/model/GifCelebrations.model.d.ts +6 -5
- package/dist/typescript/module/features/gifCelebrations/model/GifCelebrations.model.d.ts.map +1 -1
- package/dist/typescript/module/features/gifCelebrations/types/model.types.d.ts +11 -5
- package/dist/typescript/module/features/gifCelebrations/types/model.types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/gifCelebrations/components/Progress.tsx +1 -1
- package/src/features/gifCelebrations/constants.ts +2 -0
- package/src/features/gifCelebrations/helpers.ts +51 -30
- package/src/features/gifCelebrations/model/GifCelebrations.cache.ts +2 -0
- package/src/features/gifCelebrations/model/GifCelebrations.model.ts +77 -62
- package/src/features/gifCelebrations/types/model.types.ts +11 -5
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
GifsGroups,
|
|
5
5
|
GifsStructure,
|
|
6
6
|
GroupNumber,
|
|
7
|
-
|
|
7
|
+
GroupNumberString,
|
|
8
|
+
ProblemsGifsTable,
|
|
8
9
|
} from './types/model.types'
|
|
9
10
|
import { MIN_WIDTH_FOR_ONE_DIGIT, MIN_WIDTH_FOR_TWO_DIGITS } from './constants'
|
|
10
11
|
import { Image } from 'react-native'
|
|
@@ -27,40 +28,49 @@ import { Image } from 'react-native'
|
|
|
27
28
|
* Group 2: >30% <60% - Mid celebrations
|
|
28
29
|
* Group 3: >=60% - Party
|
|
29
30
|
*/
|
|
30
|
-
export const
|
|
31
|
-
|
|
31
|
+
export const generateProblemsGifsTable = (
|
|
32
|
+
totalProblems: number,
|
|
33
|
+
resolvedProblems: number,
|
|
34
|
+
gifs: GifsStructure,
|
|
35
|
+
): ProblemsGifsTable => {
|
|
36
|
+
const percentageMilestones = getPercentageMilestones(totalProblems)
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
33: Math.ceil(totalProblems * 0.33),
|
|
36
|
-
66: Math.ceil(totalProblems * 0.66),
|
|
37
|
-
}
|
|
38
|
-
}
|
|
38
|
+
const result: ProblemsGifsTable = {}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
75: Math.ceil(totalProblems * 0.75),
|
|
45
|
-
}
|
|
46
|
-
}
|
|
40
|
+
for (const percentage of percentageMilestones) {
|
|
41
|
+
const problemNumber = Math.ceil(totalProblems * (percentage / 100))
|
|
42
|
+
|
|
43
|
+
if (problemNumber <= resolvedProblems) continue
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
const groupKey = findGroupForPercentage(percentage)
|
|
46
|
+
const gifsInGroup = gifs[groupKey]
|
|
47
|
+
|
|
48
|
+
if (!gifsInGroup || gifsInGroup.length === 0) continue
|
|
49
|
+
|
|
50
|
+
const unshownGif = gifsInGroup.find((gif) => !gif.isShown)
|
|
51
|
+
|
|
52
|
+
const selectedGif = unshownGif || gifsInGroup[0]
|
|
53
|
+
|
|
54
|
+
if (selectedGif) {
|
|
55
|
+
result[problemNumber] = {
|
|
56
|
+
url: selectedGif.url,
|
|
57
|
+
group: groupKey,
|
|
58
|
+
}
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
const getPercentageMilestones = (totalProblems: number): number[] => {
|
|
65
|
+
if (totalProblems <= 12) return [50]
|
|
66
|
+
|
|
67
|
+
if (totalProblems <= 18) return [33, 66]
|
|
68
|
+
|
|
69
|
+
if (totalProblems <= 24) return [25, 50, 75]
|
|
70
|
+
|
|
71
|
+
if (totalProblems <= 30) return [20, 40, 60, 80]
|
|
72
|
+
|
|
73
|
+
return [15, 30, 45, 60, 75]
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
export const generateGifsStructure = (gifs: GifsGroups): GifsStructure =>
|
|
@@ -70,7 +80,7 @@ export const generateGifsStructure = (gifs: GifsGroups): GifsStructure =>
|
|
|
70
80
|
value.map((url) => ({
|
|
71
81
|
url,
|
|
72
82
|
isShown: false,
|
|
73
|
-
group: key as
|
|
83
|
+
group: key as GroupNumberString,
|
|
74
84
|
})),
|
|
75
85
|
]),
|
|
76
86
|
)
|
|
@@ -121,3 +131,14 @@ export const getImagePath = async (url: string, cacheMethod?: CustomGetCachePath
|
|
|
121
131
|
|
|
122
132
|
export const constrainDimension = (value: number, min: number, max: number) =>
|
|
123
133
|
Math.max(min, Math.min(max, value))
|
|
134
|
+
|
|
135
|
+
export const shouldRefreshGifs = (timestamp: string | null): boolean => {
|
|
136
|
+
if (!timestamp) return true
|
|
137
|
+
|
|
138
|
+
const REFRESH_INTERVAL_HOURS = 24
|
|
139
|
+
const lastFetchedDate = new Date(timestamp)
|
|
140
|
+
const currentDate = new Date()
|
|
141
|
+
const diffInHours = (currentDate.getTime() - lastFetchedDate.getTime()) / (1000 * 60 * 60)
|
|
142
|
+
|
|
143
|
+
return diffInHours > REFRESH_INTERVAL_HOURS
|
|
144
|
+
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { createEffect, createEvent, createStore, sample, Store } from 'effector'
|
|
1
|
+
import { createEffect, createEvent, createStore, merge, sample, Store } from 'effector'
|
|
2
2
|
import { GifCelebrationsApi } from './GifCelebrationsApi'
|
|
3
3
|
import {
|
|
4
|
+
CustomLocalStorage,
|
|
4
5
|
GifCelebrationsModelProps,
|
|
5
6
|
GifObject,
|
|
6
7
|
GifsStructure,
|
|
7
|
-
|
|
8
|
+
ProblemsGifsTable,
|
|
8
9
|
} from '../types/model.types'
|
|
9
10
|
import { GifCelebrationsCache } from './GifCelebrations.cache'
|
|
10
|
-
import {
|
|
11
|
-
import { GET_GIFS_TIMEOUT, MIN_PROBLEMS_TO_SHOW_GIF } from '../constants'
|
|
11
|
+
import { generateProblemsGifsTable, shouldRefreshGifs } from '../helpers'
|
|
12
|
+
import { GET_GIFS_TIMEOUT, MIN_PROBLEMS_TO_SHOW_GIF, GIFS_FETCHED_KEY } from '../constants'
|
|
12
13
|
|
|
13
14
|
type HandleGifSelectionProps = {
|
|
14
15
|
gifsGroups: GifsStructure
|
|
15
16
|
resolvedProblem: number
|
|
16
|
-
|
|
17
|
+
problemsGifsTable: ProblemsGifsTable
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export class GifCelebrationsModel {
|
|
@@ -36,10 +37,10 @@ export class GifCelebrationsModel {
|
|
|
36
37
|
(_, isGifCelebrationMayBeTriggered) => isGifCelebrationMayBeTriggered,
|
|
37
38
|
)
|
|
38
39
|
|
|
39
|
-
public readonly
|
|
40
|
-
public readonly $
|
|
41
|
-
|
|
42
|
-
)
|
|
40
|
+
public readonly resetProblemsGifsTable = createEvent()
|
|
41
|
+
public readonly $problemsGifsTable = createStore<ProblemsGifsTable | null>(null).reset(
|
|
42
|
+
this.resetProblemsGifsTable,
|
|
43
|
+
)
|
|
43
44
|
|
|
44
45
|
public readonly setActiveGif = createEvent<GifObject>()
|
|
45
46
|
public readonly $activeGif = createStore<GifObject | null>(null)
|
|
@@ -47,12 +48,15 @@ export class GifCelebrationsModel {
|
|
|
47
48
|
.reset(this.resetGifTriggerable)
|
|
48
49
|
.on(this.setActiveGif, (_, gif) => gif)
|
|
49
50
|
|
|
51
|
+
private readonly localStorage: typeof localStorage | CustomLocalStorage
|
|
52
|
+
|
|
50
53
|
public readonly initialize = createEvent()
|
|
51
54
|
|
|
52
55
|
constructor({
|
|
53
56
|
api,
|
|
54
57
|
totalProblems,
|
|
55
58
|
resolvedProblems,
|
|
59
|
+
customLocalStorage,
|
|
56
60
|
customGetCachePathMethod,
|
|
57
61
|
customPrefetchMethod,
|
|
58
62
|
}: GifCelebrationsModelProps) {
|
|
@@ -62,16 +66,22 @@ export class GifCelebrationsModel {
|
|
|
62
66
|
this.$totalProblems = totalProblems
|
|
63
67
|
this.$resolvedProblems = resolvedProblems
|
|
64
68
|
|
|
69
|
+
this.localStorage = customLocalStorage || localStorage
|
|
65
70
|
this.setupEventHandlers()
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
private
|
|
73
|
+
private failedGetGifxTimeout: NodeJS.Timeout | null = null
|
|
69
74
|
|
|
70
75
|
private setupEventHandlers() {
|
|
71
76
|
sample({
|
|
72
77
|
clock: this.initialize,
|
|
73
78
|
source: this.cache.$gifs,
|
|
74
|
-
filter: (gifs) =>
|
|
79
|
+
filter: (gifs) => {
|
|
80
|
+
if (!gifs) return true
|
|
81
|
+
|
|
82
|
+
const gifsFetchedTimestamp = this.localStorage.getItem(GIFS_FETCHED_KEY) as string | null
|
|
83
|
+
return shouldRefreshGifs(gifsFetchedTimestamp)
|
|
84
|
+
},
|
|
75
85
|
target: this.api.getGifsFx,
|
|
76
86
|
})
|
|
77
87
|
|
|
@@ -79,19 +89,40 @@ export class GifCelebrationsModel {
|
|
|
79
89
|
source: this.api.getGifsFx.doneData,
|
|
80
90
|
filter: (response) => response.ok,
|
|
81
91
|
fn: (response) => response.data,
|
|
82
|
-
target: this.cache.
|
|
92
|
+
target: this.cache.initialSet,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
sample({
|
|
96
|
+
source: this.api.getGifsFx.doneData,
|
|
97
|
+
filter: (response) => response.ok,
|
|
98
|
+
target: createEffect(() => {
|
|
99
|
+
this.localStorage.setItem(GIFS_FETCHED_KEY, new Date().toISOString())
|
|
100
|
+
}),
|
|
83
101
|
})
|
|
84
102
|
|
|
85
103
|
sample({
|
|
86
|
-
clock: [this.
|
|
87
|
-
source:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
clock: [this.cache.$gifs, this.$totalProblems],
|
|
105
|
+
source: {
|
|
106
|
+
gifs: this.cache.$gifs,
|
|
107
|
+
totalProblems: this.$totalProblems,
|
|
108
|
+
resolvedProblems: this.$resolvedProblems,
|
|
109
|
+
},
|
|
110
|
+
filter: ({ totalProblems, gifs }) => !!(totalProblems >= MIN_PROBLEMS_TO_SHOW_GIF && gifs),
|
|
111
|
+
fn: ({ totalProblems, resolvedProblems, gifs }) => {
|
|
112
|
+
if (!gifs) return null
|
|
113
|
+
return generateProblemsGifsTable(totalProblems, resolvedProblems, gifs)
|
|
114
|
+
},
|
|
115
|
+
target: this.$problemsGifsTable,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
sample({
|
|
119
|
+
source: this.$problemsGifsTable,
|
|
120
|
+
filter: (problemsGifsTable) => !!problemsGifsTable,
|
|
121
|
+
fn: (problemsGifsTable) => {
|
|
122
|
+
if (!problemsGifsTable) return []
|
|
123
|
+
return Object.values(problemsGifsTable).map((gif) => gif.url)
|
|
93
124
|
},
|
|
94
|
-
target: this
|
|
125
|
+
target: this.cache.prefetchGifs,
|
|
95
126
|
})
|
|
96
127
|
|
|
97
128
|
sample({
|
|
@@ -100,79 +131,63 @@ export class GifCelebrationsModel {
|
|
|
100
131
|
isGifCelebrationMayBeTriggered: this.$isGifTriggerable,
|
|
101
132
|
gifsGroups: this.cache.$gifs,
|
|
102
133
|
resolvedProblem: this.$resolvedProblems,
|
|
103
|
-
|
|
134
|
+
problemsGifsTable: this.$problemsGifsTable,
|
|
104
135
|
},
|
|
105
136
|
filter: ({
|
|
106
137
|
isGifCelebrationMayBeTriggered,
|
|
107
138
|
gifsGroups,
|
|
108
139
|
resolvedProblem,
|
|
109
|
-
|
|
140
|
+
problemsGifsTable,
|
|
110
141
|
}) =>
|
|
111
142
|
!!(
|
|
112
143
|
isGifCelebrationMayBeTriggered &&
|
|
113
144
|
gifsGroups &&
|
|
114
145
|
resolvedProblem !== undefined &&
|
|
115
|
-
|
|
146
|
+
problemsGifsTable
|
|
116
147
|
),
|
|
117
148
|
fn: (sources) => sources as HandleGifSelectionProps,
|
|
118
149
|
target: this.handleGifSelection,
|
|
119
150
|
})
|
|
120
151
|
|
|
121
152
|
this.api.getGifsFx.fail.watch(() => {
|
|
122
|
-
this.
|
|
153
|
+
this.failedGetGifxTimeout = setTimeout(() => {
|
|
123
154
|
this.api.getGifsFx()
|
|
124
155
|
}, GET_GIFS_TIMEOUT)
|
|
125
156
|
})
|
|
126
157
|
|
|
127
158
|
this.api.getGifsFx.done.watch(() => {
|
|
128
|
-
if (this.
|
|
129
|
-
clearTimeout(this.
|
|
130
|
-
this.
|
|
159
|
+
if (this.failedGetGifxTimeout) {
|
|
160
|
+
clearTimeout(this.failedGetGifxTimeout)
|
|
161
|
+
this.failedGetGifxTimeout = null
|
|
131
162
|
}
|
|
132
163
|
})
|
|
133
164
|
}
|
|
134
165
|
|
|
135
166
|
private readonly handleGifSelection = createEffect(
|
|
136
|
-
({ gifsGroups, resolvedProblem,
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
if (!percentageMilestone) return null
|
|
142
|
-
|
|
143
|
-
const groupName = findGroupForPercentage(Number(percentageMilestone))
|
|
144
|
-
|
|
145
|
-
const gifsForGroup = gifsGroups[groupName]
|
|
146
|
-
const unusedGifIndex = gifsForGroup.findIndex((gif) => !gif.isShown)
|
|
147
|
-
|
|
148
|
-
if (unusedGifIndex === -1) {
|
|
149
|
-
const updatedGifsGroups = { ...gifsGroups }
|
|
150
|
-
updatedGifsGroups[groupName] = gifsGroups[groupName].map((gif) => ({
|
|
151
|
-
...gif,
|
|
152
|
-
isShown: false,
|
|
153
|
-
}))
|
|
154
|
-
const gifObject: GifObject = {
|
|
155
|
-
...updatedGifsGroups[groupName][0],
|
|
156
|
-
isShown: true,
|
|
157
|
-
}
|
|
158
|
-
updatedGifsGroups[groupName][0].isShown = true
|
|
159
|
-
|
|
160
|
-
this.setActiveGif(gifObject)
|
|
161
|
-
this.cache.set(updatedGifsGroups)
|
|
162
|
-
}
|
|
167
|
+
({ gifsGroups, resolvedProblem, problemsGifsTable }: HandleGifSelectionProps) => {
|
|
168
|
+
const gifToShow = problemsGifsTable[resolvedProblem]
|
|
169
|
+
const groupName = gifToShow.group
|
|
163
170
|
|
|
164
|
-
const
|
|
171
|
+
const gifsInGroup = gifsGroups[groupName]
|
|
172
|
+
const selectedGifIndex = gifsInGroup?.findIndex((gif) => gif.url === gifToShow.url)
|
|
165
173
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
if (!gifsInGroup || selectedGifIndex === -1) {
|
|
175
|
+
console.warn(`Gif with URL ${gifToShow.url} not found in group ${groupName}`)
|
|
176
|
+
return
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
const updatedGifsInGroup = gifsInGroup.map((gif, index) => ({
|
|
180
|
+
...gif,
|
|
181
|
+
isShown: index === selectedGifIndex ? true : gif.isShown,
|
|
182
|
+
}))
|
|
183
|
+
console.log('updatedGifsInGroup: ', updatedGifsInGroup)
|
|
184
|
+
|
|
185
|
+
this.cache.set({
|
|
186
|
+
...gifsGroups,
|
|
187
|
+
[groupName]: updatedGifsInGroup,
|
|
188
|
+
})
|
|
174
189
|
|
|
175
|
-
this.setActiveGif(
|
|
190
|
+
this.setActiveGif(gifToShow)
|
|
176
191
|
},
|
|
177
192
|
)
|
|
178
193
|
}
|
|
@@ -2,6 +2,10 @@ import { Store } from 'effector'
|
|
|
2
2
|
|
|
3
3
|
export type CustomGetCachePathMethod = (uri: string) => Promise<string | null>
|
|
4
4
|
export type CustomPrefetchMethod = (gifs: string[] | string) => Promise<unknown>
|
|
5
|
+
export type CustomLocalStorage = {
|
|
6
|
+
setItem: (key: string, value: string) => void
|
|
7
|
+
getItem: (key: string) => string | null | undefined
|
|
8
|
+
}
|
|
5
9
|
|
|
6
10
|
export type GroupNumber = number
|
|
7
11
|
|
|
@@ -13,20 +17,22 @@ export type GifCelebrationsModelProps = {
|
|
|
13
17
|
api: GifCelebrationApiRequests
|
|
14
18
|
totalProblems: Store<number>
|
|
15
19
|
resolvedProblems: Store<number>
|
|
20
|
+
customLocalStorage?: CustomLocalStorage
|
|
16
21
|
customPrefetchMethod?: CustomPrefetchMethod
|
|
17
22
|
customGetCachePathMethod?: CustomGetCachePathMethod
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export type GifsGroup = string[]
|
|
21
|
-
export type
|
|
26
|
+
export type GroupNumberString = `group${GroupNumber}`
|
|
27
|
+
export type GifsGroups = Record<GroupNumberString, GifsGroup>
|
|
22
28
|
|
|
23
29
|
export type GifObject = {
|
|
24
30
|
url: string
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
group: GroupNumberString
|
|
32
|
+
isShown?: boolean
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
export type GifsStructure = Record
|
|
35
|
+
export type GifsStructure = Record<GroupNumberString, GifObject[]>
|
|
30
36
|
/**
|
|
31
37
|
* Usually, the response contains 3 groups of gifs:
|
|
32
38
|
* - group1: early celebrations
|
|
@@ -41,7 +47,7 @@ export type GifsPayload = {
|
|
|
41
47
|
group: number
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
export type
|
|
50
|
+
export type ProblemsGifsTable = Record<number, GifObject>
|
|
45
51
|
export type ProgressValue = 0 | 1 | 2
|
|
46
52
|
|
|
47
53
|
export type TextComponentType = (text: string) => React.ReactNode
|