@selfcommunity/react-core 0.6.4-alpha.9 → 0.6.4-courses.102

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.
Files changed (59) hide show
  1. package/lib/cjs/components/provider/SCRoutingProvider/index.js +1 -0
  2. package/lib/cjs/components/provider/SCUserProvider/index.js +6 -0
  3. package/lib/cjs/constants/Cache.d.ts +25 -2
  4. package/lib/cjs/constants/Cache.js +34 -4
  5. package/lib/cjs/constants/Notification.d.ts +1 -0
  6. package/lib/cjs/constants/Notification.js +2 -1
  7. package/lib/cjs/constants/Preferences.d.ts +8 -0
  8. package/lib/cjs/constants/Preferences.js +18 -2
  9. package/lib/cjs/constants/Routes.d.ts +8 -0
  10. package/lib/cjs/constants/Routes.js +17 -1
  11. package/lib/cjs/hooks/useSCFetchCourse.d.ts +23 -0
  12. package/lib/cjs/hooks/useSCFetchCourse.js +81 -0
  13. package/lib/cjs/hooks/useSCFetchCourses.d.ts +22 -0
  14. package/lib/cjs/hooks/useSCFetchCourses.js +83 -0
  15. package/lib/cjs/hooks/useSCFetchLesson.d.ts +22 -0
  16. package/lib/cjs/hooks/useSCFetchLesson.js +72 -0
  17. package/lib/cjs/hooks/useSCFetchLessonCommentObject.d.ts +23 -0
  18. package/lib/cjs/hooks/useSCFetchLessonCommentObject.js +72 -0
  19. package/lib/cjs/hooks/useSCFetchLessonCommentObjects.d.ts +48 -0
  20. package/lib/cjs/hooks/useSCFetchLessonCommentObjects.js +302 -0
  21. package/lib/cjs/hooks/useSCFetchLiveStream.d.ts +20 -0
  22. package/lib/cjs/hooks/useSCFetchLiveStream.js +76 -0
  23. package/lib/cjs/hooks/useSCJoinedCoursesManager.d.ts +38 -0
  24. package/lib/cjs/hooks/useSCJoinedCoursesManager.js +277 -0
  25. package/lib/cjs/index.d.ts +8 -2
  26. package/lib/cjs/index.js +14 -2
  27. package/lib/cjs/types/context.d.ts +36 -1
  28. package/lib/cjs/types/index.d.ts +2 -2
  29. package/lib/esm/components/provider/SCRoutingProvider/index.js +2 -1
  30. package/lib/esm/components/provider/SCUserProvider/index.js +6 -0
  31. package/lib/esm/constants/Cache.d.ts +25 -2
  32. package/lib/esm/constants/Cache.js +25 -2
  33. package/lib/esm/constants/Notification.d.ts +1 -0
  34. package/lib/esm/constants/Notification.js +2 -1
  35. package/lib/esm/constants/Preferences.d.ts +8 -0
  36. package/lib/esm/constants/Preferences.js +16 -0
  37. package/lib/esm/constants/Routes.d.ts +8 -0
  38. package/lib/esm/constants/Routes.js +16 -0
  39. package/lib/esm/hooks/useSCFetchCourse.d.ts +23 -0
  40. package/lib/esm/hooks/useSCFetchCourse.js +78 -0
  41. package/lib/esm/hooks/useSCFetchCourses.d.ts +22 -0
  42. package/lib/esm/hooks/useSCFetchCourses.js +81 -0
  43. package/lib/esm/hooks/useSCFetchLesson.d.ts +22 -0
  44. package/lib/esm/hooks/useSCFetchLesson.js +69 -0
  45. package/lib/esm/hooks/useSCFetchLessonCommentObject.d.ts +23 -0
  46. package/lib/esm/hooks/useSCFetchLessonCommentObject.js +69 -0
  47. package/lib/esm/hooks/useSCFetchLessonCommentObjects.d.ts +48 -0
  48. package/lib/esm/hooks/useSCFetchLessonCommentObjects.js +297 -0
  49. package/lib/esm/hooks/useSCFetchLiveStream.d.ts +20 -0
  50. package/lib/esm/hooks/useSCFetchLiveStream.js +73 -0
  51. package/lib/esm/hooks/useSCJoinedCoursesManager.d.ts +38 -0
  52. package/lib/esm/hooks/useSCJoinedCoursesManager.js +273 -0
  53. package/lib/esm/index.d.ts +8 -2
  54. package/lib/esm/index.js +7 -1
  55. package/lib/esm/types/context.d.ts +36 -1
  56. package/lib/esm/types/index.d.ts +2 -2
  57. package/lib/umd/react-core.js +1 -1
  58. package/lib/umd/react-core.js.LICENSE.txt +2 -0
  59. package/package.json +132 -127
@@ -0,0 +1,297 @@
1
+ import { useEffect, useReducer } from 'react';
2
+ import { SCOPE_SC_CORE } from '../constants/Errors';
3
+ import { SCCommentsOrderBy } from '@selfcommunity/types';
4
+ import { Endpoints, http } from '@selfcommunity/api-services';
5
+ import { CacheStrategies, Logger, LRUCache } from '@selfcommunity/utils';
6
+ import { getLessonCommentCacheKey, getLessonCommentsCacheKey } from '../constants/Cache';
7
+ import { useIsComponentMountedRef } from '../utils/hooks';
8
+ import { getCurrentPage } from '../utils/pagination';
9
+ import useSCFetchLesson from '../hooks/useSCFetchLesson';
10
+ /**
11
+ * @hidden
12
+ * We have complex state logic that involves multiple sub-values,
13
+ * so useReducer is preferable to useState.
14
+ * Define all possible auth action types label
15
+ * Use this to export actions and dispatch an action
16
+ */
17
+ export const commentsObjectActionTypes = {
18
+ LOADING_NEXT: '_loading_next',
19
+ LOADING_PREVIOUS: '_loading_previous',
20
+ DATA_NEXT_LOADED: '_data_next_loaded',
21
+ DATA_PREVIOUS_LOADED: '_data_previous_loaded',
22
+ DATA_RELOAD: '_data_reload',
23
+ DATA_RELOADED: '_data_reloaded',
24
+ DATA_REVALIDATE: '_data_revalidate',
25
+ };
26
+ /**
27
+ * commentsReducer:
28
+ * - manage the state of comments object
29
+ * - update the state base on action type
30
+ * @param state
31
+ * @param action
32
+ */
33
+ function commentsReducer(state, action) {
34
+ switch (action.type) {
35
+ case commentsObjectActionTypes.LOADING_NEXT:
36
+ return Object.assign(Object.assign({}, state), { isLoadingNext: true, isLoadingPrevious: false });
37
+ case commentsObjectActionTypes.LOADING_PREVIOUS:
38
+ return Object.assign(Object.assign({}, state), { isLoadingNext: false, isLoadingPrevious: true });
39
+ case commentsObjectActionTypes.DATA_NEXT_LOADED:
40
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, state), { comments: action.payload.comments, isLoadingNext: false, componentLoaded: true, revalidate: false, revalidateNext: null, revalidatePrevious: null, next: action.payload.next }), (action.payload.page ? { page: action.payload.page } : {})), (action.payload.nextPage ? { nextPage: action.payload.nextPage } : {})), (action.payload.previous ? { previous: action.payload.previous } : {})), (action.payload.previousPage ? { previousPage: action.payload.previousPage } : {})), (action.payload.total ? { total: action.payload.total } : {}));
41
+ case commentsObjectActionTypes.DATA_PREVIOUS_LOADED:
42
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, state), { comments: action.payload.comments, isLoadingPrevious: false, revalidate: false, revalidateNext: null, revalidatePrevious: null, previous: action.payload.previous }), (action.payload.page ? { page: action.payload.page } : {})), (action.payload.previousPage ? { previousPage: action.payload.previousPage } : {}));
43
+ case commentsObjectActionTypes.DATA_RELOAD:
44
+ return Object.assign(Object.assign({}, state), { next: action.payload.next, previousPage: null, nextPage: null, comments: [], total: 0, previous: null, isLoadingNext: true, reload: true });
45
+ case commentsObjectActionTypes.DATA_RELOADED:
46
+ return Object.assign(Object.assign({}, state), { componentLoaded: true, reload: false });
47
+ case commentsObjectActionTypes.DATA_REVALIDATE:
48
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, state), { componentLoaded: true, revalidate: true }), (action.payload.revalidateNext ? { revalidateNext: action.payload.revalidateNext } : {})), (action.payload.revalidatePrevious ? { revalidatePrevious: action.payload.revalidatePrevious } : {})), { reload: false });
49
+ default:
50
+ throw new Error(`Unhandled type: ${action.type}`);
51
+ }
52
+ }
53
+ /**
54
+ * Define initial state
55
+ * @param data
56
+ */
57
+ function stateInitializer(data) {
58
+ const __commentsObjectCacheKey = data.scLesson ? getLessonCommentsCacheKey(data.scLesson.id, data.next) : null;
59
+ let _initState = {
60
+ componentLoaded: false,
61
+ comments: [],
62
+ total: 0,
63
+ next: data.next,
64
+ previous: null,
65
+ isLoadingNext: false,
66
+ isLoadingPrevious: false,
67
+ page: Math.ceil(data.offset / data.pageSize + 1),
68
+ reload: false,
69
+ revalidate: false,
70
+ revalidateNext: null,
71
+ revalidatePrevious: null,
72
+ };
73
+ _initState['nextPage'] = _initState.next ? _initState.page + 1 : null;
74
+ _initState['previousPage'] = _initState.previous ? _initState.page - 1 : null;
75
+ if (__commentsObjectCacheKey && LRUCache.hasKey(__commentsObjectCacheKey) && data.cacheStrategy !== CacheStrategies.NETWORK_ONLY) {
76
+ const _cachedData = LRUCache.get(__commentsObjectCacheKey);
77
+ let page = Math.max(getCurrentPage(_cachedData.next, data.pageSize), 1);
78
+ const nextPage = _cachedData.next ? getCurrentPage(_cachedData.next, data.pageSize) : null;
79
+ const previousPage = _cachedData.previous ? Math.max(getCurrentPage(_cachedData.previous, data.pageSize) - 1, 1) : null;
80
+ return Object.assign(Object.assign({}, _initState), {
81
+ total: _cachedData.count,
82
+ next: _cachedData.next,
83
+ previous: _cachedData.previous,
84
+ comments: _cachedData.results,
85
+ page,
86
+ nextPage,
87
+ previousPage,
88
+ componentLoaded: true,
89
+ });
90
+ }
91
+ return _initState;
92
+ }
93
+ /**
94
+ :::info
95
+ This custom hooks is used to fetch paginated comments for a specific course lesson.
96
+ :::
97
+ * @param props
98
+ */
99
+ export default function useSCFetchLessonCommentObjects(props) {
100
+ // PROPS
101
+ const { id, lessonObject, offset = 0, pageSize = 5, orderBy = SCCommentsOrderBy.ADDED_AT_DESC, parent, onChangePage, cacheStrategy = CacheStrategies.NETWORK_ONLY, } = props;
102
+ // FeedObject
103
+ const { scLesson, setSCLesson } = useSCFetchLesson({
104
+ id: id,
105
+ lesson: lessonObject,
106
+ courseId: lessonObject.course_id,
107
+ sectionId: lessonObject.section_id,
108
+ cacheStrategy,
109
+ });
110
+ /**
111
+ * Get next url
112
+ */
113
+ const getNextUrl = () => {
114
+ const _offset = offset ? `&offset=${offset}` : '';
115
+ const _parent = parent ? `&parent=${parent}` : '';
116
+ const _lessonId = scLesson ? scLesson.id : id;
117
+ const _courseId = scLesson ? scLesson.course_id : lessonObject.course_id;
118
+ const _sectionId = scLesson ? scLesson.section_id : lessonObject.section_id;
119
+ return `${Endpoints.GetCourseLessonComments.url({
120
+ id: _courseId,
121
+ section_id: _sectionId,
122
+ lesson_id: _lessonId,
123
+ })}?limit=${pageSize}&ordering=${orderBy}${_offset}${_parent}`;
124
+ };
125
+ // STATE
126
+ const [state, dispatch] = useReducer(commentsReducer, { scLesson, offset, pageSize, next: getNextUrl(), cacheStrategy }, stateInitializer);
127
+ // REFS
128
+ const isMountedRef = useIsComponentMountedRef();
129
+ /**
130
+ * Get Comments (with cache)
131
+ */
132
+ const revalidate = (url, forward) => {
133
+ return performFetchComments(url, false).then((res) => {
134
+ let _comments;
135
+ let page = getCurrentPage(forward ? res.next : res.previous, pageSize);
136
+ if (forward) {
137
+ let start = state.comments.slice(0, state.comments.length - res.results.length);
138
+ _comments = start.concat(res.results);
139
+ }
140
+ else {
141
+ let start = state.comments.slice(res.results.length, state.comments.length);
142
+ _comments = res.results.concat(start);
143
+ }
144
+ if (isMountedRef.current) {
145
+ dispatch({
146
+ type: forward ? commentsObjectActionTypes.DATA_NEXT_LOADED : commentsObjectActionTypes.DATA_PREVIOUS_LOADED,
147
+ payload: Object.assign(Object.assign({ page, comments: _comments }, (forward ? { next: res.next } : { previous: res.previous })), { total: res.count }),
148
+ });
149
+ }
150
+ });
151
+ };
152
+ /**
153
+ * Get Comments
154
+ */
155
+ const performFetchComments = (url, seekCache = true) => {
156
+ const __commentObjectsCacheKey = getLessonCommentsCacheKey(scLesson.id, url);
157
+ if (seekCache && LRUCache.hasKey(__commentObjectsCacheKey) && cacheStrategy !== CacheStrategies.NETWORK_ONLY) {
158
+ return Promise.resolve(LRUCache.get(__commentObjectsCacheKey));
159
+ }
160
+ return http
161
+ .request({
162
+ url,
163
+ method: Endpoints.GetCourseLessonComments.method,
164
+ })
165
+ .then((res) => {
166
+ if (res.status >= 300) {
167
+ return Promise.reject(res);
168
+ }
169
+ LRUCache.set(__commentObjectsCacheKey, res.data);
170
+ res.data.results.forEach((e) => LRUCache.set(getLessonCommentCacheKey(e.id), e));
171
+ return Promise.resolve(res.data);
172
+ });
173
+ };
174
+ /**
175
+ * Fetch previous comments
176
+ */
177
+ function getPreviousPage() {
178
+ if (scLesson && state.previous && !state.isLoadingPrevious) {
179
+ const _previous = state.previous;
180
+ dispatch({ type: commentsObjectActionTypes.LOADING_PREVIOUS });
181
+ performFetchComments(_previous)
182
+ .then((res) => {
183
+ if (isMountedRef.current) {
184
+ let currentPage = getCurrentPage(_previous, pageSize);
185
+ let previousPage = res.previous ? currentPage - 1 : null;
186
+ dispatch({
187
+ type: commentsObjectActionTypes.DATA_PREVIOUS_LOADED,
188
+ payload: {
189
+ page: currentPage,
190
+ previousPage,
191
+ comments: [...res.results, ...state.comments],
192
+ previous: res.previous,
193
+ },
194
+ });
195
+ onChangePage && onChangePage(currentPage);
196
+ }
197
+ })
198
+ .catch((error) => {
199
+ Logger.error(SCOPE_SC_CORE, error);
200
+ })
201
+ .then(() => {
202
+ if (isMountedRef.current && cacheStrategy === CacheStrategies.STALE_WHILE_REVALIDATE) {
203
+ dispatch({ type: commentsObjectActionTypes.DATA_REVALIDATE, payload: { revalidatePrevious: _previous } });
204
+ }
205
+ });
206
+ }
207
+ }
208
+ /**
209
+ * Fetch next comments
210
+ */
211
+ function getNextPage() {
212
+ if (scLesson && state.next && !state.isLoadingNext) {
213
+ const _next = state.next;
214
+ dispatch({ type: commentsObjectActionTypes.LOADING_NEXT });
215
+ performFetchComments(_next)
216
+ .then((res) => {
217
+ if (isMountedRef.current) {
218
+ let currentPage = getCurrentPage(_next, pageSize);
219
+ let nextPage = res.next ? currentPage + 1 : null;
220
+ dispatch({
221
+ type: commentsObjectActionTypes.DATA_NEXT_LOADED,
222
+ payload: Object.assign({ page: currentPage, nextPage, comments: [...state.comments, ...res.results], next: res.next, total: res.count, componentLoaded: true }, (offset && state.comments.length === 0 ? { previous: res.previous, previousPage: res.previous ? currentPage - 1 : null } : {})),
223
+ });
224
+ onChangePage && onChangePage(currentPage);
225
+ }
226
+ })
227
+ .catch((error) => {
228
+ Logger.error(SCOPE_SC_CORE, error);
229
+ })
230
+ .then(() => {
231
+ if (isMountedRef.current && cacheStrategy === CacheStrategies.STALE_WHILE_REVALIDATE) {
232
+ dispatch({ type: commentsObjectActionTypes.DATA_REVALIDATE, payload: { revalidateNext: _next } });
233
+ }
234
+ });
235
+ }
236
+ }
237
+ /**
238
+ * Reset component status on change orderBy, pageSize, offset
239
+ */
240
+ const reload = () => {
241
+ if (isMountedRef.current && state.componentLoaded && Boolean(scLesson) && !state.isLoadingNext && !state.reload) {
242
+ dispatch({
243
+ type: commentsObjectActionTypes.DATA_RELOAD,
244
+ payload: {
245
+ next: getNextUrl(),
246
+ },
247
+ });
248
+ }
249
+ };
250
+ /**
251
+ * Reload fetch comments
252
+ */
253
+ useEffect(() => {
254
+ if (isMountedRef.current && state.componentLoaded && state.reload && !state.isLoadingNext && !state.isLoadingPrevious) {
255
+ dispatch({
256
+ type: commentsObjectActionTypes.DATA_RELOADED,
257
+ });
258
+ getNextPage();
259
+ }
260
+ }, [state.reload, isMountedRef]);
261
+ /**
262
+ * Revalidate last fetched comments
263
+ */
264
+ useEffect(() => {
265
+ if (isMountedRef.current && state.componentLoaded && Boolean(scLesson) && !state.reload && state.revalidate) {
266
+ revalidate(state.revalidateNext, Boolean(state.revalidateNext));
267
+ }
268
+ }, [state.revalidate, isMountedRef]);
269
+ /**
270
+ * Update lesson comments list
271
+ * @param updatedData
272
+ */
273
+ const updateLessonComments = (updatedData) => {
274
+ if (updatedData) {
275
+ dispatch({
276
+ type: commentsObjectActionTypes.DATA_NEXT_LOADED,
277
+ payload: {
278
+ comments: updatedData,
279
+ next: state.next,
280
+ total: updatedData.length,
281
+ },
282
+ });
283
+ const __commentsCacheKey = getLessonCommentsCacheKey(scLesson.id, state.next);
284
+ LRUCache.set(__commentsCacheKey, updatedData.map((comment) => {
285
+ const __commentCacheKey = getLessonCommentCacheKey(comment.id);
286
+ LRUCache.set(__commentCacheKey, comment);
287
+ return comment.id;
288
+ }));
289
+ }
290
+ };
291
+ return Object.assign(Object.assign({ lessonObject: scLesson, setLessonObject: setSCLesson }, state), { pageSize,
292
+ getNextPage,
293
+ getPreviousPage,
294
+ orderBy,
295
+ reload,
296
+ updateLessonComments });
297
+ }
@@ -0,0 +1,20 @@
1
+ import { SCLiveStreamType } from '@selfcommunity/types';
2
+ import { CacheStrategies } from '@selfcommunity/utils';
3
+ /**
4
+ :::info
5
+ This custom hook is used to fetch an liveStream object.
6
+ :::
7
+ * @param object
8
+ * @param object.id
9
+ * @param object.liveStream
10
+ * @param object.cacheStrategy
11
+ */
12
+ export default function useSCFetchLiveStream({ id, liveStream, cacheStrategy, }: {
13
+ id?: number | string;
14
+ liveStream?: SCLiveStreamType;
15
+ cacheStrategy?: CacheStrategies;
16
+ }): {
17
+ scLiveStream: SCLiveStreamType;
18
+ setSCLiveStream: (e: SCLiveStreamType) => void;
19
+ error: string;
20
+ };
@@ -0,0 +1,73 @@
1
+ import { Endpoints, http } from '@selfcommunity/api-services';
2
+ import { CacheStrategies, Logger, LRUCache, objectWithoutProperties } from '@selfcommunity/utils';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { useDeepCompareEffectNoCheck } from 'use-deep-compare-effect';
5
+ import { useSCUser } from '../components/provider/SCUserProvider';
6
+ import { getLiveStreamObjectCacheKey } from '../constants/Cache';
7
+ import { SCOPE_SC_CORE } from '../constants/Errors';
8
+ /**
9
+ :::info
10
+ This custom hook is used to fetch an liveStream object.
11
+ :::
12
+ * @param object
13
+ * @param object.id
14
+ * @param object.liveStream
15
+ * @param object.cacheStrategy
16
+ */
17
+ export default function useSCFetchLiveStream({ id = null, liveStream = null, cacheStrategy = CacheStrategies.CACHE_FIRST, }) {
18
+ const __eventId = liveStream ? liveStream.id : id;
19
+ // CONTEXT
20
+ const scUserContext = useSCUser();
21
+ const authUserId = scUserContext.user ? scUserContext.user.id : null;
22
+ // CACHE
23
+ const __liveStreamCacheKey = getLiveStreamObjectCacheKey(__eventId);
24
+ const __liveStream = authUserId ? liveStream : objectWithoutProperties(liveStream, ['subscription_status']);
25
+ const [scLiveStream, setScLiveStream] = useState(cacheStrategy !== CacheStrategies.NETWORK_ONLY ? LRUCache.get(__liveStreamCacheKey, __liveStream) : null);
26
+ const [error, setError] = useState(null);
27
+ /**
28
+ * Memoized setSCLiveStream (auto-subscription if need it)
29
+ */
30
+ const setSCLiveStream = useMemo(() => (e) => {
31
+ setScLiveStream(e);
32
+ LRUCache.set(__liveStreamCacheKey, e);
33
+ }, [authUserId, setScLiveStream]);
34
+ /**
35
+ * Memoized fetchTag
36
+ */
37
+ const fetchLiveStream = useMemo(() => (id) => {
38
+ return http
39
+ .request({
40
+ url: Endpoints.GetLiveStreamInfo.url({ id }),
41
+ method: Endpoints.GetLiveStreamInfo.method,
42
+ })
43
+ .then((res) => {
44
+ if (res.status >= 300) {
45
+ return Promise.reject(res);
46
+ }
47
+ return Promise.resolve(res.data);
48
+ });
49
+ }, []);
50
+ /**
51
+ * If id attempt to get the liveStream by id
52
+ */
53
+ useEffect(() => {
54
+ if (id !== null && id !== undefined && !liveStream) {
55
+ fetchLiveStream(id)
56
+ .then((e) => {
57
+ setSCLiveStream(e);
58
+ })
59
+ .catch((err) => {
60
+ LRUCache.delete(__liveStreamCacheKey);
61
+ setError(`LiveStream with id ${id} not found`);
62
+ Logger.error(SCOPE_SC_CORE, `LiveStream with id ${id} not found`);
63
+ Logger.error(SCOPE_SC_CORE, err.message);
64
+ });
65
+ }
66
+ }, [id, liveStream, authUserId]);
67
+ useDeepCompareEffectNoCheck(() => {
68
+ if (liveStream) {
69
+ setSCLiveStream(liveStream);
70
+ }
71
+ }, [liveStream, authUserId]);
72
+ return { scLiveStream, setSCLiveStream, error };
73
+ }
@@ -0,0 +1,38 @@
1
+ import { SCCourseType, SCUserType } from '@selfcommunity/types';
2
+ /**
3
+ :::info
4
+ This custom hook is used to manage the courses followed.
5
+ :::
6
+
7
+ :::tip How to use it:
8
+ Follow these steps:
9
+ ```jsx
10
+ 1. const scUserContext: SCUserContextType = useSCUser();
11
+ 2. const scJoinedCoursesManager: SCJoinedCoursesManagerType = scUserContext.manager.courses;
12
+ 3. scJoinedCoursesManager.isJoined(course)
13
+ ```
14
+ :::
15
+ */
16
+ export default function useSCJoinedCoursesManager(user?: SCUserType): {
17
+ courses: any[];
18
+ loading: any[];
19
+ isLoading: (v: number | {
20
+ id: number;
21
+ }) => boolean;
22
+ join?: undefined;
23
+ leave?: undefined;
24
+ joinStatus?: undefined;
25
+ refresh?: undefined;
26
+ emptyCache?: undefined;
27
+ } | {
28
+ courses: any[];
29
+ loading: any[];
30
+ isLoading: (v: number | {
31
+ id: number;
32
+ }) => boolean;
33
+ join: (course: SCCourseType, userId?: number) => Promise<any>;
34
+ leave: (course: SCCourseType) => Promise<any>;
35
+ joinStatus: (course: SCCourseType) => string;
36
+ refresh: () => void;
37
+ emptyCache: () => void;
38
+ };