Add user avatars

This commit is contained in:
Râu Cao 2023-09-06 12:15:25 +02:00
parent 86f85a90f4
commit 9e5aeaf572
Signed by: raucao
GPG Key ID: 15E65F399D084BA9
3 changed files with 82 additions and 24 deletions

View File

@ -19,6 +19,7 @@ class SettingsController < ApplicationController
def update def update
@user.preferences.merge!(user_params[:preferences] || {}) @user.preferences.merge!(user_params[:preferences] || {})
@user.display_name = user_params[:display_name] @user.display_name = user_params[:display_name]
@user.avatar = user_params[:avatar] if user_params[:avatar].present?
if @user.save if @user.save
if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name]) if @user.display_name && (@user.display_name != @user.ldap_entry[:display_name])
@ -117,7 +118,7 @@ class SettingsController < ApplicationController
end end
def user_params def user_params
params.require(:user).permit(:display_name, preferences: [ params.require(:user).permit(:display_name, :avatar, preferences: [
:lightning_notify_sats_received, :lightning_notify_sats_received,
:xmpp_exchange_contacts_with_invitees :xmpp_exchange_contacts_with_invitees
]) ])

View File

@ -5,6 +5,12 @@ class User < ApplicationRecord
serialize :preferences, UserPreferences serialize :preferences, UserPreferences
#
# File attachments
#
has_one_attached :avatar
# Relations # Relations
has_many :invitations, dependent: :destroy has_many :invitations, dependent: :destroy
has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id' has_one :invitation, inverse_of: :invitee, foreign_key: 'invited_user_id'
@ -40,6 +46,7 @@ class User < ApplicationRecord
validates_uniqueness_of :nostr_pubkey, allow_blank: true validates_uniqueness_of :nostr_pubkey, allow_blank: true
validate :acceptable_avatar
scope :confirmed, -> { where.not(confirmed_at: nil) } scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :pending, -> { where(confirmed_at: nil) } scope :pending, -> { where(confirmed_at: nil) }
scope :all_except, -> (user) { where.not(id: user) } scope :all_except, -> (user) { where.not(id: user) }
@ -140,6 +147,14 @@ class User < ApplicationRecord
@display_name ||= ldap_entry[:display_name] @display_name ||= ldap_entry[:display_name]
end end
def avatar_64px
avatar.variant(resize_to_fill: [64, 64])
end
def avatar_256px
avatar.variant(resize_to_fill: [256, 256])
end
def services_enabled def services_enabled
ldap_entry[:service] || [] ldap_entry[:service] || []
end end
@ -168,4 +183,17 @@ class User < ApplicationRecord
return @ldap_service if defined?(@ldap_service) return @ldap_service if defined?(@ldap_service)
@ldap_service = LdapService.new @ldap_service = LdapService.new
end end
def acceptable_avatar
return unless avatar.attached?
if avatar.blob.byte_size > 1.megabyte
errors.add(:avatar, "file size is too large")
end
acceptable_types = ["image/jpeg", "image/png"]
unless acceptable_types.include?(avatar.content_type)
errors.add(:avatar, "must be a JPEG or PNG file")
end
end
end end

View File

@ -1,33 +1,62 @@
<section> <section>
<h3>Profile</h3> <h3>Profile</h3>
<p class="mb-2"> <div class="mb-6">
<%= label :user_address, 'User address', class: 'font-bold' %> <p class="mb-2">
</p> <%= label :user_address, 'User address', class: 'font-bold' %>
<p data-controller="clipboard" class="flex gap-1 mb-2 sm:w-3/5"> </p>
<input type="text" id="user_address" class="grow" <p data-controller="clipboard" class="flex gap-1 mb-2 sm:w-3/5">
value=<%= @user.address %> disabled="disabled" <input type="text" id="user_address" class="grow"
data-clipboard-target="source" /> value=<%= @user.address %> disabled="disabled"
<button id="copy-user-address" class="btn-md btn-icon btn-outline shrink-0" data-clipboard-target="source" />
data-clipboard-target="trigger" data-action="clipboard#copy" <button id="copy-user-address" class="btn-md btn-icon btn-outline shrink-0"
title="Copy to clipboard"> data-clipboard-target="trigger" data-action="clipboard#copy"
<span class="content-initial"> title="Copy to clipboard">
<%= render partial: "icons/copy", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %> <span class="content-initial">
</span> <%= render partial: "icons/copy", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
<span class="content-active hidden"> </span>
<%= render partial: "icons/check", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %> <span class="content-active hidden">
</span> <%= render partial: "icons/check", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
</button> </span>
</p> </button>
<p class="text-sm text-gray-500"> </p>
Your user address for Chat and Lightning Network. <p class="text-sm text-gray-500">
</p> Your user address for Chat and Lightning Network.
</p>
</div>
<%= form_for(@user, url: setting_path(:profile), html: { :method => :put }) do |f| %> <%= form_for(@user, url: setting_path(:profile), html: { :method => :put }) do |f| %>
<%= render FormElements::FieldsetComponent.new(tag: "div", title: "Display name") do %> <%= render FormElements::FieldsetComponent.new(tag: "div", title: "Display name") do %>
<%= f.text_field :display_name, class: "w-full sm:w-3/5 mb-2" %> <%= f.text_field :display_name, class: "w-full sm:w-3/5" %>
<% if @validation_errors.present? && @validation_errors[:display_name].present? %> <% if @validation_errors.present? && @validation_errors[:display_name].present? %>
<p class="error-msg"><%= @validation_errors[:display_name].first %></p> <p class="error-msg mt-2"><%= @validation_errors[:display_name].first %></p>
<% end %> <% end %>
<% end %> <% end %>
<label class="block">
<p class="font-bold mb-1">
Avatar
</p>
<p class="text-gray-500">
Default profile picture
</p>
<div class="flex items-center gap-6">
<% if current_user.avatar.attached? %>
<p class="flex-none">
<%= image_tag current_user.reload.avatar_256px, class: "h-24 w-24 rounded-lg" %>
</p>
<% end %>
<div class="grow">
<p class="mb-2">
<%= f.file_field :avatar, class: "" %>
<p class="text-sm text-gray-500">
JPEG or PNG image, not larger than 1 megabyte
</p>
<% if @validation_errors.present? && @validation_errors[:avatar].present? %>
<p class="error-msg mb-2"><%= @validation_errors[:avatar].first %></p>
<% end %>
</div>
</div>
</label>
<p class="mt-8 pt-6 border-t border-gray-200 text-right"> <p class="mt-8 pt-6 border-t border-gray-200 text-right">
<%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %> <%= f.submit 'Save', class: "btn-md btn-blue w-full md:w-auto" %>
</p> </p>