Skip to content

Commit

Permalink
notes: public notes/workspaces
Browse files Browse the repository at this point in the history
  • Loading branch information
aayushmau5 committed May 19, 2024
1 parent 27f5639 commit 29b7312
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 6 deletions.
9 changes: 7 additions & 2 deletions lib/accumulator/notes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ defmodule Accumulator.Notes do
Repo.all(Workspace) |> Enum.map(&convert_timestamps_tz/1)
end

def get_public_workspaces() do
from(w in Workspace, where: w.is_public == true)
|> Repo.all()
|> Enum.map(&convert_timestamps_tz/1)
end

def get_workspace_by_id(id) do
Repo.get!(Workspace, id)
end
Expand All @@ -99,8 +105,7 @@ defmodule Accumulator.Notes do
%Workspace{} |> Workspace.changeset(params) |> Repo.insert()
end

def rename_workspace(id, params) do
# TODO: think about writing a query instead that changes the name without getting the workspace by its id first
def update_workspace(id, params) do
get_workspace_by_id(id)
|> Workspace.changeset(params)
|> Repo.update()
Expand Down
3 changes: 2 additions & 1 deletion lib/accumulator/notes/workspace.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ defmodule Accumulator.Notes.Workspace do

schema "workspaces" do
field(:title, :string)
field(:is_public, :boolean)
has_many(:notes, Accumulator.Notes.Note)
timestamps(type: :utc_datetime)
end

def changeset(workspace, params \\ %{}) do
workspace
|> cast(params, [:title])
|> cast(params, [:title, :is_public])
|> validate_length(:title, min: 2)
end
end
1 change: 1 addition & 0 deletions lib/accumulator_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<.navbar_link to={~p"/redirect"} label="redirect" />
<.navbar_link :if={@current_user} to={~p"/bin"} label="bin" />
<.navbar_link :if={@current_user} to={~p"/notes"} label="notes" />
<.navbar_link :if={!@current_user} to={~p"/notes/public/default"} label="notes" />
<.navbar_link :if={@current_user} to={~p"/sessions"} label="session" />
<.navbar_link :if={@current_user} to={~p"/livedashboard"} label="livedashboard" />
</.navbar>
Expand Down
3 changes: 1 addition & 2 deletions lib/accumulator_web/live/notes_live/notes_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ defmodule AccumulatorWeb.NotesLive do
form: create_empty_form(:note),
workspace_form: nil,
editing_note_id: nil,
uploaded_files: [],
search: to_form(%{"search" => ""}),
page_error: nil,
# Existing note editing state
Expand Down Expand Up @@ -269,7 +268,7 @@ defmodule AccumulatorWeb.NotesLive do

socket =
if workspace_edit_id != nil do
case Notes.rename_workspace(workspace_edit_id, workspace_params) do
case Notes.update_workspace(workspace_edit_id, workspace_params) do
{:ok, _} ->
workspaces = Notes.get_all_workspaces()

Expand Down
1 change: 1 addition & 0 deletions lib/accumulator_web/live/notes_live/notes_live.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
phx-submit="workspace-form-submit"
>
<.input label="Title" field={@workspace_form[:title]} />
<.input type="checkbox" label="Public?" field={@workspace_form[:is_public]} />
<.button>Save</.button>
</.simple_form>
</.modal>
Expand Down
167 changes: 167 additions & 0 deletions lib/accumulator_web/live/notes_live/notes_public_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
defmodule AccumulatorWeb.NotesPublicLive do
use AccumulatorWeb, :live_view

alias Accumulator.{Notes}

@impl true
def render(assigns) do
~H"""
<div class="w-full max-h-screen font-note relative p-4 flex flex-col">
<div>
Workspaces
<div id="workspaces" class="flex gap-2 mt-2 flex-wrap">
<div
:for={workspace <- @workspaces}
id={"workspace-#{workspace.id}"}
class="flex gap-2 items-center"
>
<button
class="rounded-md text-sm p-2 bg-zinc-600 hover:bg-opacity-80 disabled:bg-opacity-20"
phx-click={JS.patch("/notes/public/#{workspace.id}")}
disabled={@selected_workspace && workspace.id == @selected_workspace.id}
>
<%= workspace.title %>
</button>
</div>
</div>
</div>
<div :if={!@selected_workspace && @page_error} class="mt-40 text-center text-lg text-red-400">
Requested workspace either doesn't exist or isn't public!
</div>
<button
:if={@selected_workspace}
phx-click="more-notes"
class="block text-sm opacity-60 ml-auto"
>
Load more
</button>
<%!-- Notes UI --%>
<div
:if={@selected_workspace}
class="bg-black bg-opacity-20 p-2 rounded-md overflow-y-auto"
id="notes"
phx-update="stream"
phx-hook="ScrollToBottom"
>
<div :for={{dom_id, [date, notes]} <- @streams.notes} id={dom_id}>
<div class="leading-10 font-bold">
<%= date %> (<%= Accumulator.Helpers.days_ago(date) %>)
</div>
<div :for={note <- notes} class="my-1 bg-[#3d3d3d] p-2 rounded-md" id={"note-#{note.id}"}>
<article :if={note.text} class="note-text break-words">
<%= Earmark.as_html!(note.text, escape: false, compact_output: false)
|> Phoenix.HTML.raw() %>
</article>
<div class="text-xs opacity-40 mt-2">
<.local_time id={"note-#{note.id}-date"} date={note.updated_at} />
</div>
</div>
</div>
</div>
</div>
"""
end

@impl true
def mount(_params, _session, socket) do
{:ok,
socket
|> stream_configure(:notes, dom_id: &Enum.at(&1, 0))
|> stream(:notes, [])
|> assign(
page_title: "Notes",
workspaces: Notes.get_public_workspaces(),
selected_workspace: nil,
search: to_form(%{"search" => ""}),
page_error: nil
), layout: {AccumulatorWeb.Layouts, :note}}
end

@impl true
def handle_params(params, _uri, socket) do
workspace_id = Map.get(params, "id")

socket =
if workspace_id == "default" do
handle_default_route(socket)
else
handle_workspace(workspace_id, socket)
end

{:noreply, socket}
end

def handle_default_route(socket) do
workspaces = socket.assigns.workspaces

case Enum.at(workspaces, 0) do
nil ->
socket |> assign(page_error: :no_workspace)

workspace ->
socket
|> assign_notes(workspace)
|> assign(
selected_workspace: workspace,
page_error: nil
)
end
end

def handle_workspace(workspace_id, socket) do
workspace =
case Integer.parse(workspace_id) do
{id, ""} -> Notes.get_workspace(id)
_ -> nil
end

case workspace do
nil ->
assign(socket, page_error: :no_workspace)

workspace ->
if workspace.is_public do
socket
|> assign_notes(workspace)
|> assign(
selected_workspace: workspace,
page_error: nil
)
else
assign(socket, page_error: :not_public_workspace)
end
end
end

@impl true
def handle_event("more-notes", _params, socket) do
start_date = socket.assigns.pagination_date
workspace_id = socket.assigns.selected_workspace.id

{notes, pagination_date} =
Notes.get_notes_grouped_and_ordered_by_date(workspace_id, start_date)

socket =
socket
|> assign(pagination_date: pagination_date)
|> stream(:notes, Enum.reverse(notes), at: 0)

{:noreply, socket}
end

defp assign_notes(socket, workspace) do
{notes, pagination_date} =
Notes.get_notes_grouped_and_ordered_by_date(
workspace.id,
Notes.get_utc_datetime_from_date()
)

socket
|> stream(:notes, notes, reset: true)
|> assign(pagination_date: pagination_date)
|> push_event("new-note-scroll", %{})
end
end
4 changes: 3 additions & 1 deletion lib/accumulator_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ defmodule AccumulatorWeb.Router do
end

scope "/", AccumulatorWeb do
pipe_through :browser
pipe_through [:browser]

get "/", PageController, :home
get "/redirect", PageController, :redirect
live "/dashboard", DashboardLive
live "/spotify", SpotifyLive
delete "/logout", UserSessionController, :delete

live "/notes/public/:id", NotesPublicLive
end

# Other scopes may use custom stacks.
Expand Down
9 changes: 9 additions & 0 deletions priv/repo/migrations/20240518174558_public_workspaces.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Accumulator.Repo.Migrations.PublicWorkspaces do
use Ecto.Migration

def change do
alter table(:workspaces) do
add(:is_public, :boolean, default: false)
end
end
end

0 comments on commit 29b7312

Please sign in to comment.