fix: adding user avatar editing

This commit is contained in:
Paul Couture 2023-12-17 12:32:35 -06:00
parent a2ecb62a05
commit 3d3029ceb6
17 changed files with 502 additions and 17 deletions

View File

@ -23,6 +23,10 @@ server {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
location ^~ /livewire {
try_files $uri $uri/ /index.php?$query_string;
}
# handle .php
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;

View File

@ -31,6 +31,11 @@ public function index()
]);
}
public function avatar() {
$user = auth()->user();
return view('')
}
/**
* Show the form for creating a new resource.
*

View File

@ -18,6 +18,8 @@ public function edit(Request $request): View
{
return view('profile.edit', [
'user' => $request->user(),
'artist' => $request->user()->artists->first(),
'avatar' => $request->user()->artists->first()->avatar,
]);
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Livewire\Artist;
use Livewire\Component;
use Livewire\WithFileUploads;
use Spatie\Image\Image;
use Intervention\Image\Facades\Image as InterventionImage;
use ImageOptimizer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
use Illuminate\Http\RedirectResponse;
use Livewire\Attributes\Validate;
class Avatar extends Component
{
use WithFileUploads;
public $avatar;
public $x;
public $y;
public $width;
public $height;
public function render()
{
return view('livewire.artist.avatar');
}
public function save()
{
$disk = Storage::disk('static');
$avatar = $this->avatar->store('avatars', 'static');
Image::load($disk->path($avatar))
->manualCrop($this->width, $this->height, $this->x, $this->y)
->save();
ImageOptimizer::optimize($disk->path($avatar));
auth()->user()->artists()->first()->update(compact('avatar'));
$this->avatar = null;
return redirect(request()->header('Referer'));
}
}

View File

@ -61,4 +61,12 @@ public function wallets()
return $this->hasMany(Wallet::class);
}
public function avatar()
{
if (!$this->avatar) {
return asset('resources/img/default_avatars/default_avatar_male.svg');
}
return config('app.static_asset_url') . '/' . $this->avatar;
}
}

View File

@ -19,6 +19,7 @@
"laravel/tinker": "^2.8",
"livewire/livewire": "^3.2",
"mckenziearts/blade-untitledui-icons": "^1.2",
"spatie/image": "^2.2",
"spatie/laravel-image-optimizer": "^1.7"
},
"require-dev": {

197
site/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "78067a62b08c07fdd16ae78c4a9de27e",
"content-hash": "9d6e4f83e7c9482fd5ba88df35ad732f",
"packages": [
{
"name": "andreiio/blade-remix-icon",
@ -3157,6 +3157,71 @@
],
"time": "2023-12-04T10:14:46+00:00"
},
{
"name": "league/glide",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/glide.git",
"reference": "2ff92c8f1edc80b74e2d3c5efccfc7223f74d407"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/glide/zipball/2ff92c8f1edc80b74e2d3c5efccfc7223f74d407",
"reference": "2ff92c8f1edc80b74e2d3c5efccfc7223f74d407",
"shasum": ""
},
"require": {
"intervention/image": "^2.7",
"league/flysystem": "^2.0|^3.0",
"php": "^7.2|^8.0",
"psr/http-message": "^1.0|^2.0"
},
"require-dev": {
"mockery/mockery": "^1.3.3",
"phpunit/php-token-stream": "^3.1|^4.0",
"phpunit/phpunit": "^8.5|^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\Glide\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"homepage": "http://reinink.ca"
},
{
"name": "Titouan Galopin",
"email": "galopintitouan@gmail.com",
"homepage": "https://titouangalopin.com"
}
],
"description": "Wonderfully easy on-demand image manipulation library with an HTTP based API.",
"homepage": "http://glide.thephpleague.com",
"keywords": [
"ImageMagick",
"editing",
"gd",
"image",
"imagick",
"league",
"manipulation",
"processing"
],
"support": {
"issues": "https://github.com/thephpleague/glide/issues",
"source": "https://github.com/thephpleague/glide/tree/2.3.0"
},
"time": "2023-07-08T06:26:07+00:00"
},
{
"name": "league/mime-type-detection",
"version": "1.14.0",
@ -5069,6 +5134,75 @@
],
"time": "2022-12-18T12:58:32+00:00"
},
{
"name": "spatie/image",
"version": "2.2.7",
"source": {
"type": "git",
"url": "https://github.com/spatie/image.git",
"reference": "2f802853aab017aa615224daae1588054b5ab20e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/image/zipball/2f802853aab017aa615224daae1588054b5ab20e",
"reference": "2f802853aab017aa615224daae1588054b5ab20e",
"shasum": ""
},
"require": {
"ext-exif": "*",
"ext-json": "*",
"ext-mbstring": "*",
"league/glide": "^2.2.2",
"php": "^8.0",
"spatie/image-optimizer": "^1.7",
"spatie/temporary-directory": "^1.0|^2.0",
"symfony/process": "^3.0|^4.0|^5.0|^6.0"
},
"require-dev": {
"pestphp/pest": "^1.22",
"phpunit/phpunit": "^9.5",
"symfony/var-dumper": "^4.0|^5.0|^6.0",
"vimeo/psalm": "^4.6"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Image\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Manipulate images with an expressive API",
"homepage": "https://github.com/spatie/image",
"keywords": [
"image",
"spatie"
],
"support": {
"source": "https://github.com/spatie/image/tree/2.2.7"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2023-07-24T13:54:13+00:00"
},
{
"name": "spatie/image-optimizer",
"version": "1.7.2",
@ -5311,6 +5445,67 @@
],
"time": "2023-08-23T09:04:39+00:00"
},
{
"name": "spatie/temporary-directory",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/temporary-directory.git",
"reference": "efc258c9f4da28f0c7661765b8393e4ccee3d19c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/efc258c9f4da28f0c7661765b8393e4ccee3d19c",
"reference": "efc258c9f4da28f0c7661765b8393e4ccee3d19c",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\TemporaryDirectory\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Vanderbist",
"email": "alex@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Easily create, use and destroy temporary directories",
"homepage": "https://github.com/spatie/temporary-directory",
"keywords": [
"php",
"spatie",
"temporary-directory"
],
"support": {
"issues": "https://github.com/spatie/temporary-directory/issues",
"source": "https://github.com/spatie/temporary-directory/tree/2.2.0"
},
"funding": [
{
"url": "https://spatie.be/open-source/support-us",
"type": "custom"
},
{
"url": "https://github.com/spatie",
"type": "github"
}
],
"time": "2023-09-25T07:13:36+00:00"
},
{
"name": "symfony/console",
"version": "v6.4.1",

159
site/config/livewire.php Normal file
View File

@ -0,0 +1,159 @@
<?php
return [
/*
|---------------------------------------------------------------------------
| Class Namespace
|---------------------------------------------------------------------------
|
| This value sets the root class namespace for Livewire component classes in
| your application. This value will change where component auto-discovery
| finds components. It's also referenced by the file creation commands.
|
*/
'class_namespace' => 'App\\Livewire',
/*
|---------------------------------------------------------------------------
| View Path
|---------------------------------------------------------------------------
|
| This value is used to specify where Livewire component Blade templates are
| stored when running file creation commands like `artisan make:livewire`.
| It is also used if you choose to omit a component's render() method.
|
*/
'view_path' => resource_path('views/livewire'),
/*
|---------------------------------------------------------------------------
| Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
| In this case, the view returned by CreatePost will render into $slot.
|
*/
'layout' => 'components.layouts.app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'lazy_placeholder' => null,
/*
|---------------------------------------------------------------------------
| Temporary File Uploads
|---------------------------------------------------------------------------
|
| Livewire handles file uploads by storing uploads in a temporary directory
| before the file is stored permanently. All file uploads are directed to
| a global endpoint for temporary storage. You may configure this below:
|
*/
'temporary_file_upload' => [
'disk' => 'local', // Example: 'local', 's3' | Default: 'default'
'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
'directory' => 'tmp', // Example: 'tmp' | Default: 'livewire-tmp'
'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1'
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs...
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
'mov', 'avi', 'wmv', 'mp3', 'm4a',
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
],
'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
],
/*
|---------------------------------------------------------------------------
| Render On Redirect
|---------------------------------------------------------------------------
|
| This value determines if Livewire will run a component's `render()` method
| after a redirect has been triggered using something like `redirect(...)`
| Setting this to true will render the view once more before redirecting
|
*/
'render_on_redirect' => false,
/*
|---------------------------------------------------------------------------
| Eloquent Model Binding
|---------------------------------------------------------------------------
|
| Previous versions of Livewire supported binding directly to eloquent model
| properties using wire:model by default. However, this behavior has been
| deemed too "magical" and has therefore been put under a feature flag.
|
*/
'legacy_model_binding' => false,
/*
|---------------------------------------------------------------------------
| Auto-inject Frontend Assets
|---------------------------------------------------------------------------
|
| By default, Livewire automatically injects its JavaScript and CSS into the
| <head> and <body> of pages containing Livewire components. By disabling
| this behavior, you need to use @livewireStyles and @livewireScripts.
|
*/
'inject_assets' => true,
/*
|---------------------------------------------------------------------------
| Navigate (SPA mode)
|---------------------------------------------------------------------------
|
| By adding `wire:navigate` to links in your Livewire application, Livewire
| will prevent the default link handling and instead request those pages
| via AJAX, creating an SPA-like effect. Configure this behavior here.
|
*/
'navigate' => [
'show_progress_bar' => true,
'progress_bar_color' => '#2299dd',
],
/*
|---------------------------------------------------------------------------
| HTML Morph Markers
|---------------------------------------------------------------------------
|
| Livewire intelligently "morphs" existing HTML into the newly rendered HTML
| after each update. To make this process more reliable, Livewire injects
| "markers" into the rendered Blade surrounding @if, @class & @foreach.
|
*/
'inject_morph_markers' => true,
/*
|---------------------------------------------------------------------------
| Pagination Theme
|---------------------------------------------------------------------------
|
| When enabling Livewire's pagination feature by using the `WithPagination`
| trait, Livewire will use Tailwind templates to render pagination views
| on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
|
*/
'pagination_theme' => 'bootstrap',
];

View File

@ -20,6 +20,7 @@
"version": "0.0.0",
"dependencies": {
"aos": "^3.0.0-beta.6",
"cropperjs": "^1.6.1",
"guillotine": "^1.3.1",
"isotope-layout": "^3.0.6",
"jquery": "^3.7.0",

View File

@ -21,18 +21,6 @@
@section('page-content')
<section class="signup-wrapper signin-wrapper ptb-120">
<div class="container">
{{--
<div class="row align-items-center gutter-0">
<div class="col-xl-6 offset-xl-3 col-lg-12 ">
<div class="signin-content">
<div class="mb-6">
<h2 class="mb-2">Sign in with your existing account</h2>
<p class="normal">Welcome back! Please enter your credentials to sign in.</p>
</div>
</div>
</div>
</div>
--}}
<div class="row">
<div class="col-xl-6 offset-xl-3 col-lg-12">
<div class="authbox">

View File

@ -38,5 +38,6 @@
<script src="{{ config('app.static_asset_url') }}/v3/dist/vendor/js/jquery.custom-file-input.js"></script>
@livewireScriptConfig
@vite(['resources/js/app.js'])
@yield('additional_footers')
</body>
</html>

View File

@ -0,0 +1,42 @@
<form wire:submit.prevent="save">
@if($avatar)
<div
class="d-flex flex-column justify-content-center"
wire:ignore
x-data="{
setUp() {
const cropper = new Cropper(document.getElementById('avatar'), {
aspectRatio: 1/1,
autoCropArea: 1,
viewMode: 1,
crop (event) {
@this.set('x', event.detail.x)
@this.set('y', event.detail.y)
@this.set('width', event.detail.width)
@this.set('height', event.detail.height)
}
})
}
}"
x-init="setUp"
>
<div class="d-flex justify-content-center text-center mt-4 mb-4">
<img id="avatar" src="{{ $avatar->temporaryUrl() }}" style="width: 100%; max-width: 100%;">
</div>
<div class="d-flex justify-content-end text-end mt-4 mb-4">
<button type="submit" class="btn btn-gradient btn-small">
<span>Save Avatar</span>
</button>
</div>
</div>
@else
<div class="mb-2 mx-2">
<label for="avatar" style="width: 100%;">
<img src="{{ auth()->user()->artists()->first()->avatar() }}" style="width: 100%; max-width: 100%;">
</label>
</div>
<div class="mb-2 mx-2">
<input type="file" name="avatar" id="avatar" class="sr-only" wire:model="avatar">
</div>
@endif
</form>

View File

@ -51,12 +51,12 @@
--}}
@if (!Auth::user())
<li class="wallet-button"> <a href="/login" class="btn btn-gradient btn-small">
<span><i class="ri-account-circle-line"></i>Join In</span></a>
<span><i class="ri-user-add-line"></i>Join In</span></a>
</li>
@else
<li class="avatar-info"> <a href="#"><img
@if (Auth::user() && Auth::user()->artists->first())
src="{{ Vite::asset(Auth::user()->artists->first()->avatar ?? 'resources/img/default_avatars/default_avatar_male.svg') }}"
@if (Auth::user() && Auth::user()->artists->first() && Auth::user()->artists->first()->avatar)
src="{{ Auth::user()->artists->first()->avatar() }}"
@else
src="{{ Vite::asset('resources/img/default_avatars/default_avatar_male.svg') }}"
@endif

View File

@ -1,3 +1,11 @@
@section('additional_headers')
@parent
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.1/cropper.min.css" integrity="sha512-hvNR0F/e2J7zPPfLC9auFe3/SE0yG4aJCOd/qxew74NN7eyiSKjr7xJJMu1Jy2wf7FXITpWS1E/RY8yzuXN7VA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
@endsection
@section('additional_footers')
@parent
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.1/cropper.min.js" integrity="sha512-9KkIqdfN7ipEW6B6k+Aq20PV31bjODg4AA52W+tYtAE0jE0kMx49bjJ3FgvS56wzmyfMUHbQ4Km2b7l9+Y/+Eg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@endsection
<x-app-layout>
<x-slot name="header">
<h2 class="">
@ -7,7 +15,7 @@
<section class="signup-wrapper signin-wrapper ptb-4">
<div class="container">
<div class="row d-flex-row d-flex align-content-stretch flex-wrap">
<div class="row d-flex-row d-flex align-content-stretch flex-wrap mb-4">
<div class="col-xl-5 col-lg-12 authbox">
@include('profile.partials.update-profile-information-form')
</div>
@ -15,6 +23,12 @@
@include('profile.partials.update-password-form')
</div>
</div>
<div class="row d-flex-row d-flex align-content-stretch flex-wrap mb-4">
<div class="col-xl-5 col-lg-12 authbox">
@include('profile.partials.update-avatar-form')
</div>
</div>
</div>
</section>
</x-app-layout>

View File

@ -0,0 +1,15 @@
<div class="row mt-4 gutter-2">
<div class="col">
<div class="signin-content">
<div class="mb-6">
<h2 class="mb-2">{{ __('Update Your Artist Avatar') }}</h2>
<p class="normal">{{ __('This avatar will be used on the artist gallery and artist profile page.') }}</p>
</div>
</div>
</div>
</div>
<div class="row mt-0 gutter-2">
<div class="col">
<livewire:artist.avatar />
</div>
</div>

View File

@ -38,6 +38,7 @@
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
Route::get('/create-artwork', [ArtworkController::class, 'create']);
Route::post('/create-artwork', [ArtworkController::class, 'store']);
Route::get('/update-avatar', [ArtistController::class, 'avatar'])->name('avatar.edit');
});
Route::get('/counter', Counter::class);

View File

@ -396,6 +396,11 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
cropperjs@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.6.1.tgz#fd132021d93b824b1b0f2c2c3b763419fb792d89"
integrity sha512-F4wsi+XkDHCOMrHMYjrTEE4QBOrsHHN5/2VsVAaRq8P7E5z7xQpT75S+f/9WikmBEailas3+yo+6zPIomW+NOA==
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"