Skip to content

Commit

Permalink
💻 new customize adventure page (#5535)
Browse files Browse the repository at this point in the history
Fixes #5086
  • Loading branch information
hasan-sh authored Jul 3, 2024
1 parent 978b523 commit bdbdfac
Show file tree
Hide file tree
Showing 72 changed files with 1,013 additions and 537 deletions.
3 changes: 3 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ def load_customized_adventures(level, customizations, into_adventures):
else:
db_row['content'] = safe_format(db_row['content'],
**hedy_content.KEYWORDS.get(g.keyword_lang))
if 'solution_example' in db_row:
db_row['solution_example'] = safe_format(db_row['solution_example'],
**hedy_content.KEYWORDS.get(g.keyword_lang))
except Exception:
# We don't want teacher being able to break the student UI -> pass this adventure
pass
Expand Down
17 changes: 17 additions & 0 deletions build-tools/heroku/tailwind/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,23 @@ code {
@apply text-blue-600;
}

.cust-adv-tab {
@apply cursor-pointer block border-b-4 tracking-wide;
/* Colors */
@apply border-gray-400;
}

.cust-adv-tab.active {
/* Colors */
@apply border-blue-800 !important;
}

.cust-adv-tab-text {
@apply font-slab;
/* Colors */
@apply text-blue-800;
}

#output, #quiz_question_output_container {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
}
Expand Down
11 changes: 7 additions & 4 deletions messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-22 12:26-0400\n"
"POT-Creation-Date: 2024-06-26 16:04+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -290,9 +290,6 @@ msgstr ""
msgid "ask_needs_var"
msgstr ""

msgid "available_in"
msgstr ""

msgid "become_a_sponsor"
msgstr ""

Expand Down Expand Up @@ -1589,6 +1586,12 @@ msgstr ""
msgid "social_media"
msgstr ""

msgid "solution_example"
msgstr ""

msgid "solution_example_explanation"
msgstr ""

msgid "something_went_wrong_keyword_parsing"
msgstr ""

Expand Down
29 changes: 29 additions & 0 deletions static/css/generated.full.css
Original file line number Diff line number Diff line change
Expand Up @@ -343946,6 +343946,29 @@ code {
color: rgb(49 130 206 / var(--tw-text-opacity));
}

.cust-adv-tab {
display: block;
cursor: pointer;
border-bottom-width: 4px;
letter-spacing: 0.025em;
/* Colors */
--tw-border-opacity: 1;
border-color: rgb(203 213 224 / var(--tw-border-opacity));
}

.cust-adv-tab.active {
/* Colors */
--tw-border-opacity: 1 !important;
border-color: rgb(44 82 130 / var(--tw-border-opacity)) !important;
}

.cust-adv-tab-text {
font-family: 'Balsamiq Sans', sans-serif;
/* Colors */
--tw-text-opacity: 1;
color: rgb(44 82 130 / var(--tw-text-opacity));
}

#output, #quiz_question_output_container {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
}
Expand Down Expand Up @@ -344621,6 +344644,12 @@ input:checked ~ .toggle-path {
border-color: rgb(49 130 206 / var(--tw-border-opacity)) !important;
}

.cust-adv-tab.focused-option {
/* Colors */
--tw-border-opacity: 1 !important;
border-color: rgb(44 82 130 / var(--tw-border-opacity)) !important;
}

.stats-period-toggle.focused-option {
color: black;
text-decoration: none;
Expand Down
21 changes: 12 additions & 9 deletions static/js/adventure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ declare let window: CustomWindow;

export interface InitializeCustomizeAdventurePage {
readonly page: 'customize-adventure';
readonly level: number;
}

let $editor: ClassicEditor;
let keywordHasAlert: Map<string, boolean> = new Map()

export async function initializeCustomAdventurePage(_options: InitializeCustomizeAdventurePage) {
const editorContainer = document.querySelector('#adventure_editor') as HTMLElement;
const editorContainer = document.querySelector('#adventure-editor') as HTMLElement;
const editorSolutionExampleContainer = document.querySelector('#adventure-solution-editor') as HTMLElement;
// Initialize the editor with the default language
let lang = document.querySelector('#languages_dropdown> .option.selected')!.getAttribute('data-value') as string
const TRADUCTIONS = convert(TRADUCTION_IMPORT) as Map<string, Map<string,string>>;
Expand All @@ -28,6 +30,7 @@ export async function initializeCustomAdventurePage(_options: InitializeCustomiz

if (editorContainer) {
await initializeEditor(lang, editorContainer);
await initializeEditor(lang, editorSolutionExampleContainer, true);
showWarningIfMultipleKeywords(TRADUCTION)
$editor.model.document.on('change:data', () => {
showWarningIfMultipleKeywords(TRADUCTION)
Expand Down Expand Up @@ -107,12 +110,8 @@ function findCoincidences(name: string, TRADUCTION: Map<string, string>) {
return coincidences;
}

function initializeEditor(language: string, editorContainer: HTMLElement): Promise<void> {
function initializeEditor(language: string, editorContainer: HTMLElement, solutionExample=false): Promise<void> {
return new Promise((resolve, reject) => {
if ($editor) {
$editor.destroy();
}

ClassicEditor
.create(editorContainer, {
codeBlock: {
Expand All @@ -123,9 +122,13 @@ function initializeEditor(language: string, editorContainer: HTMLElement): Promi
language,
})
.then(editor => {
window.ckEditor = editor;
$editor = editor;
$editor.model.document.on("change:data", e => autoSave("customize_adventure", e))
if (solutionExample) {
window.ckSolutionEditor = editor;
} else {
window.ckEditor = editor;
$editor = editor;
}
editor.model.document.on("change:data", e => autoSave("customize_adventure", e))
resolve();
})
.catch(error => {
Expand Down
60 changes: 37 additions & 23 deletions static/js/appbundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -103885,7 +103885,8 @@ def note_with_error(value, err):
var $editor;
var keywordHasAlert = new Map();
async function initializeCustomAdventurePage(_options) {
const editorContainer = document.querySelector("#adventure_editor");
const editorContainer = document.querySelector("#adventure-editor");
const editorSolutionExampleContainer = document.querySelector("#adventure-solution-editor");
let lang = document.querySelector("#languages_dropdown> .option.selected").getAttribute("data-value");
const TRADUCTIONS = convert(highlighting_trad_default);
if (!TRADUCTIONS.has(lang)) {
Expand All @@ -103894,6 +103895,7 @@ def note_with_error(value, err):
let TRADUCTION3 = TRADUCTIONS.get(lang);
if (editorContainer) {
await initializeEditor(lang, editorContainer);
await initializeEditor(lang, editorSolutionExampleContainer, true);
showWarningIfMultipleKeywords(TRADUCTION3);
$editor.model.document.on("change:data", () => {
showWarningIfMultipleKeywords(TRADUCTION3);
Expand Down Expand Up @@ -103967,11 +103969,8 @@ def note_with_error(value, err):
}
return coincidences;
}
function initializeEditor(language2, editorContainer) {
function initializeEditor(language2, editorContainer, solutionExample = false) {
return new Promise((resolve2, reject) => {
if ($editor) {
$editor.destroy();
}
import_ckeditor.default.create(editorContainer, {
codeBlock: {
languages: [
Expand All @@ -103980,9 +103979,13 @@ def note_with_error(value, err):
},
language: language2
}).then((editor) => {
window.ckEditor = editor;
$editor = editor;
$editor.model.document.on("change:data", (e) => autoSave("customize_adventure", e));
if (solutionExample) {
window.ckSolutionEditor = editor;
} else {
window.ckEditor = editor;
$editor = editor;
}
editor.model.document.on("change:data", (e) => autoSave("customize_adventure", e));
resolve2();
}).catch((error2) => {
console.error(error2);
Expand Down Expand Up @@ -118469,18 +118472,7 @@ def note_with_error(value, err):
});
});
}
function update_db_adventure(adventure_id) {
const adventure_name = $("#custom_adventure_name").val();
let classes = [];
let levels = [];
document.querySelectorAll("#levels_dropdown > .option.selected").forEach((el3) => {
levels.push(el3.getAttribute("data-value"));
});
document.querySelectorAll("#classes_dropdown > .option.selected").forEach((el3) => {
classes.push(el3.getAttribute("data-value"));
});
const language2 = document.querySelector("#languages_dropdown> .option.selected").getAttribute("data-value");
const content2 = import_dompurify2.default.sanitize(window.ckEditor.getData());
function get_formatted_content(content2, levels, language2) {
const parser19 = new DOMParser();
const html = parser19.parseFromString(content2, "text/html");
const minLevel = Math.min(...levels.map((el3) => Number(el3)));
Expand Down Expand Up @@ -118511,6 +118503,23 @@ def note_with_error(value, err):
}
}
const formatted_content = html.getElementsByTagName("body")[0].outerHTML.replace(/<br>/g, "\n");
return formatted_content;
}
function update_db_adventure(adventure_id) {
const adventure_name = $("#custom_adventure_name").val();
let classes = [];
let levels = [];
document.querySelectorAll("#levels_dropdown > .option.selected").forEach((el3) => {
levels.push(el3.getAttribute("data-value"));
});
document.querySelectorAll("#classes_dropdown > .option.selected").forEach((el3) => {
classes.push(el3.getAttribute("data-value"));
});
const language2 = document.querySelector("#languages_dropdown> .option.selected").getAttribute("data-value");
const content2 = import_dompurify2.default.sanitize(window.ckEditor.getData());
const solutionExampleCode = import_dompurify2.default.sanitize(window.ckSolutionEditor.getData());
const formatted_content = get_formatted_content(content2, levels, language2);
const formatted_solution_code = get_formatted_content(solutionExampleCode, levels, language2);
const agree_public = $("#agree_public").prop("checked");
$.ajax({
type: "POST",
Expand All @@ -118520,6 +118529,7 @@ def note_with_error(value, err):
name: adventure_name,
content: content2,
formatted_content,
formatted_solution_code,
public: agree_public,
language: language2,
classes,
Expand Down Expand Up @@ -121358,16 +121368,20 @@ def note_with_error(value, err):

// static/js/initialize.ts
function initialize(options) {
var _a3;
var _a3, _b;
setClientMessageLanguage(options.lang);
let level3 = options.level;
if (!level3 && ((_a3 = options.javascriptPageOptions) == null ? void 0 : _a3.page) == "customize-adventure") {
level3 = options.javascriptPageOptions.level;
}
initializeApp({
level: options.level,
level: level3,
keywordLanguage: options.keyword_language,
staticRoot: options.staticRoot
});
initializeFormSubmits();
initializeTutorial();
switch ((_a3 = options.javascriptPageOptions) == null ? void 0 : _a3.page) {
switch ((_b = options.javascriptPageOptions) == null ? void 0 : _b.page) {
case "code":
initializeCodePage(options.javascriptPageOptions);
break;
Expand Down
4 changes: 2 additions & 2 deletions static/js/appbundle.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions static/js/custom-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
*/
export interface CustomWindow extends Window {
ckEditor: ClassicEditor;
ckSolutionEditor: ClassicEditor;
}
8 changes: 7 additions & 1 deletion static/js/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ type InitializePageOptions =
export function initialize(options: InitializeOptions) {
setClientMessageLanguage(options.lang);

let level = options.level;

if (!level && options.javascriptPageOptions?.page == "customize-adventure") {
level = options.javascriptPageOptions.level
}

initializeApp({
level: options.level,
level: level,
keywordLanguage: options.keyword_language,
staticRoot: options.staticRoot,
});
Expand Down
44 changes: 26 additions & 18 deletions static/js/teachers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,24 +215,7 @@ export function remove_student(class_id: string, student_id: string, prompt: str
});
}

function update_db_adventure(adventure_id: string) {
// Todo TB: It would be nice if we improve this with the formToJSON() function once #3077 is merged
const adventure_name = $('#custom_adventure_name').val();
let classes: string[] = [];
let levels: string[] = []

document.querySelectorAll('#levels_dropdown > .option.selected').forEach((el) => {
levels.push(el.getAttribute("data-value") as string)
})

document.querySelectorAll('#classes_dropdown > .option.selected').forEach((el) => {
classes.push(el.getAttribute("data-value") as string)
})

const language = document.querySelector('#languages_dropdown> .option.selected')!.getAttribute('data-value') as string

const content = DOMPurify.sanitize(window.ckEditor.getData());

function get_formatted_content(content: string, levels: string[], language: string) {
const parser = new DOMParser();
const html = parser.parseFromString(content, 'text/html');
const minLevel = Math.min(...levels.map((el) => Number(el)));
Expand Down Expand Up @@ -268,6 +251,30 @@ function update_db_adventure(adventure_id: string) {
}
// We have to replace <br> for newlines, because the serializer swithces them around
const formatted_content = html.getElementsByTagName('body')[0].outerHTML.replace(/<br>/g, '\n');
return formatted_content
}

function update_db_adventure(adventure_id: string) {
// Todo TB: It would be nice if we improve this with the formToJSON() function once #3077 is merged
const adventure_name = $('#custom_adventure_name').val();
let classes: string[] = [];
let levels: string[] = []

document.querySelectorAll('#levels_dropdown > .option.selected').forEach((el) => {
levels.push(el.getAttribute("data-value") as string)
})

document.querySelectorAll('#classes_dropdown > .option.selected').forEach((el) => {
classes.push(el.getAttribute("data-value") as string)
})

const language = document.querySelector('#languages_dropdown> .option.selected')!.getAttribute('data-value') as string

const content = DOMPurify.sanitize(window.ckEditor.getData());
const solutionExampleCode = DOMPurify.sanitize(window.ckSolutionEditor.getData());

const formatted_content = get_formatted_content(content, levels, language);
const formatted_solution_code = get_formatted_content(solutionExampleCode, levels, language);
const agree_public = $('#agree_public').prop('checked');

$.ajax({
Expand All @@ -278,6 +285,7 @@ function update_db_adventure(adventure_id: string) {
name: adventure_name,
content: content,
formatted_content: formatted_content,
formatted_solution_code: formatted_solution_code,
public: agree_public,
language,
classes,
Expand Down
Loading

0 comments on commit bdbdfac

Please sign in to comment.