From af7face4da060aabd9a44a0b12e0dab5c031d8fa Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 6 Apr 2021 21:17:42 -0600 Subject: [PATCH] Add Network Timeline --- app/Http/Controllers/AccountController.php | 14 +- app/Http/Controllers/PublicApiController.php | 127 +- app/Http/Controllers/TimelineController.php | 49 +- config/federation.php | 5 +- .../assets/js/components/NetworkTimeline.vue | 2197 +++++++++++++++++ resources/assets/js/network-timeline.js | 49 + resources/views/layouts/partial/nav.blade.php | 24 +- resources/views/site/help/timelines.blade.php | 68 +- routes/web.php | 898 +++---- webpack.mix.js | 23 +- 10 files changed, 2897 insertions(+), 557 deletions(-) create mode 100644 resources/assets/js/components/NetworkTimeline.vue create mode 100644 resources/assets/js/network-timeline.js diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 85ee74281..36cdd258a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers; -use Auth; -use Cache; -use Mail; +use Auth; +use Cache; +use Mail; use Illuminate\Support\Facades\Redis; use Illuminate\Support\Str; use Carbon\Carbon; @@ -81,7 +81,7 @@ class AccountController extends Controller if ($recentAttempt > 0) { return redirect()->back()->with('error', 'A verification email has already been sent recently. Please check your email, or try again later.'); - } + } EmailVerification::whereUserId(Auth::id())->delete(); @@ -247,7 +247,7 @@ class AccountController extends Controller switch ($type) { case 'user': $profile = Profile::findOrFail($item); - if ($profile->id == $user->id || $profile->user->is_admin == true) { + if ($profile->id == $user->id || ($profile->user && $profile->user->is_admin == true)) { return abort(403); } $class = get_class($profile); @@ -394,7 +394,7 @@ class AccountController extends Controller $request->session()->pull('sudoModeAttempts'); Auth::logout(); return redirect(route('login')); - } + } return view('auth.sudo'); } @@ -485,7 +485,7 @@ class AccountController extends Controller } } else { return false; - } + } } public function accountRestored(Request $request) diff --git a/app/Http/Controllers/PublicApiController.php b/app/Http/Controllers/PublicApiController.php index 7e1d6623e..23231b218 100644 --- a/app/Http/Controllers/PublicApiController.php +++ b/app/Http/Controllers/PublicApiController.php @@ -223,7 +223,7 @@ class PublicApiController extends Controller { if($profile->is_private == true && Auth::check() == false) { abort(404); - } + } switch ($status->scope) { case 'public': @@ -248,7 +248,7 @@ class PublicApiController extends Controller case 'draft': abort(404); break; - + default: abort(404); break; @@ -291,11 +291,11 @@ class PublicApiController extends Controller $dir = $min ? '>' : '<'; $id = $min ?? $max; $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', @@ -320,11 +320,11 @@ class PublicApiController extends Controller ->get(); } else { $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', @@ -351,7 +351,6 @@ class PublicApiController extends Controller $fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer()); $res = $this->fractal->createData($fractal)->toArray(); return response()->json($res); - } public function homeTimelineApi(Request $request) @@ -372,7 +371,7 @@ class PublicApiController extends Controller $max = $request->input('max_id'); $limit = $request->input('limit') ?? 3; $user = $request->user(); - + $key = 'user:last_active_at:id:'.$user->id; $ttl = now()->addMinutes(5); Cache::remember($key, $ttl, function() use($user) { @@ -396,7 +395,7 @@ class PublicApiController extends Controller // ->orWhere('status', '!=', null) // ->pluck('id'); // }); - + // $private = $private->diff($following)->flatten(); // $filters = UserFilter::whereUserId($pid) @@ -411,11 +410,11 @@ class PublicApiController extends Controller $dir = $min ? '>' : '<'; $id = $min ?? $max; $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', @@ -440,11 +439,11 @@ class PublicApiController extends Controller ->get(); } else { $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', @@ -470,12 +469,96 @@ class PublicApiController extends Controller $fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer()); $res = $this->fractal->createData($fractal)->toArray(); return response()->json($res); - } public function networkTimelineApi(Request $request) { - return response()->json([]); + abort_if(!Auth::check(), 403); + abort_if(config('federation.network_timeline') == false, 404); + + $this->validate($request,[ + 'page' => 'nullable|integer|max:40', + 'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, + 'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX, + 'limit' => 'nullable|integer|max:30' + ]); + + $page = $request->input('page'); + $min = $request->input('min_id'); + $max = $request->input('max_id'); + $limit = $request->input('limit') ?? 3; + $user = $request->user(); + + $key = 'user:last_active_at:id:'.$user->id; + $ttl = now()->addMinutes(5); + Cache::remember($key, $ttl, function() use($user) { + $user->last_active_at = now(); + $user->save(); + return; + }); + + if($min || $max) { + $dir = $min ? '>' : '<'; + $id = $min ?? $max; + $timeline = Status::select( + 'id', + 'uri', + 'caption', + 'rendered', + 'profile_id', + 'type', + 'in_reply_to_id', + 'reblog_of_id', + 'is_nsfw', + 'scope', + 'local', + 'reply_count', + 'comments_disabled', + 'place_id', + 'likes_count', + 'reblogs_count', + 'created_at', + 'updated_at' + )->where('id', $dir, $id) + ->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->whereNotNull('uri') + ->whereScope('public') + // ->where('created_at', '>', now()->subMonths(3)) + ->orderBy('created_at', 'desc') + ->limit($limit) + ->get(); + } else { + $timeline = Status::select( + 'id', + 'uri', + 'caption', + 'rendered', + 'profile_id', + 'type', + 'in_reply_to_id', + 'reblog_of_id', + 'is_nsfw', + 'scope', + 'local', + 'reply_count', + 'comments_disabled', + 'created_at', + 'place_id', + 'likes_count', + 'reblogs_count', + 'updated_at' + )->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']) + ->with('profile', 'hashtags', 'mentions') + ->whereNotNull('uri') + ->whereScope('public') + ->where('created_at', '>', now()->subMonths(3)) + ->orderBy('created_at', 'desc') + ->simplePaginate($limit); + } + + $fractal = new Fractal\Resource\Collection($timeline, new StatusTransformer()); + $res = $this->fractal->createData($fractal)->toArray(); + return response()->json($res); } public function relationships(Request $request) @@ -489,7 +572,7 @@ class PublicApiController extends Controller 'id.*' => 'required|integer' ]); $ids = collect($request->input('id')); - $filtered = $ids->filter(function($v) { + $filtered = $ids->filter(function($v) { return $v != Auth::user()->profile->id; }); $relations = Profile::whereNull('status')->findOrFail($filtered->all()); @@ -569,10 +652,10 @@ class PublicApiController extends Controller $limit = $request->limit ?? 9; $max_id = $request->max_id; $min_id = $request->min_id; - $scope = $request->only_media == true ? + $scope = $request->only_media == true ? ['photo', 'photo:album', 'video', 'video:album'] : ['photo', 'photo:album', 'video', 'video:album', 'share', 'reply']; - + if($profile->is_private) { if(!Auth::check()) { return response()->json([]); @@ -605,11 +688,11 @@ class PublicApiController extends Controller $dir = '>'; $id = 1; $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', @@ -643,11 +726,11 @@ class PublicApiController extends Controller $dir = $min_id ? '>' : '<'; $id = $min_id ?? $max_id; $timeline = Status::select( - 'id', + 'id', 'uri', 'caption', 'rendered', - 'profile_id', + 'profile_id', 'type', 'in_reply_to_id', 'reblog_of_id', diff --git a/app/Http/Controllers/TimelineController.php b/app/Http/Controllers/TimelineController.php index 52267b2d7..ba03a5d63 100644 --- a/app/Http/Controllers/TimelineController.php +++ b/app/Http/Controllers/TimelineController.php @@ -2,37 +2,32 @@ namespace App\Http\Controllers; -use Auth, Cache; -use App\Follower; -use App\Profile; -use App\Status; -use App\User; -use App\UserFilter; use Illuminate\Http\Request; class TimelineController extends Controller { - public function __construct() - { - $this->middleware('auth'); - $this->middleware('twofactor'); - } + public function __construct() + { + $this->middleware('auth'); + $this->middleware('twofactor'); + } - public function local(Request $request) - { - $this->validate($request, [ - 'layout' => 'nullable|string|in:grid,feed' - ]); - $layout = $request->input('layout', 'feed'); - return view('timeline.local', compact('layout')); - } + public function local(Request $request) + { + $this->validate($request, [ + 'layout' => 'nullable|string|in:grid,feed' + ]); + $layout = $request->input('layout', 'feed'); + return view('timeline.local', compact('layout')); + } - public function network(Request $request) - { - $this->validate($request, [ - 'layout' => 'nullable|string|in:grid,feed' - ]); - $layout = $request->input('layout', 'feed'); - return view('timeline.network', compact('layout')); - } + public function network(Request $request) + { + abort_if(config('federation.network_timeline') == false, 404); + $this->validate($request, [ + 'layout' => 'nullable|string|in:grid,feed' + ]); + $layout = $request->input('layout', 'feed'); + return view('timeline.network', compact('layout')); + } } diff --git a/config/federation.php b/config/federation.php index 81e687fe3..0e51d7dfe 100644 --- a/config/federation.php +++ b/config/federation.php @@ -10,7 +10,6 @@ return [ | ActivityPub configuration | */ - 'activitypub' => [ 'enabled' => env('ACTIVITY_PUB', false), 'outbox' => env('AP_OUTBOX', true), @@ -41,4 +40,6 @@ return [ 'enabled' => env('WEBFINGER', true) ], -]; \ No newline at end of file + 'network_timeline' => env('PF_NETWORK_TIMELINE', false) + +]; diff --git a/resources/assets/js/components/NetworkTimeline.vue b/resources/assets/js/components/NetworkTimeline.vue new file mode 100644 index 000000000..8737480b4 --- /dev/null +++ b/resources/assets/js/components/NetworkTimeline.vue @@ -0,0 +1,2197 @@ + + + + + diff --git a/resources/assets/js/network-timeline.js b/resources/assets/js/network-timeline.js new file mode 100644 index 000000000..e66d90ce5 --- /dev/null +++ b/resources/assets/js/network-timeline.js @@ -0,0 +1,49 @@ +Vue.component( + 'notification-card', + require('./components/NotificationCard.vue').default +); + +Vue.component( + 'photo-presenter', + require('./components/presenter/PhotoPresenter.vue').default +); + +Vue.component( + 'video-presenter', + require('./components/presenter/VideoPresenter.vue').default +); + +Vue.component( + 'photo-album-presenter', + require('./components/presenter/PhotoAlbumPresenter.vue').default +); + +Vue.component( + 'video-album-presenter', + require('./components/presenter/VideoAlbumPresenter.vue').default +); + +Vue.component( + 'mixed-album-presenter', + require('./components/presenter/MixedAlbumPresenter.vue').default +); + +Vue.component( + 'post-menu', + require('./components/PostMenu.vue').default +); + +Vue.component( + 'network-timeline', + require('./components/NetworkTimeline.vue').default +); + +Vue.component( + 'announcements-card', + require('./components/AnnouncementsCard.vue').default +); + +Vue.component( + 'story-component', + require('./components/StoryTimelineComponent.vue').default +); diff --git a/resources/views/layouts/partial/nav.blade.php b/resources/views/layouts/partial/nav.blade.php index 79f498e8b..6d6251f9b 100644 --- a/resources/views/layouts/partial/nav.blade.php +++ b/resources/views/layouts/partial/nav.blade.php @@ -15,7 +15,7 @@ @endauth @guest - +