Allow the quiz-builder picker to expand multiple topics at once
Sources could already draw from any number of topics, but the picker UI only ever showed one topic's questions at a time, forcing a teacher to lose their place switching between topics while assembling a quiz.
This commit is contained in:
@@ -175,34 +175,74 @@ let update (token: string option) (msg: Msg) (model: Model) : Model * Cmd<Msg> =
|
||||
{ model with QuizForm = { model.QuizForm with ShuffleQuestions = value } }, Cmd.none
|
||||
| SetQuizShuffleAnswers value ->
|
||||
{ model with QuizForm = { model.QuizForm with ShuffleAnswers = value } }, Cmd.none
|
||||
| SelectPickerTopic topicId ->
|
||||
let cmd =
|
||||
Cmd.OfAsync.either
|
||||
(Api.listQuestionsInTopic token)
|
||||
topicId
|
||||
(function
|
||||
| Ok questions -> PickerQuestionsLoaded questions
|
||||
| Error err -> PickerQuestionsLoadFailed err)
|
||||
(fun ex -> PickerQuestionsLoadFailed ex.Message)
|
||||
| ToggleTopicInPicker topicId ->
|
||||
let form = model.QuizForm
|
||||
|
||||
let existingPoolCount =
|
||||
model.QuizForm.Sources
|
||||
|> List.tryPick (function
|
||||
| PoolDraft(tid, count) when tid = topicId -> Some count
|
||||
| _ -> None)
|
||||
if form.PickerTopicIds.Contains topicId then
|
||||
// Deselecting a topic drops it from the quiz entirely — its
|
||||
// fixed picks and pool rule no longer apply.
|
||||
let belongsToTopic qid =
|
||||
form.PickerQuestions
|
||||
|> List.tryFind (fun q -> q.Id = qid)
|
||||
|> Option.map (fun q -> q.TopicId = topicId)
|
||||
|> Option.defaultValue false
|
||||
|
||||
let sourcesWithoutTopic =
|
||||
form.Sources
|
||||
|> List.filter (function
|
||||
| PoolDraft(tid, _) -> tid <> topicId
|
||||
| FixedDraft qid -> not (belongsToTopic qid))
|
||||
|
||||
{ model with
|
||||
QuizForm =
|
||||
{ form with
|
||||
PickerTopicIds = form.PickerTopicIds.Remove topicId
|
||||
PickerQuestions = form.PickerQuestions |> List.filter (fun q -> q.TopicId <> topicId)
|
||||
PoolCountTexts = form.PoolCountTexts.Remove topicId
|
||||
Sources = sourcesWithoutTopic } },
|
||||
Cmd.none
|
||||
else
|
||||
let cmd =
|
||||
Cmd.OfAsync.either
|
||||
(Api.listQuestionsInTopic token)
|
||||
topicId
|
||||
(function
|
||||
| Ok questions -> PickerQuestionsLoaded(topicId, questions)
|
||||
| Error err -> PickerQuestionsLoadFailed(topicId, err))
|
||||
(fun ex -> PickerQuestionsLoadFailed(topicId, ex.Message))
|
||||
|
||||
let existingPoolCount =
|
||||
form.Sources
|
||||
|> List.tryPick (function
|
||||
| PoolDraft(tid, count) when tid = topicId -> Some count
|
||||
| _ -> None)
|
||||
|
||||
let poolCountTexts =
|
||||
match existingPoolCount with
|
||||
| Some count -> form.PoolCountTexts |> Map.add topicId (string count)
|
||||
| None -> form.PoolCountTexts
|
||||
|
||||
{ model with
|
||||
QuizForm =
|
||||
{ form with
|
||||
PickerTopicIds = form.PickerTopicIds.Add topicId
|
||||
PickerLoadingTopics = form.PickerLoadingTopics.Add topicId
|
||||
PoolCountTexts = poolCountTexts } },
|
||||
cmd
|
||||
| PickerQuestionsLoaded(topicId, questions) ->
|
||||
{ model with
|
||||
QuizForm =
|
||||
{ model.QuizForm with
|
||||
PickerTopicId = Some topicId
|
||||
PickerLoading = true
|
||||
PickerQuestions = []
|
||||
PoolCountText = existingPoolCount |> Option.map string |> Option.defaultValue "" } },
|
||||
cmd
|
||||
| PickerQuestionsLoaded questions ->
|
||||
{ model with QuizForm = { model.QuizForm with PickerQuestions = questions; PickerLoading = false } }, Cmd.none
|
||||
| PickerQuestionsLoadFailed err ->
|
||||
{ model with QuizForm = { model.QuizForm with PickerLoading = false; Error = Some err } }, Cmd.none
|
||||
PickerQuestions = model.QuizForm.PickerQuestions @ questions
|
||||
PickerLoadingTopics = model.QuizForm.PickerLoadingTopics.Remove topicId } },
|
||||
Cmd.none
|
||||
| PickerQuestionsLoadFailed(topicId, err) ->
|
||||
{ model with
|
||||
QuizForm =
|
||||
{ model.QuizForm with
|
||||
PickerLoadingTopics = model.QuizForm.PickerLoadingTopics.Remove topicId
|
||||
Error = Some err } },
|
||||
Cmd.none
|
||||
| ToggleQuestionPick questionId ->
|
||||
let form = model.QuizForm
|
||||
|
||||
@@ -216,7 +256,7 @@ let update (token: string option) (msg: Msg) (model: Model) : Model * Cmd<Msg> =
|
||||
form.Sources @ [ FixedDraft questionId ]
|
||||
|
||||
{ model with QuizForm = { form with Sources = next } }, Cmd.none
|
||||
| SelectAllInTopic ->
|
||||
| SelectAllInTopic topicId ->
|
||||
let form = model.QuizForm
|
||||
|
||||
let alreadySelected =
|
||||
@@ -228,31 +268,32 @@ let update (token: string option) (msg: Msg) (model: Model) : Model * Cmd<Msg> =
|
||||
|
||||
let toAdd =
|
||||
form.PickerQuestions
|
||||
|> List.filter (fun q -> q.TopicId = topicId)
|
||||
|> List.map (fun q -> q.Id)
|
||||
|> List.filter (alreadySelected.Contains >> not)
|
||||
|> List.map FixedDraft
|
||||
|
||||
{ model with QuizForm = { form with Sources = form.Sources @ toAdd } }, Cmd.none
|
||||
| SetPoolCountText text -> { model with QuizForm = { model.QuizForm with PoolCountText = text } }, Cmd.none
|
||||
| AddRandomPool ->
|
||||
| SetPoolCountText(topicId, text) ->
|
||||
{ model with QuizForm = { model.QuizForm with PoolCountTexts = model.QuizForm.PoolCountTexts |> Map.add topicId text } },
|
||||
Cmd.none
|
||||
| AddRandomPool topicId ->
|
||||
let form = model.QuizForm
|
||||
let text = form.PoolCountTexts |> Map.tryFind topicId |> Option.defaultValue ""
|
||||
|
||||
match form.PickerTopicId with
|
||||
| None -> model, Cmd.none
|
||||
| Some topicId ->
|
||||
match System.Int32.TryParse form.PoolCountText with
|
||||
| true, count when count > 0 ->
|
||||
let withoutOldPool =
|
||||
form.Sources
|
||||
|> List.filter (function
|
||||
| PoolDraft(tid, _) -> tid <> topicId
|
||||
| _ -> true)
|
||||
match System.Int32.TryParse text with
|
||||
| true, count when count > 0 ->
|
||||
let withoutOldPool =
|
||||
form.Sources
|
||||
|> List.filter (function
|
||||
| PoolDraft(tid, _) -> tid <> topicId
|
||||
| _ -> true)
|
||||
|
||||
{ model with QuizForm = { form with Sources = withoutOldPool @ [ PoolDraft(topicId, count) ]; Error = None } },
|
||||
Cmd.none
|
||||
| _ ->
|
||||
{ model with QuizForm = { form with Error = Some "Количество случайных вопросов должно быть положительным числом" } },
|
||||
Cmd.none
|
||||
{ model with QuizForm = { form with Sources = withoutOldPool @ [ PoolDraft(topicId, count) ]; Error = None } },
|
||||
Cmd.none
|
||||
| _ ->
|
||||
{ model with QuizForm = { form with Error = Some "Количество случайных вопросов должно быть положительным числом" } },
|
||||
Cmd.none
|
||||
| RemoveRandomPool topicId ->
|
||||
let form = model.QuizForm
|
||||
|
||||
@@ -262,9 +303,7 @@ let update (token: string option) (msg: Msg) (model: Model) : Model * Cmd<Msg> =
|
||||
| PoolDraft(tid, _) -> tid <> topicId
|
||||
| _ -> true)
|
||||
|
||||
let poolCountText = if form.PickerTopicId = Some topicId then "" else form.PoolCountText
|
||||
|
||||
{ model with QuizForm = { form with Sources = next; PoolCountText = poolCountText } }, Cmd.none
|
||||
{ model with QuizForm = { form with Sources = next; PoolCountTexts = form.PoolCountTexts.Remove topicId } }, Cmd.none
|
||||
| SubmitQuizForm ->
|
||||
match buildQuizFields model.QuizForm with
|
||||
| Error err -> { model with QuizForm = { model.QuizForm with Error = Some err } }, Cmd.none
|
||||
|
||||
Reference in New Issue
Block a user