-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathutils.ts
More file actions
198 lines (172 loc) · 5.7 KB
/
utils.ts
File metadata and controls
198 lines (172 loc) · 5.7 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import type {
TEnvironmentState,
TEnvironmentStateProject,
TProjectStyling,
TUserState,
} from "@/types/config";
import type { Result } from "@/types/error";
import type { TSurvey } from "@/types/survey";
// Helper function to calculate difference in days between two dates
export const diffInDays = (date1: Date, date2: Date): number => {
const diffTime = Math.abs(date2.getTime() - date1.getTime());
return Math.floor(diffTime / (1000 * 60 * 60 * 24));
};
export const wrapThrowsAsync =
<T, A extends unknown[]>(fn: (...args: A) => Promise<T>) =>
async (...args: A): Promise<Result<T>> => {
try {
return {
ok: true,
data: await fn(...args),
};
} catch (error) {
return {
ok: false,
error: error as Error,
};
}
};
/**
* Filters surveys based on the displayOption, recontactDays, and segments
* @param environmentSate - The environment state
* @param userState - The user state
* @returns The filtered surveys
*/
// takes the environment and user state and returns the filtered surveys
export const filterSurveys = (
environmentState: TEnvironmentState,
userState: TUserState
): TSurvey[] => {
const { project, surveys } = environmentState.data;
const { displays, responses, lastDisplayAt, segments, userId } =
userState.data;
// Function to filter surveys based on displayOption criteria
let filteredSurveys = surveys.filter((survey: TSurvey) => {
switch (survey.displayOption) {
case "respondMultiple":
return true;
case "displayOnce":
return (
displays.filter((display) => display.surveyId === survey.id)
.length === 0
);
case "displayMultiple":
return (
responses.filter((surveyId) => surveyId === survey.id).length === 0
);
case "displaySome":
if (survey.displayLimit === null) {
return true;
}
// Check if survey response exists, if so, stop here
if (responses.filter((surveyId) => surveyId === survey.id).length) {
return false;
}
// Otherwise, check if displays length is less than displayLimit
return (
displays.filter((display) => display.surveyId === survey.id).length <
survey.displayLimit
);
default:
throw Error("Invalid displayOption");
}
});
// filter surveys that meet the recontactDays criteria
filteredSurveys = filteredSurveys.filter((survey) => {
// if no survey was displayed yet, show the survey
if (!lastDisplayAt) {
return true;
}
// if survey has recontactDays, check if the last display was more than recontactDays ago
// The previous approach checked the last display for each survey which is why we still have a surveyId in the displays array.
// NOSONAR
// TODO: Remove the surveyId from the displays array
if (survey.recontactDays !== null) {
return (
diffInDays(new Date(), new Date(lastDisplayAt)) >= survey.recontactDays
);
}
// use recontactDays of the project if survey does not have recontactDays
if (project.recontactDays) {
return (
diffInDays(new Date(), new Date(lastDisplayAt)) >= project.recontactDays
);
}
// if no recontactDays is set, show the survey
return true;
});
if (!userId) {
// exclude surveys that have a segment with filters
return filteredSurveys.filter((survey) => {
const segmentFiltersLength = survey.segment?.filters?.length ?? 0;
return segmentFiltersLength === 0;
});
}
if (!segments.length) {
return [];
}
// filter surveys based on segments
return filteredSurveys.filter((survey) => {
return survey.segment?.id && segments.includes(survey.segment.id);
});
};
export const getStyling = (
project: TEnvironmentStateProject,
survey: TSurvey
): TProjectStyling | TSurvey["styling"] => {
// allow style overwrite is enabled from the project
if (project.styling.allowStyleOverwrite) {
// survey style overwrite is disabled
if (!survey.styling?.overwriteThemeStyling) {
return project.styling;
}
// survey style overwrite is enabled
return survey.styling;
}
// allow style overwrite is disabled from the project
return project.styling;
};
export const getDefaultLanguageCode = (survey: TSurvey): string | undefined => {
const defaultSurveyLanguage = survey.languages.find((surveyLanguage) => {
return surveyLanguage.default;
});
if (defaultSurveyLanguage) return defaultSurveyLanguage.language.code;
};
export const getLanguageCode = (
survey: TSurvey,
language?: string
): string | undefined => {
const availableLanguageCodes = survey.languages.map(
(surveyLanguage) => surveyLanguage.language.code
);
if (!language) return "default";
const selectedLanguage = survey.languages.find((surveyLanguage) => {
return (
surveyLanguage.language.code === language.toLowerCase() ||
surveyLanguage.language.alias?.toLowerCase() === language.toLowerCase()
);
});
if (selectedLanguage?.default) {
return "default";
}
if (
!selectedLanguage ||
!selectedLanguage.enabled ||
!availableLanguageCodes.includes(selectedLanguage.language.code)
) {
return undefined;
}
return selectedLanguage.language.code;
};
export const shouldDisplayBasedOnPercentage = (
displayPercentage: number
): boolean => {
const randomNum = Math.floor(Math.random() * 10000) / 100;
return randomNum <= displayPercentage;
};
export const isNowExpired = (expirationDate: Date): boolean => {
return new Date() >= expirationDate;
};
export const delayedResult = async <T>(value: T, ms: number): Promise<T> => {
return new Promise((resolve) => setTimeout(() => resolve(value), ms));
};