✨ Challenges
This commit is contained in:
		
							
								
								
									
										5
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
							
								
								
									
										12
									
								
								.idea/Fuxi.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/Fuxi.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/.tmp" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/temp" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/tmp" /> | ||||
|     </content> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										57
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| <component name="ProjectCodeStyleConfiguration"> | ||||
|   <code_scheme name="Project" version="173"> | ||||
|     <HTMLCodeStyleSettings> | ||||
|       <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" /> | ||||
|     </HTMLCodeStyleSettings> | ||||
|     <JSCodeStyleSettings version="0"> | ||||
|       <option name="FORCE_SEMICOLON_STYLE" value="true" /> | ||||
|       <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> | ||||
|       <option name="FORCE_QUOTE_STYlE" value="true" /> | ||||
|       <option name="ENFORCE_TRAILING_COMMA" value="Remove" /> | ||||
|       <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> | ||||
|       <option name="SPACES_WITHIN_IMPORTS" value="true" /> | ||||
|     </JSCodeStyleSettings> | ||||
|     <TypeScriptCodeStyleSettings version="0"> | ||||
|       <option name="FORCE_SEMICOLON_STYLE" value="true" /> | ||||
|       <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> | ||||
|       <option name="FORCE_QUOTE_STYlE" value="true" /> | ||||
|       <option name="ENFORCE_TRAILING_COMMA" value="Remove" /> | ||||
|       <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> | ||||
|       <option name="SPACES_WITHIN_IMPORTS" value="true" /> | ||||
|     </TypeScriptCodeStyleSettings> | ||||
|     <VueCodeStyleSettings> | ||||
|       <option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" /> | ||||
|       <option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" /> | ||||
|     </VueCodeStyleSettings> | ||||
|     <codeStyleSettings language="HTML"> | ||||
|       <option name="SOFT_MARGINS" value="120" /> | ||||
|       <indentOptions> | ||||
|         <option name="INDENT_SIZE" value="2" /> | ||||
|         <option name="CONTINUATION_INDENT_SIZE" value="2" /> | ||||
|         <option name="TAB_SIZE" value="2" /> | ||||
|       </indentOptions> | ||||
|     </codeStyleSettings> | ||||
|     <codeStyleSettings language="JavaScript"> | ||||
|       <option name="SOFT_MARGINS" value="120" /> | ||||
|       <indentOptions> | ||||
|         <option name="INDENT_SIZE" value="2" /> | ||||
|         <option name="CONTINUATION_INDENT_SIZE" value="2" /> | ||||
|         <option name="TAB_SIZE" value="2" /> | ||||
|       </indentOptions> | ||||
|     </codeStyleSettings> | ||||
|     <codeStyleSettings language="TypeScript"> | ||||
|       <option name="SOFT_MARGINS" value="120" /> | ||||
|       <indentOptions> | ||||
|         <option name="INDENT_SIZE" value="2" /> | ||||
|         <option name="CONTINUATION_INDENT_SIZE" value="2" /> | ||||
|         <option name="TAB_SIZE" value="2" /> | ||||
|       </indentOptions> | ||||
|     </codeStyleSettings> | ||||
|     <codeStyleSettings language="Vue"> | ||||
|       <option name="SOFT_MARGINS" value="120" /> | ||||
|       <indentOptions> | ||||
|         <option name="CONTINUATION_INDENT_SIZE" value="2" /> | ||||
|       </indentOptions> | ||||
|     </codeStyleSettings> | ||||
|   </code_scheme> | ||||
| </component> | ||||
							
								
								
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <component name="ProjectCodeStyleConfiguration"> | ||||
|   <state> | ||||
|     <option name="USE_PER_PROJECT_SETTINGS" value="true" /> | ||||
|   </state> | ||||
| </component> | ||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/Fuxi.iml" filepath="$PROJECT_DIR$/.idea/Fuxi.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="SqlDialectMappings"> | ||||
|     <file url="file://$PROJECT_DIR$/supabase/migrations/20231210135930_challenges.sql" dialect="GenericSQL" /> | ||||
|     <file url="PROJECT" dialect="PostgreSQL" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
| @@ -1,13 +1,15 @@ | ||||
| <template> | ||||
|   <n-message-provider> | ||||
|     <n-dialog-provider> | ||||
|       <nuxt-layout> | ||||
|         <nuxt-loading-indicator /> | ||||
|         <nuxt-page /> | ||||
|       </nuxt-layout> | ||||
|     </n-dialog-provider> | ||||
|   </n-message-provider> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NMessageProvider } from "naive-ui"; | ||||
| import { NMessageProvider, NDialogProvider } from "naive-ui"; | ||||
| import "@/assets/css/index.css"; | ||||
| </script> | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										11
									
								
								application/components/problem/solution/program.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								application/components/problem/solution/program.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <template> | ||||
|  | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
|  | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|  | ||||
| </style> | ||||
| @@ -22,6 +22,7 @@ | ||||
|     "vue-router": "^4.2.5" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@guolao/vue-monaco-editor": "^1.4.1", | ||||
|     "@ibm/plex": "^6.3.0", | ||||
|     "@nuxtjs/mdc": "^0.2.8", | ||||
|     "@supabase/supabase-js": "^2.39.0", | ||||
|   | ||||
							
								
								
									
										30
									
								
								application/pages/about.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								application/pages/about.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| <template> | ||||
|   <div class="max-w-[720px] mx-auto"> | ||||
|     <n-card> | ||||
|       <brand-header /> | ||||
|  | ||||
|       <div>Get everyone interested in programming.</div> | ||||
|  | ||||
|       <n-divider class="mx-[-24px] w-[calc(100%+48px)]" /> | ||||
|  | ||||
|       <div class="text-gray text-xs"> | ||||
|         <div> | ||||
|           <nuxt-link class="link" target="_blank" to="https://smartsheep.studio">Made by SmartSheep Studio.</nuxt-link> | ||||
|           Proprietary software. | ||||
|         </div> | ||||
|         <div>Fuxi Development Team © {{ new Date().getFullYear() }}</div> | ||||
|       </div> | ||||
|     </n-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NCard, NDivider } from "naive-ui"; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .link { | ||||
|   all: unset; | ||||
|   cursor: pointer; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										116
									
								
								application/pages/challenges/[id].vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								application/pages/challenges/[id].vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| <template> | ||||
|   <div class="md:max-w-[720px] mx-auto"> | ||||
|     <n-card segmented> | ||||
|       <template #header> | ||||
|         <n-page-header @back="navigateTo('/')"> | ||||
|           <template #title>Challenge #{{ challenge?.id }}</template> | ||||
|           <template #subtitle> | ||||
|             <div class="flex items-center gap-2"> | ||||
|               <n-tag size="small" class="case-capital">{{ challenge?.status?.replaceAll("-", " ") }}</n-tag> | ||||
|             </div> | ||||
|           </template> | ||||
|         </n-page-header> | ||||
|       </template> | ||||
|  | ||||
|       <client-only> | ||||
|         <vue-monaco-editor | ||||
|           :options="options" | ||||
|           v-model:value="answer.code" | ||||
|           class="min-h-[360px] code-editor" | ||||
|         /> | ||||
|       </client-only> | ||||
|  | ||||
|       <n-divider class="mx-[-24px] w-[calc(100%+48px)] divider-below-code" /> | ||||
|  | ||||
|       <template #action> | ||||
|         <div class="w-full flex justify-between"> | ||||
|           <div class="flex gap-2"> | ||||
|             <n-button secondary circle class="rounded-[4px]" type="warning" :disabled="submitting" @click="save"> | ||||
|               <template #icon> | ||||
|                 <n-icon :component="Save" /> | ||||
|               </template> | ||||
|             </n-button> | ||||
|           </div> | ||||
|           <div class="flex gap-2"> | ||||
|             <n-button secondary type="error" :disabled="submitting" @click="abandon">放弃</n-button> | ||||
|             <n-button type="primary" :disabled="submitting" @click="submit">提交</n-button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </n-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NButton, NCard, NDivider, NPageHeader, NTag, NIcon, useDialog, useMessage } from "naive-ui"; | ||||
| import { VueMonacoEditor } from "@guolao/vue-monaco-editor"; | ||||
| import { Save } from "@vicons/carbon"; | ||||
|  | ||||
| const route = useRoute(); | ||||
| const client = useSupabaseClient(); | ||||
| const message = useMessage(); | ||||
| const dialog = useDialog(); | ||||
|  | ||||
| const options = { | ||||
|   minimap: { enabled: false } | ||||
| }; | ||||
|  | ||||
| const submitting = ref(false); | ||||
|  | ||||
| const { data: challenge } = await client | ||||
|   .from("challenges") | ||||
|   .select<any, any>("*") | ||||
|   .eq("id", route.params.id) | ||||
|   .single(); | ||||
|  | ||||
| useHead({ | ||||
|   title: challenge ? `挑战 #${challenge.id}` : "挑战 #404" | ||||
| }); | ||||
|  | ||||
| const answer = ref(challenge?.answer ?? {}); | ||||
|  | ||||
| async function save() { | ||||
|  | ||||
| } | ||||
|  | ||||
| function abandon() { | ||||
|   const instance = dialog.warning({ | ||||
|     title: "警告", | ||||
|     content: "你确定要放弃该次挑战?这会让该挑战立刻转化为放弃状态,并且扣除 5 点社会信用点,三思而后行!", | ||||
|     positiveText: "确定", | ||||
|     negativeText: "再试试", | ||||
|     onPositiveClick: async () => { | ||||
|       await client | ||||
|         .from("challenges") | ||||
|         // @ts-ignore | ||||
|         .update<any>({ status: "abandoned" }) | ||||
|         .eq("id", challenge.id); | ||||
|  | ||||
|       const delay = (ms: number) => new Promise(res => setTimeout(res, ms)); | ||||
|  | ||||
|       instance.loading = true | ||||
|       message.info("已放弃挑战该题"); | ||||
|  | ||||
|       await delay(1850); | ||||
|       window.close(); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| async function submit() { | ||||
|  | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .code-editor { | ||||
|   min-width: calc(100% + 48px); | ||||
|   margin-top: -20px; | ||||
|   margin-left: -24px; | ||||
|   margin-right: -24px; | ||||
| } | ||||
|  | ||||
| .divider-below-code { | ||||
|   margin-top: 0 !important; | ||||
| } | ||||
| </style> | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <div class="md:max-w-[720px] mx-auto"> | ||||
|     <problems-list /> | ||||
|     <problem-list /> | ||||
|   </div> | ||||
| </template> | ||||
|   | ||||
| @@ -31,22 +31,90 @@ | ||||
|         </div> | ||||
|         <n-empty v-else description="本题无公开样例" /> | ||||
|       </section> | ||||
|  | ||||
|       <template #action> | ||||
|         <div class="w-full flex justify-end"> | ||||
|           <n-button v-if="!answering" type="primary" :loading="submitting" @click="doChallenge">试答该题</n-button> | ||||
|           <div v-else> | ||||
|             <n-button type="primary" disabled> | ||||
|               <template #icon> | ||||
|                 <n-icon :component="Checkmark" /> | ||||
|               </template> | ||||
|               正在作答 | ||||
|             </n-button> | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </n-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NCard, NTag, NPageHeader, NDivider, NEmpty } from "naive-ui"; | ||||
| import { NCard, NTag, NPageHeader, NDivider, NEmpty, NButton, NIcon, useMessage } from "naive-ui"; | ||||
| import { Checkmark } from "@vicons/carbon"; | ||||
| import "prismjs/themes/prism.css"; | ||||
| import "prismjs/prism"; | ||||
|  | ||||
| const user = useSupabaseUser(); | ||||
| const client = useSupabaseClient(); | ||||
| const message = useMessage(); | ||||
| const route = useRoute(); | ||||
|  | ||||
| const { data: problem } = await client.from("problems").select<any, any>("*").eq("id", route.params.id).single(); | ||||
| const submitting = ref(false); | ||||
| const answering = ref(false); | ||||
|  | ||||
| const { data: problem } = await client | ||||
|   .from("problems") | ||||
|   .select<any, any>("*") | ||||
|   .eq("id", route.params.id) | ||||
|   .single(); | ||||
| const { data: example } = await client | ||||
|   .from("problem_cases") | ||||
|   .select<any, any>("*") | ||||
|   .eq("problem", route.params.id) | ||||
|   .single(); | ||||
|  | ||||
| useHead({ | ||||
|   title: problem?.title ?? "未知题目" | ||||
| }); | ||||
|  | ||||
| async function doChallenge() { | ||||
|   if (problem == null) return; | ||||
|  | ||||
|   submitting.value = true; | ||||
|  | ||||
|   let { data } = await client | ||||
|     .from("challenges") | ||||
|     .select<any, any>("id") | ||||
|     .eq("problem", problem?.id) | ||||
|     .eq("status", "in-progress") | ||||
|     .single(); | ||||
|  | ||||
|   if (data == null) { | ||||
|     const res = await client | ||||
|       .from("challenges") | ||||
|       // @ts-ignore | ||||
|       .insert<any>({ | ||||
|         answers: {}, | ||||
|         details: {}, | ||||
|         status: "in-progress", | ||||
|         problem: problem.id, | ||||
|         author: user.value?.id | ||||
|       }) | ||||
|       .select<any, any>() | ||||
|       .single(); | ||||
|  | ||||
|     if (res.error != null) { | ||||
|       message.error(`Something went wrong... ${res.error.message}`); | ||||
|       return; | ||||
|     } else { | ||||
|       data = res.data; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   submitting.value = false; | ||||
|   answering.value = true; | ||||
|   navigateTo(`/challenges/${data.id}`, { open: { target: "_blank" } }); | ||||
|   setTimeout(() => answering.value = false, 3000); | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -1,14 +1,20 @@ | ||||
| create table public.profiles ( | ||||
| create table public.profiles | ||||
| ( | ||||
|     id       uuid not null references auth.users on delete cascade, | ||||
|     username varchar(64), | ||||
|     nickname varchar(256), | ||||
|     school   int8, | ||||
|     primary key (id) | ||||
| ); | ||||
| alter table public.profiles enable row level security; | ||||
|  | ||||
| alter table public.profiles | ||||
|     enable row level security; | ||||
|  | ||||
| create policy "Public profiles are viewable by everyone." on profiles for | ||||
| select using (true); | ||||
|     select using (true); | ||||
|  | ||||
| create policy "Users can insert their own profile." on profiles for | ||||
| insert with check (auth.uid() = id); | ||||
|     insert with check (auth.uid() = id); | ||||
|  | ||||
| create policy "Users can update own profile." on profiles for | ||||
| update using (auth.uid() = id); | ||||
|     update using (auth.uid() = id); | ||||
| @@ -1,9 +1,10 @@ | ||||
| create table public.problems ( | ||||
| create table public.problems | ||||
| ( | ||||
|     id          bigint generated by default as identity, | ||||
|     title       text                     not null, | ||||
|     description text                     not null, | ||||
|     type        character varying        not null, | ||||
|   tags character varying [] null, | ||||
|     tags        character varying[]      null, | ||||
|     author      uuid                     null, | ||||
|     metadata    jsonb                    null, | ||||
|     is_draft    boolean                  null default true, | ||||
| @@ -12,10 +13,15 @@ create table public.problems ( | ||||
|     constraint problems_pkey primary key (id), | ||||
|     constraint problems_author_fkey foreign key (author) references auth.users (id) | ||||
| ) tablespace pg_default; | ||||
| alter table public.problems enable row level security; | ||||
|  | ||||
| alter table public.problems | ||||
|     enable row level security; | ||||
|  | ||||
| create policy "Public problems are viewable by everyone." on problems for | ||||
| select using (true); | ||||
| create table public.problem_cases ( | ||||
|     select using (true); | ||||
|  | ||||
| create table public.problem_cases | ||||
| ( | ||||
|     id          bigint generated by default as identity, | ||||
|     spec        jsonb not null, | ||||
|     limitations jsonb not null, | ||||
| @@ -25,6 +31,9 @@ create table public.problem_cases ( | ||||
|     constraint problem_cases_pkey primary key (id), | ||||
|     constraint problem_cases_author_fkey foreign key (problem) references public.problems (id) | ||||
| ) tablespace pg_default; | ||||
| alter table public.problem_cases enable row level security; | ||||
|  | ||||
| alter table public.problem_cases | ||||
|     enable row level security; | ||||
|  | ||||
| create policy "Public problem cases are viewable by everyone." on problem_cases for | ||||
| select using (is_hidden = false); | ||||
|     select using (is_hidden = false); | ||||
| @@ -1,4 +1,5 @@ | ||||
| create table public.challenges ( | ||||
| create table public.challenges | ||||
| ( | ||||
|     id         bigint generated by default as identity, | ||||
|     answers    jsonb        not null, | ||||
|     details    jsonb        not null, | ||||
| @@ -10,5 +11,26 @@ create table public.challenges ( | ||||
|     constraint challenges_author_fkey foreign key (author) references auth.users (id), | ||||
|     constraint challenges_problem_fkey foreign key (problem) references public.problems (id) | ||||
| ) tablespace pg_default; | ||||
|  | ||||
| alter table public.challenges enable row level security; | ||||
| create policy "The challagers can see their challenges" on "public"."challenges" for select to public using (auth.uid() = author) with check (true) | ||||
|  | ||||
| create | ||||
| policy "Enable insert for everyone" on "public"."challenges" | ||||
| as permissive for insert | ||||
| to public | ||||
| with check (true); | ||||
|  | ||||
| create | ||||
| policy "Enable read access for users' own items" on "public"."challenges" | ||||
| as permissive for | ||||
| select | ||||
|     to public | ||||
|     using (author = auth.uid()); | ||||
|  | ||||
| create | ||||
| policy "Enable update access for users' own items" on "public"."challenges" | ||||
| as permissive for | ||||
| update | ||||
|     to public | ||||
|     using (author = auth.uid()) | ||||
| with check (author = auth.uid()) | ||||
		Reference in New Issue
	
	Block a user