378 lines
13 KiB
PHP
378 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Friend;
|
|
use App\Models\ProfileComment;
|
|
use App\Models\ProfileLike;
|
|
use App\Models\ProfileReport;
|
|
use App\Models\Tip;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Inertia\Inertia;
|
|
|
|
class SocialController extends Controller
|
|
{
|
|
// Profile page
|
|
public function show(string $username)
|
|
{
|
|
$user = User::query()
|
|
->where('username', $username)
|
|
->first();
|
|
|
|
if (!$user) {
|
|
abort(404, 'User not found');
|
|
}
|
|
|
|
$auth = Auth::user();
|
|
$authId = $auth?->id;
|
|
|
|
$likesCount = ProfileLike::where('profile_id', $user->id)->count();
|
|
$hasLiked = $authId ? ProfileLike::where('profile_id', $user->id)->where('user_id', $authId)->exists() : false;
|
|
|
|
$comments = ProfileComment::with(['user:id,username,avatar'])
|
|
->where('profile_id', $user->id)
|
|
->latest()
|
|
->limit(20)
|
|
->get()
|
|
->map(function ($c) {
|
|
return [
|
|
'id' => $c->id,
|
|
'user' => [
|
|
'id' => $c->user->id,
|
|
'username' => $c->user->username,
|
|
'avatar' => $c->user->avatar,
|
|
],
|
|
'content' => $c->content,
|
|
'created_at' => $c->created_at,
|
|
];
|
|
});
|
|
|
|
// Friendship flags
|
|
$isFriend = false;
|
|
$isPending = false; // I sent them a request (outgoing)
|
|
$theyRequestedMe = false; // They sent me a request (incoming — I can accept/decline)
|
|
$friendRowId = null;
|
|
if ($authId) {
|
|
$friendRow = Friend::where(function ($q) use ($authId, $user) {
|
|
$q->where('user_id', $authId)->where('friend_id', $user->id);
|
|
})->orWhere(function ($q) use ($authId, $user) {
|
|
$q->where('user_id', $user->id)->where('friend_id', $authId);
|
|
})->first();
|
|
if ($friendRow) {
|
|
$isFriend = $friendRow->status === 'accepted';
|
|
$isPending = $friendRow->status === 'pending' && $friendRow->user_id == $authId;
|
|
$theyRequestedMe = $friendRow->status === 'pending' && $friendRow->user_id == $user->id;
|
|
$friendRowId = $friendRow->id;
|
|
}
|
|
}
|
|
|
|
$profile = [
|
|
'id' => $user->id,
|
|
'username' => $user->username,
|
|
'avatar' => $user->avatar ?? $user->avatar_url,
|
|
'banner' => $user->banner,
|
|
'bio' => $user->bio,
|
|
'vip_level' => (int) ($user->vip_level ?? 0),
|
|
'role' => $user->role ?? 'User',
|
|
'clan_tag' => $user->clan_tag,
|
|
'stats' => [
|
|
'wagered' => (float) ($user->stats?->total_wagered ?? 0),
|
|
'wins' => (int) ($user->stats?->total_wins ?? 0),
|
|
'losses' => null,
|
|
'biggest_win' => (float) ($user->stats?->biggest_win ?? 0),
|
|
'biggest_win_game' => $user->stats?->biggest_win_game ?? null,
|
|
'join_date' => optional($user->created_at)->toDateString(),
|
|
'likes_count' => $likesCount,
|
|
],
|
|
'best_wins' => [],
|
|
'comments' => $comments,
|
|
];
|
|
|
|
return Inertia::render('Social/Profile', [
|
|
'profile' => $profile,
|
|
'isOwnProfile' => $authId ? ((int)$authId === (int)$user->id) : false,
|
|
'isFriend' => $isFriend,
|
|
'isPending' => $isPending,
|
|
'theyRequestedMe' => $theyRequestedMe,
|
|
'friendRowId' => $friendRowId,
|
|
'hasLiked' => $hasLiked,
|
|
]);
|
|
}
|
|
|
|
// Update own profile
|
|
public function update(Request $request)
|
|
{
|
|
$user = Auth::user();
|
|
abort_unless($user, 403);
|
|
|
|
$data = $request->validate([
|
|
'is_public' => 'boolean',
|
|
'bio' => 'nullable|string|max:160',
|
|
'avatar' => 'nullable|string',
|
|
'banner' => 'nullable|string',
|
|
]);
|
|
|
|
$user->fill([
|
|
'is_public' => (bool) ($data['is_public'] ?? $user->is_public),
|
|
'bio' => $data['bio'] ?? $user->bio,
|
|
'avatar' => $data['avatar'] ?? $user->avatar,
|
|
'banner' => $data['banner'] ?? $user->banner,
|
|
])->save();
|
|
|
|
return back()->with('success', 'Profile updated.');
|
|
}
|
|
|
|
// Upload avatar or banner
|
|
public function uploadImage(Request $request)
|
|
{
|
|
$user = Auth::user();
|
|
abort_unless($user, 403);
|
|
|
|
$request->validate([
|
|
'type' => 'required|in:avatar,banner',
|
|
'file' => 'required|file|mimes:jpeg,jpg,png,gif,webp,bmp|max:5120',
|
|
]);
|
|
|
|
$file = $request->file('file');
|
|
$type = $request->input('type');
|
|
$path = $file->store("profile/{$type}s", 'public');
|
|
$url = Storage::disk('public')->url($path);
|
|
|
|
if ($type === 'banner') {
|
|
$user->banner = $url;
|
|
} else {
|
|
$user->avatar = $url;
|
|
}
|
|
$user->save();
|
|
|
|
return response()->json(['url' => $url], 200);
|
|
}
|
|
|
|
// Toggle like on a profile
|
|
public function like($id)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$target = User::findOrFail($id);
|
|
if ($target->id === $me->id) {
|
|
return back();
|
|
}
|
|
$like = ProfileLike::where('user_id', $me->id)->where('profile_id', $target->id)->first();
|
|
if ($like) {
|
|
$like->delete();
|
|
} else {
|
|
ProfileLike::create(['user_id' => $me->id, 'profile_id' => $target->id]);
|
|
}
|
|
return back();
|
|
}
|
|
|
|
// Add a comment to a profile
|
|
public function comment(Request $request, $id)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$data = $request->validate(['content' => 'required|string|max:500']);
|
|
$target = User::findOrFail($id);
|
|
ProfileComment::create([
|
|
'user_id' => $me->id,
|
|
'profile_id' => $target->id,
|
|
'content' => $data['content'],
|
|
]);
|
|
return back()->with('success', 'Comment posted.');
|
|
}
|
|
|
|
// Report a profile
|
|
public function report(Request $request, $id)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$data = $request->validate([
|
|
'reason' => 'required|string',
|
|
'details' => 'nullable|string',
|
|
'snapshot' => 'nullable|array',
|
|
'screenshot' => 'nullable|string',
|
|
]);
|
|
$target = User::findOrFail($id);
|
|
|
|
$screenshotPath = null;
|
|
if (!empty($data['screenshot'])) {
|
|
$base64 = $data['screenshot'];
|
|
// Strip data URI prefix if present
|
|
if (str_contains($base64, ',')) {
|
|
$base64 = explode(',', $base64, 2)[1];
|
|
}
|
|
$decoded = base64_decode($base64, true);
|
|
if ($decoded !== false) {
|
|
$filename = 'reports/profile-screenshots/' . $target->id . '_' . time() . '.png';
|
|
Storage::disk('public')->put($filename, $decoded);
|
|
$screenshotPath = $filename;
|
|
}
|
|
}
|
|
|
|
ProfileReport::create([
|
|
'reporter_id' => $me->id,
|
|
'profile_id' => $target->id,
|
|
'reason' => $data['reason'],
|
|
'details' => $data['details'] ?? null,
|
|
'snapshot' => $data['snapshot'] ?? null,
|
|
'screenshot_path' => $screenshotPath,
|
|
]);
|
|
|
|
if ($request->expectsJson()) {
|
|
return response()->json(['success' => true]);
|
|
}
|
|
return back()->with('success', 'Profile reported.');
|
|
}
|
|
|
|
// User search
|
|
public function search(Request $request)
|
|
{
|
|
$q = (string) $request->input('q', '');
|
|
if (strlen($q) < 1) return response()->json([]);
|
|
|
|
$users = User::query()
|
|
->where('username', 'like', "%" . str_replace(['%','_'], ['\\%','\\_'], $q) . "%")
|
|
->orWhere('id', '=', $q)
|
|
->orderBy('username')
|
|
->limit(10)
|
|
->get(['id', 'username', 'avatar', 'avatar_url', 'vip_level', 'balance']);
|
|
|
|
$out = $users->map(function ($u) {
|
|
return [
|
|
'id' => $u->id,
|
|
'username' => $u->username,
|
|
'avatar' => $u->avatar ?? $u->avatar_url,
|
|
'avatar_url' => $u->avatar_url ?? $u->avatar,
|
|
'vip_level' => (int) ($u->vip_level ?? 0),
|
|
'stats' => [
|
|
'wager' => 0, // Placeholder
|
|
'wins' => 0, // Placeholder
|
|
'balance' => $u->balance
|
|
]
|
|
];
|
|
});
|
|
return response()->json($out->all(), 200);
|
|
}
|
|
|
|
// Send a tip (balance transfer) and record it
|
|
public function tip(Request $request, $id)
|
|
{
|
|
$sender = Auth::user();
|
|
abort_unless($sender, 403);
|
|
|
|
$data = $request->validate([
|
|
'currency' => 'required|string|max:10',
|
|
'amount' => 'required|numeric|min:0.00000001',
|
|
'note' => 'nullable|string|max:140',
|
|
]);
|
|
|
|
if ((int)$id === (int)$sender->id) {
|
|
return back()->withErrors(['amount' => 'You cannot tip yourself.']);
|
|
}
|
|
|
|
$receiver = User::findOrFail($id);
|
|
$amount = (float) $data['amount'];
|
|
|
|
if ($sender->balance < $amount) {
|
|
return back()->withErrors(['amount' => 'Insufficient balance']);
|
|
}
|
|
|
|
DB::transaction(function () use ($sender, $receiver, $amount, $data) {
|
|
// Simple balance transfer using users.balance
|
|
$sender->balance = (float) $sender->balance - $amount;
|
|
$receiver->balance = (float) $receiver->balance + $amount;
|
|
$sender->save();
|
|
$receiver->save();
|
|
|
|
Tip::create([
|
|
'from_user_id' => $sender->id,
|
|
'to_user_id' => $receiver->id,
|
|
'currency' => strtoupper($data['currency']),
|
|
'amount' => $amount,
|
|
'note' => $data['note'] ?? null,
|
|
]);
|
|
});
|
|
|
|
return back()->with('success', 'Tip sent successfully.');
|
|
}
|
|
|
|
// --- Friends ---
|
|
public function requestFriend(Request $request)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$data = $request->validate([
|
|
'user_id' => 'required|integer',
|
|
]);
|
|
$targetId = (int) $data['user_id'];
|
|
if ($targetId === (int)$me->id) {
|
|
return back()->withErrors(['friend' => 'You cannot add yourself.']);
|
|
}
|
|
$target = User::findOrFail($targetId);
|
|
|
|
$existing = Friend::where('user_id', $me->id)->where('friend_id', $target->id)->first();
|
|
if ($existing) {
|
|
if ($existing->status === 'pending') {
|
|
return back()->with('success', 'Friend request already sent.');
|
|
}
|
|
if ($existing->status === 'accepted') {
|
|
return back()->with('success', 'You are already friends.');
|
|
}
|
|
}
|
|
Friend::updateOrCreate([
|
|
'user_id' => $me->id,
|
|
'friend_id' => $target->id,
|
|
], [ 'status' => 'pending' ]);
|
|
|
|
return back()->with('success', 'Friend request sent.');
|
|
}
|
|
|
|
public function acceptFriend($id)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$requestRow = Friend::where('user_id', $id)->where('friend_id', $me->id)->where('status', 'pending')->first();
|
|
if (!$requestRow) {
|
|
return back()->withErrors(['friend' => 'Request not found.']);
|
|
}
|
|
DB::transaction(function () use ($requestRow, $me, $id) {
|
|
$requestRow->status = 'accepted';
|
|
$requestRow->save();
|
|
// Ensure reciprocal row
|
|
Friend::updateOrCreate([
|
|
'user_id' => $me->id,
|
|
'friend_id' => (int) $id,
|
|
], [ 'status' => 'accepted' ]);
|
|
});
|
|
return back()->with('success', 'Friend request accepted.');
|
|
}
|
|
|
|
public function declineFriend($id)
|
|
{
|
|
$me = Auth::user();
|
|
abort_unless($me, 403);
|
|
$requestRow = Friend::where('user_id', $id)->where('friend_id', $me->id)->where('status', 'pending')->first();
|
|
if (!$requestRow) {
|
|
return back()->withErrors(['friend' => 'Request not found.']);
|
|
}
|
|
$requestRow->delete();
|
|
return back()->with('success', 'Friend request declined.');
|
|
}
|
|
|
|
public function hub()
|
|
{
|
|
return Inertia::render('Social/Hub');
|
|
}
|
|
|
|
public function me()
|
|
{
|
|
return Inertia::render('Social/Settings', [
|
|
'user' => Auth::user(),
|
|
]);
|
|
}
|
|
}
|