mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-14 12:10:59 +00:00
WIP
This commit is contained in:
parent
b676f7aeb4
commit
7b43323813
6 changed files with 88 additions and 53 deletions
|
@ -3,6 +3,7 @@ import {Socket} from "phoenix"
|
|||
// import {LiveSocket} from "phoenix_live_view"
|
||||
import {LiveSocket} from "/Users/chris/oss/phoenix_live_view/assets/js/phoenix_live_view"
|
||||
import topbar from "../vendor/topbar"
|
||||
import Sortable from "../vendor/sortable"
|
||||
|
||||
let nowSeconds = () => Math.round(Date.now() / 1000)
|
||||
let rand = (min, max) => Math.floor(Math.random() * (max - min) + min)
|
||||
|
|
|
@ -161,18 +161,24 @@ defmodule LiveBeats.MediaLibrary do
|
|||
# refetch user for fresh song count
|
||||
user = Accounts.get_user!(user.id)
|
||||
|
||||
multi =
|
||||
Ecto.Multi.new()
|
||||
|> Ecto.Multi.run(:starting_position, fn repo, _changes ->
|
||||
count = repo.one(from s in Song, where: s.user_id == ^user.id, select: count(s.id))
|
||||
{:ok, count - 1}
|
||||
end)
|
||||
|
||||
multi =
|
||||
changesets
|
||||
|> Enum.with_index()
|
||||
|> Enum.reduce(Ecto.Multi.new(), fn {{ref, chset}, idx}, acc ->
|
||||
chset =
|
||||
|> Enum.reduce(multi, fn {{ref, chset}, i}, acc ->
|
||||
Ecto.Multi.insert(acc, {:song, ref}, fn %{starting_position: pos_start} ->
|
||||
chset
|
||||
|> Song.put_user(user)
|
||||
|> Song.put_mp3_path()
|
||||
|> Song.put_server_ip()
|
||||
|> Ecto.Changeset.put_change(:position, idx)
|
||||
|
||||
Ecto.Multi.insert(acc, {:song, ref}, chset)
|
||||
|> Ecto.Changeset.put_change(:position, pos_start + i + 1)
|
||||
end)
|
||||
end)
|
||||
|> Ecto.Multi.run(:valid_songs_count, fn _repo, changes ->
|
||||
new_songs_count = changes |> Enum.filter(&match?({{:song, _ref}, _}, &1)) |> Enum.count()
|
||||
|
@ -244,7 +250,10 @@ defmodule LiveBeats.MediaLibrary do
|
|||
end
|
||||
|
||||
def list_profile_songs(%Profile{} = profile, limit \\ 100) do
|
||||
from(s in Song, where: s.user_id == ^profile.user_id, limit: ^limit, order_by: [asc: :position])
|
||||
from(s in Song,
|
||||
where: s.user_id == ^profile.user_id,
|
||||
limit: ^limit
|
||||
)
|
||||
|> order_by_playlist(:asc)
|
||||
|> Repo.replica().all()
|
||||
end
|
||||
|
@ -323,7 +332,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
def get_next_song(%Song{} = song, %Profile{} = profile) do
|
||||
next =
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id > ^song.id,
|
||||
where: s.user_id == ^song.user_id and s.position > ^song.position,
|
||||
limit: 1
|
||||
)
|
||||
|> order_by_playlist(:asc)
|
||||
|
@ -335,8 +344,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
def get_prev_song(%Song{} = song, %Profile{} = profile) do
|
||||
prev =
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id < ^song.id,
|
||||
order_by: [desc: s.inserted_at, desc: s.id],
|
||||
where: s.user_id == ^song.user_id and s.position < ^song.position,
|
||||
limit: 1
|
||||
)
|
||||
|> order_by_playlist(:desc)
|
||||
|
@ -356,26 +364,26 @@ defmodule LiveBeats.MediaLibrary do
|
|||
_count -> {:error, :index_out_of_range}
|
||||
end
|
||||
end)
|
||||
|> Ecto.Multi.update_all(:dec_positions, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id != ^song.id,
|
||||
where: s.position > ^old_index and s.position <= ^new_index,
|
||||
update: [inc: [position: -1]]
|
||||
)
|
||||
end, [])
|
||||
|> Ecto.Multi.update_all(:inc_positions, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id != ^song.id,
|
||||
where: s.position < ^old_index and s.position >= ^new_index,
|
||||
update: [inc: [position: 1]]
|
||||
)
|
||||
end, [])
|
||||
|> Ecto.Multi.update_all(:position, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.id == ^song.id,
|
||||
update: [set: [position: ^new_index]]
|
||||
)
|
||||
end, [])
|
||||
|> multi_update_all(:dec_positions, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id != ^song.id,
|
||||
where: s.position > ^old_index and s.position <= ^new_index,
|
||||
update: [inc: [position: -1]]
|
||||
)
|
||||
end)
|
||||
|> multi_update_all(:inc_positions, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id and s.id != ^song.id,
|
||||
where: s.position < ^old_index and s.position >= ^new_index,
|
||||
update: [inc: [position: 1]]
|
||||
)
|
||||
end)
|
||||
|> multi_update_all(:position, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.id == ^song.id,
|
||||
update: [set: [position: ^new_index]]
|
||||
)
|
||||
end)
|
||||
|
||||
case LiveBeats.Repo.transaction(multi) do
|
||||
{:ok, _} ->
|
||||
|
@ -395,14 +403,26 @@ defmodule LiveBeats.MediaLibrary do
|
|||
|
||||
def delete_song(%Song{} = song) do
|
||||
delete_song_file(song)
|
||||
old_index = song.position
|
||||
|
||||
Ecto.Multi.new()
|
||||
|> Ecto.Multi.delete(:delete, song)
|
||||
|> multi_update_all(:dec_positions, fn _ ->
|
||||
from(s in Song,
|
||||
where: s.user_id == ^song.user_id,
|
||||
where: s.position > ^old_index,
|
||||
update: [inc: [position: -1]]
|
||||
)
|
||||
end)
|
||||
|> update_user_songs_count(song.user_id, -1)
|
||||
|> Repo.transaction()
|
||||
|> case do
|
||||
{:ok, _} -> :ok
|
||||
other -> other
|
||||
{:ok, _} ->
|
||||
broadcast!(song.user_id, %Events.SongDeleted{song: song})
|
||||
:ok
|
||||
|
||||
other ->
|
||||
other
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -493,7 +513,7 @@ defmodule LiveBeats.MediaLibrary do
|
|||
end
|
||||
|
||||
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
||||
from(s in query, order_by: [{^direction, s.inserted_at}, {^direction, s.id}])
|
||||
from(s in query, order_by: [{^direction, s.position}])
|
||||
end
|
||||
|
||||
defp broadcast!(user_id, msg) when is_integer(user_id) do
|
||||
|
@ -509,4 +529,8 @@ defmodule LiveBeats.MediaLibrary do
|
|||
{:error, :songs_limit_exceeded}
|
||||
end
|
||||
end
|
||||
|
||||
defp multi_update_all(multi, name, func, opts \\ []) do
|
||||
Ecto.Multi.update_all(multi, name, func, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,8 @@ defmodule LiveBeats.MediaLibrary.Events do
|
|||
defmodule NewPosition do
|
||||
defstruct song: nil
|
||||
end
|
||||
|
||||
defmodule SongDeleted do
|
||||
defstruct song: nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -554,6 +554,7 @@ defmodule LiveBeatsWeb.CoreComponents do
|
|||
attr :id, :any, default: nil
|
||||
attr :row_id, :any, default: false
|
||||
attr :row_click, :any, default: nil
|
||||
attr :row_remove, :any, default: nil
|
||||
attr :rows, :list, required: true
|
||||
attr :streamable, :boolean, default: false
|
||||
attr :sortable_drop, :string, default: nil
|
||||
|
@ -585,23 +586,25 @@ defmodule LiveBeatsWeb.CoreComponents do
|
|||
phx-hook={@sortable_drop && "Sortable"}
|
||||
data-drop={@sortable_drop}
|
||||
>
|
||||
<%= for {row, i} <- Enum.with_index(@rows) do %>
|
||||
<tr id={@row_id && @row_id.(row)} class="hover:bg-gray-50">
|
||||
<%= for col <- @col do %>
|
||||
<td
|
||||
phx-click={@row_click && @row_click.(row)}
|
||||
class={
|
||||
col[:class!] ||
|
||||
"px-6 py-3 whitespace-nowrap text-sm font-medium text-gray-900 #{if i == 0, do: "max-w-0 w-full"} #{col[:class]}"
|
||||
}
|
||||
>
|
||||
<div class="flex items-center space-x-3 lg:pl-2">
|
||||
<%= render_slot(col, row) %>
|
||||
</div>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<tr
|
||||
:for={{row, i} <- Enum.with_index(@rows)}
|
||||
id={@row_id && @row_id.(row)}
|
||||
phx-remove={@row_remove && @row_remove.(row)}
|
||||
class="hover:bg-gray-50"
|
||||
>
|
||||
<td
|
||||
:for={col <- @col}
|
||||
phx-click={@row_click && @row_click.(row)}
|
||||
class={
|
||||
col[:class!] ||
|
||||
"px-6 py-3 whitespace-nowrap text-sm font-medium text-gray-900 #{if i == 0, do: "max-w-0 w-full"} #{col[:class]}"
|
||||
}
|
||||
>
|
||||
<div class="flex items-center space-x-3 lg:pl-2">
|
||||
<%= render_slot(col, row) %>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<%= assigns[:page_title] || "LiveBeats" %>
|
||||
</.live_title>
|
||||
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
|
||||
<script defer type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
|
||||
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -83,6 +83,7 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
rows={@songs}
|
||||
row_id={fn {id, _song} -> id end}
|
||||
row_click={fn {_id, song} -> JS.push("play_or_pause", value: %{id: song.id}) end}
|
||||
row_remove={fn {id, _song} -> hide("##{id}") end}
|
||||
streamable
|
||||
sortable_drop="row_dropped"
|
||||
>
|
||||
|
@ -108,7 +109,7 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
role="button"
|
||||
/>
|
||||
</span>
|
||||
<%= song.title %> <%= @count %>
|
||||
<%= song.title %>
|
||||
</:col>
|
||||
<:col :let={{_id, song}} label="Artist"><%= song.artist %></:col>
|
||||
<:col
|
||||
|
@ -153,7 +154,6 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
socket =
|
||||
socket
|
||||
|> assign(
|
||||
count: 0,
|
||||
active_song_id: active_song_id,
|
||||
active_profile_id: current_user.active_profile_user_id,
|
||||
profile: profile,
|
||||
|
@ -247,7 +247,11 @@ defmodule LiveBeatsWeb.ProfileLive do
|
|||
end
|
||||
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.SongsImported{songs: songs}}, socket) do
|
||||
{:noreply, update(socket, :songs, &(&1 ++ songs))}
|
||||
{:noreply, Enum.reduce(songs, socket, fn song, acc -> stream_insert(acc, :songs, song) end)}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, %MediaLibrary.Events.SongDeleted{song: song}}, socket) do
|
||||
{:noreply, stream_delete(socket, :songs, song)}
|
||||
end
|
||||
|
||||
def handle_info({MediaLibrary, {:ping, ping}}, socket) do
|
||||
|
|
Loading…
Reference in a new issue