Skip to content

Commit a3a097c

Browse files
authored
feat: add favourites
1 parent 6c2d871 commit a3a097c

File tree

26 files changed

+1020
-110
lines changed

26 files changed

+1020
-110
lines changed

assets/js/app.js

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,23 @@ window.addEventListener("phx:page-loading-start", info => topbar.show())
1717
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
1818

1919
presence.onSync(() => {
20-
let presences = []
20+
let presences = []
2121

22-
presence.list((id, {metas: [first, ...rest]}) => {
23-
presences.push(first)
24-
})
22+
presence.list((id, {metas: [first, ...rest]}) => {
23+
presences.push(first)
24+
})
25+
26+
const element = document.querySelector("#users")
27+
28+
if(element) {
29+
for (const item of presences) {
2530

26-
const element = document.querySelector("#online-users")
27-
28-
if(element) {
29-
element.innerHTML = ""
30-
31-
for (const item of presences) {
32-
element.innerHTML += `<li>
33-
<div class="relative group py-6 px-5 flex items-center">
34-
<div class="-m-1 flex-1 block p-1">
35-
<div class="absolute inset-0" aria-hidden="true"></div>
36-
<div class="flex-1 flex items-center min-w-0 relative">
37-
<span class="flex-shrink-0 inline-block relative">
38-
<span class="inline-block h-10 w-10 rounded-full overflow-hidden bg-gray-100">
39-
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
40-
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
41-
</svg>
42-
</span>
43-
<span class="bg-green-400 absolute top-0 right-0 block h-2.5 w-2.5 rounded-full ring-2 ring-white" aria-hidden="true"></span>
44-
</span>
45-
<div class="ml-4 truncate">
46-
<p class="text-sm text-gray-900 truncate whitespace-normal">${ item.user_name }</p>
47-
</div>
48-
</div>
49-
</div>
50-
</div>
51-
</li>`
31+
const user = document.querySelector(`#user-id-${item.user_id}`)
32+
33+
user.innerHTML = ""
34+
user.innerHTML += '<span class="bg-green-400 absolute top-0 right-0 block h-2.5 w-2.5 rounded-full ring-2 ring-white" aria-hidden="true"></span>'
35+
}
5236
}
53-
}
5437
})
5538

5639
channel.join()

lib/media_server/accounts.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,17 @@ defmodule MediaServer.Accounts do
240240
|> Repo.preload(:episode_continues)
241241
end
242242

243+
@doc """
244+
Gets the user with the given signed token with favourites.
245+
"""
246+
def get_user_by_session_token_with_favourites(token) do
247+
{:ok, query} = UserToken.verify_session_token_query(token)
248+
249+
Repo.one(query)
250+
|> Repo.preload(:movie_favourites)
251+
|> Repo.preload(:serie_favourites)
252+
end
253+
243254
@doc """
244255
Deletes the signed token with the given context.
245256
"""

lib/media_server/accounts/user.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ defmodule MediaServer.Accounts.User do
1111
field :is_admin, :boolean
1212
has_many :movie_continues, MediaServer.Continues.Movie
1313
has_many :episode_continues, MediaServer.Continues.Episode
14+
has_many :movie_favourites, MediaServer.Favourites.Movie
15+
has_many :serie_favourites, MediaServer.Favourites.Serie
1416

1517
timestamps()
1618
end

lib/media_server/favourites.ex

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
defmodule MediaServer.Favourites do
2+
@moduledoc """
3+
The Favourites context.
4+
"""
5+
6+
import Ecto.Query, warn: false
7+
alias MediaServer.Repo
8+
9+
alias MediaServer.Favourites.Movie
10+
11+
@doc """
12+
Returns the list of movie_favourites.
13+
14+
## Examples
15+
16+
iex> list_movie_favourites()
17+
[%Movie{}, ...]
18+
19+
"""
20+
def list_movie_favourites do
21+
Repo.all(Movie)
22+
end
23+
24+
@doc """
25+
Gets a single movie.
26+
27+
Raises `Ecto.NoResultsError` if the Movie does not exist.
28+
29+
## Examples
30+
31+
iex> get_movie!(123)
32+
%Movie{}
33+
34+
iex> get_movie!(456)
35+
** (Ecto.NoResultsError)
36+
37+
"""
38+
def get_movie!(id), do: Repo.get!(Movie, id)
39+
40+
@doc """
41+
Creates a movie.
42+
43+
## Examples
44+
45+
iex> create_movie(%{field: value})
46+
{:ok, %Movie{}}
47+
48+
iex> create_movie(%{field: bad_value})
49+
{:error, %Ecto.Changeset{}}
50+
51+
"""
52+
def create_movie(attrs \\ %{}) do
53+
%Movie{}
54+
|> Movie.changeset(attrs)
55+
|> Repo.insert()
56+
end
57+
58+
@doc """
59+
Updates a movie.
60+
61+
## Examples
62+
63+
iex> update_movie(movie, %{field: new_value})
64+
{:ok, %Movie{}}
65+
66+
iex> update_movie(movie, %{field: bad_value})
67+
{:error, %Ecto.Changeset{}}
68+
69+
"""
70+
def update_movie(%Movie{} = movie, attrs) do
71+
movie
72+
|> Movie.changeset(attrs)
73+
|> Repo.update()
74+
end
75+
76+
@doc """
77+
Deletes a movie.
78+
79+
## Examples
80+
81+
iex> delete_movie(movie)
82+
{:ok, %Movie{}}
83+
84+
iex> delete_movie(movie)
85+
{:error, %Ecto.Changeset{}}
86+
87+
"""
88+
def delete_movie(%Movie{} = movie) do
89+
Repo.delete(movie)
90+
end
91+
92+
@doc """
93+
Returns an `%Ecto.Changeset{}` for tracking movie changes.
94+
95+
## Examples
96+
97+
iex> change_movie(movie)
98+
%Ecto.Changeset{data: %Movie{}}
99+
100+
"""
101+
def change_movie(%Movie{} = movie, attrs \\ %{}) do
102+
Movie.changeset(movie, attrs)
103+
end
104+
105+
alias MediaServer.Favourites.Serie
106+
107+
@doc """
108+
Returns the list of serie_favourites.
109+
110+
## Examples
111+
112+
iex> list_serie_favourites()
113+
[%Serie{}, ...]
114+
115+
"""
116+
def list_serie_favourites do
117+
Repo.all(Serie)
118+
end
119+
120+
@doc """
121+
Gets a single serie.
122+
123+
Raises `Ecto.NoResultsError` if the Serie does not exist.
124+
125+
## Examples
126+
127+
iex> get_serie!(123)
128+
%Serie{}
129+
130+
iex> get_serie!(456)
131+
** (Ecto.NoResultsError)
132+
133+
"""
134+
def get_serie!(id), do: Repo.get!(Serie, id)
135+
136+
@doc """
137+
Creates a serie.
138+
139+
## Examples
140+
141+
iex> create_serie(%{field: value})
142+
{:ok, %Serie{}}
143+
144+
iex> create_serie(%{field: bad_value})
145+
{:error, %Ecto.Changeset{}}
146+
147+
"""
148+
def create_serie(attrs \\ %{}) do
149+
%Serie{}
150+
|> Serie.changeset(attrs)
151+
|> Repo.insert()
152+
end
153+
154+
@doc """
155+
Updates a serie.
156+
157+
## Examples
158+
159+
iex> update_serie(serie, %{field: new_value})
160+
{:ok, %Serie{}}
161+
162+
iex> update_serie(serie, %{field: bad_value})
163+
{:error, %Ecto.Changeset{}}
164+
165+
"""
166+
def update_serie(%Serie{} = serie, attrs) do
167+
serie
168+
|> Serie.changeset(attrs)
169+
|> Repo.update()
170+
end
171+
172+
@doc """
173+
Deletes a serie.
174+
175+
## Examples
176+
177+
iex> delete_serie(serie)
178+
{:ok, %Serie{}}
179+
180+
iex> delete_serie(serie)
181+
{:error, %Ecto.Changeset{}}
182+
183+
"""
184+
def delete_serie(%Serie{} = serie) do
185+
Repo.delete(serie)
186+
end
187+
188+
@doc """
189+
Returns an `%Ecto.Changeset{}` for tracking serie changes.
190+
191+
## Examples
192+
193+
iex> change_serie(serie)
194+
%Ecto.Changeset{data: %Serie{}}
195+
196+
"""
197+
def change_serie(%Serie{} = serie, attrs \\ %{}) do
198+
Serie.changeset(serie, attrs)
199+
end
200+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule MediaServer.Favourites.Movie do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
5+
schema "movie_favourites" do
6+
field :movie_id, :integer
7+
field :title, :string
8+
field :image_url, :string
9+
belongs_to :user, MediaServer.Accounts.User
10+
11+
timestamps()
12+
end
13+
14+
@doc false
15+
def changeset(movie, attrs) do
16+
movie
17+
|> cast(attrs, [:movie_id, :title, :image_url, :user_id])
18+
|> validate_required([:movie_id, :title, :user_id])
19+
end
20+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule MediaServer.Favourites.Serie do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
5+
schema "serie_favourites" do
6+
field :serie_id, :integer
7+
field :title, :string
8+
field :image_url, :string
9+
belongs_to :user, MediaServer.Accounts.User
10+
11+
timestamps()
12+
end
13+
14+
@doc false
15+
def changeset(serie, attrs) do
16+
serie
17+
|> cast(attrs, [:serie_id, :title, :image_url, :user_id])
18+
|> validate_required([:serie_id, :title, :user_id])
19+
end
20+
end

lib/media_server_web/components/poster_component.ex

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ defmodule MediaServerWeb.Components.PosterComponent do
55
~H"""
66
<div class="group relative">
77
8-
<div class="w-full bg-gray-200 aspect-[4/6] rounded-md overflow-hidden group-hover:opacity-75 lg:aspect-none">
9-
<img class="h-full object-cover" src={assigns.img_src}>
10-
</div>
8+
<div class="w-full bg-gray-200 aspect-[4/6] rounded-md overflow-hidden group-hover:opacity-75 lg:aspect-none">
9+
<img class="h-full object-cover" src={assigns.img_src}>
10+
</div>
1111
12-
<div class="mt-2 flex">
13-
<h3 class="text-sm text-slate-600">
14-
<%= live_redirect to: assigns.link, class: "hover:text-slate-800 hover:underline" do %>
15-
<span aria-hidden="true" class="absolute inset-0"></span>
16-
<%= assigns.title %>
17-
<% end %>
18-
</h3>
19-
</div>
12+
<div class="mt-2 flex">
13+
<h3 class="text-sm text-slate-600">
14+
<%= live_redirect to: assigns.link, class: "hover:text-slate-800 hover:underline" do %>
15+
<span aria-hidden="true" class="absolute inset-0"></span>
16+
<%= assigns.title %>
17+
<% end %>
18+
</h3>
19+
</div>
2020
</div>
2121
"""
2222
end

lib/media_server_web/components/user_invitation_component.html.heex

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,24 @@
3636
</div>
3737

3838
<div>
39-
<ul role="list" class="divide-y divide-gray-200">
39+
<ul id="users" role="list" class="divide-y divide-gray-200">
4040

4141
<%= for item <- @users do %>
4242
<li class="py-4 flex">
43-
<span class="inline-block h-10 w-10 rounded-full overflow-hidden bg-gray-100">
44-
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
45-
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
46-
</svg>
47-
</span>
48-
<div class="ml-3 flex flex-col">
49-
<span class="text-sm font-medium text-gray-900"><%= item.name %></span>
50-
<span class="text-sm text-gray-500"><%= item.email %></span>
51-
</div>
43+
<span class="flex-shrink-0 inline-block relative">
44+
<span class="inline-block h-8 w-8 rounded-full overflow-hidden bg-gray-100">
45+
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
46+
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
47+
</svg>
48+
</span>
49+
<span id={"user-id-#{item.id}"} phx-update="ignore">
50+
</span>
51+
</span>
52+
53+
<div class="ml-3 flex flex-col">
54+
<span class="text-sm font-medium text-gray-900"><%= item.name %></span>
55+
<span class="text-sm text-gray-500"><%= item.email %></span>
56+
</div>
5257
</li>
5358
<% end %>
5459
</ul>

0 commit comments

Comments
 (0)