From 6d6bd05ac6b4aa04010484b905cf166491cd214e Mon Sep 17 00:00:00 2001 From: Afonso Martins Date: Tue, 17 Sep 2024 19:01:41 +0100 Subject: [PATCH] fix: profile picture upload --- lib/atomic/accounts.ex | 3 +- lib/atomic/accounts/user.ex | 1 - lib/atomic_web/components/sidebar.ex | 10 +- .../live/profile_live/form_component.ex | 215 ++++++++++++------ .../profile_live/form_component.html.heex | 154 ------------- lib/atomic_web/live/profile_live/show.ex | 4 - .../live/profile_live/show.html.heex | 10 +- 7 files changed, 159 insertions(+), 238 deletions(-) delete mode 100644 lib/atomic_web/live/profile_live/form_component.html.heex diff --git a/lib/atomic/accounts.ex b/lib/atomic/accounts.ex index 97b67564c..9bd128519 100644 --- a/lib/atomic/accounts.ex +++ b/lib/atomic/accounts.ex @@ -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 """ diff --git a/lib/atomic/accounts/user.ex b/lib/atomic/accounts/user.ex index 59650399e..a8ded2443 100644 --- a/lib/atomic/accounts/user.ex +++ b/lib/atomic/accounts/user.ex @@ -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 diff --git a/lib/atomic_web/components/sidebar.ex b/lib/atomic_web/components/sidebar.ex index 083ac4b49..31dc86f70 100644 --- a/lib/atomic_web/components/sidebar.ex +++ b/lib/atomic_web/components/sidebar.ex @@ -122,7 +122,7 @@ defmodule AtomicWeb.Components.Sidebar do <:wrapper> @@ -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 diff --git a/lib/atomic_web/live/profile_live/form_component.ex b/lib/atomic_web/live/profile_live/form_component.ex index 1130b1d11..3482433e1 100644 --- a/lib/atomic_web/live/profile_live/form_component.ex +++ b/lib/atomic_web/live/profile_live/form_component.ex @@ -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""" +
+ <.form :let={f} for={@changeset} id="profile-form" phx-target={@myself} phx-change="validate" phx-submit="save"> + +
+ +
+ <%= label(f, :cover_image, "Cover Image", class: "mt-3 mb-1 text-sm font-medium text-gray-700") %> +
+ +
+ <%= 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} /> +
+
+ +
+ +
+
+ <%= 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" + ) %> +
<%= error_tag(f, :name) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :email) %>
+
+ +
+ <%= label(f, :username, "Username", class: "mb-1 text-sm font-medium text-gray-700") %> +
+ @ + <%= 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" + ) %> +
+
<%= error_tag(f, :slug) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :phone_number) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :bio) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :instagram) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :facebook) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :twitter) %>
+
+ +
+ <%= 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" + ) %> +
<%= error_tag(f, :tiktok) %>
+
+
+ +
+ +
+ +
+ """ + 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 @@ -41,18 +159,24 @@ 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, @@ -60,22 +184,6 @@ defmodule AtomicWeb.ProfileLive.FormComponent do &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) @@ -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) @@ -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 diff --git a/lib/atomic_web/live/profile_live/form_component.html.heex b/lib/atomic_web/live/profile_live/form_component.html.heex deleted file mode 100644 index 16922dfa8..000000000 --- a/lib/atomic_web/live/profile_live/form_component.html.heex +++ /dev/null @@ -1,154 +0,0 @@ -
- <.form :let={f} for={@changeset} id="profile-form" phx-target={@myself} phx-change="validate" phx-submit="save"> - - - -
- -
-
- <%= 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" - ) %> -
<%= error_tag(f, :name) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :email) %>
-
- -
- <%= label(f, :username, "Username", class: "mb-1 text-sm font-medium text-gray-700") %> -
- @ - <%= 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" - ) %> -
-
<%= error_tag(f, :slug) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :phone_number) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :bio) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :instagram) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :facebook) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :twitter) %>
-
- -
- <%= 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" - ) %> -
<%= error_tag(f, :tiktok) %>
-
-
- -
- -
- -
diff --git a/lib/atomic_web/live/profile_live/show.ex b/lib/atomic_web/live/profile_live/show.ex index 01a63a3d0..a04e74d7d 100644 --- a/lib/atomic_web/live/profile_live/show.ex +++ b/lib/atomic_web/live/profile_live/show.ex @@ -12,8 +12,6 @@ defmodule AtomicWeb.ProfileLive.Show do end @impl true - @spec handle_params(map(), any(), %{optional(:assigns) => map(), optional(any()) => any()}) :: - {:noreply, map()} def handle_params(%{"slug" => user_slug}, _, socket) do user = Accounts.get_user_by_slug(user_slug) @@ -31,5 +29,3 @@ defmodule AtomicWeb.ProfileLive.Show do |> assign(:is_current_user, is_current_user)} end end - -## TODO: Add Socials diff --git a/lib/atomic_web/live/profile_live/show.html.heex b/lib/atomic_web/live/profile_live/show.html.heex index 2b92c7ae1..617395e4d 100644 --- a/lib/atomic_web/live/profile_live/show.html.heex +++ b/lib/atomic_web/live/profile_live/show.html.heex @@ -1,6 +1,12 @@
- <.gradient class="h-64 w-full bg-cover bg-center" seed={@user.id} /> + <%= if @user.cover_image < 0 do %> +
+ <.avatar name={@user.name} color={:light_gray} class="h-36 w-36 text-4xl rounded-full border-4 border-white" type={:user} src={Uploaders.ProfilePicture.url({@user.profile_picture, @user}, :original)} /> +
+ <% else %> + <.gradient class="h-64 w-full bg-cover bg-center" seed={@user.id} /> + <% end %>
@@ -8,7 +14,7 @@
- <.avatar class="h-36 w-36 rounded-full border-4 border-white" name={@user.name} size={:xl} color={:light_gray} /> + <.avatar name={@user.name} color={:light_gray} class="h-36 w-36 text-4xl rounded-full border-4 border-white" type={:user} src={Uploaders.ProfilePicture.url({@user.profile_picture, @user}, :original)} />