Skip to content

Commit

Permalink
draft for playing fill in the blank steps
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Ceolin committed Aug 16, 2024
1 parent 33fae28 commit fb0dd1f
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 1 deletion.
29 changes: 29 additions & 0 deletions lib/content/course_live/components/fill_options.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule ZoonkWeb.Components.Content.FillOptions do
@moduledoc false
use ZoonkWeb, :html

attr :options, :list, required: true
attr :selected_segments, :list, required: true

def fill_options(assigns) do

Check warning on line 8 in lib/content/course_live/components/fill_options.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.

Check warning on line 8 in lib/content/course_live/components/fill_options.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.
~H"""
<div class="mt-8 flex flex-wrap gap-2">
<button
:for={option <- @options}
disabled={disabled?(option, @selected_segments)}
type="button"
phx-click="select-fill-option"
phx-value-option-id={option.id}
class={[
"rounded-lg bg-white p-4 text-slate-700 shadow-md hover:bg-slate-100 focus:outline-none focus:ring-2 focus:ring-slate-400",
disabled?(option, @selected_segments) && "cursor-not-allowed opacity-50"
]}
>
<%= option.title %>
</button>
</div>
"""
end

def disabled?(option, selected_segments), do: Enum.member?(selected_segments, option)

Check warning on line 28 in lib/content/course_live/components/fill_options.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.

Check warning on line 28 in lib/content/course_live/components/fill_options.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.
end
40 changes: 40 additions & 0 deletions lib/content/course_live/components/fill_step.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule ZoonkWeb.Components.Content.FillStep do
@moduledoc false
use ZoonkWeb, :html

attr :segments, :list, required: true
attr :selected_segments, :list, required: true

def fill_step(assigns) do

Check warning on line 8 in lib/content/course_live/components/fill_step.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.

Check warning on line 8 in lib/content/course_live/components/fill_step.ex

View workflow job for this annotation

GitHub Actions / test

Functions should have a @SPEC type specification.
~H"""
<div id="fill-step" class="flex flex-wrap items-center gap-2">
<button
:for={{segment, index} <- Enum.with_index(get_segments(@segments, @selected_segments))}
type="button"
disabled={!selectable?(@selected_segments, index)}
phx-click="remove-segment"
phx-value-index={index}
class={["p-2", selectable?(@selected_segments, index) && "semibold rounded-md bg-slate-50"]}
>
<%= segment %>
</button>
</div>
"""
end

defp get_segments(segments, selected) do
segments
|> Enum.with_index()
|> Enum.map(fn {segment, idx} ->
get_segment(segment, idx, selected)
end)
end

defp get_segment(nil, idx, selected), do: get_segment(Enum.at(selected, idx))
defp get_segment(segment, _idx, _selected), do: segment

defp get_segment(nil), do: "_ _ _ _ _ _ _ _"
defp get_segment(option), do: option.title

defp selectable?(segments, index), do: !is_nil(Enum.at(segments, index))
end
46 changes: 46 additions & 0 deletions lib/content/course_live/lesson_play.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule ZoonkWeb.Live.LessonPlay do
use ZoonkWeb, :live_view

import Zoonk.Shared.Utilities, only: [boolean_to_integer: 1]
import ZoonkWeb.Components.Content.FillOptions
import ZoonkWeb.Components.Content.FillStep
import ZoonkWeb.Components.Content.LessonStep

alias Zoonk.Accounts.User
Expand All @@ -28,11 +30,33 @@ defmodule ZoonkWeb.Live.LessonPlay do
|> assign(:options, shuffle_options(current_step))
|> assign(:lesson_start, DateTime.utc_now())
|> assign(:step_start, DateTime.utc_now())
|> assign(:selected_segments, List.duplicate(nil, segment_count(current_step.segments)))

{:ok, socket}
end

@impl Phoenix.LiveView
def handle_event("select-fill-option", %{"option-id" => option_id}, socket) do
%{current_step: step, selected_segments: selected_segments} = socket.assigns
option = get_option(step.options, String.to_integer(option_id))
segment_index = next_segment_index(step.segments, selected_segments)

socket =
assign(socket, :selected_segments, List.replace_at(selected_segments, segment_index, option))

{:noreply, socket}
end

def handle_event("remove-segment", %{"index" => index}, socket) do
%{selected_segments: selected_segments} = socket.assigns
segment_index = String.to_integer(index)

socket =
assign(socket, :selected_segments, List.replace_at(selected_segments, segment_index, nil))

{:noreply, socket}
end

def handle_event("next", %{"selected_option" => selected_option}, socket) when is_nil(socket.assigns.selected_option) do
%{current_step: step} = socket.assigns

Expand Down Expand Up @@ -116,4 +140,26 @@ defmodule ZoonkWeb.Live.LessonPlay do

defp maybe_play_sound_effect(%{assigns: %User{sound_effects?: false}} = socket, _correct?), do: socket
defp maybe_play_sound_effect(socket, correct?), do: push_event(socket, "option-selected", %{isCorrect: correct?})

defp segment_count(nil), do: 0
defp segment_count(segments), do: length(segments)

# Find the index from a segment not selected by the user
# We know a segment can be selected if it's nil.
# For example: ["this", nil, "a", nil]
# Users can select segments at index 1 and 3.
# If they haven't selected an option, then we can get the first nil segment.
# However, if they have selected an option, then we need to get the next nil segment.
# This is why we need to check if the same index at selected_segments is also nil.
defp next_segment_index(segments, selected_segments) do
segments
|> Enum.with_index()
|> Enum.find(fn {segment, index} ->
is_nil(segment) && is_nil(Enum.at(selected_segments, index))
end)
|> case do
{_, index} -> index
nil -> Enum.find_index(segments, &is_nil/1)
end
end
end
4 changes: 3 additions & 1 deletion lib/content/course_live/lesson_play.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
</header>

<article class="mx-auto flex max-w-3xl flex-col items-center overflow-y-auto p-8 before:content-[''] before:flex-1 after:content-[''] after:flex-1 sm:px-16">
<.lesson_step step={@current_step} selected={@selected_option} />
<.lesson_step :if={@current_step.kind != :fill} step={@current_step} selected={@selected_option} />
<.fill_step :if={@current_step.kind == :fill} segments={@current_step.segments} selected_segments={@selected_segments} />
<.fill_options :if={@current_step.kind == :fill} options={@current_step.options} selected_segments={@selected_segments} />

<section :if={@current_step.suggested_courses != []} class="grid grid-cols-2 gap-2 pt-8">
<.link
Expand Down

0 comments on commit fb0dd1f

Please sign in to comment.