Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users Can Manage Resources #7

Open
SophieDeBenedetto opened this issue Jul 1, 2021 · 11 comments
Open

Users Can Manage Resources #7

SophieDeBenedetto opened this issue Jul 1, 2021 · 11 comments

Comments

@SophieDeBenedetto
Copy link
Collaborator

User Story

As a user
When I visit /resources (or whatever we decide to call this route)
Then I can create a new resource,
View all resources,
View a single resource with all of its details,
Edit an existing resource, or
Delete a resource

Implementation Details

This page should be supported by a live view that uses LiveView live_actions to control what the user sees and how they can interact with the page.

Let's start by delivering:

  • User can visit /resources and see a list of all of the resources.

This should mount a live view with the live_action set to :index and display a list of all resources. For more info on routing with live_actions, see the docs here https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.Router.html#live/4-actions-and-live-navigation.

@herminiotorres
Copy link
Contributor

Hi @SophieDeBenedetto, after chatt, I had been reflecting about this name what we use. Maybe it isn't a good name though, because many libraries and the entire industry use or reference "resource" as a term by part of some domain in some context, very generic, and after my researching maybe we should change resource for content? What do you think?

@ghost
Copy link

ghost commented Jul 2, 2021

Hi @SophieDeBenedetto, after chatt, I had been reflecting about this name what we use. Maybe it isn't a good name though, because many libraries and the entire industry use or reference "resource" as a term by part of some domain in some context, very generic, and after my researching maybe we should change resource for content? What do you think?

I agree with @herminiotorres! To be honest, I was a little bit confused because Phoenix already use the name "resource" with a different meaning. I think that "content" could makes things clearer and more explicit for us!
Anyway, I'm super excited about our new project! 💜💜💜

@SophieDeBenedetto
Copy link
Collaborator Author

Thanks for sharing your thoughts @cristineguadelupe and @herminiotorres! I agree Resource can be a little confusing for the reasons you both mentioned. I think Content might be similarly confusing though. What do you guys think about calling it Post?

@herminiotorres
Copy link
Contributor

I like it, we definitely need to go forward and use it Post.

@SophieDeBenedetto
Copy link
Collaborator Author

SophieDeBenedetto commented Jul 22, 2021

We have made some great progress on this issue with this PR #18. Let's work on the following steps next:

  • Post show page that shows the details of a post.
  • User can click on a post from the index page and see the post show page. I.e if i'm on /posts and I click on a post, I get directed to posts/1 and see the post details. The post show page should also be a live view.
  • From there, let's build a "post edit" page that is also a live view. From the post show page, I should see a link to "edit" the post and be taken to a form to edit the post.

First, we'll get each of these pages working as their own separate live view. Once that's done, I'll walk you guys through adding some modal functionality so that a user can edit a post without getting redirected to a different live view. Instead a modal will pop up with the edit form. That's for next time though 😄

@SophieDeBenedetto
Copy link
Collaborator Author

Create New Post Feature

Desired Behavior

As a user
When I visit /posts
Then I should a see button "add a new post"
And when I click that button
I should see:

  • The URL to change -> posts/new
  • BUT the page stays on the list of posts
  • And I see a form appear at the bottom of the list of posts
  • I should be able to fill out the form and submit it
  • When I submit the form, the form should disappear, and I should see the new post added to the list of posts

@SophieDeBenedetto
Copy link
Collaborator Author

@SophieDeBenedetto
Copy link
Collaborator Author

SophieDeBenedetto commented Aug 2, 2021

New Post Form Submission Feature

We will do three things to get this working:

  • Make the current user available to the FormComponent so that we can create a new post that belongs to the correct user.
  • Submit the form to the FormComponent
  • Handling the form submission by creating a new post and updating the page

Making the current user available to the form component

  • In the form component's parent live view, PostLive, we will need to extract the user_token from the session. Any live view always calls the mount/3 function when it first starts up. The second argument given to mount/3 is the current session object. Thanks to our usage of the Phoenix Auth generator, the session map will contain a key, :user_token. But wait! Since we need a logged-in user in order to create new posts, and we need to get the logged in user when we load the PostLive live view that backs the /posts route, we need to make sure that we have a logged-in user before allowing the user to click the "New Post" button. So, first things first...
  • Create two versions of the PostLive mount function---one that pattern matches :user_token out of the session argument and one that doesn't, something like this: (note that the "get all products code is not shown here, this is just an example of how to work with the session).
# this will run when "user_token" is present in the session map, which will happen if there is a logged-in user
  def mount(_params, %{"user_token" => token}, socket) do
    # ...
  end

# I think you will want to assign current_user to `nil` here so that you can still refer to `@current_user` assignment in the template to check its value. If there is no such key in socket assigns at all, the template will through an error. You can double check me on this though.
  def mount(_params, _session, socket) do
    {:ok, assign(socket, :current_user, nil)}
  end

In the mount function that gets called with "user_token" is present, use the token to fetch the current user from the database using the Accounts.get_user_by_session_token(token) function that the Phoenix Auth generator made available to use. Store that user in the socket:

def mount(_params, %{"user_token" => token}, socket) do
  current_user =    Accounts.get_user_by_session_token(token)
   {:ok, assign(socket, :current_user, current_user)}
end
  • Then, in your PostLive's template, add an if condition that only shows the "New Post" button if socket assigns has a @current_user present.

Once that is done, you're ready to pass the current user into the FormComponent so that you can use that user to create a new post that belongs to that person.

When you call the FormComponent from the PostLive template, pass in the current user as part of the assigns, like this:

<%= live_component FormComponent, current_user: @current_user %>

Now, recalls that FormComponent's update/2 function looks like this:

def update(assigns, socket)
  # ...
end

The assigns argument is a map that contains whatever key/value pairs you called live_component with, in our case, :current_user. So, in your update/2 callback function, when you call:

def update(assigns, socket)
  {:ok,
     socket
     |> assign(assigns)
     # ... 
  }
end

You are making sure that the key/value pairs you call the component with get added to the component's socket assigns so that you can look them up later. This way, when you handle the form event to create a new post, you have access to socket.assigns.current_user in order to give that post a user_id of socket.assigns.current_user.

That's it! With this code, you have ensured that the FormComponent has the current user stored in its own socket assigns and you can use that user to create a new post record.

Submitting the New Post Form

Use the docs here to guide you https://hexdocs.pm/phoenix_live_view/form-bindings.html#form-events.

  • Start by adding the phx_submit: "save" event to your form.
  • This way, when a user submits the form, it will send an event to the FormComponent. So, you will need to implement an event handler function in the FormComponent, like this:
def handle_event("save", %{"post" => post_params}, socket) do

end
  • Inside this handle_event/3 function, you will need to create and save a new post that belongs to socket.assigns.current_user. Use the Post.create/2 core function you defined earlier to create a post that belongs to a user.

Handling Successful Post Creation

  • Once the post is successfully created, you will want the following to happen:
    • New post form disappears
    • List of posts now includes the newly created post
  • To accomplish both of these goals, we can use the push_redirect function, see the linked docs. This will redirect back to /posts route, which means that the PostLive live view will mount again with a live_action of :index, the form will not be shown, and a fresh set of posts will be queried from the DB in the PostLive mount/3 function. The mount/3 function will be called again because the live view is being redirected to, and therefore it is re-mounted.

@SophieDeBenedetto
Copy link
Collaborator Author

SophieDeBenedetto commented Aug 26, 2021

Users can delete posts

As a user
When I view the /posts page
Then I see a "delete" link or button next to any posts that I added
And I can click the "delete" link or button and the post is deleted from the list

Implementation

  1. Create a link or button that uses a phx-click event binding to sent a "delete" event to the PostLive live view. It should send the event with a parameter of the post ID. Docs on event bindings here https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#module-bindings
  2. Create an event handler in PostLive to receive this event.
  3. The event handler should delete the event with the given ID from the params and return the socket with the updated list of posts

@herminiotorres
Copy link
Contributor

Also, I am thinking how we can do a better shape to our app architecture.
Context: Bucket
Schema: Item, and the item schema, new to answer your type/kind, like blog, book, website, course, YouTube, video and so on.
And we could use a List schema to tie some kind of items, like a blog post has sequential of blogs split as many parts, and we intended to tie these things all together. Besides, we could create a list of something, imagine I need to learn about concurrency data, I like to create my curated list with the concurrency data book, and many others links where it will be crucial to get dig in some context.

What do you say it about my suggestion to refactoring what we have until now?

@SophieDeBenedetto
Copy link
Collaborator Author

Also, I am thinking how we can do a better shape to our app architecture.
Context: Bucket
Schema: Item, and the item schema, new to answer your type/kind, like blog, book, website, course, YouTube, video and so on.
And we could use a List schema to tie some kind of items, like a blog post has sequential of blogs split as many parts, and we intended to tie these things all together. Besides, we could create a list of something, imagine I need to learn about concurrency data, I like to create my curated list with the concurrency data book, and many others links where it will be crucial to get dig in some context.

What do you say it about my suggestion to refactoring what we have until now?

  • Post context -> doesn't necessarily refer to the fact that a given link is a blog post, it refers to the fact that each item is posted by a user. So a post on our site, can be of any type--link to a video, a blog post, a book, etc.
  • The idea of collecting a list/set/bucket of resources on a topic seems like it will really help our users to learn more about a specific category (concurrency, supervisors, Ecto, etc.). This feels like a separate feature from our current "Post Index/Show/Edit/Delete" feature-set. The feature would allow users to create their own "buckets" or "notebooks", add posts from the available list of posts, and share buckets.

Next Steps

@herminiotorres will open up a new issue that describes the user story here 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants