diff --git a/app/Ejaculation.php b/app/Ejaculation.php index 83b3ad2..3794add 100644 --- a/app/Ejaculation.php +++ b/app/Ejaculation.php @@ -2,7 +2,9 @@ namespace App; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Auth; class Ejaculation extends Model { @@ -34,4 +36,25 @@ class Ejaculation extends Model return $v->name; })->all()); } + + public function likes() + { + return $this->hasMany(Like::class); + } + + public function scopeWithLikes(Builder $query) + { + if (Auth::check()) { + // (ejaculation_id, user_id) でユニークなわけですが、サブクエリ発行させるのとLeft JoinしてNULLかどうかで結果を見るのどっちがいいんでしょうね + return $query->withCount([ + 'likes', + 'likes as is_liked' => function ($query) { + $query->where('user_id', Auth::id()); + } + ]); + } else { + return $query->withCount('likes') + ->addSelect('0 as is_liked'); + } + } } diff --git a/app/Http/Controllers/EjaculationController.php b/app/Http/Controllers/EjaculationController.php index d2292e0..13ef8c4 100644 --- a/app/Http/Controllers/EjaculationController.php +++ b/app/Http/Controllers/EjaculationController.php @@ -78,7 +78,9 @@ class EjaculationController extends Controller public function show($id) { - $ejaculation = Ejaculation::findOrFail($id); + $ejaculation = Ejaculation::where('id', $id) + ->withLikes() + ->firstOrFail(); $user = User::findOrFail($ejaculation->user_id); // 1つ前のチェックインからの経過時間を求める diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 95807a5..fb8147f 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -69,6 +69,7 @@ SQL ->orderBy('ejaculations.ejaculated_date', 'desc') ->select('ejaculations.*') ->with('user', 'tags') + ->withLikes() ->take(10) ->get(); diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 3fa2725..2ca3801 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -21,6 +21,7 @@ class SearchController extends Controller ->where('is_private', false) ->orderBy('ejaculated_date', 'desc') ->with(['user', 'tags']) + ->withLikes() ->paginate(20) ->appends($inputs); diff --git a/app/Http/Controllers/TimelineController.php b/app/Http/Controllers/TimelineController.php index a179537..2f05b71 100644 --- a/app/Http/Controllers/TimelineController.php +++ b/app/Http/Controllers/TimelineController.php @@ -16,6 +16,7 @@ class TimelineController extends Controller ->orderBy('ejaculations.ejaculated_date', 'desc') ->select('ejaculations.*') ->with('user', 'tags') + ->withLikes() ->paginate(21); return view('timeline.public')->with(compact('ejaculations')); diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 1640c78..442dcf9 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -41,6 +41,7 @@ SQL } $ejaculations = $query->orderBy('ejaculated_date', 'desc') ->with('tags') + ->withLikes() ->paginate(20); // よく使っているタグ diff --git a/app/User.php b/app/User.php index 5469e52..3b2b251 100644 --- a/app/User.php +++ b/app/User.php @@ -51,4 +51,9 @@ class User extends Authenticatable { return Auth::check() && $this->id === Auth::user()->id; } + + public function likes() + { + return $this->hasMany(Like::class); + } } diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js index 6af9993..bf79d54 100644 --- a/resources/assets/js/app.js +++ b/resources/assets/js/app.js @@ -27,4 +27,69 @@ $(() => { event.preventDefault(); $deleteCheckinModal.modal('show', this); }); + + $(document).on('click', '[data-href]', function (event) { + location.href = $(this).data('href'); + }); + + $(document).on('click', '.like-button', function (event) { + event.preventDefault(); + + const $this = $(this); + const targetId = $this.data('id'); + const isLiked = $this.data('liked'); + + if (isLiked) { + const callback = (data) => { + $this.data('liked', false); + $this.find('.oi-heart').removeClass('text-danger'); + + const count = data.ejaculation ? data.ejaculation.likes_count : 0; + $this.find('.like-count').text(count ? count : ''); + }; + + $.ajax({ + url: '/api/likes/' + encodeURIComponent(targetId), + method: 'delete', + type: 'json' + }) + .then(callback) + .catch(function (xhr) { + if (xhr.status === 404) { + callback(JSON.parse(xhr.responseText)); + return; + } + + console.error(xhr); + alert('いいねを解除できませんでした。'); + }); + } else { + const callback = (data) => { + $this.data('liked', true); + $this.find('.oi-heart').addClass('text-danger'); + + const count = data.ejaculation ? data.ejaculation.likes_count : 0; + $this.find('.like-count').text(count ? count : ''); + }; + + $.ajax({ + url: '/api/likes', + method: 'post', + type: 'json', + data: { + id: targetId + } + }) + .then(callback) + .catch(function (xhr) { + if (xhr.status === 409) { + callback(JSON.parse(xhr.responseText)); + return; + } + + console.error(xhr); + alert('いいねできませんでした。'); + }); + } + }); }); \ No newline at end of file diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss index e691a70..c3b8f50 100644 --- a/resources/assets/sass/app.scss +++ b/resources/assets/sass/app.scss @@ -12,4 +12,12 @@ $primary: #e53fb1; @import "tissue.css"; // Components -@import "components/link-card"; \ No newline at end of file +@import "components/link-card"; + +.like-button { + text-decoration: none !important; +} + +.like-count:not(:empty) { + padding-left: 0.25rem; +} \ No newline at end of file diff --git a/resources/assets/sass/tissue.css b/resources/assets/sass/tissue.css index 2dd99e2..c8d5f15 100644 --- a/resources/assets/sass/tissue.css +++ b/resources/assets/sass/tissue.css @@ -40,10 +40,6 @@ border-top: none; } -.timeline-action-item { - margin-left: 16px; -} - .tis-global-count-graph { height: 90px; border-bottom: 1px solid rgba(0, 0, 0, .125); diff --git a/resources/views/components/ejaculation.blade.php b/resources/views/components/ejaculation.blade.php new file mode 100644 index 0000000..a1d4a3f --- /dev/null +++ b/resources/views/components/ejaculation.blade.php @@ -0,0 +1,35 @@ + +
+
+ {{ $ejaculation->user->display_name }} + {{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }} +
+
+ + +
+
+ +@if ($ejaculation->tags->isNotEmpty()) +

+ @foreach ($ejaculation->tags as $tag) + {{ $tag->name }} + @endforeach +

+@endif + +@if (!empty($ejaculation->link)) +
+ @component('components.link-card', ['link' => $ejaculation->link]) + @endcomponent +

+ {{ $ejaculation->link }} +

+
+@endif + +@if (!empty($ejaculation->note)) +

+ {!! Formatter::linkify(nl2br(e($ejaculation->note))) !!} +

+@endif \ No newline at end of file diff --git a/resources/views/ejaculation/show.blade.php b/resources/views/ejaculation/show.blade.php index 0f2c78a..69ae3e9 100644 --- a/resources/views/ejaculation/show.blade.php +++ b/resources/views/ejaculation/show.blade.php @@ -33,10 +33,11 @@
{{ $ejaculatedSpan ?? '精通' }} {{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ~ ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}
- + + @if ($user->isMe()) - - + + @endif
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 5daf319..5b0be88 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -55,40 +55,8 @@