173 lines
4.2 KiB
Elixir
173 lines
4.2 KiB
Elixir
defmodule FrenzyWeb.Fervor.ItemsController do
|
|
use FrenzyWeb, :controller
|
|
alias Frenzy.{Repo, Item, Group, Feed}
|
|
import Ecto.Query
|
|
alias FrenzyWeb.Fervor.Paginator
|
|
|
|
plug :get_specific_item
|
|
|
|
def get_specific_item(%Plug.Conn{path_params: %{"id" => id}} = conn, _opts) when id != "sync" do
|
|
user = conn.assigns[:user] |> Repo.preload(:feeds)
|
|
|
|
item = Repo.get(Item, id)
|
|
|
|
if Enum.any?(user.feeds, fn f -> f.id == item.feed_id end) do
|
|
assign(conn, :item, item)
|
|
else
|
|
conn
|
|
|> put_status(404)
|
|
|> json(%{error: "Unknown item"})
|
|
|> halt()
|
|
end
|
|
end
|
|
|
|
def get_specific_item(conn, _opts), do: conn
|
|
|
|
def items_list(conn, params) do
|
|
user = conn.assigns[:user] |> Repo.preload(groups: [:feeds])
|
|
|
|
feed_ids =
|
|
user.groups
|
|
|> Enum.flat_map(fn g -> g.feeds end)
|
|
|> Enum.map(fn f -> f.id end)
|
|
|
|
query = from(i in Item, where: i.feed_id in ^feed_ids)
|
|
|
|
query =
|
|
case Map.get(params, "only") do
|
|
"read" -> from(i in query, where: i.read)
|
|
"unread" -> from(i in query, where: not i.read)
|
|
nil -> query
|
|
end
|
|
|> Paginator.paginate(params)
|
|
|> Paginator.limit(params)
|
|
|
|
items =
|
|
query
|
|
|> Repo.all()
|
|
|> Enum.map(&Item.to_fervor/1)
|
|
|
|
json(conn, items)
|
|
end
|
|
|
|
def specific_item(conn, _params) do
|
|
item = conn.assigns[:item]
|
|
|
|
json(conn, Item.to_fervor(item))
|
|
end
|
|
|
|
defp mark_item(conn, changes) do
|
|
item = conn.assigns[:item] |> Repo.preload(:feed)
|
|
|
|
changeset = Item.changeset(item, changes)
|
|
|
|
if changeset.valid? do
|
|
{:ok, item} = Repo.update(changeset)
|
|
|
|
json(conn, Item.to_fervor(item))
|
|
else
|
|
json(conn, Item.to_fervor(item))
|
|
end
|
|
end
|
|
|
|
def read_specific_item(conn, _params) do
|
|
mark_item(conn, %{read: true})
|
|
end
|
|
|
|
def unread_specific_item(conn, _params) do
|
|
mark_item(conn, %{read: false})
|
|
end
|
|
|
|
defp mark_multiple_items(conn, %{"ids" => ids}, changes) do
|
|
user = conn.assigns[:user] |> Repo.preload(groups: [:feeds])
|
|
feeds = Enum.flat_map(user.groups, fn g -> g.feeds end)
|
|
|
|
read_ids =
|
|
ids
|
|
|> String.split(",")
|
|
|> Enum.map(fn s ->
|
|
{id, _} =
|
|
s
|
|
|> String.trim()
|
|
|> Integer.parse()
|
|
|
|
Repo.get(Item, id)
|
|
end)
|
|
|> Enum.filter(fn item ->
|
|
Enum.any?(feeds, fn f -> f.id == item.feed_id end) && !item.tombstone
|
|
end)
|
|
|> Enum.map(fn item ->
|
|
item = Repo.preload(item, :feed)
|
|
changeset = Item.changeset(item, changes)
|
|
|
|
case Repo.update(changeset) do
|
|
{:ok, item} -> item.id
|
|
_ -> nil
|
|
end
|
|
end)
|
|
|> Enum.reject(&is_nil/1)
|
|
|
|
json(conn, read_ids)
|
|
end
|
|
|
|
defp mark_multiple_items(conn, _params, _changes) do
|
|
conn
|
|
|> put_status(400)
|
|
|> json(%{error: "No items provided."})
|
|
end
|
|
|
|
def read_multiple(conn, params) do
|
|
mark_multiple_items(conn, params, %{read: true})
|
|
end
|
|
|
|
def unread_multiple(conn, params) do
|
|
mark_multiple_items(conn, params, %{read: false})
|
|
end
|
|
|
|
def sync(conn, params) do
|
|
sync_timestamp = Timex.now()
|
|
|
|
feed_ids =
|
|
Group
|
|
|> where([g], g.user_id == ^conn.assigns.user.id)
|
|
|> join(:inner, [g], f in Feed, on: f.group_id == g.id)
|
|
|> select([g, f], f.id)
|
|
|> Repo.all()
|
|
|
|
last_sync =
|
|
with s when is_binary(s) <- Map.get(params, "last_sync"),
|
|
{:ok, datetime} <- Timex.parse(s, "{ISO:Extended:Z}") do
|
|
datetime
|
|
else
|
|
_ ->
|
|
nil
|
|
end
|
|
|
|
{deleted_ids, upserted} =
|
|
case last_sync do
|
|
nil ->
|
|
items =
|
|
Item
|
|
|> where([i], not i.tombstone and i.feed_id in ^feed_ids)
|
|
|> order_by([i], desc: i.inserted_at)
|
|
|> limit(1000)
|
|
|> Repo.all()
|
|
|
|
{[], items}
|
|
|
|
_ ->
|
|
all_items =
|
|
Repo.all(from i in Item, where: i.feed_id in ^feed_ids and i.updated_at >= ^last_sync)
|
|
|
|
{tombstones, rest} = Enum.split_with(all_items, & &1.tombstone)
|
|
{Enum.map(tombstones, & &1.id), rest}
|
|
end
|
|
|
|
json(conn, %{
|
|
sync_timestamp: Timex.format!(sync_timestamp, "{ISO:Extended:Z}"),
|
|
delete: Enum.map(deleted_ids, &to_string/1),
|
|
upsert: Enum.map(upserted, &Item.to_fervor/1)
|
|
})
|
|
end
|
|
end
|