Skip to content

Commit

Permalink
Fix mounting woes by rendering content as live_component within DM (#86)
Browse files Browse the repository at this point in the history
basically took the whole day, a bunch of refactoring has happened 

# How to Verify / Test

1. navigate through the thing, if you change the mode then navigate into other parts of the content (e.g. :show_sources -> :show_chapters -> :show_verses) then the ControlPanel mode should be the same, no dismount and remounting or anything either

2. handshake gets done properly. The handshake will init when you're in the :show_verses content view and only then does the audio load. Note that once the audio is loaded, any content navigation will never kill the audio playback either. In fact the audio playback will continue and scribing and all that fun stuff will still happen


# Workplan Notes

```
***** Approach 2: ContentLiveview
:LOGBOOK:
CLOCK: [2024-08-16 Fri 08:39]--[2024-08-16 Fri 17:39] =>  9:00
:END:
****** Test:
1. navigate through content without overlay states being affected
****** 0. Redefine Router, let everything point to DM
****** 1. Add content liveview
- all 3 added
- [LOW IMPORTANCE]

  TOVERIFY: seems like it's correct to preload the stream content @ the liveview then pass it onto a live_component? ([[https://elixirforum.com/t/how-to-use-phoenix-1-7-streams-on-livecomponent-preload/54454][ref]])
  my own implementations:

  PRELOAD @
  #+begin_src elixir
# preload @ display_manager/display_live
  defp apply_action(
         %Socket{} = socket,
         :show_chapters,
         %{"source_title" => source_title} =
           params
       ) do

    [%Chapter{source: src} | _] = chapters = Written.get_chapters_by_src(source_title)
    socket
    |> assign(:content_action, :show_chapters)
    |> assign(:page_title, to_title_case(src.title))
    |> assign(:source, src)
    |> assign(:meta, %{
      title: to_title_case(src.title),
      description: "Explore the #{to_title_case(src.title)}",
      type: "website",
      image: url(~p"/og/#{VyasaWeb.OgImageController.get_by_binding(%{source: src})}"),
      url: url(socket, ~p"/explore/#{src.title}")
    })
    |> stream_configure(:chapters, dom_id: &"Chapter-#{&1.no}")
    |> stream(:chapters, chapters |> Enum.sort_by(fn chap -> chap.no end))
  end
  #+end_src

  INJECT INTO LIVE_COMPONENT:

  #+begin_src elixir
<%= if @content_action == :show_chapters do%>
    <div>
        SHOW CHAPTERS
   <.live_component
      module={VyasaWeb.Content.Chapters}
      id={"content-sources"}
      source={@source}
      chapters={@streams.chapters}
      />
    </div>
<% end %>
  #+end_src

****** 2. defdelegate or add contentFetcher to separate out the repo logic
SKIPPED this, not too important
****** 3. test the push_patch() for this
WORKSSSS

```




---- 


* [WIP]: basic DM, demo state & assigned components

Still have no idea how the VyasaWeb.SourceLive.Chapter.Index is supposed
to be rendered as @inner_content within the new
display_manager.html.heex

* Rename CommandGroup -> ControlPanel

* Initial Wire up %UserMode{} to Control Panel

* Switch modes, add user_mode.ex to tw config

* Prevent full page-reloads via push_navigate()

This prevents the socket from being killed at every navigation, so the
overlay's states is still managed without any issue.

Actually, currently, there's a issue with definitions on the router-side

problem: if I do a push_navigate to the path matching =/explore/:source_title/:chap_no= from =/explore/:source_title/=,
it will trigger a socket error:
=phx-F-vfg0fv3-NR4yFB error: unauthorized live_redirect. Falling back to page request -=

Hunch: it's because of the router defining two different live_session scopes (anon vs sangh) so it's
not allowing a =push_navigate()= and hence it's reverting to a full page reload.

Do you know a quick fix to this?

    live_session :gen_anon_session,
      on_mount: [{VyasaWeb.Session, :anon}] do
      live "/explore/", SourceLive.Index, :index
      live "/explore/:source_title/", SourceLive.Show, :show
      #live "/explore/:source_title/:chap_no", SourceLive.Chapter.Index, :index
      live "/explore/:source_title/:chap_no", SourceLive.Chapter.Index, :index
      live "/explore/:source_title/:chap_no/:verse_no", SourceLive.Chapter.ShowVerse, :show
    end

    live_session :gen_sangh_session,
      on_mount: [{VyasaWeb.Session, :sangh}] do
        live "/explore/:source_title/:chap_no", SourceLive.Chapter.Index, :index
      end

* [temp] shift all from anon session to sangh sess

* Minor changes

* [WIP, Attempt] Shift from layout to heex template

Still the thing dies on me

* Trigger DIFF

* Use DM @ router actions, load data :show_sources

* Add data load for :show_chapters action on DM

* Use push_patch() @ :show_sources -> :show_chapters

* Handle data load & nav:show_chapters->:show_verses

* Create VyasaWeb.Content.Verses live_component

* Wire up handshakes successfully, shift to patches

Also done:
1. use maybe_stream_configure/3 (see note in definition)
2. change backlinks from navigate -> patch so that a full-page reload is
NOT done. This is also what allows the state of ControlPanel and
ActionBar to be maintained despite going back and forth content.
3. actually wiring up the event handlers and stuff that allowed the
handshakes to happen

TODOs:
1. fix css nonsense with how the content is being displayed.
2. future iteration needs a cleanup, this module is too fat.

* Fix css issues

* More minor css fixes
  • Loading branch information
rtshkmr authored Aug 19, 2024
1 parent b6aa231 commit 861295d
Show file tree
Hide file tree
Showing 13 changed files with 1,067 additions and 59 deletions.
8 changes: 5 additions & 3 deletions assets/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import MiniPlayer from "./mini_player.js";
import MediaBridge from "./media_bridge.js";
import AudioPlayer from "./audio_player.js";
import ProgressBar from "./progress_bar.js";
import Floater from "./floater.js"
import ApplyModal from "./apply_modal.js"
import Floater from "./floater.js";
import ApplyModal from "./apply_modal.js";
import MargiNote from "./marginote.js";
import HoveRune from "./hoverune.js";
import Scrolling from "./scrolling.js";

let Hooks = {
ShareQuoteButton,
Expand All @@ -23,7 +24,8 @@ let Hooks = {
Floater,
ApplyModal,
MargiNote,
HoveRune
HoveRune,
Scrolling,
};

export default Hooks;
11 changes: 11 additions & 0 deletions assets/js/hooks/scrolling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Scrolling = {
mounted() {
console.log("SCROLLING MOUNTED");
this.handleEvent("scroll-to-top", this.handleScrollToTop);
},
handleScrollToTop() {
window.scrollTo({ top: 0, behavior: "smooth" });
},
};

export default Scrolling;
102 changes: 61 additions & 41 deletions lib/vyasa/written.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ defmodule Vyasa.Written do
"""
defguard is_uuid?(value)
when is_bitstring(value) and
byte_size(value) == 36 and
binary_part(value, 8, 1) == "-" and
binary_part(value, 13, 1) == "-" and
binary_part(value, 18, 1) == "-" and
binary_part(value, 23, 1) == "-"
when is_bitstring(value) and
byte_size(value) == 36 and
binary_part(value, 8, 1) == "-" and
binary_part(value, 13, 1) == "-" and
binary_part(value, 18, 1) == "-" and
binary_part(value, 23, 1) == "-"

@doc """
Returns the list of texts.
Expand Down Expand Up @@ -80,8 +80,6 @@ defmodule Vyasa.Written do
|> Repo.preload([:verses])
end



@doc """
Gets a single text.
Expand Down Expand Up @@ -114,72 +112,94 @@ defmodule Vyasa.Written do
** (Ecto.NoResultsError)
"""
def get_source!(id), do: Repo.get!(Source, id)
|> Repo.preload([:chapters, :verses])
def get_source!(id),
do:
Repo.get!(Source, id)
|> Repo.preload([:chapters, :verses])

def get_source_by_title(title) do
query = from src in Source,
where: src.title == ^title,
preload: [verses: [:translations], chapters: [:translations]]
query =
from src in Source,
where: src.title == ^title,
preload: [verses: [:translations], chapters: [:translations]]

Repo.one(query)
end

def get_chapters_by_src(src_title) do
(from c in Chapter,
from(c in Chapter,
inner_join: src in assoc(c, :source),
where: src.title == ^src_title,
inner_join: t in assoc(c, :translations),
on: t.source_id == src.id)
on: t.source_id == src.id
)
|> select_merge([c, src, t], %{
c | translations: [t], source: src
})
c
| translations: [t],
source: src
})
|> Repo.all()
end

def get_chapter(no, source_title) do
(from c in Chapter, where: c.no == ^no,
from(c in Chapter,
where: c.no == ^no,
inner_join: src in assoc(c, :source),
where: src.title == ^source_title)
where: src.title == ^source_title
)
|> Repo.one()
end

def get_chapter(no, sid, lang) when is_uuid?(sid) do

target_lang = (from ts in Translation,
where: ts.lang == ^lang and ts.source_id == ^sid)

(from c in Chapter,
where: c.no == ^no and c.source_id == ^sid,
preload: [verses: ^(from v in Verse, where: v.source_id == ^sid, order_by: v.no,
preload: [translations: ^target_lang]),
translations: ^target_lang]
target_lang =
from ts in Translation,
where: ts.lang == ^lang and ts.source_id == ^sid

from(c in Chapter,
where: c.no == ^no and c.source_id == ^sid,
preload: [
verses:
^from(v in Verse,
where: v.source_id == ^sid,
order_by: v.no,
preload: [translations: ^target_lang]
),
translations: ^target_lang
]
)
|> Repo.one()
end

def get_chapter(no, source_title, lang) do
%Source{id: id} = _src = get_source_by_title(source_title)

target_lang = (from ts in Translation,
where: ts.lang == ^lang and ts.source_id == ^id)

(from c in Chapter,
where: c.no == ^no and c.source_id == ^id,
preload: [verses: ^(from v in Verse, where: v.source_id == ^id, order_by: v.no,
preload: [translations: ^target_lang]),
translations: ^target_lang]
target_lang =
from ts in Translation,
where: ts.lang == ^lang and ts.source_id == ^id

from(c in Chapter,
where: c.no == ^no and c.source_id == ^id,
preload: [
verses:
^from(v in Verse,
where: v.source_id == ^id,
order_by: v.no,
preload: [translations: ^target_lang]
),
translations: ^target_lang
]
)
|> Repo.one()
end

def get_verses_in_chapter(no, source_id) do
query_verse = from v in Verse,
where: v.chapter_no == ^no and v.source_id == ^source_id,
preload: [:chapter]
query_verse =
from v in Verse,
where: v.chapter_no == ^no and v.source_id == ^source_id,
preload: [:chapter]

Repo.all(query_verse)
end
end

@doc """
Creates a text.
Expand Down Expand Up @@ -306,4 +326,4 @@ defmodule Vyasa.Written do
def change_source(%Source{} = source, attrs \\ %{}) do
Source.mutate_changeset(source, attrs)
end
end
end
27 changes: 19 additions & 8 deletions lib/vyasa_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -779,19 +779,30 @@ defmodule VyasaWeb.CoreComponents do
<.back navigate={~p"/posts"}>Back to posts</.back>
"""
attr :navigate, :any, required: true
attr :navigate, :any, default: nil
attr :patch, :any, default: nil
slot :inner_block, required: true

def back(assigns) do
~H"""
<div class="mt-16">
<.link
navigate={@navigate}
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
>
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
<%= render_slot(@inner_block) %>
</.link>
<%= if @patch do %>
<.link
patch={@patch}
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
>
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
<%= render_slot(@inner_block) %>
</.link>
<% else %>
<.link
navigate={@navigate}
class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700"
>
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
<%= render_slot(@inner_block) %>
</.link>
<% end %>
</div>
"""
end
Expand Down
1 change: 0 additions & 1 deletion lib/vyasa_web/components/layouts/display_manager.html.heex
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<!-- Collapsible Vertical Button Group -->
<div>
<%= @inner_content %>
</div>
5 changes: 4 additions & 1 deletion lib/vyasa_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<!DOCTYPE html>
<html lang="en" class="[scrollbar-gutter:stable]">
<head>
<head
id="root-container"
phx-hook="Scrolling"
>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={get_csrf_token()} />
Expand Down
73 changes: 73 additions & 0 deletions lib/vyasa_web/components/source_content/chapters.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule VyasaWeb.Content.Chapters do
use VyasaWeb, :live_component

@impl true
def update(params, socket) do
{
:ok,
socket
|> assign(params)
}
end

@impl true
def render(assigns) do
~H"""
<div>
<.header>
<div class="font-dn text-4xl">
<%= to_title_case(@source.title) %>
</div>
</.header>
<.back patch={~p"/explore/"}>Back to All Sources</.back>
<.table
id="chapters"
rows={@chapters}
row_click={
fn {_id, chap} ->
JS.push("navigate_to_chapter",
value: %{target: ~p"/explore/#{@source.title}/#{chap.no}/"},
target: @myself
)
end
}
>
<:col :let={{_id, chap}} label="Chapter">
<div class="font-dn text-lg">
<%= chap.no %>. <%= hd(chap.translations).target.translit_title %>
</div>
</:col>
<:col :let={{_id, chap}} label="Description">
<div class="font-dn text-md">
<%= chap.title %>
</div>
<div class="font-dn text-md">
<%= hd(chap.translations).target.title %>
</div>
</:col>
</.table>
<.back patch={~p"/explore/"}>Back to All Sources</.back>
<span :if={@chapters |> Enum.count() < 10} class="block h-96" />
</div>
"""
end

# @impl true
# def handle_event("reportVideoStatus", payload, socket) do
# IO.inspect(payload)
# {:noreply, socket}
# end
@impl true
def handle_event("navigate_to_chapter", %{"target" => target} = _payload, socket) do
IO.inspect(target, label: "TRACE: push patch to the following target by @myself:")

{:noreply,
socket
|> push_patch(to: target)
|> push_event("scroll-to-top", %{})}
end
end
50 changes: 50 additions & 0 deletions lib/vyasa_web/components/source_content/sources.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule VyasaWeb.Content.Sources do
use VyasaWeb, :live_component

@impl true
def update(params, socket) do
{
:ok,
socket
|> assign(params)
}
end

@impl true
def render(assigns) do
~H"""
<div>
<.table
id="sources"
rows={@sources}
row_click={
fn {_id, source} ->
JS.push("navigate_to_source",
value: %{target: ~p"/explore/#{source.title}/"},
target: @myself
)
end
}
>
<:col :let={{_id, source}} label="">
<div class="font-dn text-2xl">
<%= to_title_case(source.title) %>
</div>
</:col>
</.table>
<span :if={@sources |> Enum.count() < 10} class="block h-96" />
</div>
"""
end

@impl true
def handle_event("navigate_to_source", %{"target" => target} = _payload, socket) do
IO.inspect(target, label: "TRACE: push patch to the following target by @myself:")

{:noreply,
socket
|> push_patch(to: target)
|> push_event("scroll-to-top", %{})}
end
end
Loading

0 comments on commit 861295d

Please sign in to comment.