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
|
||||
- [ ] Remote posts should be fetched
|
||||
- [x] Follows
|
||||
- [ ] I cannot follow myself
|
||||
- [ ] Check when waiting for approval
|
||||
- [ ] Likes
|
||||
- [ ] Comments
|
||||
|
||||
@ -25,7 +27,7 @@ Notice that the styles were taken from [AnySpace](https://anyspace.3to.moe/about
|
||||
- [ ] Show when the user is online
|
||||
- [ ] Set mood
|
||||
- [ ] Set interests
|
||||
- [ ] Update profile picture
|
||||
- [x] Update profile picture
|
||||
- [ ] Mark account as private (in federation manual approval is needed)
|
||||
- [ ] Allow custom CSS
|
||||
- [ ] 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\Actor;
|
||||
use App\Models\Activity;
|
||||
|
||||
use App\Types\TypeActivity;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -11,9 +14,65 @@ use App\Http\Controllers\Controller;
|
||||
|
||||
class APOutboxController extends Controller
|
||||
{
|
||||
public function outbox (User $user)
|
||||
public function outbox (User $user, Request $request)
|
||||
{
|
||||
switch ($request->get ("type"))
|
||||
{
|
||||
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;
|
||||
|
||||
use App\Types\TypeActor;
|
||||
use App\Actions\ActionsFriends;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Actor;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HomeController extends Controller
|
||||
@ -48,7 +51,7 @@ class HomeController extends Controller
|
||||
|
||||
foreach ($user->friend_requests () as $request)
|
||||
{
|
||||
$actor = Actor::where ("actor_id", $request->actor)->first ();
|
||||
$actor = Actor::where ("actor_id", $request)->first ();
|
||||
if (!$actor)
|
||||
continue;
|
||||
|
||||
@ -57,4 +60,22 @@ class HomeController extends Controller
|
||||
|
||||
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;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Activity;
|
||||
|
||||
use App\Types\TypeActor;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@ -52,4 +54,15 @@ class Actor extends Model
|
||||
{
|
||||
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) . '"';
|
||||
|
||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->get ();
|
||||
$following = Activity::where ("type", "Follow")->where ("actor", $actor_id)->get ();
|
||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->pluck ("actor")->toArray ();
|
||||
$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 ()
|
||||
{
|
||||
$actor_id = '"' . str_replace ("/", "\/", $this->actor->actor_id) . '"';
|
||||
|
||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->get ();
|
||||
$following = Activity::where ("type", "Follow")->where ("actor", $actor_id)->get ();
|
||||
$followers = Activity::where ("type", "Follow")->where ("object", $actor_id)->pluck ("actor")->toArray ();
|
||||
$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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!$source->user)
|
||||
|
@ -46,12 +46,23 @@
|
||||
<h4>Contacting {{ $actor->preferredUsername }}</h4>
|
||||
</div>
|
||||
|
||||
@auth
|
||||
<div class="inner">
|
||||
<div class="f-row">
|
||||
<div class="f-col">
|
||||
<a href="#">
|
||||
@if (auth ()->user ()->actor->friends_with ($actor))
|
||||
<form action="{{ route ('user.unfriend') }}" onclick="this.submit ()" method="post">
|
||||
@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
|
||||
</a>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="f-col">
|
||||
@ -103,6 +114,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endauth
|
||||
</div>
|
||||
|
||||
<div class="url-info">
|
||||
|
@ -43,9 +43,10 @@
|
||||
<p>
|
||||
<b>Friend Request</b>
|
||||
</p>
|
||||
<form action="#" method="POST">
|
||||
<form method="POST">
|
||||
@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>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\UserActionController;
|
||||
|
||||
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/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
|
||||
Route::get ("/user/edit", [ ProfileController::class, "edit" ])->name ("users.edit")->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
|
||||
Route::get ("/search", [ HomeController::class, "search" ])->name ("search");
|
||||
Route::get ("/requests", [ HomeController::class, "requests" ])->name ("requests")->middleware ("auth");
|
||||
Route::post ("/requests", [ HomeController::class, "requests_accept" ])->middleware ("auth");
|
||||
|
||||
require __DIR__ . "/api.php";
|
||||
|
Loading…
x
Reference in New Issue
Block a user