Skip to content

Commit

Permalink
Merge pull request #81 from loftwah/dl/avatar-border
Browse files Browse the repository at this point in the history
Dl/avatar border
  • Loading branch information
loftwah authored Sep 2, 2024
2 parents ffa9d64 + 578aaf6 commit 184dd3c
Show file tree
Hide file tree
Showing 9 changed files with 127 additions and 98 deletions.
8 changes: 4 additions & 4 deletions app/controllers/users/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,18 @@ def update
protected

def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled])
devise_parameter_sanitizer.permit(:account_update, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled])
devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border])
devise_parameter_sanitizer.permit(:account_update, keys: [:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border])
end

def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled).tap do |user_params|
params.require(:user).permit(:email, :password, :password_confirmation, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :avatar_border).tap do |user_params|
user_params[:tags] = user_params[:tags].split(',').map(&:strip).to_json if user_params[:tags].present?
end
end

def account_update_params
params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :public_analytics).tap do |user_params|
params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :username, :full_name, :tags, :avatar, :banner, :description, :banner_enabled, :public_analytics, :avatar_border).tap do |user_params|
user_params[:tags] = user_params[:tags].split(',').map(&:strip).to_json if user_params[:tags].present?
end
end
Expand Down
11 changes: 11 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ def auto_link_urls(text)
end.html_safe
end

def avatar_border_class(border_preference)
case border_preference
when 'white'
'border-4 border-white'
when 'black'
'border-4 border-black'
else
''
end
end

def format_referrer(referrer)
return 'Direct' if referrer.blank?

Expand Down
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class User < ApplicationRecord
validates :username, uniqueness: true, allow_blank: true
validates :full_name, presence: true
validate :ensure_username_presence
validates :avatar_border, inclusion: { in: ['white', 'black', 'none'] }

after_save :generate_open_graph_image, unless: -> { Rails.env.test? }
after_save :download_and_store_avatar
Expand Down
164 changes: 87 additions & 77 deletions app/views/devise/registrations/edit.html.erb
Original file line number Diff line number Diff line change
@@ -1,80 +1,90 @@
<h2 class="text-2xl font-bold mb-4">Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<%= devise_error_messages! %>

<div class="mb-2">
<%= f.label :email, class: 'block text-gray-300 mb-1' %>
<%= f.email_field :email, autofocus: true, class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :username, class: 'block text-gray-300 mb-1' %>
<%= f.text_field :username, class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :full_name, class: 'block text-gray-300 mb-1' %>
<%= f.text_field :full_name, class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :tags, class: 'block text-gray-300 mb-1' %>
<%= f.text_field :tags, value: @user.parsed_tags.join(', '), class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :avatar, class: 'block text-gray-300 mb-1' %>
<%= f.text_field :avatar, class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :banner, class: 'block text-gray-300 mb-1' %>
<%= f.text_field :banner, class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :banner_enabled, class: 'block text-gray-300 mb-1' %>
<%= f.check_box :banner_enabled, class: 'rounded text-black' %>
<div class="max-w-2xl mx-auto p-6 bg-gray-800 rounded-lg shadow-md">
<h2 class="text-2xl font-bold mb-6 text-lime-200">Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: "space-y-6" }) do |f| %>
<%= devise_error_messages! %>

<div>
<%= f.label :email, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.email_field :email, autofocus: true, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :username, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_field :username, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :full_name, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_field :full_name, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :tags, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_field :tags, value: @user.parsed_tags.join(', '), class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :avatar, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_field :avatar, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :avatar_border, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.select :avatar_border, options_for_select([['White', 'white'], ['Black', 'black'], ['None', 'none']], @user.avatar_border), {}, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :banner, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_field :banner, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div class="flex items-center">
<%= f.check_box :banner_enabled, class: 'mr-2' %>
<%= f.label :banner_enabled, class: 'text-lime-200 font-semibold' %>
</div>

<div>
<%= f.label :description, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.text_area :description, rows: 3, class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div class="flex items-center">
<%= f.check_box :public_analytics, class: 'mr-2' %>
<%= f.label :public_analytics, class: 'text-lime-200 font-semibold' %>
</div>

<div>
<%= f.label :password, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.password_field :password, autocomplete: "off", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
<% if @minimum_password_length %>
<p class="text-gray-400 mt-2 text-sm"><%= @minimum_password_length %> characters minimum (leave blank if you don't want to change it)</p>
<% end %>
</div>

<div>
<%= f.label :password_confirmation, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.password_field :password_confirmation, autocomplete: "off", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
</div>

<div>
<%= f.label :current_password, class: 'block text-lime-200 font-semibold mb-2' %>
<%= f.password_field :current_password, autocomplete: "off", class: 'block w-full px-4 py-2 border border-gray-700 rounded bg-gray-900 text-white focus:outline-none focus:border-lime-500' %>
<p class="text-gray-400 mt-2 text-sm">We need your current password to confirm your changes</p>
</div>

<div class="actions text-center mt-8">
<%= f.submit "Update", class: 'bg-lime-500 hover:bg-lime-600 text-white font-bold py-2 px-4 rounded-sm text-sm uppercase tracking-wide transition duration-300 ease-in-out focus:outline-none' %>
</div>
<% end %>

<div class="mt-8 border-t border-gray-700 pt-6">
<h3 class="text-xl font-bold mb-4 text-lime-200">Cancel my account</h3>
<p class="mb-4 text-gray-300">Unhappy? You can cancel your account here.</p>
<%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure? This action cannot be undone." }, method: :delete, class: 'bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out' %>
</div>

<div class="mb-2">
<%= f.label :description, class: 'block text-gray-300 mb-1' %>
<%= f.text_area :description, class: 'w-full p-1 rounded text-black' %>
<div class="mt-6 text-center">
<%= link_to "Back", :back, class: 'text-lime-300 hover:text-lime-500 underline transition duration-300 ease-in-out' %>
</div>

<div class="field">
<%= f.label :public_analytics %>
<%= f.check_box :public_analytics %>
</div>

<div class="mb-2">
<%= f.label :password, class: 'block text-gray-300 mb-1' %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %>
<%= f.password_field :password, autocomplete: "off", class: 'w-full p-1 rounded text-black' %>
<i>(leave blank if you don't want to change it)</i>
</div>

<div class="mb-2">
<%= f.label :password_confirmation, class: 'block text-gray-300 mb-1' %>
<%= f.password_field :password_confirmation, autocomplete: "off", class: 'w-full p-1 rounded text-black' %>
</div>

<div class="mb-2">
<%= f.label :current_password, class: 'block text-gray-300 mb-1' %>
<%= f.password_field :current_password, autocomplete: "off", class: 'w-full p-1 rounded text-black' %>
<i>(we need your current password to confirm your changes)</i>
</div>

<div class="actions mb-2">
<%= f.submit "Update", class: 'bg-lime-500 hover:bg-lime-700 text-white font-bold py-1 px-2 rounded' %>
</div>
<% end %>

<h3 class="text-xl font-bold mb-2">Cancel my account</h3>

<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class: 'bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded' %></p>

<%= link_to "Back", :back, class: 'bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-2 rounded' %>
</div>
23 changes: 12 additions & 11 deletions app/views/links/user_links.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
</div>
<% end %>
<div class="avatar-container flex flex-col items-center <%= @user.banner_enabled && @user.banner.present? ? '-mt-16' : 'mt-8' %>">
<div class="avatar">
<% local_avatar_path = "/avatars/#{@user.username}_avatar#{File.extname(@user.avatar)}" %>
<% if File.exist?(Rails.root.join('public' + local_avatar_path)) %>
<%= image_tag local_avatar_path, alt: @user.email, class: "rounded-full border-4 border-gray-900 object-cover", style: "width: 8rem; height: 8rem;" %>
<% elsif @user.avatar.present? %>
<%= image_tag @user.avatar, alt: @user.email, class: "rounded-full border-4 border-gray-900 object-cover", style: "width: 8rem; height: 8rem;" %>
<% else %>
<%= image_tag "greg.jpg", alt: @user.email, class: "rounded-full border-4 border-gray-900 object-cover", style: "width: 8rem; height: 8rem;" %>
<% end %>
</div>
<div class="avatar">
<% local_avatar_path = "/avatars/#{@user.username}_avatar#{File.extname(@user.avatar)}" %>
<% if File.exist?(Rails.root.join('public' + local_avatar_path)) %>
<%= image_tag local_avatar_path, alt: @user.email, class: "rounded-full object-cover #{avatar_border_class(@user.avatar_border)}", style: "width: 8rem; height: 8rem;" %>
<% elsif @user.avatar.present? %>
<%= image_tag @user.avatar, alt: @user.email, class: "rounded-full object-cover #{avatar_border_class(@user.avatar_border)}", style: "width: 8rem; height: 8rem;" %>
<% else %>
<%= image_tag "greg.jpg", alt: @user.email, class: "rounded-full object-cover #{avatar_border_class(@user.avatar_border)}", style: "width: 8rem; height: 8rem;" %>
<% end %>
</div>
</div>
<div class="user-info mt-4">
<h1 class="text-2xl font-bold text-white text-stroke"><%= @user.full_name %></h1>
<h2 class="text-xl font-bold text-white text-stroke"><%= @user.username %></h2>
Expand All @@ -39,7 +40,7 @@
</div>
</div>
<% end %>
<div class="description pb-4">
<div class="description pb-4 text-center">
<p class="text-lg text-white"><%= auto_link_urls(@user.description) %></p>
</div>
</div>
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20240902002819_add_avatar_border_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddAvatarBorderToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :avatar_border, :string, default: 'white'
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions spec/controllers/users/registrations_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# spec/controllers/users/registrations_controller_spec.rb
require 'rails_helper'

RSpec.describe Users::RegistrationsController, type: :controller do
Expand All @@ -9,7 +8,7 @@
describe "POST #create" do
let(:valid_attributes) {
{ email: "[email protected]", password: "password", password_confirmation: "password",
username: "testuser", full_name: "Test User", tags: "tag1,tag2" }
username: "testuser", full_name: "Test User", tags: "tag1,tag2", avatar_border: "white" }
}

it "creates a new User" do
Expand All @@ -35,7 +34,7 @@

context "with valid params" do
let(:new_attributes) {
{ full_name: "New Name", tags: "new_tag1,new_tag2" }
{ full_name: "New Name", tags: "new_tag1,new_tag2", avatar_border: "black" }
}

it "updates the requested user" do
Expand All @@ -44,6 +43,7 @@
expect(user.full_name).to eq("New Name")
tags = user.tags.is_a?(String) ? JSON.parse(user.tags) : user.tags
expect(tags).to eq(["new_tag1", "new_tag2"])
expect(user.avatar_border).to eq("black")
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/factories/users.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# spec/factories/users.rb
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
password { "password123" }
password_confirmation { "password123" }
sequence(:username) { |n| "user#{n}" }
full_name { "Test User" }
tags { ["tag1", "tag2"] }
tags { ["tag1", "tag2"].to_json }
avatar_border { ['white', 'black', 'none'].sample }
end
end

0 comments on commit 184dd3c

Please sign in to comment.