Skip to content

Commit bbfe290

Browse files
adds post liveview and schema
1 parent 76d2b9c commit bbfe290

File tree

16 files changed

+590
-0
lines changed

16 files changed

+590
-0
lines changed

lib/reddit/category/community.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule Reddit.Category.Community do
44

55
alias Reddit.Users.User
66
alias Reddit.Community.Subscription
7+
alias Reddit.Community.Post
78

89
schema "communities" do
910
field :name, :string
@@ -12,6 +13,7 @@ defmodule Reddit.Category.Community do
1213
field :members, :integer, default: 0
1314
belongs_to :user, User
1415
has_many :subscriptions, Subscription
16+
has_many :posts, Post
1517

1618
timestamps()
1719
end

lib/reddit/community.ex

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
defmodule Reddit.Community do
2+
@moduledoc """
3+
The Community context.
4+
"""
5+
6+
import Ecto.Query, warn: false
7+
alias Reddit.Repo
8+
9+
alias Reddit.Community.Post
10+
11+
@doc """
12+
Returns the list of posts.
13+
14+
## Examples
15+
16+
iex> list_posts()
17+
[%Post{}, ...]
18+
19+
"""
20+
def list_posts do
21+
Repo.all(Post)
22+
end
23+
24+
@doc """
25+
Gets a single post.
26+
27+
Raises `Ecto.NoResultsError` if the Post does not exist.
28+
29+
## Examples
30+
31+
iex> get_post!(123)
32+
%Post{}
33+
34+
iex> get_post!(456)
35+
** (Ecto.NoResultsError)
36+
37+
"""
38+
def get_post!(id), do: Repo.get!(Post, id)
39+
40+
@doc """
41+
Creates a post.
42+
43+
## Examples
44+
45+
iex> create_post(%{field: value})
46+
{:ok, %Post{}}
47+
48+
iex> create_post(%{field: bad_value})
49+
{:error, %Ecto.Changeset{}}
50+
51+
"""
52+
def create_post(attrs \\ %{}) do
53+
%Post{}
54+
|> Post.changeset(attrs)
55+
|> Repo.insert()
56+
end
57+
58+
@doc """
59+
Updates a post.
60+
61+
## Examples
62+
63+
iex> update_post(post, %{field: new_value})
64+
{:ok, %Post{}}
65+
66+
iex> update_post(post, %{field: bad_value})
67+
{:error, %Ecto.Changeset{}}
68+
69+
"""
70+
def update_post(%Post{} = post, attrs) do
71+
post
72+
|> Post.changeset(attrs)
73+
|> Repo.update()
74+
end
75+
76+
@doc """
77+
Deletes a post.
78+
79+
## Examples
80+
81+
iex> delete_post(post)
82+
{:ok, %Post{}}
83+
84+
iex> delete_post(post)
85+
{:error, %Ecto.Changeset{}}
86+
87+
"""
88+
def delete_post(%Post{} = post) do
89+
Repo.delete(post)
90+
end
91+
92+
@doc """
93+
Returns an `%Ecto.Changeset{}` for tracking post changes.
94+
95+
## Examples
96+
97+
iex> change_post(post)
98+
%Ecto.Changeset{data: %Post{}}
99+
100+
"""
101+
def change_post(%Post{} = post, attrs \\ %{}) do
102+
Post.changeset(post, attrs)
103+
end
104+
end

lib/reddit/community/post.ex

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
defmodule Reddit.Community.Post do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
5+
alias Reddit.Category.Community
6+
alias Reddit.Users.User
7+
8+
schema "posts" do
9+
field :body, :string
10+
field :title, :string
11+
field :upvote, :integer, default: 0
12+
field :downvote, :integer, default: 0
13+
field :comments, :integer, default: 0
14+
belongs_to :community, Community
15+
belongs_to :user, User
16+
17+
timestamps()
18+
end
19+
20+
@doc false
21+
def changeset(post, attrs) do
22+
post
23+
|> cast(attrs, [:title, :body, :upvote, :downvote, :comments])
24+
|> validate_required([:title, :body])
25+
end
26+
end

lib/reddit/users/user.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ defmodule Reddit.Users.User do
1111
alias Reddit.Repo
1212
alias Reddit.Category.Community
1313
alias Reddit.Community.Subscription
14+
alias Reddit.Community.Post
1415

1516
schema "users" do
1617
pow_user_fields()
@@ -21,6 +22,7 @@ defmodule Reddit.Users.User do
2122
field :karma, :integer, default: 0
2223
has_many :communities, Community
2324
has_many :subscriptions, Subscription
25+
has_many :posts, Post
2426

2527
timestamps()
2628
end

lib/reddit_web.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ defmodule RedditWeb do
9494

9595
# Import LiveView helpers (live_render, live_component, live_patch, etc)
9696
import Phoenix.LiveView.Helpers
97+
import RedditWeb.LiveHelpers
9798

9899
# Import basic rendering functionality (render, render_layout, etc)
99100
import Phoenix.View

lib/reddit_web/live/live_helpers.ex

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule RedditWeb.LiveHelpers do
2+
import Phoenix.LiveView.Helpers
3+
4+
@doc """
5+
Renders a component inside the `RedditWeb.ModalComponent` component.
6+
7+
The rendered modal receives a `:return_to` option to properly update
8+
the URL when the modal is closed.
9+
10+
## Examples
11+
12+
<%= live_modal @socket, RedditWeb.PostLive.FormComponent,
13+
id: @post.id || :new,
14+
action: @live_action,
15+
post: @post,
16+
return_to: Routes.post_index_path(@socket, :index) %>
17+
"""
18+
def live_modal(socket, component, opts) do
19+
path = Keyword.fetch!(opts, :return_to)
20+
modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
21+
live_component(socket, RedditWeb.ModalComponent, modal_opts)
22+
end
23+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
defmodule RedditWeb.ModalComponent do
2+
use RedditWeb, :live_component
3+
4+
@impl true
5+
def render(assigns) do
6+
~L"""
7+
<div id="<%= @id %>" class="phx-modal"
8+
phx-capture-click="close"
9+
phx-window-keydown="close"
10+
phx-key="escape"
11+
phx-target="#<%= @id %>"
12+
phx-page-loading>
13+
14+
<div class="phx-modal-content">
15+
<%= live_patch raw("&times;"), to: @return_to, class: "phx-modal-close" %>
16+
<%= live_component @socket, @component, @opts %>
17+
</div>
18+
</div>
19+
"""
20+
end
21+
22+
@impl true
23+
def handle_event("close", _, socket) do
24+
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
25+
end
26+
end
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
defmodule RedditWeb.PostLive.FormComponent do
2+
use RedditWeb, :live_component
3+
4+
alias Reddit.Community
5+
6+
@impl true
7+
def update(%{post: post} = assigns, socket) do
8+
changeset = Community.change_post(post)
9+
10+
{:ok,
11+
socket
12+
|> assign(assigns)
13+
|> assign(:changeset, changeset)}
14+
end
15+
16+
@impl true
17+
def handle_event("validate", %{"post" => post_params}, socket) do
18+
changeset =
19+
socket.assigns.post
20+
|> Community.change_post(post_params)
21+
|> Map.put(:action, :validate)
22+
23+
{:noreply, assign(socket, :changeset, changeset)}
24+
end
25+
26+
def handle_event("save", %{"post" => post_params}, socket) do
27+
save_post(socket, socket.assigns.action, post_params)
28+
end
29+
30+
defp save_post(socket, :edit, post_params) do
31+
case Community.update_post(socket.assigns.post, post_params) do
32+
{:ok, _post} ->
33+
{:noreply,
34+
socket
35+
|> put_flash(:info, "Post updated successfully")
36+
|> push_redirect(to: socket.assigns.return_to)}
37+
38+
{:error, %Ecto.Changeset{} = changeset} ->
39+
{:noreply, assign(socket, :changeset, changeset)}
40+
end
41+
end
42+
43+
defp save_post(socket, :new, post_params) do
44+
case Community.create_post(post_params) do
45+
{:ok, _post} ->
46+
{:noreply,
47+
socket
48+
|> put_flash(:info, "Post created successfully")
49+
|> push_redirect(to: socket.assigns.return_to)}
50+
51+
{:error, %Ecto.Changeset{} = changeset} ->
52+
{:noreply, assign(socket, changeset: changeset)}
53+
end
54+
end
55+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<h2><%= @title %></h2>
2+
3+
<%= f = form_for @changeset, "#",
4+
id: "post-form",
5+
phx_target: @myself,
6+
phx_change: "validate",
7+
phx_submit: "save" %>
8+
9+
<%= label f, :title %>
10+
<%= text_input f, :title %>
11+
<%= error_tag f, :title %>
12+
13+
<%= label f, :body %>
14+
<%= textarea f, :body %>
15+
<%= error_tag f, :body %>
16+
17+
<%= submit "Save", phx_disable_with: "Saving..." %>
18+
</form>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
defmodule RedditWeb.PostLive.Index do
2+
use RedditWeb, :live_view
3+
4+
alias Reddit.Community
5+
alias Reddit.Community.Post
6+
7+
@impl true
8+
def mount(_params, _session, socket) do
9+
{:ok, assign(socket, :posts, list_posts())}
10+
end
11+
12+
@impl true
13+
def handle_params(params, _url, socket) do
14+
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
15+
end
16+
17+
defp apply_action(socket, :edit, %{"id" => id}) do
18+
socket
19+
|> assign(:page_title, "Edit Post")
20+
|> assign(:post, Community.get_post!(id))
21+
end
22+
23+
defp apply_action(socket, :new, _params) do
24+
socket
25+
|> assign(:page_title, "New Post")
26+
|> assign(:post, %Post{})
27+
end
28+
29+
defp apply_action(socket, :index, _params) do
30+
socket
31+
|> assign(:page_title, "Listing Posts")
32+
|> assign(:post, nil)
33+
end
34+
35+
@impl true
36+
def handle_event("delete", %{"id" => id}, socket) do
37+
post = Community.get_post!(id)
38+
{:ok, _} = Community.delete_post(post)
39+
40+
{:noreply, assign(socket, :posts, list_posts())}
41+
end
42+
43+
defp list_posts do
44+
Community.list_posts()
45+
end
46+
end

0 commit comments

Comments
 (0)