Skip to content
111 changes: 111 additions & 0 deletions lib/vyasa/sangh.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ defmodule Vyasa.Sangh do
import Ecto.Query, warn: false
import EctoLtree.Functions, only: [nlevel: 1]
alias Vyasa.Repo
alias EctoLtree.LabelTree, as: Ltree
# alias Vyasa.Draft
alias Vyasa.Sangh.{Sheaf}

@max_sheaf_depth 3

@doc """
Returns a list of sheafs associated with a specific session.

Expand Down Expand Up @@ -403,11 +406,117 @@ defmodule Vyasa.Sangh do

# """
def update_sheaf(%Sheaf{} = sheaf, attrs) do
IO.puts("CHECKPOINT: update_sheaf")

sheaf
|> Sheaf.mutate_changeset(attrs)
|> Repo.update()
end

@doc """
Given a sheaf that is ready to be published as a reply, ensures that the
replies adhere to the 3-level nesting limit.

There are 3 ways that the reply to context is determined:
1. if the attrs contain :parent, then use that sheaf. This is more of an override-case,
for example, in the context of quick replies
2. if the attrs don't contain parent, but the draft sheaf already has an associated parent,
then that's the reply to context
3. there are no parents associated -- so the reply's parent is nil and there's no :parent in the attrs

Suppose the reply to context is already at depth = 3 (zero-indexed, then level = 2) then we need to reconcile this.
To reconcile, we shall update the attrs payload such that:
1. the parent of the reply sheaf ends up being the grandparent instead
2. the reply sheaf's path gets updated, is now encoded as though its the child of its grandparent
"""
# way number 1 -- using parent in attr
def make_reply(
%Sheaf{
id: id
} = reply,
%{
parent:
%Sheaf{
parent_id: grandparent_id,
path: %Ltree{
labels: parent_path_labels
}
} = _injected_parent
} = attrs
) do
IO.puts("CHECKPOINT Make Reply way 1 -- use injected parent in attr")

reconciled_attrs =
case parent_path_labels |> Enum.count() do
# need to reassign both path and parent:
@max_sheaf_depth ->
attrs |> reconcile_payload_using_grandparent(grandparent_id, id)

# keep as is
_ ->
attrs
end

reply |> update_sheaf(reconciled_attrs)
end

# way number 2 -- if not in attr, then use pre-existing associated parent to the reply sheaf
def make_reply(
%Sheaf{
id: id,
parent: %Sheaf{
parent_id: grandparent_id,
path: %Ltree{
labels: parent_path_labels
}
}
} = reply,
attrs
) do
IO.puts("CHECKPOINT Make Reply way 2 -- use pre-associated parent")

reconciled_attrs =
case parent_path_labels |> Enum.count() do
# need to reassign both path and parent:
@max_sheaf_depth ->
attrs |> reconcile_payload_using_grandparent(grandparent_id, id)

_ ->
attrs
end

reply |> update_sheaf(reconciled_attrs)
end

# way number 3 -- no assoced parent, nothing to reconcile
def make_reply(
%Sheaf{
parent: nil
} = reply,
attrs
) do
IO.puts("CHECKPOINT Make Reply way 3 -- no parent to associate to")
reply |> update_sheaf(attrs)
end

# To reconcile, we shall update the attrs payload such that:
# 1. the parent of the reply sheaf ends up being the grandparent instead
# 2. the reply sheaf's path gets updated, is now encoded as though its the child of its grandparent
defp reconcile_payload_using_grandparent(%{} = attrs, grandparent_id, child_id)
when is_binary(grandparent_id) do
%Sheaf{path: %Ltree{} = grandparent_path} = grandparent = get_sheaf!(grandparent_id)

IO.puts(
"CHECKPOINT -- turns out we need to reconcile this sheaf and assoc to its grandparent instead"
)

attrs
|> Map.merge(%{
parent: grandparent,
path: child_id |> Sheaf.encode_path(grandparent_path)
})
end

# @doc """
# Updates a sheaf.

Expand All @@ -421,6 +530,8 @@ defmodule Vyasa.Sangh do
# """

def update_sheaf!(%Sheaf{} = sheaf, attrs) do
IO.puts("CHECKPOINT: update_sheaf!")

sheaf
|> Sheaf.mutate_changeset(attrs)
|> Repo.update!()
Expand Down
44 changes: 36 additions & 8 deletions lib/vyasa/sangh/sheaf.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,19 @@ defmodule Vyasa.Sangh.Sheaf do
end

def mutate_changeset(%Sheaf{} = sheaf, attrs) do
sheaf
|> Vyasa.Repo.preload([:marks])
|> cast(attrs, [:id, :body, :active, :signature, :traits])
|> cast_path(attrs)
|> assoc_marks(attrs)
|> Map.put(:repo_opts, on_conflict: {:replace_all_except, [:id]}, conflict_target: :id)
|> validate_include_subset(:traits, ["personal", "draft", "published"])
IO.inspect(%{sheaf: sheaf, attrs: attrs}, label: "CHECKPOINT: mutate_changeset")

cs =
sheaf
|> Vyasa.Repo.preload([:marks])
|> cast(attrs, [:id, :body, :active, :signature, :traits, :updated_at, :inserted_at])
|> cast_path(attrs)
|> assoc_marks(attrs)
|> Map.put(:repo_opts, on_conflict: {:replace_all_except, [:id]}, conflict_target: :id)
|> validate_include_subset(:traits, ["personal", "draft", "published"])

IO.inspect(cs, label: "CHECKPOINT: mutate changeset outcome changeset:")
cs
end

defp assoc_marks(sheaf, %{marks: [%Mark{} | _] = marks}) do
Expand All @@ -87,17 +93,25 @@ defmodule Vyasa.Sangh.Sheaf do
end

defp cast_path(%{changes: %{id: sheaf_id}} = sheaf, %{
parent: %Sheaf{id: p_sheaf_id, path: lpath}
parent: %Sheaf{id: p_sheaf_id, path: lpath} = parent
}) do
IO.inspect(parent, label: "SEE ME : cast_path, parent:")

sheaf
|> cast(%{parent_id: p_sheaf_id, path: encode_path(sheaf_id, lpath)}, [:parent_id, :path])
end

defp cast_path(%{changes: %{id: sheaf_id}} = sheaf, _) do
IO.inspect(sheaf, label: "SEE ME : cast_path, sheaf:")

sheaf
|> cast(%{path: encode_path(sheaf_id)}, [:path])
end

defp cast_path(%{data: %{id: sheaf_id}} = sheaf, %{parent: %Sheaf{id: p_sheaf_id, path: lpath}}) do
cast(sheaf, %{parent_id: p_sheaf_id, path: encode_path(sheaf_id, lpath)}, [:parent_id, :path])
end

defp cast_path(sheaf, _) do
sheaf
end
Expand Down Expand Up @@ -204,4 +218,18 @@ defmodule Vyasa.Sangh.Sheaf do
) do
labels
end

@doc """
For sorting sheafs, we should just default to only caring about their insertion time,
ignoring the update time.

TODO: update clauses once we are supporting other kinds of ordering
"""
def sort_sheafs_chrono([%Sheaf{} | _] = sheafs) do
Enum.sort_by(sheafs, &elem(&1, 1).inserted_at, :desc)
end

def sort_sheaf_chrono(sheafs) do
sheafs
end
end
10 changes: 10 additions & 0 deletions lib/vyasa/sangh/sheaf_lattice.ex
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ defmodule Vyasa.Sangh.SheafLattice do
sheaf_lattice
|> read_sheaf_lattice(level, match)
|> Enum.reject(fn %Sheaf{traits: traits} -> "draft" in traits end)
# TODO: to verify this, may not be correct
|> sort_sheaf_lattice_entries_chrono()
end

# fetches all sheafs in level 0:
Expand Down Expand Up @@ -449,4 +451,12 @@ defmodule Vyasa.Sangh.SheafLattice do

lattice |> update_sheaf_in_lattice(lattice_key, %Sheaf{old_sheaf | marks: updated_marks})
end

def sort_sheaf_lattice_entries_chrono([{label, %Sheaf{}} | _] = entries) when is_list(label) do
entries |> Enum.sort_by(fn {_, %Sheaf{} = sheaf} -> sheaf.inserted_at end, :desc)
end

def sort_sheaf_lattice_entries_chrono(entries) do
entries
end
end
24 changes: 17 additions & 7 deletions lib/vyasa_web/components/contexts/components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule VyasaWeb.Context.Components do
"""
use VyasaWeb, :html
alias Vyasa.Sangh.{Sheaf}
alias VyasaWeb.CoreComponents
alias VyasaWeb.Context.Components.UiState.Mark, as: MarkUiState
alias VyasaWeb.Context.Components.UiState.Sheaf, as: SheafUiState

Expand Down Expand Up @@ -323,7 +324,7 @@ defmodule VyasaWeb.Context.Components do
<div id="sheaf-creator-container" class="flex flex-col">
<.form
for={%{}}
phx-submit={JS.push("sheaf::publish")}
phx-submit={CoreComponents.hide_modal(JS.push("sheaf::publish"), @id)}
phx-target={@event_target}
class="flex items-center"
>
Expand Down Expand Up @@ -377,7 +378,7 @@ defmodule VyasaWeb.Context.Components do
<div class="flex justify-between space-x-2">
<button
type="button"
phx-click={@on_cancel_callback}
phx-click={CoreComponents.hide_modal(@on_cancel_callback, @id)}
class="w-2/5 text-bold mt-4 flex items-center justify-center p-3 rounded-full border-2 border-brand text-grey-800 bg-brand-dark hover:bg-brand-light transition-colors duration-200 shadow-lg hover:shadow-xl focus:outline-none focus:ring-2 focus:ring-brand focus:ring-opacity-50"
phx-target={@event_target}
>
Expand Down Expand Up @@ -564,18 +565,27 @@ defmodule VyasaWeb.Context.Components do
attr :time_class, :string, default: "text-sm italic"

def sheaf_signature_display(assigns) do
assigns =
assigns
|> assign(
edited_suffix:
cond do
assigns.sheaf.inserted_at < assigns.sheaf.updated_at ->
"(edited)"

true ->
""
end
)

~H"""
<div class="w-auto flex mt-2 text-sm text-gray-600">
<div class="mx-1 text-gray-800 font-semibold">
<p><%= @sheaf.signature %></p>
</div>
<!-- Time Display -->
<div class="mx-1 text-gray-500 italic">
<%= if is_nil(@sheaf.updated_at) do %>
<%= (@sheaf.inserted_at |> Utils.Formatters.Time.friendly()).formatted_time %>
<% else %>
<%= (@sheaf.updated_at |> Utils.Formatters.Time.friendly()).formatted_time %> (edited)
<% end %>
<%= (@sheaf.inserted_at |> Utils.Formatters.Time.friendly()).formatted_time <> @edited_suffix %>
</div>
</div>
"""
Expand Down
Loading