Skip to content

Commit

Permalink
Feature/quiz functionality (#564)
Browse files Browse the repository at this point in the history
* quiz squash #3

Modified the layout and page svelte files for the quizzes so they
work together

fix the quiz JSON file so the answers were correct for one
question, and the answers didn't show pictured, which were improper.

Made a layout.js file "+layout.js"

Started a layout svelte page for layout of quiz GUI. May not be needed.

recommitted the original changes for a new branch

Quizzes can now be viewed from dropdown menu in English.

Added a page.js file for quizzes, created a new id and collections
folder in the routes/quizzes folder and moved everything into that.

You can now navigate to the quizzes page by clicking on it in the
dropdown.

Rerouted navigation to the quiz page to include a docset folder.

Moved everything in the quiz collections folder into a docset folder

Moved this into the quizzes docset folder

A bunch of files got removed, so I had to rebuild PWA data files

the +page.js file for the quiz page now fetches quiz.json

Shifted some of the files around under the route/quiz folder.
Also changed the path in BookSelector to better match the path in the
quiz folder

Changed the path to the quiz files again.

in BookSelector, line 58: changed path from 'collection' to 'id'

fixed navigation to quiz

Removed unnecessary import

* quiz squash #2

Deleted unnecessary layout files

format for lint

Added more elements to page.svelte

changes load data to be export instead of import

Fixed the path for navigating the static quiz folder in BookSelector

Corrected a mistake with changing the route for navigating to quizzes

Was able to get a quiz question with answers to pop onto the screen!

Removed languages from imports for quiz page. Not needed.

Formatted the quiz buttons in tailwind css and made them buttons.

got ready of unneeded comments

got rid of unneeded comments

Corrected the pathing for quizzes so that it now fetches a quiz from its
source.

Export quiz types

Got quizzes to cycle through questions and images to appear when they
exist.

Had to add a variable so images can be displayed on the quizzes page for
answers too.

Removed duplicate sound files in static

removed the appdef.xml file duplicate in static

format for lint

Playing around with the grid functionality for quiz answer buttons

* quiz squash #1

trying to center answer buttons and also preparing to implement audio
files

Quiz questions programmed to play questions based on correctness of
answer.

Centered the answer block correctly for quizzes

Deleted old quiz JSON file and got quiz page to detect correct from
incorrect answers.

created 2 separate quiz answer blocks for text and images, then
separated them in if statements

Removed some redundancies in the quiz page code

Everytime you get a quiz question right, a point is added to your score

I randomized the order in which quiz answers are displayed.

Removed some excess comments

Quiz buttons now get highlighted green when answered correctly

When a wrong quiz answer is selected, it's highlighted in red.

Clicking a quiz answer now colors the answer box red or green rather
than what's behind it.

Set a timer after a quiz answer is guessed to give the user some time to
observe the result of their answer.

After a quiz, you now get a score.

After a quiz, you now get a message if you getting a passing score.

Got rid of unused imports from the quiz Svelte page

removed unneeded console logs from the quiz svelte page

Divided elements of the score for quizzes in their own div's

put the elements of quiz score in flex grids with margins

I changed the quiz page code so the navbar is outside the if statement
and doesn't get created when switching between score and the quiz itself

Finished the quiz score page styling

The clicked quiz answer text is now white, and images now display images

Started implementing next arrow per quiz question.

Paired quiz answers when in image format

Put a quiz label on the navbar

Corrected the color change for quiz answers that are images to the
correct ones

Added a display: block to the class of quiz answer images

Updated the quiz fork so shuffling answers works and correct answer is
highlighted when incorrect quiz answers are selected

The next button for quizzes is now centered properly

For quizzes, fixed width and padding of image answers a bit. Got the
image highlighting working.

Completely fixed highlighting for quiz image questions.

Fixed an issue for quiz questions where question images wasn't being
centered.

Removed pairing from quiz page so columns is now handled by the quiz
itself

gave question-images a class name of quiz-question-image

For the quizzes, I made the next button an identifier

Quiz image answers are now columned

Quiz answers now have flexible sizes for lg, md, and small screens

For quizzes, score messages "You Pass" and "Oh Dear" are now centered
properly.

Removed some unneeded comments.

* Fix formatting

Use fetch from load method

Handle special book type navigation

* pass book.type in cell and through menuaction
* handle special book types before normal book navigation

Fix quiz interaction issues

* only accept answer once
* style Forward Arrow button

Quiz: Set image height

The native app hardcodes the image height to 160px

Quiz: translate score text

Quiz: Set colors for image answers

Quiz: Use bodyFontSize and bodyLineHeight

Quiz: Get Commentary from Quiz file

Quiz: Implement NavBar

Quiz: Style image answer and next button

Quiz: Style question line-height separate

* remove accidentally added static files

* Removed unneeded lines

* Got the audio button added to the navbar for quizzes

* The audio icon is now properly placed in the navbar for quizzes

* Added a quizAudioActive store to the audio.js file

* Quiz: switch from book.type to url in events

* Have one place that converts book.type to url
* Pass url around
* Add url to history when navigating
* Use url in history

* Fix formatting

* I think I've been able to implement most of what's needed for
quiz answer and question audio to be played from.

* For the playQuizAnswerAudio function, I put back both the highlighted
and unhighlighted functions

* I think I almost got the audio file pathing sorted out, but it doesn't
seem to be working yet.

* Quick change in quizzes page from answer.hasAudio to answer.audio

* Set the quiz answer images to a maximum size and width to keep one set
from getting bigger than the other set

* Removed unnecessary comments

* got rid of the currentAnswerIndex stuff in onNextQuestion function for
the quiz page

* Streamlined quiz answer and question audio to play from the playSound
function

* Add listener back into the quiz page

* audio.js quizAudioActive is now set to true by default

* audio for quiz questions and answers fully works

* Quiz answers are now highlighted when audio is played

* For quizzes, the audio now cuts off when an answer is selected.

* Removed or commented out some console logs. Set textHighlightIndex to
-1 at the beginning of the onQuestionAnswered function

* text highlighting bugs for the quizzes has been fixed.

* Removed console.logs from quiz svelte page

* Quiz questions now shuffle

* Reviewed the code and got rid of some unused variables and console logs

* Corrected an issue where the first quiz question wasn't getting shuffled
by calling the shuffle function inside onMount

* removed an unused quizzes variable from the bookSelector

* I implemented onDestory for the quiz page so the audio stops playing
when the HTML page is unrendered or destroyed

* Restore delay in display correct answer when wrong

* For quizzes, I added the ability for an explanation to an answer to be
displayed. Needs work.

* Took out old shuffle toggle stuff from quiz page. The explanation is
good to go

* I believe I got explanation audio to work for quizzes. Need to test it

* Removed unneeded comments

* Removed more unneeded comments

* Quiz page now checks if explanations are on the question or answer level
then delivers an explanation accordingly, if it exists (audio included)

* Convert Quiz to put explanation at correct level

* If the explanation is before the answers, then it is at the
  question-level.
* If the explanation is after all of the answers, it is at the
  question-level.
* Otherwise, it is at the question level

* Explanation audio now stops once the next button is clicked

* Quiz right and wrong answer audio now tied to the json file

* Quiz right and wrong answer audio can now select one randomy based on
multiple if multiple exist in the json quiz file

* Modified it so explanations aren't given on correct answers.

* Corrected the pathing of quiz right and wrong answer audio files

* ConvertBooks now has default right and wrong quiz audio files if other
audio files, right or wrong, don't exist

* Got rid of an unneeded comment in the quiz svelte page

* Display quiz book in list of books converted

* Fix answer audio sound location

* default sounds are in /assets/
* custom sounds are in /clips/

* For quizzes, put back the answer audio and highlights

* For quizzes, got the right and wrong answer audio working again

* Audio is no longer stopping like it's supposed to. Need to fix

* Corrected an issue where the audio wasn't stopping when onDestroy
function is called

* changed quiz right/wrong audio pathing to clips instead of assets

* Made some more corrections to quiz right/wrong audio pathing

* for quizzes, right/wrong audio plays, and then explanation audio

* Fix runtime a11y and unused style warnings

* Change columns (\ac) to be per-question

* Quiz answers are now divided into columns based on what's in the JSON
file

* Fixed the quiz column changes so everything is now there.

* Got rid of an extra bracket in a div class

* The default number of columns is now 2 for quiz answers

* quiz answers can now be divided into 3 columns

* Removed tables from quiz page and converted to tailwind grid

* Quiz answer sizes now accommodate for columns

* Made some changes to quiz answer div classes

* Made some changes to the sizing of quiz answers

* Styled the quiz answers again

* fixed a columns typo in the quiz page

* I think I've finished touching up the quiz answer styling

* Ignore ally warning

* Tightening up quiz classes

* Finished tightening up quiz classes

* Made one last change to quiz class tightening

* Added the h-40 back into quiz question images

---------

Co-authored-by: Chris Hubbard <[email protected]>
  • Loading branch information
aidanpscott and chrisvire authored Jun 26, 2024
1 parent e7fc37b commit e28dc91
Show file tree
Hide file tree
Showing 10 changed files with 620 additions and 49 deletions.
81 changes: 58 additions & 23 deletions convert/convertBooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,9 @@ export async function convertBooks(
'quizzes',
book.id + '.json'
),
content: JSON.stringify(convertQuizBook(context, book))
content: JSON.stringify(convertQuizBook(context, book), null, 2)
});
process.stdout.write(` ${book.id}`);
break;
default:
bookConverted = true;
Expand Down Expand Up @@ -291,32 +292,34 @@ export async function convertBooks(
};
}

type QuizAnswer = {
export type QuizExplanation = {
text?: string;
audio?: string;
};

export type QuizAnswer = {
//\aw or \ar
correct: boolean;
text?: string;
image?: string;
audio?: string;
explanation?: {
//\ae
text: string;
audio?: string;
};
explanation?: QuizExplanation;
};

type QuizQuestion = {
export type QuizQuestion = {
//\qu
text: string;
image?: string;
audio?: string;
columns?: number; //\ac
explanation?: QuizExplanation;
answers: QuizAnswer[];
};

type Quiz = {
export type Quiz = {
id: string; //\id
name?: string; //\qn
shortName?: string; //\qs
columns?: number; //\ac
rightAnswerAudio?: string[]; //\ra
wrongAnswerAudio?: string[]; //\wa
questions: QuizQuestion[];
Expand All @@ -343,9 +346,6 @@ function convertQuizBook(context: ConvertBookContext, book: Book): Quiz {
id: quizSFM.match(/\\id ([^\\\r\n]+)/i)![1],
name: quizSFM.match(/\\qn ([^\\\r\n]+)/i)?.at(1),
shortName: quizSFM.match(/\\qs ([^\\\r\n]+)/i)?.at(1),
columns: quizSFM.match(/\\ac ([^\\\r\n]+)/i)?.at(1)
? parseInt(quizSFM.match(/\\ac ([^\\\r\n]+)/i)![1])
: undefined,
rightAnswerAudio: quizSFM.match(/\\ra ([^\\\r\n]+)/gi)?.map((m) => {
return m.match(/\\ra ([^\\\r\n]+)/i)![1];
}),
Expand All @@ -359,7 +359,7 @@ function convertQuizBook(context: ConvertBookContext, book: Book): Quiz {
const parsed = m.match(/([0-9]+)( *- *[0-9]+)? ([^\\\r\n]+)/i)!;
return {
rangeMin: parseInt(parsed[1]),
rangeMax: parsed[2] ? parseInt(parsed[2]) : undefined,
rangeMax: parsed[2] ? parseInt(parsed[2].replace('-', '')) : parseInt(parsed[1]),
message: parsed[3]
};
}),
Expand All @@ -370,8 +370,8 @@ function convertQuizBook(context: ConvertBookContext, book: Book): Quiz {
let aCount = 0;
let question: QuizQuestion = { text: '', answers: [] };
let answer: QuizAnswer = { correct: false };
quizSFM.match(/\\(qu|aw|ar|ae) ([^\\\r\n]+)/gi)?.forEach((m) => {
const parsed = m.match(/\\(qu|aw|ar|ae) ([^\\\r\n]+)/i)!;
quizSFM.match(/\\(qu|aw|ar|ae|ac) ([^\\\r\n]+)/gi)?.forEach((m) => {
const parsed = m.match(/\\(qu|aw|ar|ae|ac) ([^\\\r\n]+)/i)!;
switch (parsed[1]) {
case 'qu':
if (aCount > 0) {
Expand Down Expand Up @@ -408,14 +408,34 @@ function convertQuizBook(context: ConvertBookContext, book: Book): Quiz {
aCount++;
}
break;
case 'ac':
question.columns = parseInt(parsed[2]);
break;
case 'ae':
if (!question.answers[aCount - 1].explanation) {
question.answers[aCount - 1].explanation = { text: '' };
}
if (hasAudioExtension(parsed[2])) {
question.answers[aCount - 1].explanation!.audio = parsed[2];
} else {
question.answers[aCount - 1].explanation!.text = parsed[2];
{
const isAudio = hasAudioExtension(parsed[2]);
const hasExplanationsInAnswers = isAudio
? question.answers.some((answer) => answer.explanation?.audio !== undefined)
: question.answers.some((answer) => answer.explanation?.text != undefined);

if (aCount == 0) {
// Question-level explanation
question.explanation = updateExplanation(question.explanation, parsed[2]);
} else {
if (aCount == 1 || hasExplanationsInAnswers) {
// Answer-specific explanation
question.answers[aCount - 1].explanation = updateExplanation(
question.answers[aCount - 1].explanation,
parsed[2]
);
} else {
// Question-level explanation (same for all answers)
question.explanation = updateExplanation(
question.explanation,
parsed[2]
);
}
}
}
break;
}
Expand All @@ -424,6 +444,21 @@ function convertQuizBook(context: ConvertBookContext, book: Book): Quiz {
return quiz;
}

function updateExplanation(
explanation: QuizExplanation | undefined,
text: string
): QuizExplanation {
if (!explanation) {
explanation = {};
}
if (hasAudioExtension(text)) {
explanation.audio = text;
} else {
explanation.text = text;
}
return explanation;
}

function convertScriptureBook(
pk: SABProskomma,
context: ConvertBookContext,
Expand Down
50 changes: 40 additions & 10 deletions src/lib/components/BookSelector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ The navbar component.
import config from '$lib/data/config';
import SelectList from './SelectList.svelte';
import * as numerals from '$lib/scripts/numeralSystem';
import { goto } from '$app/navigation';
import { base } from '$app/paths';
export let displayLabel;
$: book = $nextRef.book === '' ? $refs.book : $nextRef.book;
$: chapter = $nextRef.chapter === '' ? $refs.chapter : $nextRef.chapter;
$: verseCount = getVerseCount(chapter, chapters);
Expand All @@ -29,9 +32,11 @@ The navbar component.
$: v = $t.Selector_Verse;
let bookSelector;
$: label = config.bookCollections
.find((x) => x.id === $refs.collection)
.books.find((x) => x.id === book).name;
$: label =
displayLabel ??
config.bookCollections
.find((x) => x.id === $refs.collection)
.books.find((x) => x.id === book).name;
function chapterCount(book) {
let count = Object.keys(books.find((x) => x.bookCode === book).versesByChapters).length;
Expand All @@ -48,7 +53,20 @@ The navbar component.
/**
* Pushes reference changes to nextRef. Pushes final change to default reference.
*/
async function navigateReference(e) {
// Handle special book navigation first
if (e.detail.tab === b && e.detail?.url) {
const book = e.detail.text;
addHistory({
collection: $refs.collection,
book,
chapter: '',
url: e.detail.url
});
goto(e.detail.url);
return;
}
if (!showChapterSelector) {
$nextRef.book = e.detail.text;
await refs.set({ book: $nextRef.book, chapter: 1 });
Expand Down Expand Up @@ -104,6 +122,7 @@ The navbar component.
verse: $nextRef.verse
});
document.activeElement.blur();
goto(`${base}/`);
}
function resetNavigation() {
Expand All @@ -113,24 +132,35 @@ The navbar component.
nextRef.reset();
}
/**list of books in current docSet*/
/**list of books, quizzes, and quiz groups in current docSet*/
$: books = $refs.catalog.documents;
/**list of chapters in current book*/
$: chapters = books.find((d) => d.bookCode === book).versesByChapters;
function getBookUrl(book) {
let url;
if (book.type === 'quiz') {
url = `${base}/quiz/${$refs.collection}/${book.id}`;
}
return url;
}
let bookGridGroup = ({ colId, bookLabel = 'abbreviation' }) => {
let groups = [];
var lastGroup = null;
config.bookCollections
.find((x) => x.id === colId)
.books.forEach((book) => {
// Include books only in the catalog (i.e. only supported book types)
if (books.find((x) => x.bookCode === book.id)) {
const url = getBookUrl(book);
if (books.find((x) => x.bookCode === book.id) || url) {
let label = book[bookLabel] || book.name;
let cell = { label: label, id: book.id };
let cell = { label, id: book.id, url };
let group = book.testament || '';
if ((lastGroup == null || group !== lastGroup) && config.mainFeatures['book-group-titles']) {
if (
(lastGroup == null || group !== lastGroup) &&
config.mainFeatures['book-group-titles']
) {
// Create new group
groups.push({
header: book.testament
Expand All @@ -143,8 +173,8 @@ The navbar component.
lastGroup = group;
} else {
// Add Book to last group
let cells = groups[groups.length - 1].cells;
groups[groups.length - 1].cells = [...cells, cell];
let cells = groups.at(-1).cells;
groups.at(-1).cells = [...cells, cell];
}
}
});
Expand Down
29 changes: 19 additions & 10 deletions src/lib/components/HistoryCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ TODO:
import { formatDateAndTime } from '$lib/scripts/dateUtils';
import { base } from '$app/paths';
import config from '$lib/data/config';
import { goto } from '$app/navigation';
export let history: HistoryItem;
$: bc = config.bookCollections.find((x) => x.id === history.collection);
Expand All @@ -22,21 +23,29 @@ TODO:
: history.chapter;
$: dateFormat = formatDateAndTime(new Date(history.date));
$: textDirection = bc.style.textDirection;
</script>
<!-- history cards are alway LTR with the reference following the text direction -->
<div class="history-item-block dy-card w-100 bg-base-100 shadow-lg my-4" style:direction="ltr">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<a
style="text-decoration:none;"
href="{base}/"
on:click={() =>
function onHistoryClick() {
if (history.url) {
goto(history.url);
} else {
refs.set({
docSet,
book: history.book,
chapter: history.chapter,
verse: history.verse
})}
})
goto(`${base}/`);
}
}
</script>

<!-- history cards are alway LTR with the reference following the text direction -->
<div class="history-item-block dy-card w-100 bg-base-100 shadow-lg my-4" style:direction="ltr">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
style="text-decoration:none;"
on:click={onHistoryClick}
>
<div
class="history-card grid grid-cols-1"
Expand All @@ -55,5 +64,5 @@ TODO:
</div>
<div class="history-item-date justify-self-start">{dateFormat}</div>
</div>
</a>
</div>
</div>
9 changes: 6 additions & 3 deletions src/lib/components/SelectGrid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ A component to display menu options in a grid.
return color;
};
function handleClick(opt: string) {
function handleClick(opt: any) {
const text = opt.id;
const url = opt?.url;
dispatch('menuaction', {
text: opt
text,
url
});
}
</script>
Expand Down Expand Up @@ -114,7 +117,7 @@ A component to display menu options in a grid.
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<span
on:click={() => handleClick(cell.id)}
on:click={() => handleClick(cell)}
id={cell.id}
class="dy-btn dy-btn-square dy-btn-ghost normal-case truncate text-clip"
style={cellStyle}
Expand Down
9 changes: 6 additions & 3 deletions src/lib/components/SelectList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ A component to display menu options in a list.
const dispatch = createEventDispatcher();
function handleClick(opt: string) {
function handleClick(opt: any) {
const text = opt.id;
const url = opt?.url;
dispatch('menuaction', {
text: opt
text,
url
});
}
Expand All @@ -32,7 +35,7 @@ A component to display menu options in a list.
<!-- svelte-ignore a11y-interactive-supports-focus -->
<span
on:click={() =>
handleClick(group.cells[ri * group.cells.length + ci].id)}
handleClick(group.cells[ri * group.cells.length + ci])}
class="menu p-0 cursor-pointer hover:bg-base-100 min-w-[16rem]"
role="button"
>
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/TabsMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ A component to display tabbed menus.
function handleMenuaction({ detail }: CustomEvent) {
dispatch('menuaction', {
text: detail.text,
url: detail?.url,
tab: active
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/data/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface HistoryItem {
book: string;
chapter: string;
verse?: string;
url?: string;
}
interface History extends DBSchema {
history: {
Expand Down Expand Up @@ -39,6 +40,7 @@ export async function addHistory(item: {
book: string;
chapter: string;
verse?: string;
url?: string;
}) {
let history = await openHistory();
if (nextTimer) {
Expand Down
5 changes: 5 additions & 0 deletions src/lib/data/stores/audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import config from '../config';
setDefaultStorage('audioActive', config.mainFeatures['audio-turn-on-at-startup']);
export const audioActive = writable(localStorage.audioActive === 'true');
audioActive.subscribe((value) => (localStorage.audioActive = value));

setDefaultStorage('quizAudioActive', true);
export const quizAudioActive = writable(localStorage.quizAudioActive);
quizAudioActive.subscribe((value) => (localStorage.quizAudioActive = value));

/**which element should be highlighted as the audio is playing*/
function createaudioHighlightElements() {
const external = writable([]);
Expand Down
Loading

0 comments on commit e28dc91

Please sign in to comment.