Skip to content

Commit

Permalink
Add a generic modal wrapper (#120)
Browse files Browse the repository at this point in the history
* Add generic debug dump component

This makes life easy when wiring things up

* Rename namespace for Context.Read.*

* Groundwork: add discuss component w sheafs

* Init Context.Components mod for common components

* Add skeletons for Sheaf Container

Container managers sheaf state within itself.
Should actually make the verse-matrix use this container, sometime.

* Minor changes, add some TODOs to share

explicit todos

Minor change

* Add live component EditableMarkDisplay

* Add UI improvements to EditableMarkDisplay

The edit button on quick draft should only be seen when the dropdown is
expanded and all the marks can be seen. The general pattern I'm trying
to follow is that buttons should only be shown when an action makes
sense. So here, if the user can't even see the marks currently
accumulating, then the user shouldn't have the chance to edit any marks.

Notice that the EditableMarkDisplay is a live_component that is a child
of a function component (collapsible_marks_display). This is so that it can keep its own state and
do it's own event-handling. I'm not sure if this is pattern-breaking.
A reference to the actual parent live_component that calls collapsible_marks_display
will always be there in case there's a need for the parent to handle
any particular events. So comms b/w live_components is still going to be
via phx-target values.

* Change <input/> -> <textarea/> fr quick_draft_form

* Add tailwind config for custom svg icons

not sure how the scaling for this works, seems like i can't just put in
the class="h-5 w-5" to do the scaling

* Let custom svg be scalable

* Subtly shift the ping indicator to control panel

* Marks ordering in Sheaf and L-Trees  (#114)

* do sheafs grow on trees

* sangh session dealing with lineage

* Solve initial order problem

* fix: ensure non-null id used @ EditableMarkDisplay

---------

Co-authored-by: ks0m1c_dharma <[email protected]>

* Trigger Diff

* TOMBSTONING

* Fix bugs and revert ui for read_mode:delete marks

1.  JIT generate id for marks yet to be inserted to the db: instead of
    using a temp id, I'm actually assigning a JIT-generated id for a
    mark even if that isn't inserted into the db yet. this has two
    benefits: div ids have meaningful static ids (e.g.
    mark-container-\<\> mark.id) regardless of whether that mark has
    been inserted into the db or not. While doing this, the changeset
    for marks has been udpated to add id as well. This fixes a bunch of
    id-based errors as well.

2.  while doing 1, it came to light that the bindHoverune handlers were
    not handling the draft mark update properly (i.e. not using the
    already existing update function in mark) . it's changed now,
    example of the diff:

    ``` diff
      @impl true
      def handle_event(
            "bindHoveRune",
            %{"binding" => bind = %{"verse_id" => verse_id}},
            %{
              assigns: %{
                kv_verses: verses,
                marks: [%Mark{state: :draft, verse_id: curr_verse_id} = d_mark | marks]
              }
            } = socket
          )
          when is_binary(curr_verse_id) and verse_id != curr_verse_id do
        # binding here blocks the stream from appending to quote
        bind = Draft.bind_node(bind)

        bound_verses =
          verses
           |> then(&put_in(&1[verse_id].binding, bind))
           |> then(&put_in(&1[curr_verse_id].binding, nil))

    +    updated_draft_mark = d_mark |> Mark.update_mark(%{binding: bind, verse_id: verse_id})
    +
         {:noreply,
          socket
          |> mutate_verses(curr_verse_id, bound_verses)
          |> mutate_verses(verse_id, bound_verses)
    -     |> assign(:marks, [%{d_mark | binding: bind, verse_id: verse_id} | marks])}
    +     |> assign(:marks, [updated_draft_mark | marks])}
       end
    ```

3.  add ui state structs: all of the relevant ui states have structs
    that are intended to be static interfaces. this felt better than
    directly keeping state within various sockets and is done with a
    view to remove live components and change them to function
    components wherever possible.

4.  bugfix for `order()`:

    - it had an off-by-one-error
    - shifted it to Vyasa.Sangh.Mark
    - also note that the order shall be 1-indexed instead of 0-indexed
      for human-friendliness

5.  minor fixes:

    1.  fix update<sub>mark</sub> default argument

        ``` diff
        -def update_mark(%Mark{} = draft_mark, opts \\ []) do
        +def update_mark(%Mark{} = draft_mark, opts \\ %{}) do
        ```

1.  In this commit, the marks<sub>ui</sub> is NOT kept by the read
    context module, that's why when I update things by create mark or
    delete mark, the ui state is not preserved and initial, default
    state is seen. This was intentionally done in this commit because
    I'm having problems with duplicate ids for verses for some reason,
    I'll show it in the next commit, so that the problem can be clearly
    seen.

* [BUGGY] keep marks_ui within ReadContext

This is buggy, not sure why there's a duplicate yet.  will come back to
it after lunch

Replication Steps:
1. delete all marks and see there's no error
2. create first mark ==> no error
3. create second mark ==> crashes due to duplicate verse id

Crash dump:

[debug] Replied in 13ms
[error] GenServer #PID<0.8416.0> terminating
** (RuntimeError) found duplicate ID "verse-7d9d588a-34fc-4f19-b974-fc32b49b6a61" for component VyasaWeb.Context.Read.VerseMatrix when rendering template
    (phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/diff.ex:634: anonymous fn/5 in Phoenix.LiveView.Diff.render_pending_components/6
    (elixir 1.17.2) lib/enum.ex:2531: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/diff.ex:631: anonymous fn/4 in Phoenix.LiveView.Diff.render_pending_components/6
    (stdlib 5.2.3) maps.erl:416: :maps.fold_1/4
    (phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/diff.ex:625: Phoenix.LiveView.Diff.render_pending_components/6
    (phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/diff.ex:217: Phoenix.LiveView.Diff.write_component/4
    (phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/channel.ex:662: Phoenix.LiveView.Channel.component_handle/4
    (stdlib 5.2.3) gen_server.erl:1095: :gen_server.try_handle_info/3
    (stdlib 5.2.3) gen_server.erl:1183: :gen_server.handle_msg/6
    (stdlib 5.2.3) proc_lib.erl:251: :proc_lib.wake_up/3
Last message: %Phoenix.Socket.Message{topic: "lv:phx-F_1MH_6F0ciqVqwi", event: "event", payload: %{"cid" => 3, "event" => "createMark", "type" => "form", "value" => "body=how+about+now%2C+any+duplicate%3F"}, ref: "23", join_ref: "4"}
State: %{socket: #Phoenix.LiveView.Socket<id: "phx-F_1MH_6F0ciqVqwi", endpoint: VyasaWeb.Endpoint, view: VyasaWeb.ModeLive.Mediator, parent_pid: nil, root_pid: #PID<0.8416.0>, router: VyasaWeb.Router, assigns: %{mode: %VyasaWeb.ModeLive.UserMode{mode: "read", default_ui_state: %VyasaWeb.ModeLive.UiState{show_media_bridge?: true, show_action_bar?: true}, mode_icon_name: "hero-book-open", quick_actions: [:mark_quote, :bookmark], control_panel_modes: ["discuss"], mode_actions: [:mark_quote, :bookmark], action_bar_actions: [:nav_back, :nav_fwd], action_bar_info_types: nil, action_bar_component: VyasaWeb.MediaLive.MediaBridge, action_bar_component_selector: "media-bridge", control_panel_component: VyasaWeb.ControlPanel, control_panel_component_selector: "control-panel", mode_context_component: VyasaWeb.Context.Read, mode_context_component_selector: "read"}, session: %VyasaWeb.Session{name: "ritesh kumar", id: "IhrQV0ypqg8bHakbvb2bymNF", active: true, sangh: %Vyasa.Sangh.Session{__meta__: #Ecto.Schema.Metadata<:loaded, "sessions">, id: "164eb05d-221a-4939-b180-8394e1a5515f", sheafs: #Ecto.Association.NotLoaded<association :sheafs is not loaded>, inserted_at: ~U[2024-09-27 00:51:48Z], updated_at: ~U[2024-09-27 00:51:48Z]}, last_opened: "2024-09-23T11:03:32.022987Z"}, __changed__: %{}, flash: %{}, locale: "en-GB", url_params: %{"chap_no" => "1", "source_title" => "hanuman_chalisa"}, live_action: :show_verses, ui_state: %VyasaWeb.ModeLive.UiState{show_media_bridge?: true, show_action_bar?: true}, device_type: :desktop, tz: %{timezone: "Asia/Singapore", timezone_offset: 480}}, transport_pid: #PID<0.8403.0>, ...>, components: {%{15 => {VyasaWeb.Context.Read.VerseMatrix, "verse-aa3ad88b-0de1-443e-b471-c08a44e67c61", %{id: "verse-aa3ad88b-0de1-443e-b471-c08a44e67c61", edge: [%{title: "1.10", field: [:body], verseup: {:big, "dn"}}, %{node: %Vyasa.Written.Translation{__meta__: #Ecto.Schema.Metadata<:loaded, "translations">, id: "651676b6-ffc3-41c1-b525-f376989f639e", lang: "en", type: "verses", target: %Vyasa.Written.Translation.Target{id: "dd20a94f-8f34-49f8-8da8-4321cfecf172", title: nil, title_translit: nil, body: "प्रभु चरित्र सुनिबे को रसिया ।\n\nराम लखन सीता मन बसिया ॥८॥", body_meant: nil, body_translit: "prabhu caritra sunibe ko rasiyā .\n\nrāma lakhana sītā mana basiyā ..8..", body_translit_meant: "You feel extremely delighted in listening to Lord Rama’s doings and conduct. Lord Rama, Mother Sita, and Lord Laxmana dwell forever in your heart."}, verse_id: "aa3ad88b-0de1-443e-b471-c08a44e67c61", verse: #Ecto.Association.NotLoaded<association :verse is not loaded>, chapter_no: nil, chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>, source_id: "63918acd-77a9-451a-b8d1-8d5b73208eae", source: #Ecto.Association.NotLoaded<association :source is not loaded>}, field: [:target, :body_translit], verseup: :mid}, %{node: %Vyasa.Written.Translation{__meta__: #Ecto.Schema.Metadata<:loaded, "translations">, id: "651676b6-ffc3-41c1-b525-f376989f639e", lang: "en", type: "verses", target: %Vyasa.Written.Translation.Target{id: "dd20a94f-8f34-49f8-8da8-4321cfecf172", title: nil, title_translit: nil, body: "प्रभु चरित्र सुनिबे को रसिया ।\n\nराम लखन सीता मन बसिया ॥८॥", body_meant: nil, body_translit: "prabhu caritra sunibe ko rasiyā .\n\nrāma lakhana sītā mana basiyā ..8..", body_translit_meant: "You feel extremely delighted in listening to Lord Rama’s doings and conduct. Lord Rama, Mother Sita, and Lord Laxmana dwell forever in your heart."}, verse_id: "aa3ad88b-0de1-443e-b471-c08a44e67c61", verse: #Ecto.Association.NotLoaded<association :verse is not loaded>, chapter_no: nil, chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>, source_id: "63918acd-77a9-451a-b8d1-8d5b73208eae", source: #Ecto.Association.NotLoaded<association :source is not loaded>}, field: [:target, :body_translit_meant], verseup: :mid}, %{node: %Vyasa.Written.Translation{__meta__: #Ecto.Schema.Metadata<:loaded, "translations">, id: "651676b6-ffc3-41c1-b525-f376989f639e", lang: "en", type: "verses", target: %Vyasa.Written.Translation.Target{id: "dd20a94f-8f34-49f8-8da8-4321cfecf172", title: nil, title_translit: nil, body: "प्रभु चरित्र सुनिबे को रसिया ।\n\nराम लखन सीता मन बसिया ॥८॥", body_meant: nil, body_translit: "prabhu caritra sunibe ko rasiyā .\n\nrāma lakhana sītā mana basiyā ..8..", body_translit_meant: "You feel extremely delighted in listening to Lord Rama’s doings and conduct. Lord Rama, Mother Sita, and Lord Laxmana dwell forever in your heart."}, verse_id: "aa3ad88b-0de1-443e-b471-c08a44e67c61", verse: #Ecto.Association.NotLoaded<association :verse is not loaded>, chapter_no: nil, chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>, source_id: "63918acd-77a9-451a-b8d1-8d5b73208eae", source: #Ecto.Association.NotLoaded<association :source is not loaded>}, field: [:target, :body], verseup: :mid}], __changed__: %{}, flash: %{}, marks: [%Vyasa.Sangh.Mark{__meta__: #Ecto.Schema.Metadata<:loaded, "marks">, id: "5dfdb167-6c57-46dd-a0c4-10b52cb03a85", body: "wonderful", order: 2, state: :live, verse_id: nil, sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa", sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>, binding_id: "001da594-3808-46cf-9b9c-5dfb3559780e", binding: %Vyasa.Adapters.Binding{__meta__: #Ecto.Schema.Metadata<:loaded, "bindings">, id: "001da594-3808-46cf-9b9c-5dfb3559780e", w_type: nil, field_key: ["target", "body_translit_meant"], node_id: nil, verse_id: nil, verse: #Ecto.Association.NotLoaded<association :verse is not loaded>, chapter_no: nil, chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>, source_id: nil, source: #Ecto.Association.NotLoaded<association :source is not loaded>, translation_id: "97a7a662-a91b-486f-93e8-7ae932d01a1a", translation: #Ecto.Association.NotLoaded<association :translation is not loaded>, sheaf_id: nil, sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>, window: nil}, inserted_at: ~U[2024-10-11 04:31:50Z], updated_at: ~U[2024-10-11 04:31:50Z]}, %Vyasa.Sangh.Mark{__meta__: #Ecto.Schema.Metadata<:loaded, "marks">, id: "273a093d-4f90-40a4-b19a-2f8f7825d201", body: "truncated utc datetime", order: 1, state: :live, verse_id: nil, sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa", sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>, binding_id: "ef39315b-530c-4841-ba32-86773f451727", binding: %Vyasa.Adapt (truncated)
[debug] MOUNT VyasaWeb.ModeLive.Mediator
  Parameters: %{"chap_no" => "1", "source_title" => "hanuman_chalisa"}
  Session: %{"_csrf_token" => "DJ-RIoXwEzHW82rbb21le_8k"}
[debug] QUERY OK source="sessions" db=3.5ms idle=1690.4ms
SELECT s0."id", s0."inserted_at", s0."updated_at" FROM "sessions" AS s0 WHERE (s0."id" = $1) ["164eb05d-221a-4939-b180-8394e1a5515f"]
↳

* Fix duplicate id issue on verse matrix

* Standardise use of @event_target, cleanups

When using @event_target, the intent is that phx-target values
will be passed around. This must be agnostic to the 2 main ways of
supplying that argument, as a HTML selector, or as a module name (or @Myself).
Therefore, we should discourage ourselves from doing format-strings at
the point of the @event_target being supplied as an argument, meaning:

good, because agnostic:
phx-target={@event_target}

bad, because hardcoded:
phx-target={"#" <> @event_target}
* in this case, the event trigger will throw a dom error.

* [Attempt]: Sanitise, defrag orders in marks-list

If this is an anti-pattern, then this commit should be reverted.

* UI fixes: hide buttons on marks when !is_editable?

* Add in stub event handlers to prevent crashes

Temporary stuff, will be addressed in immediate-next PR

* Minor changes

* Toggle edit state for marks content

* Partial implementation of edit marks

the form stuff doesn't exist so it's not wired up yet. To be frank,
im' not sure how to set up a form without it needing to hold state.

* [BUGGY] Wire up form update for edit

this does the following:
1. wires up the form like how we usedd it in createMark
2. actually captures the event properly within the event handler,
everything looks good at that point, even the edit_mark_in_marks
function on inspection looks good
3. the marks is getting assigned to properly because the newly edited
mark content is seen after doing a dom refresh
4. HOWEVER, the print statement at the sheaf_update function DOES NOT
show the correct, updated marks in the sheaf

here's a dump for it for reference

[debug] HANDLE EVENT "editMarkContent" in VyasaWeb.ModeLive.Mediator
  Component: VyasaWeb.Context.Read
  Parameters: %{"mark_id" => "02218551-591f-4450-abbd-a7b7d76fd48e"}
[debug] Replied in 2ms
[debug] HANDLE EVENT "editMarkContent" in VyasaWeb.ModeLive.Mediator
  Component: VyasaWeb.Context.Read
  Parameters: %{"mark_body" => "  testing\n\n\nwhat hte heck", "mark_id" => "02218551-591f-4450-abbd-a7b7d76fd48e"}
>>> UPDATE SHEAF -- SHOULD BE WRITING TO DB NOW: %Vyasa.Sangh.Sheaf{
  __meta__: #Ecto.Schema.Metadata<:loaded, "sheafs">,
  id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
  body: nil,
  active: true,
  path: %EctoLtree.LabelTree{labels: ["7f4ab16b"]},
  signature: nil,
  traits: ["draft"],
  child_count: 0,
  session_id: "164eb05d-221a-4939-b180-8394e1a5515f",
  session: #Ecto.Association.NotLoaded<association :session is not loaded>,
  parent_id: nil,
  parent: #Ecto.Association.NotLoaded<association :parent is not loaded>,
  marks: [
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "555eb625-e392-496e-9831-4f4c130be6fd",
      body: "wonderful stuff",
      order: 5,
      state: :live,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "072124d0-5e27-4601-832f-35eaa6426879",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "072124d0-5e27-4601-832f-35eaa6426879",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "97a7a662-a91b-486f-93e8-7ae932d01a1a",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-12 08:46:54Z],
      updated_at: ~U[2024-10-12 08:46:54Z]
    },
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "b22a5661-8268-4875-be26-a12256414136",
      body: nil,
      order: 5,
      state: :draft,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "90e18044-73b2-4ede-a149-a381de893b9f",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "90e18044-73b2-4ede-a149-a381de893b9f",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "fc80709a-39dc-4b9c-95c3-1d64ce818302",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-12 09:52:40Z],
      updated_at: ~U[2024-10-12 09:52:40Z]
    },
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "8af43833-575c-4eb8-83b1-eb4e58509e4c",
      body: nil,
      order: 4,
      state: :draft,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "23b70937-ed81-47fe-a560-ee14287bff7d",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "23b70937-ed81-47fe-a560-ee14287bff7d",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "97a7a662-a91b-486f-93e8-7ae932d01a1a",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-12 09:46:13Z],
      updated_at: ~U[2024-10-12 09:46:13Z]
    },
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "c7ad47e6-629d-4382-936d-64d757772c4e",
      body: nil,
      order: 3,
      state: :draft,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "d5758230-7966-4f9b-8c16-ae13998cd71b",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "d5758230-7966-4f9b-8c16-ae13998cd71b",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "fc80709a-39dc-4b9c-95c3-1d64ce818302",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-12 09:44:09Z],
      updated_at: ~U[2024-10-12 09:44:09Z]
    },
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "02218551-591f-4450-abbd-a7b7d76fd48e",
      body: "testing",
      order: 2,
      state: :live,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "19ffdc82-a5d8-49c3-8b01-1559736dad80",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "19ffdc82-a5d8-49c3-8b01-1559736dad80",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "fc80709a-39dc-4b9c-95c3-1d64ce818302",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-12 09:43:19Z],
      updated_at: ~U[2024-10-12 09:43:19Z]
    },
    %Vyasa.Sangh.Mark{
      __meta__: #Ecto.Schema.Metadata<:loaded, "marks">,
      id: "90c6036a-9bd1-4b65-82d5-f121ec4d7867",
      body: nil,
      order: 2,
      state: :draft,
      verse_id: nil,
      sheaf_id: "7f4ab16b-0caf-4009-ba2a-3ddded81d9aa",
      sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
      binding_id: "b92f990d-99a2-4f04-a2d4-d1135021ee8c",
      binding: %Vyasa.Adapters.Binding{
        __meta__: #Ecto.Schema.Metadata<:loaded, "bindings">,
        id: "b92f990d-99a2-4f04-a2d4-d1135021ee8c",
        w_type: nil,
        field_key: ["target", "body_translit_meant"],
        node_id: nil,
        verse_id: nil,
        verse: #Ecto.Association.NotLoaded<association :verse is not loaded>,
        chapter_no: nil,
        chapter: #Ecto.Association.NotLoaded<association :chapter is not loaded>,
        source_id: nil,
        source: #Ecto.Association.NotLoaded<association :source is not loaded>,
        translation_id: "97a7a662-a91b-486f-93e8-7ae932d01a1a",
        translation: #Ecto.Association.NotLoaded<association :translation is not loaded>,
        sheaf_id: nil,
        sheaf: #Ecto.Association.NotLoaded<association :sheaf is not loaded>,
        window: nil
      },
      inserted_at: ~U[2024-10-11 09:03:00Z],
      updated_at: ~U[2024-10-11 09:03:00Z]
    }
  ],
  inserted_at: ~N[2024-09-27 00:51:48],
  updated_at: ~N[2024-09-27 00:51:48]
}
[debug] Replied in 81ms
[debug] HANDLE EVENT "verses::focus_toggle_on_quick_mark_drafting" in VyasaWeb.ModeLive.Mediator
  Component: VyasaWeb.Context.Read
  Parameters: %{"is_focusing?" => false, "value" => ""}
[debug] Replied in 129µs
iex([email protected])17>

* marks order force

* old man mark is not feeling so good

* ReWrite how marks get initialised @ :show_verse (#119)

* Minor change in fn signature

* Add init_draft_reflector()

* Use custom generic_modal_wrapper

This one has more specific callback attributes and tailwind classes can
be injected to it.

---------

Co-authored-by: ks0m1c_dharma <[email protected]>
  • Loading branch information
rtshkmr and ks0m1c_dharma authored Oct 17, 2024
1 parent 3eccd83 commit a131bde
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 15 deletions.
25 changes: 25 additions & 0 deletions lib/vyasa_web/components/contexts/components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,29 @@ defmodule VyasaWeb.Context.Components do
</span>
"""
end

def sheaf_creator_modal(assigns) do
~H"""
<.debug_dump label="Sheaf Creator Dump" show={@marks_ui.show_sheaf_modal?} class="relative" />
<.generic_modal_wrapper
id="my-modal"
show={@marks_ui.show_sheaf_modal?}
on_cancel_callback={JS.push("toggle_show_sheaf_modal?", target: "#content-display")}
on_click_away_callback={JS.push("toggle_show_sheaf_modal?", target: "#content-display")}
window_keydown_callback={JS.push("toggle_show_sheaf_modal?", target: "#content-display")}
container_class="rounded-lg shadow-lg overflow-hidden"
background_class="bg-gray-800 bg-opacity-75 backdrop-blur-md"
dialog_class="rounded-lg shadow-xl p-6 w-3/4 h-3/4 max-w-lg max-h-screen mx-auto my-auto"
focus_wrap_class="flex flex-col items-center justify-center h-full"
inner_block_container_class="w-full p-4"
close_button_icon_class="text-red-500 hover:text-red-700"
>
<h2 class="text-2xl font-semibold text-gray-800">My Modal Title</h2>
<p class="mt-2 text-gray-600">
This is a description of what the modal is about. You can provide additional information here.
</p>
<.debug_dump assigns={assigns} class="mt-4" />
</.generic_modal_wrapper>
"""
end
end
47 changes: 39 additions & 8 deletions lib/vyasa_web/components/contexts/read.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule VyasaWeb.Context.Read do
alias Phoenix.LiveView.Socket
alias Vyasa.Sangh.{Mark, Sheaf}
alias VyasaWeb.OgImageController
import VyasaWeb.Context.Components

@impl true
def update(
Expand Down Expand Up @@ -116,7 +117,6 @@ defmodule VyasaWeb.Context.Read do
|> stream(:chapters, chapters |> Enum.sort_by(fn chap -> chap.no end))
else
[%Chapter{} = chapter | _] ->

socket
|> apply_action(:show_verses, params |> Map.put("chap_no", chapter.no))

Expand Down Expand Up @@ -224,6 +224,8 @@ defmodule VyasaWeb.Context.Read do
This reflector is hot-swappable to other sheafs if there's a need to switch what
sheaf to focus on and gather marks for.
TODO: add other params-based sheaf-setting
"""
def init_draft_reflector(
%Socket{
Expand Down Expand Up @@ -283,6 +285,37 @@ defmodule VyasaWeb.Context.Read do
|> cascade_stream_change()}
end

@impl true
def handle_event(
"toggle_show_sheaf_modal?",
_,
%Socket{
assigns:
%{
marks_ui: %MarksUiState{} = ui_state
} = _assigns
} = socket
) do
{
:noreply,
socket
|> assign(marks_ui: ui_state |> MarksUiState.toggle_show_sheaf_modal?())
|> cascade_stream_change()
}
end

@impl true
def handle_event(
"toggle_show_sheaf_modal?",
_,
%Socket{} = socket
) do
{
:noreply,
socket
}
end

@impl true
def handle_event(
"toggle_is_editing_mark_content?",
Expand Down Expand Up @@ -614,13 +647,11 @@ defmodule VyasaWeb.Context.Read do
<% end %>
<%= if @content_action == :show_verses do %>
<.debug_dump
:for={mark <- @marks}
label={Atom.to_string(mark.state) <> " Mark " <> Integer.to_string(mark.order) }
mark_state={mark.state}
mark_id={mark.id}
class="relative"
mark_order={mark.order}
<.debug_dump label="UI State Info" marks_ui={@marks_ui} class="relative w-screen" />
<.sheaf_creator_modal
id="sheaf-creator"
marks_ui={@marks_ui}
event_target="content-display"
/>
<.live_component
module={VyasaWeb.Context.Read.Verses}
Expand Down
6 changes: 3 additions & 3 deletions lib/vyasa_web/components/contexts/read/verse_matrix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ defmodule VyasaWeb.Context.Read.VerseMatrix do
<button
type="button"
phx-click={
JS.push("change_form_type",
value: %{type: if(@form_type == :mark, do: "sheaf", else: "mark")}
JS.push("toggle_show_sheaf_modal?",
value: %{}
)
}
phx-target={@myself}
phx-target={@event_target}
class="p-1 rounded-full text-gray-400 hover:text-brand transition-colors duration-200 ml-1"
>
<.icon
Expand Down
18 changes: 17 additions & 1 deletion lib/vyasa_web/components/contexts/ui_state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@ defmodule VyasaWeb.Context.Components.UiState.Marks do
alias Vyasa.Sangh.Mark
alias VyasaWeb.Context.Components.UiState.Mark, as: MarkUiState
alias VyasaWeb.Context.Components.UiState.Marks, as: MarksUiState
defstruct [:is_expanded_view?, :is_editable_marks?, :mark_id_to_ui]

defstruct [
:is_expanded_view?,
:show_sheaf_modal?,
:is_editable_marks?,
:mark_id_to_ui
]

@initial %{
is_expanded_view?: false,
show_sheaf_modal?: false,
is_editable_marks?: false,
mark_id_to_ui: %{}
}
Expand Down Expand Up @@ -44,6 +51,15 @@ defmodule VyasaWeb.Context.Components.UiState.Marks do
}
end

def toggle_show_sheaf_modal?(
%MarksUiState{
show_sheaf_modal?: curr
} = ui_state
) do
# show_modal("sheaf-creator")
%MarksUiState{ui_state | show_sheaf_modal?: !curr}
end

def toggle_is_editable(
%MarksUiState{
is_editable_marks?: curr
Expand Down
137 changes: 134 additions & 3 deletions lib/vyasa_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,137 @@ defmodule VyasaWeb.CoreComponents do
"""
end

attr(:id, :string, required: true)
attr(:show, :boolean, default: false)

attr(:on_cancel_callback, JS,
default: %JS{},
doc: "Defines a callback to invoke on cancellation / exit of the modal"
)

attr(:on_click_away_callback, :any,
default: %JS{},
doc: "Defines a callback to invoke on click away of dialog"
)

attr(:cancel_key, :string, default: "escape")

attr(:on_mount_callback, :any,
default: %JS{},
doc: "Defines a callback to invoke on mounting of the modal"
)

attr(:window_keydown_callback, :any,
default: %JS{},
doc: "Defines a callback to invoke on keypress @ the window level"
)

attr(:container_class, :string, default: "", doc: "inline style for the outermost container")
attr(:background_class, :string, default: "", doc: "inline style for the background / backdrop")
attr(:dialog_class, :string, default: "", doc: "inline style for the dialog container")
attr(:focus_wrap_class, :string, default: "", doc: "inline style for the focus wrap")

attr(:inner_block_container_class, :string,
default: "",
doc: "inline style for the container for the inner content slot"
)

attr(:close_button_icon, :string, default: "hero-x-mark-solid")
attr(:close_button_class, :string, default: "")
attr(:close_button_icon_class, :string, default: "")
attr(:focus_container_class, :string, default: "")

slot(:inner_block, required: true)

@doc """
A generic implementation of the modal wrapper. The attributes and slots have corresponding docstrings for reference.
In order to use this:
1) supply the show boolean properly, this controls hidden state
2) supply some callbacks:
Most of these callbacks target a particular event, the caveat is that if there are
multiple events that are expected to fire the same callback, then the current implementation
requires some duplication of the callbacks passed.
For example, if you pass in a callback for on_cancel_callback, you would probably
want to do the same for the on_click_away_callback. Currently this duplication
is required, in the future if there's no reason to target these events separately,
then the function component definition might be changed such that closing and click-away
callbacks refer to the same argument.
3) supply some class definitions to override the default ones provided in this function definition.
By inspecting the attributes corresponding to class definitions, we can also determine how this
modal-wrapper is structured (containers, children...)
"""
def generic_modal_wrapper(assigns) do
IO.inspect(assigns, label: "SEE ME: modal wrapper assigns")

~H"""
<div
:if={@show}
id={@id}
phx-mounted={@show && @on_mount_callback && show_modal(@id)}
class={["relative z-50 hidden w-full mx-auto", @container_class]}
>
<div
id={"#{@id}-bg"}
class={[
"fixed inset-0 bg-gray-500 dark:bg-black opacity-90 dark:opacity-80",
@background_class
]}
aria-hidden="true"
/>
<div
aria-describedby={"#{@id}-description"}
role="dialog"
aria-modal="true"
tabindex="0"
class={["w-full fixed inset-0 flex", @dialog_class]}
>
<div class="flex w-full absolute lg:inset-0 bottom-0 lg:items-center lg:justify-center">
<div class={["w-full lg:py-8 rounded-none lg:rounded-2xl", @focus_container_class]}>
<.focus_wrap
id={"#{@id}-container"}
phx-mounted={@show && @on_mount_callback && show_modal(@id)}
phx-window-keydown={hide_modal(@window_keydown_callback, @id)}
phx-key={@cancel_key}
phx-click-away={hide_modal(@on_click_away_callback, @id)}
class={[
"hidden relative flex w-full bg-white lg:rounded-3xl lg:shadow-2xl",
@focus_wrap_class
]}
>
<div :if={@close_button_icon} class="absolute top-4 right-4">
<button
phx-click={hide_modal(@on_cancel_callback, @id)}
type="button"
class={["-m-3 flex-none p-3 opacity-80 hover:opacity-40", @close_button_class]}
aria-label={gettext("close")}
>
<.icon
name={@close_button_icon}
class={"h-5 w-5 text-black " <> (@close_button_icon_class || "")}
/>
</button>
</div>
<div id={"#{@id}-content"}>
<div
id={"#{@id}-main"}
class={[
"w-full lg:max-w-2xl lg:items-center lg:justify-center flex",
@inner_block_container_class
]}
>
<%= render_slot(@inner_block) %>
</div>
</div>
</.focus_wrap>
</div>
</div>
</div>
</div>
"""
end

@doc """
Renders a waiting spinner
"""
Expand Down Expand Up @@ -919,13 +1050,13 @@ defmodule VyasaWeb.CoreComponents do
A generic debug dump component that displays all assigned properties.
## Examples
<DebugDump assigns={@assigns} class={["custom-class", "another-class"]} />
<DebugDump assigns={@assigns} label="Custom Label" class={["custom-class", "another-class"]} />
"""
def debug_dump(assigns) do
~H"""
<div class={[
"fixed bottom-0 right-0 m-4 p-4 bg-white border border-gray-300 rounded-lg shadow-lg max-w-md max-h-80 overflow-auto z-50 bg-opacity-50 p-4",
if(Map.has_key?(assigns, :class), do: assigns.class, else: [])
"fixed bottom-0 right-0 m-4 p-4 bg-white border border-gray-300 rounded-lg shadow-lg max-w-md max-h-80 overflow-auto z-50 bg-opacity-50",
assigns.class
]}>
<h2 class="text-lg font-bold mb-2">
<%= Map.get(assigns, :label, "Developer Dump") %>
Expand Down

0 comments on commit a131bde

Please sign in to comment.