Skip to content

Commit

Permalink
fix: profile picture upload
Browse files Browse the repository at this point in the history
  • Loading branch information
AfonsoMartins26 committed Sep 17, 2024
1 parent 4fd7220 commit 6d6bd05
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 238 deletions.
3 changes: 2 additions & 1 deletion lib/atomic/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,11 @@ defmodule Atomic.Accounts do
{:error, %Ecto.Changeset{}}
"""
def update_user(%User{} = user, attrs \\ %{}, _after_save \\ &{:ok, &1}) do
def update_user(%User{} = user, attrs \\ %{}, after_save \\ &{:ok, &1}) do
user
|> User.changeset(attrs)
|> Repo.update()
|> after_save(after_save)
end

@doc """
Expand Down
1 change: 0 additions & 1 deletion lib/atomic/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ defmodule Atomic.Accounts.User do

def picture_changeset(user, attrs) do
user
|> cast(attrs, @required_fields ++ @optional_fields)
|> cast_attachments(attrs, [:profile_picture])
end

Expand Down
10 changes: 1 addition & 9 deletions lib/atomic_web/components/sidebar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ defmodule AtomicWeb.Components.Sidebar do
<AtomicWeb.Components.Dropdown.dropdown orientation={@orientation} items={dropdown_items(@current_user)} id="user-menu-button">
<:wrapper>
<button class="flex w-full select-none flex-row items-center gap-x-2 px-4 py-3 text-sm font-semibold leading-6 text-zinc-700 lg:px-0">
<AtomicWeb.Components.Avatar.avatar name={@current_user.name} src={user_image(@current_user)} size={:xs} color={:light_gray} class="!text-sm" />
<AtomicWeb.Components.Avatar.avatar name={@current_user.name} src={Uploaders.ProfilePicture.url({@current_user.profile_picture, @current_user}, :original)} size={:xs} color={:light_gray} class="!text-sm" />
<span class="text-sm font-semibold leading-6" aria-hidden="true"><%= @current_user.name %></span>
<.icon name={:chevron_right} solid class="h-5 w-5" />
</button>
Expand Down Expand Up @@ -188,14 +188,6 @@ defmodule AtomicWeb.Components.Sidebar do
|> JS.dispatch("focus", to: "#mobile-sidebar")
end

defp user_image(user) do
if user.profile_picture do
Uploaders.ProfilePicture.url({user, user.profile_picture}, :original)
else
nil
end
end

defp get_organizations(nil), do: []
defp get_organizations(user), do: Organizations.list_user_organizations(user.id)
end
215 changes: 148 additions & 67 deletions lib/atomic_web/live/profile_live/form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,149 @@ defmodule AtomicWeb.ProfileLive.FormComponent do

alias Atomic.Accounts
alias Atomic.Socials
alias AtomicWeb.Components.ImageUploader

@extensions_whitelist ~w(.jpg .jpeg .gif .png)

@impl true
def render(assigns) do
~H"""
<div class="px-4 pt-4">
<.form :let={f} for={@changeset} id="profile-form" phx-target={@myself} phx-change="validate" phx-submit="save">
<!-- Upload de Foto -->
<div class="flex flex-wrap justify-between">
<!-- Secção para a imagem de capa -->
<div class="flex-1 max-w-[70%] pr-4">
<%= label(f, :cover_image, "Cover Image", class: "mt-3 mb-1 text-sm font-medium text-gray-700") %>
</div>
<!-- Secção para a imagem de perfil -->
<div class="flex flex-shrink-0 flex-col items-center">
<%= label(f, :name, "Profile Picture", class: "mt-3 mb-1 text-sm font-medium text-gray-700") %>
<.live_component module={ImageUploader} id="uploader-profile-picture" uploads={@uploads} target={@myself} />
</div>
</div>
<!-- Linha de separação -->
<hr class="mb-6 h-full w-full border-gray-200" />
<!-- Campos do formulário em duas colunas -->
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
<div>
<%= label(f, :name, "Full name", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :name,
required: true,
placeholder: gettext("John Doe"),
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :name) %></div>
</div>
<div>
<%= label(f, :email, "Email", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :email,
required: true,
placeholder: gettext("john_doe@mail.com"),
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :email) %></div>
</div>
<div>
<%= label(f, :username, "Username", class: "mb-1 text-sm font-medium text-gray-700") %>
<div class="relative flex w-full appearance-none rounded border border-zinc-300 px-3 text-zinc-900 placeholder-zinc-500 focus-within:z-10 focus-within:border-orange-400 focus-within:outline-none focus-within:ring-orange-400 sm:text-sm">
<span class="select-none self-center">@</span>
<%= text_input(f, :slug,
required: true,
spellcheck: false,
placeholder: gettext("john_doe"),
class: "w-full appearance-none border-none pl-0 text-zinc-900 placeholder-zinc-500 focus:outline-none focus:ring-transparent sm:text-sm"
) %>
</div>
<div class="text-sm text-red-600"><%= error_tag(f, :slug) %></div>
</div>
<div>
<%= label(f, :phone_number, "Phone number", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :phone_number,
required: true,
placeholder: gettext("912345678"),
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :phone_number) %></div>
</div>
<div>
<%= label(f, :bio, "Bio", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= textarea(f, :bio,
placeholder: "Tell us about yourself",
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :bio) %></div>
</div>
<div>
<%= label(f, :instagram, "Instagram Username", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :instagram,
placeholder: "your_username",
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :instagram) %></div>
</div>
<div>
<%= label(f, :facebook, "Facebook Username", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :facebook,
placeholder: "your_username",
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :facebook) %></div>
</div>
<div>
<%= label(f, :twitter, "X Username", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :twitter,
placeholder: "your_username",
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :twitter) %></div>
</div>
<div>
<%= label(f, :tiktok, "TikTok Username", class: "mb-1 text-sm font-medium text-gray-700") %>
<%= text_input(f, :tiktok,
placeholder: "your_username",
class: "w-full appearance-none rounded border border-zinc-300 px-3 py-2 text-zinc-900 placeholder-zinc-500 focus:z-10 focus:border-orange-400 focus:outline-none focus:ring-orange-400 sm:text-sm"
) %>
<div class="text-sm text-red-600"><%= error_tag(f, :tiktok) %></div>
</div>
</div>
<!-- Botões -->
<div class="mt-8 flex w-full justify-end">
<button type="submit" class="inline-flex rounded-md border-2 border-orange-500 bg-white px-6 py-2 text-sm font-medium text-orange-500 shadow-sm hover:bg-orange-600 hover:text-white">
Save
</button>
</div>
</.form>
</div>
"""
end

@impl true
def mount(socket) do
{:ok,
socket
|> allow_upload(:picture, accept: @extensions_whitelist, max_entries: 1)
|> allow_upload(:cover_image, accept: @extensions_whitelist, max_entries: 1)}
|> allow_upload(:image,
accept: Uploaders.ProfilePicture.extension_whitelist(),
max_entries: 1
)}
end

@impl true
def update(%{user: user} = assigns, socket) do
user_changeset = Accounts.change_user(user)
socials_changeset = Socials.changeset(%Socials{}, %{})

combined_changeset = %{
user_changeset
| changes: Map.merge(user_changeset.changes, socials_changeset.changes)
}
changeset = Accounts.change_user(user)

{:ok,
socket
|> assign(assigns)
|> assign(:changeset, combined_changeset)}
|> assign(:changeset, changeset)}
end

@impl true
Expand All @@ -41,41 +159,31 @@ defmodule AtomicWeb.ProfileLive.FormComponent do
end

@impl true
def handle_event("save", %{"user" => user_params, "socials" => socials_params}, socket) do
def handle_event("save", %{"user" => user_params}, socket) do
user = socket.assigns.user

socials_changeset = Socials.changeset(%Socials{}, socials_params)

case socials_changeset.valid? do
true ->
:ok

false ->
{:noreply, assign(socket, :changeset, socials_changeset)}
end
flash_text =
if user_params["email"] != user.email do
case Accounts.apply_user_email(user, %{email: user_params["email"]}) do
{:ok, applied_user} ->
Accounts.deliver_update_email_instructions(
applied_user,
user.email,
&Routes.profile_edit_url(socket, :confirm_email, &1)
)

"Profile updated successfully, please check your email to confirm the new address."
end
else
"Profile updated successfully."
end

case Accounts.update_user(
user,
Map.put(user_params, "email", user.email),
&consume_image_data(socket, &1)
) do
{:ok, _user} ->
flash_text =
if user_params["email"] != user.email do
case Accounts.apply_user_email(user, %{email: user_params["email"]}) do
{:ok, applied_user} ->
Accounts.deliver_update_email_instructions(
applied_user,
user.email,
&Routes.profile_edit_url(socket, :confirm_email, &1)
)

"Profile updated successfully, please check your email to confirm the new address."
end
else
"Profile updated successfully."
end

{:noreply,
socket
|> put_flash(:success, flash_text)
Expand All @@ -87,26 +195,12 @@ defmodule AtomicWeb.ProfileLive.FormComponent do
end

defp consume_image_data(socket, user) do
consume_uploaded_entries(socket, :picture, fn %{path: path}, entry ->
resized_path = resize_image(path, 200, 200)

Accounts.update_user(user, %{
"picture" => %Plug.Upload{
consume_uploaded_entries(socket, :image, fn %{path: path}, entry ->
Accounts.update_user_picture(user, %{
"profile_picture" => %Plug.Upload{
content_type: entry.client_type,
filename: entry.client_name,
path: resized_path
}
})
end)

consume_uploaded_entries(socket, :cover_image, fn %{path: path}, entry ->
resized_path = resize_image(path, 800, 250)

Accounts.update_user(user, %{
"cover_image" => %Plug.Upload{
content_type: entry.client_type,
filename: entry.client_name,
path: resized_path
path: path
}
})
end)
Expand All @@ -118,17 +212,4 @@ defmodule AtomicWeb.ProfileLive.FormComponent do
{:ok, user}
end
end

defp resize_image(path, width, height) do
command = "convert"
args = ["-resize", "#{width}x#{height}", path, path]

case System.cmd(command, args) do
{_, 0} ->
{:ok, path}

{error_msg, _} ->
{:error, error_msg}
end
end
end
Loading

0 comments on commit 6d6bd05

Please sign in to comment.