Skip to content

Commit

Permalink
First steps toward feed management
Browse files Browse the repository at this point in the history
  • Loading branch information
jerodsanto committed Jul 25, 2024
1 parent a887d16 commit d1fad5f
Show file tree
Hide file tree
Showing 15 changed files with 390 additions and 32 deletions.
41 changes: 41 additions & 0 deletions assets/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,47 @@ u(document).handle("click", ".js-subscribe-all", function (event) {
});
});

// Manage custom feed podcasts
u(document).on("click", ".js-feed-podcast_ids button", function (event) {
event.preventDefault();
let podcast = u(event.target).closest("button");
let id = podcast.data("id");

if (podcast.hasClass("disabled")) {
podcast.removeClass("disabled");
podcast.append(
`<input type="hidden" name="feed[podcast_ids][]" value="${id}">`
);
} else {
podcast.addClass("disabled");
podcast.find("input").remove();
}
});

// Manage custom feed covers
u(document).on("change", ".js-feed-cover_select", function (event) {
let coverUrl = event.target.value;

u(".js-feed-cover_field").find("img").attr("src", coverUrl);
u(".js-feed-cover_field").find("input[type=hidden]").first().value = coverUrl;
});

u(document).on(
"change",
".js-feed-cover_field input[type=file]",
function (event) {
let file = event.target.files[0];

if (file) {
let reader = new FileReader();
reader.onload = function (e) {
u(".js-feed-cover_field").find("img").attr("src", e.target.result);
};
reader.readAsDataURL(file);
}
}
);

u(document).handle("click", ".js-toggle_element", function (event) {
const href = u(event.target).attr("href");
u(href).toggleClass("is-hidden");
Expand Down
44 changes: 37 additions & 7 deletions assets/app/components/form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
}

textarea,
select,
input {
border: none;
border-bottom: $border;
Expand All @@ -62,6 +63,7 @@
line-height: 1.5em;
padding: 0.75em 0;
width: 100%;
min-height: 55px;

&:disabled {
background: none;
Expand Down Expand Up @@ -89,7 +91,9 @@
font-style: italic;
margin: 5px 0;
}
&-error { color: $red; }
&-error {
color: $red;
}
}

&-submit {
Expand All @@ -110,7 +114,9 @@
max-width: 340px;
text-align: left;

@include breakpoint(mobile) { margin-bottom: 0; }
@include breakpoint(mobile) {
margin-bottom: 0;
}

a {
color: $blue-grey;
Expand All @@ -132,7 +138,9 @@
padding: 0.5em 30px 0.4em;
transition: background 0.1s $base-easing;

&:hover { background: darken($green, 10%); }
&:hover {
background: darken($green, 10%);
}
}

input[disabled] {
Expand Down Expand Up @@ -163,8 +171,10 @@
user-select: none;
}

input[type=checkbox] { display: none; }
input[type=checkbox] + .form-checklist-item-box {
input[type="checkbox"] {
display: none;
}
input[type="checkbox"] + .form-checklist-item-box {
flex: 0 0 21px;
border: 1px solid $black;
display: block;
Expand All @@ -174,10 +184,30 @@
position: relative;
top: -1px;
}
input[type=checkbox]:checked + .form-checklist-item-box {
background: url('../images/icons/form-checkmark.svg') center no-repeat;
input[type="checkbox"]:checked + .form-checklist-item-box {
background: url("../images/icons/form-checkmark.svg") center no-repeat;
}
}
}
}

&-grid {
display: flex;
flex-wrap: wrap;

&-card {
float: none;
width: calc(25% - 1.5em);
margin: 0.75em;

.disabled {
opacity: 0.45;
}

img {
display: block;
width: 100%;
}
}
}
}
15 changes: 11 additions & 4 deletions lib/changelog/policies/feed.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
defmodule Changelog.Policies.Feed do
use Changelog.Policies.Default

def index(actor), do: is_active_member(actor)
def create(actor), do: is_active_member(actor)
def index(actor), do: is_admin_or_active_member(actor)

def update(actor, feed), do: is_owner(actor, feed)
def delete(actor, feed), do: is_owner(actor, feed)
def new(actor), do: is_admin_or_active_member(actor)
def create(actor), do: new(actor)

def edit(actor, feed), do: is_owner(actor, feed)
def update(actor, feed), do: edit(actor, feed)
def delete(actor, feed), do: edit(actor, feed)

defp is_admin_or_active_member(actor) do
is_admin(actor) || is_active_member(actor)
end

defp is_active_member(nil), do: false
defp is_active_member(actor), do: Map.get(actor, :active_membership, false)
Expand Down
3 changes: 3 additions & 0 deletions lib/changelog/schema/person.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Changelog.Person do
EpisodeHost,
EpisodeRequest,
Faker,
Feed,
Files,
Membership,
NewsItem,
Expand Down Expand Up @@ -96,6 +97,8 @@ defmodule Changelog.Person do
has_many :subscriptions, Subscription, where: [unsubscribed_at: nil], on_delete: :delete_all
has_many :episode_requests, EpisodeRequest, foreign_key: :submitter_id, on_delete: :delete_all

has_many :feeds, Feed, foreign_key: :owner_id

timestamps()
end

Expand Down
111 changes: 111 additions & 0 deletions lib/changelog_web/controllers/home/feed_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
defmodule ChangelogWeb.Home.FeedController do
use ChangelogWeb, :controller

alias Changelog.{Feed, Podcast}
alias Changelog.ObanWorkers.FeedUpdater

plug :assign_podcasts when action in [:index, :new, :create, :edit, :update]
plug :assign_feed when action in [:edit, :update, :delete, :refresh]
plug Authorize, [Policies.Feed, :feed]
plug :preload_current_user_extras

def index(conn = %{assigns: %{current_user: me}}, _params) do
feeds = me |> assoc(:feeds) |> Repo.all()

conn
|> assign(:feeds, feeds)
|> render()
end

def new(conn, _params) do
changeset = Feed.insert_changeset(%Feed{})

conn
|> assign(:changeset, changeset)
|> render(:new)
end

def create(conn = %{assigns: %{current_user: user}}, %{"feed" => feed_params}) do
changeset = Feed.insert_changeset(%Feed{owner_id: user.id}, feed_params)

case Repo.insert(changeset) do
{:ok, feed} ->
Repo.update(Feed.file_changeset(feed, feed_params))

conn
|> put_flash(:success, "Your new feed has been created! 👏")
|> redirect(to: ~p"/~/feeds")

{:error, changeset} ->
require IEx
IEx.pry()

conn
|> put_flash(:error, "There was a problem saving your feed. 😭")
|> assign(:changeset, changeset)
|> render(:new)
end
end

def edit(conn = %{assigns: %{feed: feed}}, _params) do
changeset = Feed.update_changeset(feed)
render(conn, :edit, feed: feed, changeset: changeset)
end

def update(conn = %{assigns: %{feed: feed}}, params = %{"feed" => feed_params}) do
changeset = Feed.update_changeset(feed, feed_params)

case Repo.update(changeset) do
{:ok, feed} ->
params = replace_next_edit_path(params, ~p"/~/feeds/#{feed}/edit")

conn
|> put_flash(:success, "Your feed has been updated! ✨")
|> redirect_next(params, ~p"/~/feeds")

{:error, changeset} ->
conn
|> put_flash(:error, "There was a problem updating your feed. 😭")
|> render(:edit, feed: feed, changeset: changeset)
end
end

def delete(conn = %{assigns: %{feed: feed}}, _params) do
Repo.delete!(feed)

conn
|> put_flash(:success, "Your feed has been put out to pasture. 🐑")
|> redirect(to: ~p"/~/feeds")
end

def refresh(conn = %{assigns: %{feed: feed}}, _params) do
FeedUpdater.queue(feed)

conn
|> put_flash(:success, "Your feed is being rebuilt as we speak. 🥂")
|> redirect(to: ~p"/~/feeds")
end

defp preload_current_user_extras(conn = %{assigns: %{current_user: me}}, _) do
me =
me
|> Repo.preload(:sponsors)
|> Repo.preload(:active_membership)

assign(conn, :current_user, me)
end

defp assign_podcasts(conn, _params) do
podcasts =
Podcast.active()
|> Podcast.by_position()
|> Repo.all()

assign(conn, :podcasts, podcasts)
end

defp assign_feed(conn = %{params: %{"id" => id}}, _params) do
feed = Feed |> Repo.get(id) |> Feed.preload_all()
assign(conn, :feed, feed)
end
end
2 changes: 2 additions & 0 deletions lib/changelog_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ defmodule ChangelogWeb.Router do
post "/~/subscribe", HomeController, :subscribe
post "/~/unsubscribe", HomeController, :unsubscribe

resources "/~/feeds", Home.FeedController

get "/in", AuthController, :new, as: :sign_in
post "/in", AuthController, :new, as: :sign_in
get "/in/:token", AuthController, :create, as: :sign_in
Expand Down
20 changes: 0 additions & 20 deletions lib/changelog_web/templates/home/_nav.html.eex

This file was deleted.

25 changes: 25 additions & 0 deletions lib/changelog_web/templates/home/_nav.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<nav class="subnav">
<ul class="subnav-list">
<li class={"subnav-list-item #{SharedHelpers.active_class(@conn, ["home-show"])}"}>
<%= link("Subscriptions", to: ~p"/~") %>
</li>
<%= if Policies.Feed.index(@current_user) do %>
<li class={"subnav-list-item #{SharedHelpers.active_class(@conn, ["feed-"])}"}>
<%= link("Custom Feeds", to: ~p"/~/feeds") %>
</li>
<% end %>
<li class={"subnav-list-item #{SharedHelpers.active_class(@conn, ["home-profile"])}"}>
<%= link to: ~p"/~/profile" do %>
Public Profile <%= if !@current_user.public_profile, do: "🤫" %>
<% end %>
</li>
<li class={"subnav-list-item #{SharedHelpers.active_class(@conn, ["home-account"])}"}>
<%= link("Account Settings", to: ~p"/~/account") %>
</li>
<%= for sponsor <- @current_user.sponsors do %>
<li class="subnav-list-item">
<%= link(sponsor.name, to: ~p"/sponsor/#{sponsor.id}") %>
</li>
<% end %>
</ul>
</nav>
Loading

0 comments on commit d1fad5f

Please sign in to comment.