Skip to content

Commit b3bfa6d

Browse files
committed
Merge branch 'main' of github.com:cuixueshe/earthworm
2 parents 1dad46b + baa7b95 commit b3bfa6d

File tree

15 files changed

+257
-40
lines changed

15 files changed

+257
-40
lines changed

apps/api/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"class-validator": "^0.14.1",
3333
"reflect-metadata": "^0.1.13",
3434
"rxjs": "^7.8.1",
35-
"start": "^5.1.0"
35+
"start": "^5.1.0",
36+
"superagent": "^8.1.2"
3637
},
3738
"devDependencies": {
3839
"@nestjs/cli": "^10.0.0",

apps/api/src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { UserModule } from '../user/user.module';
44
import { AuthModule } from '../auth/auth.module';
55
import { CourseModule } from '../course/course.module';
66
import { UserProgressModule } from '../user-progress/user-progress.module';
7+
import { ToolModule } from 'src/tool/tool.module';
78

89
@Module({
910
imports: [
@@ -12,6 +13,7 @@ import { UserProgressModule } from '../user-progress/user-progress.module';
1213
AuthModule,
1314
CourseModule,
1415
UserProgressModule,
16+
ToolModule,
1517
],
1618
})
1719
export class AppModule {}

apps/api/src/tool/tool.controller.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Controller, Get } from '@nestjs/common';
2+
import { ToolService } from './tool.service';
3+
4+
@Controller('tool')
5+
export class ToolController {
6+
constructor(private readonly toolService: ToolService) {}
7+
8+
@Get('dailySentence')
9+
dailySentence() {
10+
return this.toolService.dailySentence();
11+
}
12+
}

apps/api/src/tool/tool.module.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Module } from '@nestjs/common';
2+
import { ToolService } from './tool.service';
3+
import { ToolController } from './tool.controller';
4+
5+
@Module({
6+
controllers: [ToolController],
7+
providers: [ToolService],
8+
})
9+
export class ToolModule {}

apps/api/src/tool/tool.service.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Injectable } from '@nestjs/common';
2+
import * as superagent from 'superagent';
3+
4+
@Injectable()
5+
export class ToolService {
6+
async dailySentence() {
7+
const { text } = await superagent.get(
8+
'https://open.iciba.com/dsapi/?date=2023-05-03',
9+
);
10+
const data = JSON.parse(text);
11+
const res = {
12+
zh: data.note,
13+
en: data.content,
14+
};
15+
return res;
16+
}
17+
}

apps/api/src/user-progress/model/update-user-progress.dto.ts renamed to apps/api/src/user-progress/model/user-progress.dto.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ export class CreateUserProgressDto {
66
@IsNotEmpty({ message: '课程不能为空' })
77
courseId: number;
88
}
9+
10+
export class UpdateUserProgressDto {
11+
@ApiProperty()
12+
@IsNotEmpty({ message: '课程不能为空' })
13+
courseId: number;
14+
}

apps/api/src/user-progress/user-progress.controller.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import {
66
Request,
77
Get,
88
Put,
9-
Param,
109
} from '@nestjs/common';
1110
import { UserProgressService } from './user-progress.service';
1211
import { AuthGuard } from '../auth/auth.guard';
13-
import { CreateUserProgressDto } from './model/update-user-progress.dto';
12+
import {
13+
CreateUserProgressDto,
14+
UpdateUserProgressDto,
15+
} from './model/user-progress.dto';
1416

1517
@Controller('user-progress')
1618
export class UserProgressController {
@@ -29,8 +31,8 @@ export class UserProgressController {
2931
}
3032

3133
@UseGuards(AuthGuard)
32-
@Put(':courseId')
33-
updateOne(@Request() req, @Param('courseId') courseId: number) {
34-
return this.userProgressService.update(+req.user.userId, +courseId);
34+
@Put()
35+
updateOne(@Request() req, @Body() dto: UpdateUserProgressDto) {
36+
return this.userProgressService.update(+req.user.userId, +dto.courseId);
3537
}
3638
}

apps/client/api/tool.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { http } from "./http";
2+
3+
interface DailySentenceVo {
4+
zh: string
5+
en: string
6+
}
7+
8+
export async function fetchDailySentence() {
9+
return await http.get<DailySentenceVo, DailySentenceVo>("/tool/dailySentence");
10+
}

apps/client/api/userProgress.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { http } from "./http";
2+
3+
interface UserProgressVo {
4+
courseId: number
5+
}
6+
7+
interface UserProgressDto {
8+
courseId: number
9+
}
10+
11+
export async function fetchUserProgress() {
12+
return await http.get<UserProgressVo, UserProgressVo>("/user-progress");
13+
}
14+
15+
export async function fetchUpdateProgress(dto: UserProgressDto) {
16+
return await http.put<UserProgressVo, UserProgressVo>("/user-progress", dto);
17+
}

apps/client/components/main/Summary.vue

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,76 @@
11
<template>
22
<div>
3-
<n-modal v-model:show="showModal" transform-origin="center">
4-
<n-card style="width: 800px; height: 400px" title="结算面板" :bordered="false" size="huge" role="dialog"
5-
aria-modal="true">
6-
不错不错 又学到了那么多句子和单词 加油 坚持就是胜利:)
7-
<template #footer>
3+
<dialog className="modal" :open="showModal">
4+
<div className="modal-box max-w-[48rem]">
5+
<h3 className="font-bold text-lg mb-4">🎉 Congratulations!</h3>
6+
<div class="flex flex-col">
87
<div class="flex">
9-
<n-button @click="handleDoAgain">再来一次</n-button>
10-
<n-button @click="handleGoToNextCourse">开始下一课</n-button>
8+
<span class="text-6xl font-bold">"</span>
9+
<div class="flex-1 text-xl text-center leading-loose">{{ enSentence }}</div>
10+
<span class="text-6xl font-bold invisible">"</span>
1111
</div>
12-
</template>
13-
</n-card>
14-
</n-modal>
12+
13+
<div class="flex">
14+
<span class="text-6xl font-bold invisible">"</span>
15+
<div class="flex-1 text-center text-xl leading-loose">{{ zhSentence }}</div>
16+
<span class="text-6xl font-bold">"</span>
17+
</div>
18+
</div>
19+
<div className="modal-action">
20+
<button class="btn" @click="handleDoAgain">再来一次</button>
21+
<button class="btn" @click="handleGoToNextCourse">开始下一课</button>
22+
</div>
23+
</div>
24+
<canvas ref="confettiCanvasRef" class="absolute top-0 left-0 h-full w-full pointer-events-none"></canvas>
25+
</dialog>
1526
</div>
1627
</template>
1728

1829
<script setup lang="ts">
1930
import { useCourseStore } from "~/store/course";
20-
import { useSummary } from "~/composables/main/summary";
31+
import { useDailySentence, useSummary } from "~/composables/main/summary";
2132
import { useGameMode } from "~/composables/main/game";
33+
import { fetchUpdateProgress } from "~/api/userProgress";
34+
import confetti from 'canvas-confetti';
35+
36+
const useConfetti = () => {
37+
const confettiCanvasRef = ref<HTMLCanvasElement>()
38+
39+
const playConfetti = () => {
40+
console.log('----- 1');
41+
const myConfetti = confetti.create(confettiCanvasRef.value, {
42+
resize: true,
43+
useWorker: true
44+
})
45+
46+
myConfetti({
47+
particleCount: 300,
48+
spread: 180,
49+
origin: { y: -0.1 },
50+
startVelocity: -35
51+
})
52+
}
53+
54+
return {
55+
confettiCanvasRef,
56+
playConfetti,
57+
}
58+
}
2259
60+
const { confettiCanvasRef, playConfetti } = useConfetti()
2361
const courseStore = useCourseStore();
2462
const { showModal, hideSummary } = useSummary();
2563
64+
watch(showModal, (val) => {
65+
val && setTimeout(() => {
66+
playConfetti()
67+
}, 300);
68+
})
69+
2670
const { handleDoAgain } = useDoAgain()
2771
const { handleGoToNextCourse } = useGoToNextCourse()
2872
73+
const { zhSentence, enSentence } = useDailySentence()
2974
3075
function useDoAgain() {
3176
const { showQuestion } = useGameMode();
@@ -51,7 +96,16 @@ function useGoToNextCourse() {
5196
+router.currentRoute.value.params.id
5297
);
5398
54-
router.push(`/main/${courseStore.currentCourse?.id}`);
99+
console.log(courseStore.currentCourse.id)
100+
101+
if (!courseStore.currentCourse.id) {
102+
return
103+
}
104+
await fetchUpdateProgress({
105+
courseId: courseStore.currentCourse.id
106+
})
107+
router.push(`/main/${courseStore.currentCourse.id}`);
108+
55109
hideSummary();
56110
showQuestion();
57111
}

apps/client/composables/main/summary.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { fetchDailySentence } from "~/api/tool";
2+
13
const showModal = ref(false);
24
export function useSummary() {
35
function showSummary() {
@@ -14,3 +16,29 @@ export function useSummary() {
1416
hideSummary,
1517
};
1618
}
19+
20+
const enSentence = ref('To be, or not to be, that is the question.')
21+
const zhSentence = ref('生存还是毁灭,这是一个问题。')
22+
const setenceLoading = ref(false)
23+
export function useDailySentence() {
24+
const getDailySentence = async () => {
25+
if (!setenceLoading.value) {
26+
const {
27+
en,
28+
zh
29+
} = await fetchDailySentence()
30+
enSentence.value = en
31+
zhSentence.value = zh
32+
setenceLoading.value = true
33+
}
34+
}
35+
36+
onMounted(() => {
37+
getDailySentence()
38+
})
39+
40+
return {
41+
enSentence,
42+
zhSentence
43+
}
44+
}

apps/client/composables/userProgress.ts

Whitespace-only changes.

apps/client/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
"dependencies": {
3131
"@nuxt/image": "^1.3.0",
3232
"@pinia/nuxt": "^0.5.1",
33+
"@types/canvas-confetti": "^1.6.4",
3334
"axios": "^1.6.6",
35+
"canvas-confetti": "^1.9.2",
3436
"pinia": "^2.1.7"
3537
}
3638
}

apps/client/pages/index.vue

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
Star us on GitHub
2525
</button>
2626
</a>
27-
<NuxtLink href="/main/1">
28-
<button
29-
class="btn btn-outline w-48 hover:text-fuchsia-400 hover:border-fuchsia-400 hover:bg-fuchsia-100 text-fuchsia-300 border-fuchsia-300"
30-
>
31-
Go and get it <kbd class="kbd"> ↵ </kbd>
32-
</button>
33-
</NuxtLink>
27+
<button
28+
:disabled="initing"
29+
@click="handleKeydown"
30+
class="btn btn-outline w-48 hover:text-fuchsia-400 hover:border-fuchsia-400 hover:bg-fuchsia-100 text-fuchsia-300 border-fuchsia-300">
31+
<span v-show="initing" class="loading loading-spinner"></span>
32+
Go and get it <kbd class="kbd"> ↵ </kbd>
33+
</button>
3434
</div>
3535
<div
3636
class="w-1/2 flex items-center justify-center group select-none cursor-pointer rounded-xl relative m-4"
@@ -77,27 +77,76 @@
7777
</template>
7878

7979
<script setup lang="ts">
80+
import { fetchUpdateProgress, fetchUserProgress } from "~/api/userProgress";
81+
import { useUserStore } from "~/store/user";
8082
import { registerShortcut, cancelShortcut } from "~/utils/keyboardShortcuts";
8183
82-
registerShortcutToStart();
8384
85+
const useProgress = () => {
86+
87+
const activeCourseId = ref(1)
88+
const ACTIVE_COURSE_ID = 'activeCourseId'
89+
90+
const initing = ref(false)
91+
const initProgress = async () => {
92+
initing.value = true
93+
const { courseId } = await fetchUserProgress()
94+
activeCourseId.value = +courseId
95+
updateProgressLocal(+courseId)
96+
initing.value = false
97+
}
98+
99+
const updateProgress = async (courseId: number) => {
100+
const { courseId: updatedCourseId } = await fetchUpdateProgress({courseId})
101+
updateProgressLocal(updatedCourseId)
102+
}
103+
104+
const updateProgressLocal = async (courseId: number) => {
105+
localStorage.setItem(ACTIVE_COURSE_ID, `${courseId}`)
106+
}
107+
108+
return {
109+
activeCourseId,
110+
initing,
111+
updateProgressLocal,
112+
updateProgress,
113+
initProgress
114+
}
115+
}
116+
117+
const { handleKeydown, initing } = useShortcutToGame();
84118
85-
function registerShortcutToStart() {
119+
function useShortcutToGame() {
86120
const router = useRouter();
121+
const userStore = useUserStore();
122+
const { activeCourseId, initing, initProgress } = useProgress()
123+
87124
function handleKeydown() {
88-
router.push("/main/1");
125+
if (userStore.user) {
126+
if (initing.value)
127+
return
128+
router.push(`/main/${activeCourseId.value}`);
129+
} else {
130+
router.push("/main/1");
131+
}
89132
}
90133
onMounted(() => {
91134
registerShortcut("enter", handleKeydown);
135+
console.log(userStore.user);
136+
if (userStore.user) {
137+
initProgress()
138+
}
92139
});
93140
94141
onUnmounted(() => {
95142
cancelShortcut("enter", handleKeydown);
96143
});
97-
}
98-
99-
100144
145+
return {
146+
initing,
147+
handleKeydown
148+
}
149+
}
101150
</script>
102151

103152
<style>

0 commit comments

Comments
 (0)