Skip to content

Commit

Permalink
Session Local Persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
ks0m1c_dharma committed Sep 21, 2024
1 parent 793bed0 commit 0edf1d2
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 79 deletions.
39 changes: 38 additions & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,48 @@ let liveSocket = new LiveSocket("/live", Socket, {
locale: Intl.NumberFormat().resolvedOptions().locale,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
timezone_offset: -new Date().getTimezoneOffset(),
session: JSON.parse(localStorage.getItem("session")) || { active: true },

session: fetchSession(),
},
hooks: Hooks,
});

function fetchSession() {
try {
sess = JSON.parse(localStorage.getItem("session"))
if(sess && sess.id && typeof sess.id == 'string' ) return sess
new_sess = {id: genAnonId()}
localStorage.setItem("session", JSON.stringify(new_sess))
return new_sess;
} catch (error) {
new_sess = {id: genAnonId()}
localStorage.setItem("session", JSON.stringify(new_sess))
return new_sess
}
};

function genAnonId(length = 18) {
try {
// Generate cryptographically strong random bytes
const arrayBuffer = new Uint8Array(length);
window.crypto.getRandomValues(arrayBuffer);

// Convert the array buffer to a string
const binaryString = String.fromCharCode.apply(null, arrayBuffer);

// Encode the string using Base64
const base64String = btoa(binaryString);

return base64String;
} catch (error) {
console.error('Error generating random Base64 string:', error);
throw error;
}
}




// Show progress bar on live navigation and form submits
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" });
window.addEventListener("phx:page-loading-start", (_info) => topbar.show(300));
Expand Down
2 changes: 2 additions & 0 deletions assets/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import MargiNote from "./marginote.js";
import HoveRune from "./hoverune.js";
import Scrolling from "./scrolling.js";
import ButtonClickRelayer from "./button_click_relayer.js";
import SessionBox from "./session_box.js";

let Hooks = {
ShareQuoteButton,
Expand All @@ -28,6 +29,7 @@ let Hooks = {
HoveRune,
Scrolling,
ButtonClickRelayer,
SessionBox,
};

export default Hooks;
8 changes: 1 addition & 7 deletions assets/js/hooks/media_bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ MediaBridge = {
this.handleEvent("media_bridge:registerPlayback", (params) =>
this.registerPlaybackInfo(params),
);
this.handleEvent("initSession", (sess) => this.initSession(sess));
// pub: external action
// this callback pubs to others
this.handleEvent("media_bridge:play_pause", (payload) =>
Expand All @@ -52,12 +51,7 @@ MediaBridge = {
),
};
},
/**
* Saves current session id
* */
initSession(sess) {
localStorage.setItem("session", JSON.stringify(sess));
},

toggleFollowMode() {
this.isFollowMode = !this.isFollowMode;
},
Expand Down
11 changes: 11 additions & 0 deletions assets/js/hooks/session_box.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SessionBox = {
mounted() {
this.handleEvent("initSession", (sess) => this.initSession(sess));
},

initSession(sess) {
localStorage.setItem("session", JSON.stringify(sess));
},
};

export default SessionBox;
30 changes: 11 additions & 19 deletions lib/vyasa_web/components/source_content/reading_content.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ defmodule VyasaWeb.Content.ReadingContent do
%{id: "reading-content", sess_id: sess_id} = _props,
%{
assigns: %{
session: %{"id" => sess_id},
session: %{id: sess_id},
chap: %Chapter{no: c_no, source_id: src_id}
}
} = socket
Expand Down Expand Up @@ -117,21 +117,14 @@ defmodule VyasaWeb.Content.ReadingContent do
defp apply_action(
%Socket{} = socket,
:show_verses,
%{"source_title" => source_title, "chap_no" => chap_no} = params
%{"source_title" => source_title, "chap_no" => chap_no}
) do
IO.inspect(:show_verses, label: "TRACE: apply action DM action show_verses:")
IO.inspect(params, label: "TRACE: apply action DM params:")
IO.inspect(source_title, label: "TRACE: apply action DM params source_title:")
IO.inspect(chap_no, label: "TRACE: apply action DM params chap_no:")

with %Source{id: sid} = source <- Written.get_source_by_title(source_title),
%{verses: verses, translations: [ts | _], title: chap_title, body: chap_body} = chap <-
Written.get_chapter(chap_no, sid, @default_lang) do
fmted_title = to_title_case(source.title)

IO.inspect(fmted_title, label: "TRACE: am i WITH it?")

IO.inspect("sid: #{sid} title: #{source_title}", label: "SEE ME:")

socket
|> sync_session()
Expand Down Expand Up @@ -213,11 +206,10 @@ defmodule VyasaWeb.Content.ReadingContent do
socket
end

defp sync_session(%Socket{assigns: %{session: %{"id" => sess_id}}} = socket) do
# dbg()
defp sync_session(%Socket{assigns: %{session: %{id: sess_id}}} = socket) when is_binary(sess_id) do
Vyasa.PubSub.subscribe("written:session:" <> sess_id)
Vyasa.PubSub.publish(:init, :written_handshake, "media:session:" <> sess_id)
# send(self(), :sync_session)
send(self(), %{"cmd" => :sub_to_topic, "topic" => "written:session:" <> sess_id})
IO.inspect(sess_id <> " sync la")
socket
end

Expand Down Expand Up @@ -249,7 +241,7 @@ defmodule VyasaWeb.Content.ReadingContent do
def handle_event(
"clickVerseToSeek",
%{"verse_id" => verse_id} = _payload,
%{assigns: %{session: %{"id" => sess_id}}} = socket
%{assigns: %{session: %{id: sess_id}}} = socket
) do
IO.inspect("handle_event::clickVerseToSeek media:session:#{sess_id}", label: "checkpoint")
Vyasa.PubSub.publish(%{verse_id: verse_id}, :playback_sync, "media:session:" <> sess_id)
Expand Down Expand Up @@ -322,7 +314,7 @@ defmodule VyasaWeb.Content.ReadingContent do
%{"key" => "Enter"} = _payload,
%Socket{} = socket
) do
send(self(), {:change_ui, "update_media_bridge_visibility", [false]})
send(self(), {"mutate_UiState", "update_media_bridge_visibility", [false]})

{
:noreply,
Expand All @@ -336,7 +328,7 @@ defmodule VyasaWeb.Content.ReadingContent do
%{"is_focusing?" => is_focusing?} = _payload,
%Socket{} = socket
) do
send(self(), {:change_ui, "update_media_bridge_visibility", [is_focusing?]})
send(self(), {"mutate_UiState", "update_media_bridge_visibility", [is_focusing?]})

{:noreply, socket}
end
Expand All @@ -361,7 +353,7 @@ defmodule VyasaWeb.Content.ReadingContent do
}
} = socket
) do
send(self(), {:change_ui, "update_media_bridge_visibility", [false]})
send(self(), {"mutate_UiState", "update_media_bridge_visibility", [false]})

{
:noreply,
Expand All @@ -385,7 +377,7 @@ defmodule VyasaWeb.Content.ReadingContent do
}
} = socket
) do
send(self(), {:change_ui, "update_media_bridge_visibility", [false]})
send(self(), {"mutate_UiState", "update_media_bridge_visibility", [false]})

{:noreply,
socket
Expand All @@ -402,7 +394,7 @@ defmodule VyasaWeb.Content.ReadingContent do
_event,
%Socket{} = socket
) do
send(self(), {:change_ui, "update_media_bridge_visibility", [false]})
send(self(), {"mutate_UiState", "update_media_bridge_visibility", [false]})

{
:noreply,
Expand Down
61 changes: 29 additions & 32 deletions lib/vyasa_web/live/display_manager/display_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ defmodule VyasaWeb.DisplayManager.DisplayLive do
on_mount VyasaWeb.Hook.UserAgentHook
alias Vyasa.Display.{UserMode, UiState}
alias Phoenix.LiveView.Socket
alias VyasaWeb.Session
@supported_modes UserMode.supported_modes()

@mod_registry %{"UiState" => UiState}

@impl true
def mount(_params, sess, socket) do
def mount(_params, _sess, socket) do
%UserMode{
default_ui_state: %UiState{} = initial_ui_state
} = mode = UserMode.get_initial_mode()

{
:ok,
socket
|> assign(stored_session: sess)
|> assign(mode: mode)
|> assign(url_params: nil)
|> assign(ui_state: initial_ui_state),
Expand All @@ -26,31 +28,24 @@ defmodule VyasaWeb.DisplayManager.DisplayLive do
end

@impl true
def handle_params(
params,
url,
%Socket{
assigns: %{
live_action: live_action
}
} = socket
) do
IO.inspect(url, label: "TRACE: handle param url")
IO.inspect(live_action, label: "TRACE: handle param live_action")

def handle_params(params, _url, socket) do
{
:noreply,
socket
|> assign(url_params: params)
# | apply_action(live_action, params)
|> sync_session()
}
end

@impl true
def handle_params(_params, _url, socket) do
{:noreply, socket}
defp sync_session(%{assigns: %{session: %Session{name: name} = sess}} = socket) when is_binary(name) do
# currently needs name prerequisite to save
socket
|> push_event("initSession", sess)
end

defp sync_session(socket) do
socket
end

defp change_mode(socket, curr, target)
when is_binary(curr) and is_binary(target) and target in @supported_modes do
Expand All @@ -69,6 +64,17 @@ defmodule VyasaWeb.DisplayManager.DisplayLive do
socket
end

def handle_event(
"name",
%{"name" => name},
%{assigns: %{session: sess}} = socket
) do
{:noreply,
socket
|> assign(session: %{sess | name: name})
|> sync_session()}
end

@impl true
def handle_event(
"change_mode",
Expand Down Expand Up @@ -143,34 +149,25 @@ defmodule VyasaWeb.DisplayManager.DisplayLive do
{_, :media_handshake, :init} = _msg,
%{
assigns: %{
session: %{"id" => sess_id},
session: %VyasaWeb.Session{id: sess_id},
mode: %UserMode{
mode_context_component: component,
mode_context_component_selector: selector
}
}
} = socket
) do
IO.inspect(sess_id, label: "media handshake")
send_update(component, id: selector, sess_id: sess_id)
{:noreply, socket}
end

@impl true
# this enables the children of DM to allow DM to subscribe to a generic topic.
# Thereafter, messages sent through that topic, to the DM can be listened to by the DM and
# the DM can pass that message to the appropriate slotted child component.
def handle_info(%{"cmd" => :sub_to_topic, "topic" => topic} = _msg, socket) do
# dbg()
Vyasa.PubSub.subscribe(topic)
{:noreply, socket}
end

@impl true
def handle_info({:change_ui, function_name, args}, socket)
def handle_info({"mutate_" <> mod, function_name, args}, socket)
when is_binary(function_name) and is_list(args) do
with :ok <- validate_function_name(function_name, UiState) do
with :ok <- validate_function_name(function_name, @mod_registry[mod]) do
func = String.to_existing_atom(function_name)
updated_socket = apply(UiState, func, [socket | args])
updated_socket = apply(@mod_registry[mod], func, [socket | args])
{:noreply, updated_socket}
else
{:error, reason} ->
Expand Down
34 changes: 33 additions & 1 deletion lib/vyasa_web/live/display_manager/display_live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,38 @@
</div>
</header>

<div
:if={@session.active}
id="sessionbox"
class="absolute p-2 mr-4 w-1/4 top-20 right-10 font-mono text-xs rounded text-brand "
phx-hook="SessionBox"
>
<div :if={!@session.name}>
<form class="flex items-center" id="name" phx-submit="name">
<span class="flex-1 transition-opacity duration-700 animate-pulse block">your name:</span>
<input
value={@session.name}
name="name"
type="text"
class="flex-grow text-xs bg-transparent border-none outline-none caret-current focus:border-1 focus:ring-0"
autoFocus
/>
</form>
<span class="text-primaryAccent flex items-center justify-left">
☙ –‹›––– ❊ –––‹›– ❧
</span>
</div>
<!-- --
live session ongoing
---->
<div :if={@session.name} class="absolute right-5 animate-ping rounded-full rounded-2xl bg-brand opacity-75">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-brand opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-primaryAccent"></span>
</span>
</div>
</div>

<main id="main-content" class="bg-primaryBackground px-4 py-4 sm:px-6 lg:px-8">
<div>
<div id="control-panel-container">
Expand Down Expand Up @@ -71,7 +103,7 @@

<!-- Bottom Action Bar -->
<div id="media-bridge-container" class={["fixed inset-x-0 bottom-0 z-10", not @ui_state.show_media_bridge? && "hidden"]}>
<%= live_render(@socket, VyasaWeb.MediaLive.MediaBridge, id: "MediaBridge", session: @session, sticky: true) %>
<%= live_render(@socket, VyasaWeb.MediaLive.MediaBridge, id: "MediaBridge", sticky: true) %>
</div>
</div>
</main>
Expand Down
Loading

0 comments on commit 0edf1d2

Please sign in to comment.