the browser-facing portion of osu!
at master 4.3 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace App\Http\Controllers; 7 8use App\Jobs\UpdateUserFollowerCountCache; 9use App\Models\User; 10use App\Models\UserRelation; 11use App\Transformers\UserCompactTransformer; 12use App\Transformers\UserRelationTransformer; 13 14class FriendsController extends Controller 15{ 16 public function __construct() 17 { 18 $this->middleware('auth'); 19 20 $this->middleware('verify-user', [ 21 'only' => [ 22 'store', 23 'destroy', 24 ], 25 ]); 26 27 $this->middleware('require-scopes:friends.read', ['only' => ['index']]); 28 29 parent::__construct(); 30 } 31 32 public function index() 33 { 34 $currentUser = \Auth::user(); 35 $currentMode = default_mode(); 36 37 $relationFriends = $currentUser->relationFriends->sortBy('username'); 38 $relationFriends->load(array_map( 39 fn ($userPreload) => "target.{$userPreload}", 40 UserCompactTransformer::listIncludesPreload($currentMode), 41 )); 42 43 $isApi = is_api_request(); 44 45 if ($isApi && api_version() >= 20241022) { 46 return json_collection($relationFriends, new UserRelationTransformer(), [ 47 "target:ruleset({$currentMode})", 48 ...array_map( 49 fn ($userInclude) => "target.{$userInclude}", 50 UserCompactTransformer::LIST_INCLUDES, 51 ), 52 ]); 53 } 54 55 $friends = $relationFriends->pluck('target'); 56 $usersJson = json_collection( 57 $friends, 58 (new UserCompactTransformer())->setMode($currentMode), 59 UserCompactTransformer::LIST_INCLUDES 60 ); 61 62 return $isApi 63 ? $usersJson 64 : ext_view('friends.index', compact('usersJson')); 65 } 66 67 public function store() 68 { 69 $currentUser = \Auth::user(); 70 71 if ($currentUser->friends()->count() >= $currentUser->maxFriends()) { 72 return error_popup(osu_trans('friends.too_many')); 73 } 74 75 $targetId = get_int(request('target')); 76 $targetUser = User::lookup($targetId, 'id'); 77 78 if ($targetUser === null) { 79 abort(404); 80 } 81 82 if ($currentUser->getKey() === $targetId) { 83 abort(422); 84 } 85 86 $relationQuery = $currentUser->relations()->where('zebra_id', $targetId); 87 while (true) { 88 $existingRelation = $relationQuery->first(); 89 $updateCount = false; 90 91 if ($existingRelation === null) { 92 try { 93 UserRelation::create([ 94 'user_id' => $currentUser->getKey(), 95 'zebra_id' => $targetId, 96 'friend' => true, 97 ]); 98 $updateCount = true; 99 } catch (\Throwable $e) { 100 if (is_sql_unique_exception($e)) { 101 // redo the loop with what should be a non-null 102 // $existingRelation on the next one 103 continue; 104 } 105 106 throw $e; 107 } 108 } elseif (!$existingRelation->friend) { 109 $existingRelation->update([ 110 'friend' => true, 111 'foe' => false, 112 ]); 113 $updateCount = true; 114 } 115 116 break; 117 } 118 119 if ($updateCount) { 120 dispatch(new UpdateUserFollowerCountCache($targetId)); 121 } 122 123 return [ 124 'user_relation' => json_item( 125 $relationQuery->withMutual()->first(), 126 new UserRelationTransformer(), 127 ), 128 ]; 129 } 130 131 public function destroy($id) 132 { 133 $currentUser = \Auth::user(); 134 135 $currentUser 136 ->friends() 137 ->wherePivot('zebra_id', $id) 138 ->firstOrFail(); 139 140 UserRelation::where([ 141 'user_id' => $currentUser->getKey(), 142 'zebra_id' => $id, 143 'friend' => 1, 144 ])->delete(); 145 146 dispatch(new UpdateUserFollowerCountCache($id)); 147 148 return response(null, 204); 149 } 150}