Skip to content

Commit c29a32c

Browse files
committed
feed fix
1 parent a019b7a commit c29a32c

File tree

17 files changed

+1221
-495
lines changed

17 files changed

+1221
-495
lines changed

app/Events/SocialPostUpdated.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace App\Events;
4+
5+
use Illuminate\Broadcasting\Channel;
6+
use Illuminate\Broadcasting\InteractsWithSockets;
7+
use Illuminate\Broadcasting\PresenceChannel;
8+
use Illuminate\Broadcasting\PrivateChannel;
9+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
10+
use Illuminate\Foundation\Events\Dispatchable;
11+
use Illuminate\Queue\SerializesModels;
12+
13+
class SocialPostUpdated implements \Illuminate\Contracts\Broadcasting\ShouldBroadcast
14+
{
15+
use Dispatchable, InteractsWithSockets, SerializesModels;
16+
17+
public $post;
18+
19+
/**
20+
* Create a new event instance.
21+
*/
22+
public function __construct($post)
23+
{
24+
// Load relationships needed for the front-end
25+
if ($post) {
26+
$post->load(['user', 'files', 'comments.user']);
27+
$post->loadCount(['likes', 'comments']);
28+
// is_liked is handled per user on the front-end or via a separate mechanism,
29+
// but for generic feed updates we just need the core data.
30+
}
31+
$this->post = $post;
32+
}
33+
34+
/**
35+
* Get the channels the event should broadcast on.
36+
*
37+
* @return array<int, \Illuminate\Broadcasting\Channel>
38+
*/
39+
public function broadcastOn(): array
40+
{
41+
return [
42+
new \Illuminate\Broadcasting\Channel('social-feed'),
43+
];
44+
}
45+
}

app/Livewire/Social/Feed.php

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
use Livewire\WithPagination;
1212
use Illuminate\Support\Facades\Auth;
1313
use Illuminate\Support\Facades\Log;
14+
use App\Models\Friendship;
15+
use App\Models\SocialPostHide;
16+
use Illuminate\View\View;
17+
use Illuminate\Contracts\Pagination\Paginator;
18+
use App\Events\SocialPostUpdated;
1419

1520
class Feed extends Component
1621
{
@@ -32,11 +37,56 @@ class Feed extends Component
3237

3338
public ?int $deletingPostId = null;
3439

35-
public ?string $fullscreenImageUrl = null;
3640
public ?int $activeCommentPostId = null;
37-
public string $commentContent = '';
41+
public ?int $fullscreenPostId = null;
42+
public ?string $fullscreenImageUrl = null;
43+
public array $commentContent = [];
44+
45+
public function getListeners(): array
46+
{
47+
return [
48+
'loadMore' => 'loadMore',
49+
"echo:social-feed,SocialPostUpdated" => 'handlePostUpdated',
50+
];
51+
}
52+
53+
public function handlePostUpdated(array $event): void
54+
{
55+
// When a post is updated (like, comment, etc.), we refresh to get latest counts
56+
// Livewire will only re-render affected parts.
57+
}
3858

39-
protected $listeners = ['loadMore' => 'loadMore'];
59+
protected function rules(): array
60+
{
61+
return [
62+
'content' => 'required|string|max:1000',
63+
'image' => 'nullable|image|max:10240',
64+
65+
'editContent' => 'required|string|max:1000',
66+
'reportReason' => 'required|string',
67+
];
68+
}
69+
70+
protected function validationAttributes(): array
71+
{
72+
return [
73+
'content' => 'conteúdo da postagem',
74+
'image' => 'imagem',
75+
76+
'editContent' => 'conteúdo editado',
77+
'reportReason' => 'motivo da denúncia',
78+
];
79+
}
80+
81+
protected function messages(): array
82+
{
83+
return [
84+
'content.required' => 'O que você está pensando? Escreva algo!',
85+
'image.max' => 'A imagem deve ter no máximo 10MB.',
86+
'editContent.required' => 'O conteúdo não pode ficar vazio.',
87+
'reportReason.required' => 'Por favor, selecione um motivo.',
88+
];
89+
}
4090

4191
public function loadMore(): void
4292
{
@@ -45,10 +95,11 @@ public function loadMore(): void
4595

4696
public function submitPost(SocialService $socialService, FileService $fileService): void
4797
{
48-
$this->validate([
49-
'content' => 'required|string|max:1000',
50-
'image' => 'nullable|image|max:10240', // 10MB
51-
]);
98+
if (empty($this->content) && empty($this->image)) return;
99+
100+
if ($this->image) {
101+
$this->validateOnly('image');
102+
}
52103

53104
try {
54105
$post = $socialService->createPost($this->content);
@@ -72,12 +123,13 @@ public function toggleLike(int $postId, SocialService $socialService): void
72123

73124
public function submitComment(int $postId, SocialService $socialService): void
74125
{
75-
if (empty($this->commentContent)) return;
126+
if (empty($this->commentContent[$postId] ?? '')) return;
76127

77-
$socialService->addComment($postId, $this->commentContent);
78-
$this->reset('commentContent');
79-
$this->activeCommentPostId = null;
80-
$this->dispatch('toast', message: 'Comentário adicionado!', type: 'success');
128+
$socialService->addComment($postId, $this->commentContent[$postId]);
129+
130+
// Reset specific comment input
131+
unset($this->commentContent[$postId]);
132+
$this->dispatch('toast', message: 'Incentivo enviado!', type: 'success');
81133
}
82134

83135
public function toggleComments(int $postId): void
@@ -100,9 +152,7 @@ public function deletePost(SocialService $socialService): void
100152

101153
public function reportPost(SocialService $socialService): void
102154
{
103-
$this->validate([
104-
'reportReason' => 'required|string',
105-
]);
155+
$this->validateOnly('reportReason');
106156

107157
if ($socialService->reportPost($this->reportingPostId, $this->reportReason, $this->reportDetails)) {
108158
$this->showReportSuccess = true;
@@ -122,9 +172,7 @@ public function hidePost(int $postId, SocialService $socialService): void
122172

123173
public function updatePost(SocialService $socialService): void
124174
{
125-
$this->validate([
126-
'editContent' => 'required|string|max:1000',
127-
]);
175+
$this->validateOnly('editContent');
128176

129177
if ($socialService->editPost($this->editingPostId, $this->editContent)) {
130178
$this->dispatch('toast', message: 'Post atualizado!', type: 'success');
@@ -136,43 +184,14 @@ public function updatePost(SocialService $socialService): void
136184
$this->reset('editContent');
137185
}
138186

139-
public function render(SocialService $socialService): \Illuminate\View\View
187+
public function render(SocialService $socialService): View
140188
{
141-
$userId = Auth::id();
142189
$user = Auth::user();
143190

144-
// Get feed items manually to support infinite scroll with perPage
145-
// In a real app we might use pagination, but let's follow the React logic of "allPosts" list
146-
$feedData = $socialService->getFeed(); // This returns paginated results based on internal logic
147-
148-
// Let's refactor slightly to use perPage directly
149-
$friendIds = \App\Models\Friendship::query()
150-
->where(function($q) use ($userId) {
151-
$q->where('user_id', $userId)
152-
->orWhere('friend_id', $userId);
153-
})
154-
->where('status', 'accepted')
155-
->get()
156-
->flatMap(fn($f) => [$f->user_id, $f->friend_id])
157-
->unique()
158-
->toArray();
159-
160-
$userIds = array_unique(array_merge($friendIds, [$userId]));
161-
$hiddenPostIds = \App\Models\SocialPostHide::where('user_id', $userId)->pluck('post_id')->toArray();
162-
163-
$posts = SocialPost::query()
164-
->with(['user', 'files', 'comments.user'])
165-
->withCount(['likes', 'comments'])
166-
->withExists(['likes as is_liked' => function($q) use ($userId) {
167-
$q->where('user_id', $userId);
168-
}])
169-
->whereIn('user_id', $userIds)
170-
->whereNotIn('id', $hiddenPostIds)
171-
->latest()
172-
->paginate($this->perPage);
191+
$feed = $socialService->getFeed($this->perPage);
173192

174193
return view('livewire.social.feed', [
175-
'posts' => $posts,
194+
'posts' => $feed['paginator'],
176195
'suggestions' => $socialService->getSuggestions(),
177196
'isAdmin' => $user->hasAnyRole(['Administrador', 'Suporte']),
178197
]);

app/Livewire/Social/Profile.php

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Models\User;
66
use App\Models\SocialPost;
77
use App\Models\Friendship;
8+
use App\Services\ExperienceService;
89
use App\Services\SocialService;
910
use App\Services\StreakService;
1011
use App\Services\FileService;
@@ -17,13 +18,13 @@ class Profile extends Component
1718
{
1819
use WithFileUploads;
1920

20-
public string $identifier = '';
21+
public ?string $identifier = null;
2122
public ?User $profileUser = null;
2223
public $avatar;
2324

2425
// Post interaction states
25-
public ?int $activeCommentPostId = null;
26-
public string $commentContent = '';
26+
public array $commentContent = [];
27+
public ?int $fullscreenPostId = null;
2728
public ?string $fullscreenImageUrl = null;
2829

2930
// Modals
@@ -70,21 +71,46 @@ public function toggleLike(int $postId, SocialService $socialService): void
7071
$socialService->toggleLike($postId);
7172
}
7273

73-
public function submitComment(int $postId, SocialService $socialService): void
74+
protected function rules(): array
75+
{
76+
return [
77+
'editContent' => 'required|string|max:1000',
78+
'reportReason' => 'required|string',
79+
'avatar' => 'nullable|image|max:2048',
80+
];
81+
}
82+
83+
protected function validationAttributes(): array
7484
{
75-
if (empty($this->commentContent)) return;
85+
return [
86+
'editContent' => 'conteúdo do post',
87+
'reportReason' => 'motivo da denúncia',
88+
'avatar' => 'foto de perfil',
89+
];
90+
}
7691

77-
$socialService->addComment($postId, $this->commentContent);
78-
$this->reset('commentContent');
79-
$this->activeCommentPostId = null;
80-
$this->dispatch('toast', message: 'Comentário adicionado!', type: 'success');
92+
protected function messages(): array
93+
{
94+
return [
95+
'required' => 'O campo :attribute é obrigatório.',
96+
'max' => 'O campo :attribute não pode ter mais que :max caracteres.',
97+
'image' => 'O arquivo deve ser uma imagem.',
98+
];
8199
}
82100

83-
public function toggleComments(int $postId): void
101+
public function submitComment(int $postId, SocialService $socialService): void
84102
{
85-
$this->activeCommentPostId = $this->activeCommentPostId === $postId ? null : $postId;
103+
if (empty($this->commentContent[$postId] ?? '')) return;
104+
105+
$content = $this->commentContent[$postId];
106+
$socialService->addComment($postId, $content);
107+
108+
unset($this->commentContent[$postId]);
109+
$this->dispatch('toast', message: 'Comentário adicionado!', type: 'success');
86110
}
87111

112+
113+
88114
public function toggleFollow(SocialService $socialService): void
89115
{
90116
if ($this->profileUser->id === Auth::id()) return;
@@ -182,19 +208,30 @@ public function hidePost(int $postId, SocialService $socialService): void
182208
$this->dispatch('toast', message: 'Post ocultado!', type: 'success');
183209
}
184210

185-
public function render(SocialService $socialService, StreakService $streakService): \Illuminate\View\View
211+
public function render(SocialService $socialService, StreakService $streakService, ExperienceService $experienceService): \Illuminate\View\View
186212
{
187213
$userId = Auth::id();
188214
$isOwnProfile = $userId === $this->profileUser->id;
189215

190216
// Streak info
191217
$streak = $streakService->getGlobalStreak($this->profileUser);
192218

193-
// XP info
219+
// XP info (Aligned with Dashboard)
220+
$currentXp = $this->profileUser->current_xp ?? 0;
221+
$level = $experienceService->calculateLevel($currentXp);
222+
$nextLevelThreshold = $experienceService->getNextLevelXp($level);
223+
$previousLevelThreshold = $level > 1 ? $experienceService->getNextLevelXp($level - 1) : 0;
224+
225+
$levelXp = $currentXp - $previousLevelThreshold;
226+
$levelTarget = $nextLevelThreshold - $previousLevelThreshold;
227+
$progressPercentage = $levelTarget > 0 ? (int) min(100, round(($levelXp / $levelTarget) * 100)) : 0;
228+
194229
$xpInfo = [
195-
'current' => $this->profileUser->current_xp,
196-
'level' => floor($this->profileUser->current_xp / 1000) + 1,
197-
'next_level_threshold' => 1000,
230+
'current' => $currentXp,
231+
'level' => $level,
232+
'levelXp' => $levelXp,
233+
'levelTarget' => $levelTarget,
234+
'progressPercentage' => $progressPercentage,
198235
];
199236

200237
// Is following

app/Models/User.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class User extends Authenticatable implements Auditable
2828
'email',
2929
'password',
3030
'google_id',
31-
// 'avatar_url', // Removed in favor of polymorphic relationship
31+
'last_ip',
32+
'city',
33+
'state',
34+
'location_metadata',
3235
];
3336

3437
/**
@@ -107,6 +110,7 @@ protected function casts(): array
107110
return [
108111
'email_verified_at' => 'datetime',
109112
'password' => 'hashed',
113+
'location_metadata' => 'json',
110114
];
111115
}
112116

0 commit comments

Comments
 (0)