From 5d4d21dcec121dc342b76ea2bc49795bcaf00176 Mon Sep 17 00:00:00 2001 From: ghassan Date: Wed, 29 Jul 2026 00:31:33 +0300 Subject: [PATCH] Fix avatar/banner showing broken right after cropping & saving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cropper's onAvatarSaved/onBannerSaved callback used the url from image.upload, which points at the temp buffer file. updateAvatar/updateBanner then move that temp file to the NAS profile dir and delete the local copy, so the url is dead the moment it's used — the avatar/banner rendered broken until the next page load (the file itself was saved fine). - updateAvatar/updateBanner now return the canonical media route URL (route('media.avatar'|'media.banner', $relPath)), matching the model's avatar_url/banner_url accessors and the project's no-asset('storage/...') rule. - The image-cropper prefers the update endpoint's returned url over the temp image.upload url; falls back to the old behavior when none is returned. Co-Authored-By: Claude Opus 4.8 --- app/Http/Controllers/UserController.php | 9 +++++++-- resources/views/components/image-cropper.blade.php | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index abb9fcc..cc6f68d 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -572,7 +572,10 @@ class UserController extends Controller $nas->deleteLocalAvatar($user); } - return response()->json(['ok' => true]); + // Return the canonical media URL. The url from image.upload points at the + // temp file we just moved to NAS and deleted, so the client must use this + // instead — otherwise the avatar shows broken until the next page load. + return response()->json(['ok' => true, 'url' => route('media.avatar', $relPath)]); } public function updateBanner(Request $request) @@ -606,6 +609,8 @@ class UserController extends Controller $nas->deleteLocalBanner($user); } - return response()->json(['ok' => true]); + // Return the canonical media URL (see updateAvatar) so the client updates + // the banner in place instead of pointing at the deleted temp file. + return response()->json(['ok' => true, 'url' => route('media.banner', $relPath)]); } } diff --git a/resources/views/components/image-cropper.blade.php b/resources/views/components/image-cropper.blade.php index 399b62b..0b5b3d4 100644 --- a/resources/views/components/image-cropper.blade.php +++ b/resources/views/components/image-cropper.blade.php @@ -234,7 +234,15 @@ method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrf() }, body: JSON.stringify({ path: res.path }) - }).then(function () { return res; }); + }) + .then(function (r) { return r.json().catch(function () { return {}; }); }) + .then(function (upd) { + // The update step moves the temp upload to its final home (e.g. NAS) + // and deletes the temp, so res.url is now dead. Prefer the canonical + // URL the update endpoint returns; fall back to res.url otherwise. + if (upd && upd.url) res = Object.assign({}, res, { url: upd.url }); + return res; + }); } return res; })