Skip to content

Commit 3e08553

Browse files
committed
add wait spinner while fetching studySession items
1 parent bd159f8 commit 3e08553

File tree

2 files changed

+154
-58
lines changed

2 files changed

+154
-58
lines changed

packages/common-ui/src/components/StudySession.vue

Lines changed: 75 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export default defineComponent({
158158
},
159159
},
160160
161-
emits: ['session-finished', 'session-started', 'card-loaded', 'card-response', 'time-changed'],
161+
emits: ['session-finished', 'session-started', 'card-loaded', 'card-response', 'time-changed', 'session-prepared', 'session-error'],
162162
163163
data() {
164164
return {
@@ -224,7 +224,9 @@ export default defineComponent({
224224
225225
async created() {
226226
this.userCourseRegDoc = await this.user.getCourseRegistrationsDoc();
227-
this.initSession();
227+
console.log('[StudySession] Created lifecycle hook - starting initSession');
228+
await this.initSession();
229+
console.log('[StudySession] InitSession completed in created hook');
228230
},
229231
230232
methods: {
@@ -271,58 +273,84 @@ export default defineComponent({
271273
},
272274
273275
async initSession() {
274-
console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`);
275-
276-
this.sessionContentSources = (
277-
await Promise.all(
278-
this.contentSources.map(async (s) => {
279-
try {
280-
return await getStudySource(s, this.user);
281-
} catch (e) {
282-
console.error(`Failed to load study source: ${s.type}/${s.id}`, e);
283-
return null;
284-
}
285-
})
286-
)
287-
).filter((s) => s !== null);
288-
289-
this.timeRemaining = this.sessionTimeLimit * 60;
290-
291-
const sessionClassroomDBs = await Promise.all(
292-
this.contentSources
293-
.filter((s) => s.type === 'classroom')
294-
.map(async (c) => await this.dataLayer.getClassroomDB(c.id, 'student'))
295-
);
296-
297-
sessionClassroomDBs.forEach((db) => {
298-
// db.setChangeFcn(this.handleClassroomMessage());
299-
});
276+
let sessionClassroomDBs = [];
277+
try {
278+
console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`);
279+
console.log('[StudySession] Beginning preparation process');
280+
281+
this.sessionContentSources = (
282+
await Promise.all(
283+
this.contentSources.map(async (s) => {
284+
try {
285+
return await getStudySource(s, this.user);
286+
} catch (e) {
287+
console.error(`Failed to load study source: ${s.type}/${s.id}`, e);
288+
return null;
289+
}
290+
})
291+
)
292+
).filter((s) => s !== null);
300293
301-
this.sessionController = new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit);
302-
this.sessionController.sessionRecord = this.sessionRecord;
294+
this.timeRemaining = this.sessionTimeLimit * 60;
303295
304-
await this.sessionController.prepareSession();
305-
this.intervalHandler = setInterval(this.tick, 1000);
296+
sessionClassroomDBs = await Promise.all(
297+
this.contentSources
298+
.filter((s) => s.type === 'classroom')
299+
.map(async (c) => await this.dataLayer.getClassroomDB(c.id, 'student'))
300+
);
306301
307-
this.sessionPrepared = true;
302+
sessionClassroomDBs.forEach((db) => {
303+
// db.setChangeFcn(this.handleClassroomMessage());
304+
});
308305
309-
this.contentSources
310-
.filter((s) => s.type === 'course')
311-
.forEach(
312-
async (c) => (this.courseNames[c.id] = (await this.dataLayer.getCoursesDB().getCourseConfig(c.id)).name)
313-
);
306+
this.sessionController = new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit);
307+
this.sessionController.sessionRecord = this.sessionRecord;
308+
309+
await this.sessionController.prepareSession();
310+
this.intervalHandler = setInterval(this.tick, 1000);
311+
312+
this.sessionPrepared = true;
313+
314+
console.log('[StudySession] Session preparation complete, emitting session-prepared event');
315+
this.$emit('session-prepared');
316+
console.log('[StudySession] Event emission completed');
317+
} catch (error) {
318+
console.error('[StudySession] Error during session preparation:', error);
319+
// Notify parent component about the error
320+
this.$emit('session-error', { message: 'Failed to prepare study session', error });
321+
}
314322
315-
console.log(`[StudySession] Session created:
316-
${this.sessionController.toString()}
317-
User courses: ${this.contentSources
323+
try {
324+
this.contentSources
318325
.filter((s) => s.type === 'course')
319-
.map((c) => c.id)
320-
.toString()}
321-
User classrooms: ${sessionClassroomDBs.map((db) => db._id)}
322-
`);
326+
.forEach(
327+
async (c) => (this.courseNames[c.id] = (await this.dataLayer.getCoursesDB().getCourseConfig(c.id)).name)
328+
);
329+
330+
console.log(`[StudySession] Session created:
331+
${this.sessionController?.toString() || 'Session controller not initialized'}
332+
User courses: ${this.contentSources
333+
.filter((s) => s.type === 'course')
334+
.map((c) => c.id)
335+
.toString()}
336+
User classrooms: ${sessionClassroomDBs.map((db) => db._id).toString() || 'No classrooms'}
337+
`);
338+
} catch (error) {
339+
console.error('[StudySession] Error during final session setup:', error);
340+
}
323341
324-
this.$emit('session-started');
325-
this.loadCard(this.sessionController.nextCard());
342+
if (this.sessionController) {
343+
try {
344+
this.$emit('session-started');
345+
this.loadCard(this.sessionController.nextCard());
346+
} catch (error) {
347+
console.error('[StudySession] Error loading next card:', error);
348+
this.$emit('session-error', { message: 'Failed to load study card', error });
349+
}
350+
} else {
351+
console.error('[StudySession] Cannot load card: session controller not initialized');
352+
this.$emit('session-error', { message: 'Study session initialization failed' });
353+
}
326354
},
327355
328356
countCardViews(course_id: string, card_id: string): number {

packages/platform-ui/src/views/Study.vue

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,53 @@
2929
</v-row>
3030
</div>
3131

32-
<StudySession
33-
v-if="sessionPrepared"
34-
:content-sources="sessionContentSources"
35-
:session-time-limit="sessionTimeLimit"
36-
:user="user as UserDBInterface"
37-
:session-config="studySessionConfig"
38-
:data-layer="dataLayer"
39-
:get-view-component="getViewComponent"
40-
@session-finished="handleSessionFinished"
41-
/>
32+
<!-- Study Session Component (may be in loading state) -->
33+
<div v-if="inSession">
34+
<!-- Loading indicator while session is being prepared -->
35+
<div v-if="!sessionPrepared && !sessionError" class="session-loading">
36+
<v-container class="text-center">
37+
<v-row justify="center" align="center" style="min-height: 50vh">
38+
<v-col cols="12">
39+
<v-progress-circular
40+
size="70"
41+
width="7"
42+
color="primary"
43+
indeterminate
44+
></v-progress-circular>
45+
<div class="text-h5 mt-4">Preparing your study session...</div>
46+
<div class="text-subtitle-1 mt-2">Getting your learning materials ready</div>
47+
</v-col>
48+
</v-row>
49+
</v-container>
50+
</div>
51+
52+
<!-- Error state -->
53+
<div v-if="sessionError" class="session-error">
54+
<v-container class="text-center">
55+
<v-row justify="center" align="center" style="min-height: 50vh">
56+
<v-col cols="12">
57+
<v-icon size="64" color="error">mdi-alert-circle</v-icon>
58+
<div class="text-h5 mt-4 text-error">Session Preparation Failed</div>
59+
<div class="text-subtitle-1 mt-2">{{ errorMessage || 'There was a problem preparing your study session.' }}</div>
60+
<v-btn color="primary" class="mt-6" @click="refreshRoute">Try Again</v-btn>
61+
</v-col>
62+
</v-row>
63+
</v-container>
64+
</div>
65+
66+
<StudySession
67+
:content-sources="sessionContentSources"
68+
:session-time-limit="sessionTimeLimit"
69+
:user="user as UserDBInterface"
70+
:session-config="studySessionConfig"
71+
:data-layer="dataLayer"
72+
:get-view-component="getViewComponent"
73+
:class="{ 'hidden-session': !sessionPrepared }"
74+
@session-finished="handleSessionFinished"
75+
@session-prepared="handleSessionPrepared"
76+
@session-error="handleSessionError"
77+
/>
78+
</div>
4279
</div>
4380
</template>
4481

@@ -107,6 +144,8 @@ export default defineComponent({
107144
sessionTimeLimit: 5,
108145
inSession: false,
109146
sessionPrepared: false,
147+
sessionError: false,
148+
errorMessage: '',
110149
sessionContentSources: [] as ContentSourceID[],
111150
dataInputFormStore: useDataInputFormStore(),
112151
getViewComponent: (view_id: string) => allCourses.getView(view_id),
@@ -176,7 +215,10 @@ export default defineComponent({
176215
this.sessionContentSources = sources;
177216
this.sessionTimeLimit = timeLimit;
178217
this.inSession = true;
179-
this.sessionPrepared = true;
218+
this.sessionPrepared = false;
219+
220+
// Adding a console log to debug event handling
221+
console.log('[Study] Waiting for session-prepared event from StudySession component');
180222
},
181223
182224
registerUserForPreviewCourse() {
@@ -188,6 +230,32 @@ export default defineComponent({
188230
handleSessionFinished() {
189231
this.refreshRoute();
190232
},
233+
234+
handleSessionPrepared() {
235+
console.log('[Study] Session preparation complete - received session-prepared event');
236+
this.sessionPrepared = true;
237+
this.sessionError = false;
238+
this.errorMessage = '';
239+
},
240+
241+
handleSessionError({ message, error }) {
242+
console.error('[Study] Session error:', message, error);
243+
this.sessionError = true;
244+
this.errorMessage = message || 'An error occurred while preparing your study session.';
245+
this.sessionPrepared = false;
246+
},
191247
},
192248
});
193249
</script>
250+
251+
<style scoped>
252+
.hidden-session {
253+
visibility: hidden;
254+
position: absolute;
255+
z-index: -1;
256+
}
257+
258+
.session-error {
259+
color: var(--v-error-base);
260+
}
261+
</style>

0 commit comments

Comments
 (0)