diff --git a/lib/accumulator/notes.ex b/lib/accumulator/notes.ex index 8c9f4a4..6182438 100644 --- a/lib/accumulator/notes.ex +++ b/lib/accumulator/notes.ex @@ -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 @@ -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() diff --git a/lib/accumulator/notes/workspace.ex b/lib/accumulator/notes/workspace.ex index 755255a..5fa9cdc 100644 --- a/lib/accumulator/notes/workspace.ex +++ b/lib/accumulator/notes/workspace.ex @@ -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 diff --git a/lib/accumulator_web/components/layouts/root.html.heex b/lib/accumulator_web/components/layouts/root.html.heex index d92934c..643fddd 100644 --- a/lib/accumulator_web/components/layouts/root.html.heex +++ b/lib/accumulator_web/components/layouts/root.html.heex @@ -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" /> diff --git a/lib/accumulator_web/live/notes_live/notes_live.ex b/lib/accumulator_web/live/notes_live/notes_live.ex index 40ae32f..a40bab9 100644 --- a/lib/accumulator_web/live/notes_live/notes_live.ex +++ b/lib/accumulator_web/live/notes_live/notes_live.ex @@ -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 @@ -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() diff --git a/lib/accumulator_web/live/notes_live/notes_live.html.heex b/lib/accumulator_web/live/notes_live/notes_live.html.heex index f82213a..7e00a99 100644 --- a/lib/accumulator_web/live/notes_live/notes_live.html.heex +++ b/lib/accumulator_web/live/notes_live/notes_live.html.heex @@ -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 diff --git a/lib/accumulator_web/live/notes_live/notes_public_live.ex b/lib/accumulator_web/live/notes_live/notes_public_live.ex new file mode 100644 index 0000000..700e114 --- /dev/null +++ b/lib/accumulator_web/live/notes_live/notes_public_live.ex @@ -0,0 +1,167 @@ +defmodule AccumulatorWeb.NotesPublicLive do + use AccumulatorWeb, :live_view + + alias Accumulator.{Notes} + + @impl true + def render(assigns) do + ~H""" +
+
+ Workspaces +
+
+ +
+
+
+ +
+ Requested workspace either doesn't exist or isn't public! +
+ + + + <%!-- Notes UI --%> +
+
+
+ <%= date %> (<%= Accumulator.Helpers.days_ago(date) %>) +
+
+
+ <%= Earmark.as_html!(note.text, escape: false, compact_output: false) + |> Phoenix.HTML.raw() %> +
+
+ <.local_time id={"note-#{note.id}-date"} date={note.updated_at} /> +
+
+
+
+
+ """ + 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 diff --git a/lib/accumulator_web/router.ex b/lib/accumulator_web/router.ex index d83b75e..feb02f5 100644 --- a/lib/accumulator_web/router.ex +++ b/lib/accumulator_web/router.ex @@ -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. diff --git a/priv/repo/migrations/20240518174558_public_workspaces.exs b/priv/repo/migrations/20240518174558_public_workspaces.exs new file mode 100644 index 0000000..05bb86c --- /dev/null +++ b/priv/repo/migrations/20240518174558_public_workspaces.exs @@ -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