Provide basic functionalities for create/update quiz
This commit is contained in:
parent
52c0c6f7f8
commit
091429be58
24 changed files with 356 additions and 145 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
build/bin
|
build/bin
|
||||||
node_modules
|
node_modules
|
||||||
frontend/dist
|
frontend/dist
|
||||||
|
data
|
||||||
|
|
||||||
|
|
71
app.go
71
app.go
|
@ -3,14 +3,19 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"git.andreafazzi.eu/andrea/probo/client"
|
||||||
"git.andreafazzi.eu/andrea/probo/models"
|
"git.andreafazzi.eu/andrea/probo/models"
|
||||||
"git.andreafazzi.eu/andrea/probo/store/file"
|
"git.andreafazzi.eu/andrea/probo/store/file"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App struct
|
// App struct
|
||||||
type App struct {
|
type App struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
store *file.FileProboCollectorStore
|
|
||||||
|
store *file.FileProboCollectorStore
|
||||||
|
watcher *fsnotify.Watcher
|
||||||
}
|
}
|
||||||
|
|
||||||
type Quiz struct {
|
type Quiz struct {
|
||||||
|
@ -34,6 +39,68 @@ func (a *App) startup(ctx context.Context) {
|
||||||
a.ctx = ctx
|
a.ctx = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) onShutdown(ctx context.Context) {
|
||||||
|
a.watcher.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) onDomReady(ctx context.Context) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Create new watcher.
|
||||||
|
a.watcher, err = fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
runtime.LogFatal(ctx, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening for events.
|
||||||
|
go func() {
|
||||||
|
runtime.LogDebug(ctx, "Filesystem watcher initialized...")
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-a.watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.LogDebugf(ctx, "Event: %v", event)
|
||||||
|
if event.Has(fsnotify.Write) {
|
||||||
|
runtime.LogDebugf(ctx, "Modified file: %v", event.Name)
|
||||||
|
runtime.EventsEmit(ctx, "fsChangeEvent")
|
||||||
|
}
|
||||||
|
case err, ok := <-a.watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.LogDebugf(ctx, "Error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Add a path.
|
||||||
|
|
||||||
|
runtime.LogDebugf(ctx, "Begin watching path %v", a.store.Dir)
|
||||||
|
|
||||||
|
err = a.watcher.Add(a.store.Dir)
|
||||||
|
if err != nil {
|
||||||
|
runtime.LogFatalf(ctx, "Error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) ReadAllQuizzes() ([]*models.Quiz, error) {
|
func (a *App) ReadAllQuizzes() ([]*models.Quiz, error) {
|
||||||
return a.store.ReadAllQuizzes()
|
return a.store.ReadAllQuizzes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) MarkdownFromQuiz(quiz *models.Quiz) (string, error) {
|
||||||
|
return file.MarkdownFromQuiz(quiz)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) QuizFromMarkdown(markdown string) (*client.Quiz, error) {
|
||||||
|
return file.QuizFromMarkdown(markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateQuiz(quiz *client.Quiz, id string) (*models.Quiz, error) {
|
||||||
|
return a.store.UpdateQuiz(&client.CreateUpdateQuizRequest{Quiz: quiz}, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CreateQuiz(quiz *client.Quiz) (*models.Quiz, error) {
|
||||||
|
return a.store.CreateQuiz(&client.CreateUpdateQuizRequest{Quiz: quiz})
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
Per intensità di corrente elettrica si intende
|
|
||||||
|
|
||||||
* La quantità di carica che scorre in un circuito per unità di tempo
|
|
||||||
* La differenza di potenziale elettrico
|
|
||||||
* La quantità di carica elettrica
|
|
||||||
* L'accelerazione a cui sono sottoposti gli elettroni all'interno del circuito
|
|
|
@ -1,6 +0,0 @@
|
||||||
L'amperometro è
|
|
||||||
|
|
||||||
* Uno strumento di misura della corrente elettrica
|
|
||||||
* Uno strumento di misura della differenza di potenziale
|
|
||||||
* Si collega al circuito in parallelo
|
|
||||||
* Uno strumento di misura della carica elettrostatica
|
|
|
@ -1,6 +0,0 @@
|
||||||
Il voltmetro
|
|
||||||
|
|
||||||
* Serve a misurare la differenza di potenziale tra due punti del circuito
|
|
||||||
* Serve a misurare la corrente elettrica circolante tra due punti del circuito
|
|
||||||
* Misura la carica elettrostatica presente sulle armature di un condensatore
|
|
||||||
* E' sempre collegato in serie
|
|
|
@ -1,6 +0,0 @@
|
||||||
La corrente elettrica si misura in
|
|
||||||
|
|
||||||
* Ampere (A)
|
|
||||||
* Volt (V)
|
|
||||||
* Coulomb (C)
|
|
||||||
* Metri al secondo (m/s)
|
|
|
@ -1,6 +0,0 @@
|
||||||
Un generatore all'interno di un circuito elettrico ha la funzione di
|
|
||||||
|
|
||||||
* Mantenere le cariche in movimento mantenendo stabile una differenza di potenziale
|
|
||||||
* Generare energia dal nulla
|
|
||||||
* Rallentare le cariche che altrimenti si muoverebbero troppo velocemente
|
|
||||||
* Nessuna delle risposte previste è corretta
|
|
|
@ -1,6 +0,0 @@
|
||||||
La prima legge di Ohm descrive la relazione tra
|
|
||||||
|
|
||||||
* Corrente elettrica e differenza di potenziale
|
|
||||||
* Tra potenza dissipata e corrente elettrica
|
|
||||||
* Tra capacità di un condensatore e differenza di potenziale
|
|
||||||
* Tra potenziale gravitazionale e corrente elettrica
|
|
|
@ -1,6 +0,0 @@
|
||||||
La resistenza elettrica R
|
|
||||||
|
|
||||||
* E' pari al rapporto tra differenza di potenziale e corrente elettrica e si misura in ohm
|
|
||||||
* E' pari al rapporto tra differenza di potenziale e forza elettromotrice e si misura in volt
|
|
||||||
* E' pari al prodotto tra differenza di potenziale e corrente elettrica e si misura in ohm
|
|
||||||
* E' pari al rapporto tra differenza di potenziale e potenza dissipata e si misura in joule
|
|
|
@ -1,6 +0,0 @@
|
||||||
Il cosidetto effetto Joule
|
|
||||||
|
|
||||||
* Descrive la potenza dissipata in un circuito sotto forma di calore
|
|
||||||
* Descrive la quantità di calore dissipata dal circuito
|
|
||||||
* Descrive la differenza di potenziale presente nel circuito
|
|
||||||
* Nessuna delle risposte previste è corretta
|
|
|
@ -1,6 +0,0 @@
|
||||||
La seconda legge di Ohm
|
|
||||||
|
|
||||||
* Mette in relazione la resistenza di un conduttore con la sua lunghezza e la sua sezione
|
|
||||||
* Mette in relazione la differenza di potenziale con la corrente elettrica
|
|
||||||
* Non esiste
|
|
||||||
* Descrive la quantità di calore dissipata da un circuito per effetto Joule
|
|
|
@ -1,6 +0,0 @@
|
||||||
Affinché in un circuito possa circolare corrente continua, il generatore di tensione
|
|
||||||
|
|
||||||
* Fornisce una potenza pari a quella dissipata per effetto Joule
|
|
||||||
* Fornisce una potenza minore a quella dissipata per effetto Joule
|
|
||||||
* Rallenta le cariche elettriche
|
|
||||||
* Dev'essere spento
|
|
|
@ -1,6 +0,0 @@
|
||||||
Se in un circuito l'energia non venisse dissipata per effetto Joule
|
|
||||||
|
|
||||||
* Una volta attivata la circolazione della corrente elettrica, le cariche continuerebbero a muoversi indefinitamente
|
|
||||||
* Le cariche continuerebbero a muoversi indefinitamente senza necessità di attivare la loro circolazione
|
|
||||||
* Allora sarebbe dissipata sotto forma di calore
|
|
||||||
* Le cariche si fermerebbero immediatamente
|
|
|
@ -1,7 +0,0 @@
|
||||||
Quali grandezze fisiche mette in relazione l'esperienza di Oersted?
|
|
||||||
|
|
||||||
* Campo magnetico con campo elettrico
|
|
||||||
* Campo gravitazionale con campo elettrico
|
|
||||||
* L'energia nucleare forte con quella debole
|
|
||||||
* Cariche elettriche con densità di carica superficiale
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
La corrente elettrica rappresenta
|
|
||||||
|
|
||||||
* Un moto ordinato di cariche elettriche
|
|
||||||
* Un moto disordinato di cariche elettriche
|
|
||||||
* Un moto disordinato di masse
|
|
||||||
* Un moto ordinato di masse
|
|
|
@ -1,49 +1,95 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { models } from "$lib/wailsjs/go/models"
|
import { models, client } from "$lib/wailsjs/go/models"
|
||||||
export let quiz: models.Quiz
|
import { QuizFromMarkdown, MarkdownFromQuiz, UpdateQuiz } from "$lib/wailsjs/go/main/App"
|
||||||
|
export let quiz:models.Quiz
|
||||||
|
import { tick } from 'svelte';
|
||||||
|
|
||||||
|
let editing = false;
|
||||||
|
let content: string;
|
||||||
|
let textarea: HTMLTextAreaElement;
|
||||||
|
|
||||||
|
async function handleKeyDown(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
editing = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientQuiz: client.Quiz = await QuizFromMarkdown(content)
|
||||||
|
await UpdateQuiz(clientQuiz, quiz.id)
|
||||||
|
} catch (error) {
|
||||||
|
console.log("An error occurred:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEdit() {
|
||||||
|
editing = true;
|
||||||
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
MarkdownFromQuiz(quiz).then((result:string) => content = result)
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlur() {
|
||||||
|
editing = false;
|
||||||
|
window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card p-4 my-3 mx-4 font-heading-token">
|
<div on:click={handleEdit} class="card p-4 my-3 mx-4 font-heading-token">
|
||||||
<header class="p-3">
|
{#if editing}
|
||||||
<div class="flex justify-between">
|
<textarea class="textarea" rows="20" bind:this={textarea} bind:value={content} on:blur={handleBlur}></textarea>
|
||||||
<div>
|
{:else}
|
||||||
<button type="button" class="btn-icon variant-filled-primary">
|
<header class="p-3">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
<div class="flex justify-between">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
<div>
|
||||||
</svg>
|
<button type="button" class="btn-icon variant-filled-primary">
|
||||||
</button>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||||
<button type="button" class="btn-icon variant-filled-error">
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
</svg>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
</button>
|
||||||
</svg>
|
<button type="button" class="btn-icon variant-filled-error">
|
||||||
</button>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<span class="badge variant-ghost-secondary"><h1 class="text-xs">{quiz.hash.slice(0,10)+'…'}</h1></span>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge variant-ghost-secondary"><h1 class="text-xs">{quiz.hash.slice(0,10)+'…'}</h1></span>
|
</header>
|
||||||
</div>
|
<hr class="opacity-50" />
|
||||||
</header>
|
<p class="p-4">
|
||||||
<hr class="opacity-50" />
|
{quiz.question.text}
|
||||||
<p class="p-4">
|
</p>
|
||||||
{quiz.question.Text}
|
<form id="form-id-{quiz.id}">
|
||||||
</p>
|
<div class="space-y-2 p-4">
|
||||||
<form id="form-id-{quiz.id}">
|
|
||||||
<div class="space-y-2 p-4">
|
|
||||||
{#each quiz.answers as answer}
|
|
||||||
<label class="flex items-center space-x-2">
|
<label class="flex items-center space-x-2">
|
||||||
<input class="radio" type="radio" checked name="radio-direct" value="{answer.ID}" />
|
<input class="radio" type="radio" checked name="radio-direct" value="{quiz.correct.id}" />
|
||||||
<p>{answer.Text}</p>
|
<p>{quiz.correct.text}</p>
|
||||||
</label>
|
</label>
|
||||||
{/each}
|
{#each quiz.answers as answer}
|
||||||
</div>
|
{#if answer.id != quiz.correct.id}
|
||||||
</form>
|
<label class="flex items-center space-x-2">
|
||||||
<hr class="opacity-50" />
|
<input class="radio" type="radio" name="radio-direct" value="{answer.id}" />
|
||||||
<footer class="p-3 flex justify-start items-center space-x-4">
|
<p>{answer.text}</p>
|
||||||
<div class="flex-auto flex gap-1">
|
</label>
|
||||||
<span class="badge chip variant-filled-tertiary">#corrente</span>
|
{/if}
|
||||||
<span class="badge chip variant-filled-tertiary">#quinte</span>
|
{/each}
|
||||||
<span class="badge chip variant-filled-tertiary">#circuiti</span>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<div class="flex-auto flex justify-end">
|
<hr class="opacity-50" />
|
||||||
<small>Created on {new Date().toLocaleDateString()}</small>
|
<footer class="p-3 flex justify-start items-center space-x-4">
|
||||||
</div>
|
<div class="flex-auto flex gap-1">
|
||||||
</footer>
|
<span class="badge chip variant-filled-tertiary">#corrente</span>
|
||||||
|
<span class="badge chip variant-filled-tertiary">#quinte</span>
|
||||||
|
<span class="badge chip variant-filled-tertiary">#circuiti</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-auto flex justify-end">
|
||||||
|
<small>Created on {new Date().toLocaleDateString()}</small>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
9
frontend/src/lib/wailsjs/go/main/App.d.ts
vendored
9
frontend/src/lib/wailsjs/go/main/App.d.ts
vendored
|
@ -1,5 +1,14 @@
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
import {client} from '../models';
|
||||||
import {models} from '../models';
|
import {models} from '../models';
|
||||||
|
|
||||||
|
export function CreateQuiz(arg1:client.Quiz):Promise<models.Quiz>;
|
||||||
|
|
||||||
|
export function MarkdownFromQuiz(arg1:models.Quiz):Promise<string>;
|
||||||
|
|
||||||
|
export function QuizFromMarkdown(arg1:string):Promise<client.Quiz>;
|
||||||
|
|
||||||
export function ReadAllQuizzes():Promise<Array<models.Quiz>>;
|
export function ReadAllQuizzes():Promise<Array<models.Quiz>>;
|
||||||
|
|
||||||
|
export function UpdateQuiz(arg1:client.Quiz,arg2:string):Promise<models.Quiz>;
|
||||||
|
|
|
@ -2,6 +2,22 @@
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function CreateQuiz(arg1) {
|
||||||
|
return window['go']['main']['App']['CreateQuiz'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MarkdownFromQuiz(arg1) {
|
||||||
|
return window['go']['main']['App']['MarkdownFromQuiz'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function QuizFromMarkdown(arg1) {
|
||||||
|
return window['go']['main']['App']['QuizFromMarkdown'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function ReadAllQuizzes() {
|
export function ReadAllQuizzes() {
|
||||||
return window['go']['main']['App']['ReadAllQuizzes']();
|
return window['go']['main']['App']['ReadAllQuizzes']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function UpdateQuiz(arg1, arg2) {
|
||||||
|
return window['go']['main']['App']['UpdateQuiz'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,104 @@
|
||||||
|
export namespace client {
|
||||||
|
|
||||||
|
export class Question {
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Question(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.text = source["text"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Answer {
|
||||||
|
text: string;
|
||||||
|
correct: boolean;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Answer(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.text = source["text"];
|
||||||
|
this.correct = source["correct"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Quiz {
|
||||||
|
question?: Question;
|
||||||
|
answers: Answer[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Quiz(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.question = this.convertValues(source["question"], Question);
|
||||||
|
this.answers = this.convertValues(source["answers"], Answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
if (!a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (a.slice) {
|
||||||
|
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||||
|
} else if ("object" === typeof a) {
|
||||||
|
if (asMap) {
|
||||||
|
for (const key of Object.keys(a)) {
|
||||||
|
a[key] = new classs(a[key]);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return new classs(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export namespace models {
|
export namespace models {
|
||||||
|
|
||||||
|
export class Answer {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Answer(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.text = source["text"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Question {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
answer_ids: string[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Question(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.id = source["id"];
|
||||||
|
this.text = source["text"];
|
||||||
|
this.answer_ids = source["answer_ids"];
|
||||||
|
}
|
||||||
|
}
|
||||||
export class Quiz {
|
export class Quiz {
|
||||||
id: string;
|
id: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
// Go type: Question
|
question?: Question;
|
||||||
question?: any;
|
|
||||||
answers: Answer[];
|
answers: Answer[];
|
||||||
// Go type: Answer
|
correct?: Answer;
|
||||||
correct?: any;
|
|
||||||
type: number;
|
type: number;
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
|
@ -18,9 +109,9 @@ export namespace models {
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
this.id = source["id"];
|
this.id = source["id"];
|
||||||
this.hash = source["hash"];
|
this.hash = source["hash"];
|
||||||
this.question = this.convertValues(source["question"], null);
|
this.question = this.convertValues(source["question"], Question);
|
||||||
this.answers = this.convertValues(source["answers"], Answer);
|
this.answers = this.convertValues(source["answers"], Answer);
|
||||||
this.correct = this.convertValues(source["correct"], null);
|
this.correct = this.convertValues(source["correct"], Answer);
|
||||||
this.type = source["type"];
|
this.type = source["type"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,70 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import QuizCard from "$lib/components/QuizCard.svelte"
|
import QuizCard from "$lib/components/QuizCard.svelte"
|
||||||
|
import { EventsOn, LogDebug } from "$lib/wailsjs/runtime/runtime"
|
||||||
|
import { invalidateAll } from "$app/navigation";
|
||||||
|
import { tick } from 'svelte';
|
||||||
|
import { client } from "$lib/wailsjs/go/models"
|
||||||
|
import { QuizFromMarkdown, CreateQuiz } from "$lib/wailsjs/go/main/App"
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
|
let creatingNewQuiz: boolean
|
||||||
|
let newQuizContent: string
|
||||||
|
let textarea: HTMLTextAreaElement;
|
||||||
|
|
||||||
|
EventsOn("fsChangeEvent", async () => {
|
||||||
|
try {
|
||||||
|
await invalidateAll()
|
||||||
|
LogDebug("Reload from disk")
|
||||||
|
} catch (error) {
|
||||||
|
console.log("An error occurred:", error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleKeyDown(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
creatingNewQuiz = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientQuiz: client.Quiz = await QuizFromMarkdown(newQuizContent)
|
||||||
|
await CreateQuiz(clientQuiz)
|
||||||
|
} catch (error) {
|
||||||
|
console.log("An error occurred:", error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlur() {
|
||||||
|
creatingNewQuiz = false;
|
||||||
|
window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEdit() {
|
||||||
|
creatingNewQuiz = true;
|
||||||
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<div class="h-full flex flex-col">
|
<div class="h-full flex flex-col">
|
||||||
|
{#if creatingNewQuiz}
|
||||||
<button type="button" class="btn sticky top-0 mt-4 mx-4 variant-ghost-secondary">
|
<div class="card p-4 my-3 mx-4 font-heading-token">
|
||||||
<span><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
<textarea bind:this={textarea} bind:value={newQuizContent} on:blur={handleBlur} class="textarea" rows="20"></textarea>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
</div>
|
||||||
</svg>
|
{:else}
|
||||||
</span>
|
<button on:click={handleEdit} type="button" class="btn sticky top-0 mt-4 mx-4 variant-ghost-secondary">
|
||||||
<span>Add a new quiz</span>
|
<span><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
</button>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span>Add a new quiz</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
{#each data.quizzes as quiz}
|
{#each data.quizzes as quiz}
|
||||||
<QuizCard {quiz} />
|
<QuizCard {quiz} />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -16,7 +16,8 @@ const config = {
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter({
|
adapter: adapter({
|
||||||
fallback: 'index.html'
|
fallback: 'index.html'
|
||||||
})
|
}),
|
||||||
|
files: { lib: './src/lib/' }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export default config;
|
export default config;
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -3,7 +3,8 @@ module changeme
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.andreafazzi.eu/andrea/probo v0.0.0-20230707161509-7a1135de0fd3
|
git.andreafazzi.eu/andrea/probo v0.0.0-20230713044636-dd4636a89dd0
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/wailsapp/wails/v2 v2.5.1
|
github.com/wailsapp/wails/v2 v2.5.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ require (
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
golang.org/x/crypto v0.11.0 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect
|
||||||
golang.org/x/net v0.12.0 // indirect
|
golang.org/x/net v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
golang.org/x/sys v0.10.0 // indirect
|
||||||
golang.org/x/text v0.11.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
|
|
11
go.sum
11
go.sum
|
@ -1,10 +1,12 @@
|
||||||
git.andreafazzi.eu/andrea/probo v0.0.0-20230707161509-7a1135de0fd3 h1:Ldb0JVvfbvWLyaJfPefWIy85I1c2GO1dPOSVg/vcVMw=
|
git.andreafazzi.eu/andrea/probo v0.0.0-20230713044636-dd4636a89dd0 h1:WrR000VnRjLXbdRDz61NBzoUpJGdGTD2TRgubCxEoks=
|
||||||
git.andreafazzi.eu/andrea/probo v0.0.0-20230707161509-7a1135de0fd3/go.mod h1:lv4LRymK1SeOIs3Y1nJDA/8Ps/fRHQJDbIR+4Tdxtm4=
|
git.andreafazzi.eu/andrea/probo v0.0.0-20230713044636-dd4636a89dd0/go.mod h1:lv4LRymK1SeOIs3Y1nJDA/8Ps/fRHQJDbIR+4Tdxtm4=
|
||||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
|
@ -65,8 +67,8 @@ github.com/wailsapp/wails/v2 v2.5.1 h1:mfG+2kWqQXYOwdgI43HEILjOZDXbk5woPYI3jP2b+
|
||||||
github.com/wailsapp/wails/v2 v2.5.1/go.mod h1:jbOZbcr/zm79PxXxAjP8UoVlDd9wLW3uDs+isIthDfs=
|
github.com/wailsapp/wails/v2 v2.5.1/go.mod h1:jbOZbcr/zm79PxXxAjP8UoVlDd9wLW3uDs+isIthDfs=
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us=
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
|
@ -79,6 +81,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
2
main.go
2
main.go
|
@ -28,6 +28,8 @@ func main() {
|
||||||
},
|
},
|
||||||
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
||||||
OnStartup: app.startup,
|
OnStartup: app.startup,
|
||||||
|
OnDomReady: app.onDomReady,
|
||||||
|
OnShutdown: app.onShutdown,
|
||||||
Bind: []interface{}{
|
Bind: []interface{}{
|
||||||
app,
|
app,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue