Display task status in TaskDisplay
This commit is contained in:
		
							parent
							
								
									b20594d6b9
								
							
						
					
					
						commit
						af57e530f8
					
				
					 6 changed files with 88 additions and 20 deletions
				
			
		|  | @ -6,6 +6,7 @@ | |||
|   import type { TasksFile, TaskDescriptor } from "./task-loader"; | ||||
|   import { createNodesAndEdges } from "./graph-types"; | ||||
|   import { taskForce } from "./task-force"; | ||||
|   import { taskStatuses } from './task-status-cache' | ||||
|   import { grabTaskStates, isLoggedIn } from "./ksp-task-grabber"; | ||||
|   import type { TaskStatus } from "./ksp-task-grabber" | ||||
| 
 | ||||
|  | @ -20,7 +21,6 @@ | |||
|   let clientHeight: number; | ||||
|   let clientWidth: number; | ||||
|   let svgElement: SVGElement; | ||||
|   let taskStatuses = new Map<string, TaskStatus>(); | ||||
| 
 | ||||
|   // this prevents svelte from updating nodes and edges | ||||
|   // when we update nodes and edges | ||||
|  | @ -94,17 +94,6 @@ | |||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   if (isLoggedIn()) { | ||||
|     const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache") | ||||
|     if (cachedTaskStatuses) { | ||||
|       try { taskStatuses = new Map(JSON.parse(cachedTaskStatuses)) } catch(e) { console.warn(e) } | ||||
|     } | ||||
|     grabTaskStates(tasks.tasks.map(t => t.id)).then(t => { | ||||
|       taskStatuses = t | ||||
|       localStorage.setItem("taskStatuses-cache", JSON.stringify(Array.from(t.entries()))) | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   // start simulation and center view on create | ||||
|   onMount(() => { | ||||
|     // set center of the SVG at (0,0) | ||||
|  | @ -155,7 +144,7 @@ | |||
|             on:click={nodeClick(task.task)} | ||||
|             on:hoveringChange={nodeHover(task.task)} | ||||
|             on:positionChange={() => { tasks = tasks; }} | ||||
|             status={taskStatuses.get(task.id)} | ||||
|             status={$taskStatuses.get(task.id)} | ||||
|             draggingEnabled={nodeDraggingEnabled} | ||||
|             on:dblclick={nodeDoubleClick(task.task)} /> | ||||
|         {/each} | ||||
|  |  | |||
|  | @ -83,11 +83,23 @@ | |||
|     fill: #69b3a2 | ||||
|   } | ||||
|   .submitted ellipse { | ||||
|     fill: red | ||||
|     fill: red; /* TODO */ | ||||
|   } | ||||
|   .solved ellipse { | ||||
|     fill: green; /* TODO */ | ||||
|   } | ||||
| </style> | ||||
| 
 | ||||
| <g on:mouseenter={enter} on:mouseleave={leave} on:click={click} on:mousedown={dragStart} on:mouseup={dragStop} on:mousemove={drag} class={status && status.submitted ? "submitted" : ""} on:dblclick={dblclick}> | ||||
| <g on:mouseenter={enter} | ||||
|    on:mouseleave={leave} | ||||
|    on:click={click} | ||||
|    on:mousedown={dragStart} | ||||
|    on:mouseup={dragStop} | ||||
|    on:mousemove={drag} | ||||
|    on:dblclick={dblclick} | ||||
|    class={status == null ? "" : | ||||
|           status.solved ? "solved" : | ||||
|           status.submitted ? "submitted" : ""}> | ||||
|   <ellipse rx={ellipse_rx} ry={20} {cx} {cy} /> | ||||
|   <text | ||||
|     bind:this={text_element} | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| <script type="ts"> | ||||
|     import { grabAssignment, grabSolution } from "./ksp-task-grabber"; | ||||
|     import type { TaskStatus } from "./ksp-task-grabber"; | ||||
|     import { nonNull } from './helpers' | ||||
| import App from "./App.svelte"; | ||||
| import { taskStatuses } from "./task-status-cache"; | ||||
|     export let taskId: string | null | undefined | ||||
| 
 | ||||
|     export let showSolution: boolean = false | ||||
|  | @ -9,11 +11,26 @@ import App from "./App.svelte"; | |||
|         taskId | ||||
|         showSolution = false | ||||
|     } | ||||
| 
 | ||||
|     let status: TaskStatus | undefined | ||||
|     $: if (taskId) status = $taskStatuses.get(taskId) | ||||
| </script> | ||||
| <style> | ||||
|     div { | ||||
|         text-align: justify; | ||||
|     } | ||||
|     .header { | ||||
|         display: flex; | ||||
|         width: 100%; | ||||
|         flex-direction: row; | ||||
|     } | ||||
|     .header div { | ||||
|         flex-grow: 1; | ||||
|     } | ||||
|     .header .status { | ||||
|         text-align: right; | ||||
|         font-style: italic; | ||||
|     } | ||||
| </style> | ||||
| 
 | ||||
| <div> | ||||
|  | @ -21,7 +38,22 @@ import App from "./App.svelte"; | |||
|     {#await grabAssignment(nonNull(taskId))} | ||||
|         Načítám úlohu | ||||
|     {:then task} | ||||
|         {@html task.titleHtml} | ||||
|         <div class="header"> | ||||
|             <div class="title"><h3>{task.name}</h3></div> | ||||
| 
 | ||||
|             <div class="status"> | ||||
|                 <p> | ||||
|                     {task.id} | {task.points} bodů | ||||
|                     {#if status && status.submitted} | ||||
|                         {#if nonNull(status).solved} | ||||
|                         | Vyřešeno 🥳 | ||||
|                         {:else} | ||||
|                         | odevzdáno za {nonNull(status).points} bod{ "ů yyy"[nonNull(status).points] ?? "ů" } | ||||
|                         {/if} | ||||
|                     {/if} | ||||
|                 </p> | ||||
|             </div> | ||||
|         </div> | ||||
|         {@html task.description} | ||||
|         <div class="clearfloat" /> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| <script lang="ts"> | ||||
|   import type { TasksFile } from "./task-loader"; | ||||
|   import { refresh } from './task-status-cache' | ||||
| 
 | ||||
|   export let promise: Promise<TasksFile>; | ||||
| 
 | ||||
|  | @ -8,6 +9,7 @@ | |||
|   let err: any | null = null; | ||||
|     promise.then( | ||||
|         (d) => { | ||||
|             refresh(d.tasks.map(t => t.id)) | ||||
|             data = d; | ||||
|         }, | ||||
|         (e) => { | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ export type TaskStatus = { | |||
|     id: string | ||||
|     name: string | ||||
|     submitted: boolean | ||||
|     solved: boolean | ||||
|     points: number | ||||
|     maxPoints: number | ||||
|     type: string | ||||
|  | @ -80,15 +81,17 @@ function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentDat | |||
| 
 | ||||
|     let e = titleElement | ||||
| 
 | ||||
|     const titleMatch = /(\d-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?/.exec(e.innerText.trim()) | ||||
|     const titleMatch = /^(\d+-Z?\d+-\d+) (.*?)( \((\d+) bod.*\))?$/.exec(e.innerText.trim()) | ||||
|     if (!titleMatch) { | ||||
|         var [_, id, name, __, points] = ["", startElementId, "Neznámé jméno úlohy", "", ""] | ||||
|     } else { | ||||
|         var [_, id, name, __, points] = titleMatch | ||||
|     } | ||||
| 
 | ||||
|     e = e.nextElementSibling as HTMLElement | ||||
| 
 | ||||
|     while (e.nextElementSibling && | ||||
|            e.nextElementSibling?.tagName.toLowerCase() == "hr") | ||||
|            e.tagName.toLowerCase() == "hr") | ||||
|         e = e.nextElementSibling as HTMLElement | ||||
| 
 | ||||
|     while (!e.classList.contains("story") && | ||||
|  | @ -124,11 +127,12 @@ function parseTaskStatuses(doc: HTMLDocument): TaskStatus[] { | |||
|         const type = r.cells[1].innerText.trim() | ||||
|         const name = r.cells[2].innerText.trim() | ||||
|         const pointsStr = r.cells[4].innerText.trim() | ||||
|         const pointsMatch = /(–|\d+) *\/ *(\d+)/.exec(pointsStr) | ||||
|         const pointsMatch = /((–|\.|\d)+) *\/ *(\d+)/.exec(pointsStr) | ||||
|         if (!pointsMatch) throw new Error() | ||||
|         const points = +pointsMatch[1] | ||||
|         const maxPoints = +pointsMatch[2] | ||||
|         return { id, name, submitted, type, points, maxPoints } | ||||
|         const solved = r.classList.contains("zs-submitted") | ||||
|         return { id, name, submitted, type, points, maxPoints, solved } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										29
									
								
								frontend/src/task-status-cache.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								frontend/src/task-status-cache.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| import { grabTaskStates, isLoggedIn} from "./ksp-task-grabber" | ||||
| import type { TaskStatus } from "./ksp-task-grabber" | ||||
| import { readable } from 'svelte/store'; | ||||
| 
 | ||||
| let writeFn: (value: Map<string, TaskStatus>) => void = null!; | ||||
| let lastVal = new Map<string, TaskStatus>() | ||||
| if (isLoggedIn()) { | ||||
|     const cachedTaskStatuses = localStorage.getItem("taskStatuses-cache") | ||||
|     if (cachedTaskStatuses) { | ||||
|         lastVal = new Map(JSON.parse(cachedTaskStatuses)) | ||||
|     } | ||||
| } | ||||
| export const taskStatuses = readable(lastVal, write => { | ||||
|     writeFn = v => { lastVal = v; write(v); } | ||||
| }) | ||||
| 
 | ||||
| console.log(isLoggedIn()) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| export function refresh(ids: string[]) { | ||||
|     if (!isLoggedIn()) return; | ||||
| 
 | ||||
|     grabTaskStates(ids).then(t => { | ||||
|         const tt = Array.from(t.entries()) | ||||
|         writeFn(new Map(Array.from(lastVal.entries()).concat(tt))) | ||||
|         localStorage.setItem("taskStatuses-cache", JSON.stringify(tt)) | ||||
|     }) | ||||
| } | ||||
		Reference in a new issue