now users can be followed through the profile page
This commit is contained in:
parent
1f3c063ec5
commit
0d14d08246
@ -17,6 +17,8 @@ Notice that the styles were taken from [AnySpace](https://anyspace.3to.moe/about
|
|||||||
- [ ] Local posts should be federated
|
- [ ] Local posts should be federated
|
||||||
- [ ] Remote posts should be fetched
|
- [ ] Remote posts should be fetched
|
||||||
- [x] Follows
|
- [x] Follows
|
||||||
|
- [ ] I cannot follow myself
|
||||||
|
- [ ] Check when waiting for approval
|
||||||
- [ ] Likes
|
- [ ] Likes
|
||||||
- [ ] Comments
|
- [ ] Comments
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ Notice that the styles were taken from [AnySpace](https://anyspace.3to.moe/about
|
|||||||
- [ ] Show when the user is online
|
- [ ] Show when the user is online
|
||||||
- [ ] Set mood
|
- [ ] Set mood
|
||||||
- [ ] Set interests
|
- [ ] Set interests
|
||||||
- [ ] Update profile picture
|
- [x] Update profile picture
|
||||||
- [ ] Mark account as private (in federation manual approval is needed)
|
- [ ] Mark account as private (in federation manual approval is needed)
|
||||||
- [ ] Allow custom CSS
|
- [ ] Allow custom CSS
|
||||||
- [ ] Profile audio
|
- [ ] Profile audio
|
||||||
|
60
app/Actions/ActionsFriends.php
Normal file
60
app/Actions/ActionsFriends.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
use App\Types\TypeActor;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class ActionsFriends {
|
||||||
|
public static function add_friend ($target)
|
||||||
|
{
|
||||||
|
if (!auth ()->check ())
|
||||||
|
return ["error" => "You must be logged in to add friends."];
|
||||||
|
|
||||||
|
$target_actor = TypeActor::actor_exists_or_obtain ($target);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client = new Client ();
|
||||||
|
$response = $client->post (auth ()->user ()->actor->outbox, [
|
||||||
|
"json" => [
|
||||||
|
"type" => "Follow",
|
||||||
|
"object" => $target
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
catch (\Exception $e)
|
||||||
|
{
|
||||||
|
Log::error ("Error adding friend: " . $e->getMessage ());
|
||||||
|
return ["error" => "Error adding friend"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["success" => "Friend added"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function remove_friend ($target)
|
||||||
|
{
|
||||||
|
if (!auth ()->check ())
|
||||||
|
return ["error" => "You must be logged in to remove friends."];
|
||||||
|
|
||||||
|
$target_actor = TypeActor::actor_exists_or_obtain ($target);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client = new Client ();
|
||||||
|
$response = $client->post (auth ()->user ()->actor->outbox, [
|
||||||
|
"json" => [
|
||||||
|
"type" => "Unfollow",
|
||||||
|
"object" => $target
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
catch (\Exception $e)
|
||||||
|
{
|
||||||
|
Log::error ("Error removing friend: " . $e->getMessage ());
|
||||||
|
return ["error" => "Error removing friend"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["success" => "Friend removed"];
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,9 @@ namespace App\Http\Controllers\AP;
|
|||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Actor;
|
use App\Models\Actor;
|
||||||
|
use App\Models\Activity;
|
||||||
|
|
||||||
|
use App\Types\TypeActivity;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -11,9 +14,65 @@ use App\Http\Controllers\Controller;
|
|||||||
|
|
||||||
class APOutboxController extends Controller
|
class APOutboxController extends Controller
|
||||||
{
|
{
|
||||||
public function outbox (User $user)
|
public function outbox (User $user, Request $request)
|
||||||
{
|
{
|
||||||
Log::info ("APOutboxController@index");
|
switch ($request->get ("type"))
|
||||||
Log::info (json_encode (request ()->all ()));
|
{
|
||||||
|
case "Follow":
|
||||||
|
return $this->handle_follow ($user, $request->get ("object"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Unfollow":
|
||||||
|
return $this->handle_unfollow ($user, $request->get ("object"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log::info ("APOutboxController@index");
|
||||||
|
Log::info (json_encode (request ()->all ()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_follow (User $user, string $object)
|
||||||
|
{
|
||||||
|
$object_actor = Actor::where ("actor_id", $object)->first ();
|
||||||
|
if (!$object_actor)
|
||||||
|
return response ()->json ([ "error" => "object not found" ], 404);
|
||||||
|
|
||||||
|
$follow_activity = TypeActivity::craft_follow ($user->actor ()->first (), $object_actor);
|
||||||
|
$response = TypeActivity::post_activity ($follow_activity, $user->actor ()->first (), $object_actor);
|
||||||
|
|
||||||
|
if ($response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
|
||||||
|
return response ()->json ([ "error" => "failed to post activity" ], 500);
|
||||||
|
|
||||||
|
return [
|
||||||
|
"success" => "followed"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_unfollow (User $user, string $object)
|
||||||
|
{
|
||||||
|
$object_actor = Actor::where ("actor_id", $object)->first ();
|
||||||
|
if (!$object_actor)
|
||||||
|
return response ()->json ([ "error" => "object not found" ], 404);
|
||||||
|
$object_id = '"' . str_replace ("/", "\/", $object_actor->actor_id) . '"';
|
||||||
|
|
||||||
|
$follow_activity = Activity::where ("actor", $user->actor ()->first ()->actor_id)
|
||||||
|
->where ("object", $object_id)
|
||||||
|
->where ("type", "Follow")
|
||||||
|
->first ();
|
||||||
|
if (!$follow_activity)
|
||||||
|
return response ()->json ([ "error" => "no follow activity found" ], 404);
|
||||||
|
|
||||||
|
$unfollow_activity = TypeActivity::craft_undo ($follow_activity, $user->actor ()->first ());
|
||||||
|
$response = TypeActivity::post_activity ($unfollow_activity, $user->actor ()->first (), $object_actor);
|
||||||
|
|
||||||
|
if ($response->getStatusCode () < 200 || $response->getStatusCode () >= 300)
|
||||||
|
return response ()->json ([ "error" => "failed to post activity" ], 500);
|
||||||
|
|
||||||
|
$follow_activity->delete ();
|
||||||
|
return [
|
||||||
|
"success" => "unfollowed"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,13 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Types\TypeActor;
|
use App\Types\TypeActor;
|
||||||
|
use App\Actions\ActionsFriends;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Actor;
|
use App\Models\Actor;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
@ -48,7 +51,7 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
foreach ($user->friend_requests () as $request)
|
foreach ($user->friend_requests () as $request)
|
||||||
{
|
{
|
||||||
$actor = Actor::where ("actor_id", $request->actor)->first ();
|
$actor = Actor::where ("actor_id", $request)->first ();
|
||||||
if (!$actor)
|
if (!$actor)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -57,4 +60,22 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
return view ("users.requests", compact ("user", "requests"));
|
return view ("users.requests", compact ("user", "requests"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requests_accept (Request $request)
|
||||||
|
{
|
||||||
|
$user = auth ()->user ();
|
||||||
|
|
||||||
|
if (isset ($request->accept))
|
||||||
|
{
|
||||||
|
// accept a single request
|
||||||
|
$target = $request->accept;
|
||||||
|
$action = ActionsFriends::add_friend ($target);
|
||||||
|
if (isset ($action ["error"]))
|
||||||
|
{
|
||||||
|
return back ()->with ("error", $action ["error"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return back ()->with ("success", $action ["success"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
28
app/Http/Controllers/UserActionController.php
Normal file
28
app/Http/Controllers/UserActionController.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Actions\ActionsFriends;
|
||||||
|
|
||||||
|
class UserActionController extends Controller
|
||||||
|
{
|
||||||
|
public function friend (Request $request)
|
||||||
|
{
|
||||||
|
$response = ActionsFriends::add_friend ($request->get ("object"));
|
||||||
|
if (isset ($response ["error"]))
|
||||||
|
return back ()->with ("error", $response ["error"]);
|
||||||
|
|
||||||
|
return back ()->with ("success", $response ["success"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unfriend (Request $request)
|
||||||
|
{
|
||||||
|
$response = ActionsFriends::remove_friend ($request->get ("object"));
|
||||||
|
if (isset ($response ["error"]))
|
||||||
|
return back ()->with ("error", $response ["error"]);
|
||||||
|
|
||||||
|
return back ()->with ("success", $response ["success"]);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Activity;
|
||||||
|
|
||||||
use App\Types\TypeActor;
|
use App\Types\TypeActor;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -52,4 +54,15 @@ class Actor extends Model
|
|||||||
{
|
{
|
||||||
return TypeActor::build_response ($actor);
|
return TypeActor::build_response ($actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function friends_with (Actor $actor)
|
||||||
|
{
|
||||||
|
$self_id = '"' . str_replace ("/", "\/", $this->actor_id) . '"';
|
||||||
|
$other_id = '"' . str_replace ("/", "\/", $actor->actor_id) . '"';
|
||||||
|
|
||||||
|
$following = Activity::where ("actor", $this->actor_id)->where ("type", "Follow")->where ("object", $other_id)->first ();
|
||||||
|
$followers = Activity::where ("actor", $actor->actor_id)->where ("type", "Follow")->where ("object", $self_id)->first ();
|
||||||
|
|
||||||
|
return $following && $followers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,19 +77,19 @@ class User extends Authenticatable
|
|||||||
{
|
{
|
||||||
$actor_id = '"' . str_replace ("/", "\/", $this->actor->actor_id) . '"';
|
$actor_id = '"' . str_replace ("/", "\/", $this->actor->actor_id) . '"';
|
||||||
|
|
||||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->get ();
|
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->pluck ("actor")->toArray ();
|
||||||
$following = Activity::where ("type", "Follow")->where ("actor", $actor_id)->get ();
|
$following = Activity::where ("type", "Follow")->where ("actor", $this->actor->actor_id)->pluck ("object")->toArray ();
|
||||||
|
|
||||||
return $followers->intersect ($following);
|
return array_intersect ($followers, $following);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function friend_requests ()
|
public function friend_requests ()
|
||||||
{
|
{
|
||||||
$actor_id = '"' . str_replace ("/", "\/", $this->actor->actor_id) . '"';
|
$actor_id = '"' . str_replace ("/", "\/", $this->actor->actor_id) . '"';
|
||||||
|
|
||||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->get ();
|
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->pluck ("actor")->toArray ();
|
||||||
$following = Activity::where ("type", "Follow")->where ("actor", $actor_id)->get ();
|
$following = Activity::where ("type", "Follow")->where ("actor", $this->actor->actor_id)->pluck ("object")->toArray ();
|
||||||
|
|
||||||
return $followers->diff ($following);
|
return array_diff ($followers, $following);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,30 @@ class TypeActivity {
|
|||||||
return $accept_activity;
|
return $accept_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function craft_undo (Activity $activity, Actor $self)
|
||||||
|
{
|
||||||
|
$undo_activity = new Activity ();
|
||||||
|
$undo_activity->activity_id = env ("APP_URL") . "/activity/" . uniqid ();
|
||||||
|
$undo_activity->type = "Undo";
|
||||||
|
$undo_activity->actor = $self->actor_id;
|
||||||
|
$undo_activity->object = $activity;
|
||||||
|
$undo_activity->save ();
|
||||||
|
|
||||||
|
return $undo_activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function craft_follow (Actor $actor, Actor $object)
|
||||||
|
{
|
||||||
|
$follow_activity = new Activity ();
|
||||||
|
$follow_activity->activity_id = env ("APP_URL") . "/activity/" . uniqid ();
|
||||||
|
$follow_activity->type = "Follow";
|
||||||
|
$follow_activity->actor = $actor->actor_id;
|
||||||
|
$follow_activity->object = $object->actor_id;
|
||||||
|
$follow_activity->save ();
|
||||||
|
|
||||||
|
return $follow_activity;
|
||||||
|
}
|
||||||
|
|
||||||
public static function craft_signed_headers ($activity, Actor $source, Actor $target)
|
public static function craft_signed_headers ($activity, Actor $source, Actor $target)
|
||||||
{
|
{
|
||||||
if (!$source->user)
|
if (!$source->user)
|
||||||
|
@ -151,7 +151,7 @@ class TypeActor {
|
|||||||
|
|
||||||
foreach ($well_known->links as $link)
|
foreach ($well_known->links as $link)
|
||||||
{
|
{
|
||||||
if ($link->rel == "self")
|
if ($link->rel == "self")
|
||||||
{
|
{
|
||||||
$client = new Client ();
|
$client = new Client ();
|
||||||
$res = $client->request ("GET", $link->href, [
|
$res = $client->request ("GET", $link->href, [
|
||||||
|
@ -46,12 +46,23 @@
|
|||||||
<h4>Contacting {{ $actor->preferredUsername }}</h4>
|
<h4>Contacting {{ $actor->preferredUsername }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@auth
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="f-row">
|
<div class="f-row">
|
||||||
<div class="f-col">
|
<div class="f-col">
|
||||||
<a href="#">
|
@if (auth ()->user ()->actor->friends_with ($actor))
|
||||||
<img loading="lazy" src="/resources/icons/add.png" alt=""> Add to Friends
|
<form action="{{ route ('user.unfriend') }}" onclick="this.submit ()" method="post">
|
||||||
</a>
|
@csrf
|
||||||
|
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
||||||
|
<img loading="lazy" src="/resources/icons/delete.png" alt=""> Remove Friend
|
||||||
|
</form>
|
||||||
|
@else
|
||||||
|
<form action="{{ route ('user.friend') }}" onclick="this.submit ()" method="post">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="object" value="{{ $actor->actor_id }}">
|
||||||
|
<img loading="lazy" src="/resources/icons/add.png" alt=""> Add to Friends
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="f-col">
|
<div class="f-col">
|
||||||
@ -103,6 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@endauth
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="url-info">
|
<div class="url-info">
|
||||||
|
@ -43,9 +43,10 @@
|
|||||||
<p>
|
<p>
|
||||||
<b>Friend Request</b>
|
<b>Friend Request</b>
|
||||||
</p>
|
</p>
|
||||||
<form action="#" method="POST">
|
<form method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="submit" name="accept" value="Accept">
|
<input type="hidden" name="accept" value="{{ $frequest->actor_id }}">
|
||||||
|
<input type="submit" name="submit" value="Accept">
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Route;
|
|||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\HomeController;
|
||||||
use App\Http\Controllers\UserController;
|
use App\Http\Controllers\UserController;
|
||||||
use App\Http\Controllers\ProfileController;
|
use App\Http\Controllers\ProfileController;
|
||||||
|
use App\Http\Controllers\UserActionController;
|
||||||
|
|
||||||
Route::get('/', [ HomeController::class, "home" ])->name ("home");
|
Route::get('/', [ HomeController::class, "home" ])->name ("home");
|
||||||
|
|
||||||
@ -15,6 +16,10 @@ Route::get ("/auth/logout", [ UserController::class, "logout" ])->name ("logout"
|
|||||||
Route::post ("/auth/signup", [ UserController::class, "do_signup" ])->middleware ("guest");
|
Route::post ("/auth/signup", [ UserController::class, "do_signup" ])->middleware ("guest");
|
||||||
Route::post ("/auth/login", [ UserController::class, "do_login" ])->middleware ("guest");
|
Route::post ("/auth/login", [ UserController::class, "do_login" ])->middleware ("guest");
|
||||||
|
|
||||||
|
// user actions
|
||||||
|
Route::post ("/user/action/friend", [ UserActionController::class, "friend" ])->name ("user.friend")->middleware ("auth");
|
||||||
|
Route::post ("/user/action/unfriend", [ UserActionController::class, "unfriend" ])->name ("user.unfriend")->middleware ("auth");
|
||||||
|
|
||||||
// user routes
|
// user routes
|
||||||
Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->middleware ("auth");
|
Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->middleware ("auth");
|
||||||
Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth");
|
Route::post ("/user/edit", [ ProfileController::class, "update" ])->middleware ("auth");
|
||||||
@ -23,5 +28,6 @@ Route::get ("/user/{user_name}", [ ProfileController::class, "show" ])->name ("u
|
|||||||
// other routes
|
// other routes
|
||||||
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
|
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
|
||||||
Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth");
|
Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth");
|
||||||
|
Route::post ("/requests", [ HomeController::class, "requests_accept" ])->middleware ("auth");
|
||||||
|
|
||||||
require __DIR__ . "/api.php";
|
require __DIR__ . "/api.php";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user