Implement notifications page

Created a new page that displays notifications and added it to the
global nav. Abstracted out the pagination methods from the timeline
controller and put it in a helper class. Abstracted out the event info
(account, timestamp, etc.) into its own component.
This commit is contained in:
St John Karp 2018-08-25 17:29:36 -07:00
parent 7e617469cc
commit d8c1ed6884
10 changed files with 177 additions and 77 deletions

View File

@ -0,0 +1,62 @@
<?php
namespace App\Helpers;
use Mastodon;
use GuzzleHttp\Psr7;
use Illuminate\Http\Request;
class Pagination
{
public static function compile_links(string $route)
{
$links = [
'next' => null,
'prev' => null
];
# Parse out the links header returned from the Mastodon API.
$links_header = Mastodon::getResponse()->getHeader('link');
foreach (Psr7\parse_header($links_header) as $link)
{
# Find the prev and next links.
if ($link['rel'] === 'prev' || $link['rel'] === 'next')
{
$url = parse_url(trim($link[0], '<>'));
foreach (explode('&', $url['query']) as $query_param)
{
# Grab the ID query parameters from the link.
if (strpos($query_param, 'max_id=') === 0
|| strpos($query_param, 'since_id=') === 0)
{
# Construct new links with the correct offset.
$links[$link['rel']] = route($route) . '?' . $query_param;
}
}
}
}
return $links;
}
public static function compile_params(Request $request)
{
$params = [];
if ($request->has('max_id') && $request->has('since_id'))
{
# This scenario makes no sense. Someone's trying to dicker with the URL.
abort(400);
}
elseif ($request->has('max_id'))
{
$params['max_id'] = $request->max_id;
}
elseif ($request->has('since_id'))
{
$params['since_id'] = $request->since_id;
}
return $params;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Mastodon;
use Illuminate\Http\Request;
use App\Helpers\Pagination;
class NotificationsController extends Controller
{
public function get_notifications(Request $request)
{
$user = session('user');
$params = Pagination::compile_params($request);
$notifications = Mastodon::domain(env('MASTODON_DOMAIN'))
->token($user->token)
->get('/notifications', $params);
$vars = [
'notifications' => $notifications,
'mastodon_domain' => explode('//', env('MASTODON_DOMAIN'))[1],
'links' => Pagination::compile_links('notifications')
];
return view('notifications', $vars);
}
}

View File

@ -4,14 +4,14 @@ namespace App\Http\Controllers;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Mastodon; use Mastodon;
use GuzzleHttp\Psr7;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Helpers\Pagination;
class TimelineController extends Controller class TimelineController extends Controller
{ {
public function public_timeline(Request $request) public function public_timeline(Request $request)
{ {
$params = $this->compile_params($request); $params = Pagination::compile_params($request);
$params['local'] = true; $params['local'] = true;
$timeline = Mastodon::domain(env('MASTODON_DOMAIN')) $timeline = Mastodon::domain(env('MASTODON_DOMAIN'))
@ -20,7 +20,7 @@ class TimelineController extends Controller
$vars = [ $vars = [
'statuses' => $timeline, 'statuses' => $timeline,
'mastodon_domain' => explode('//', env('MASTODON_DOMAIN'))[1], 'mastodon_domain' => explode('//', env('MASTODON_DOMAIN'))[1],
'links' => $this->compile_links('public') 'links' => Pagination::compile_links('public')
]; ];
return view('public_timeline', $vars); return view('public_timeline', $vars);
@ -30,7 +30,7 @@ class TimelineController extends Controller
{ {
$user = session('user'); $user = session('user');
$params = $this->compile_params($request); $params = Pagination::compile_params($request);
$timeline = Mastodon::domain(env('MASTODON_DOMAIN')) $timeline = Mastodon::domain(env('MASTODON_DOMAIN'))
->token($user->token) ->token($user->token)
@ -39,7 +39,7 @@ class TimelineController extends Controller
$vars = [ $vars = [
'statuses' => $timeline, 'statuses' => $timeline,
'mastodon_domain' => explode('//', env('MASTODON_DOMAIN'))[1], 'mastodon_domain' => explode('//', env('MASTODON_DOMAIN'))[1],
'links' => $this->compile_links('home') 'links' => Pagination::compile_links('home')
]; ];
return view('home_timeline', $vars); return view('home_timeline', $vars);
@ -82,56 +82,4 @@ class TimelineController extends Controller
return redirect()->route('home'); return redirect()->route('home');
} }
private function compile_links(string $route)
{
$links = [
'next' => null,
'prev' => null
];
# Parse out the links header returned from the Mastodon API.
$links_header = Mastodon::getResponse()->getHeader('link');
foreach (Psr7\parse_header($links_header) as $link)
{
# Find the prev and next links.
if ($link['rel'] === 'prev' || $link['rel'] === 'next')
{
$url = parse_url(trim($link[0], '<>'));
foreach (explode('&', $url['query']) as $query_param)
{
# Grab the ID query parameters from the link.
if (strpos($query_param, 'max_id=') === 0
|| strpos($query_param, 'since_id=') === 0)
{
# Construct new links with the correct offset.
$links[$link['rel']] = route($route) . '?' . $query_param;
}
}
}
}
return $links;
}
private function compile_params(Request $request)
{
$params = [];
if ($request->has('max_id') && $request->has('since_id'))
{
# This scenario makes no sense. Someone's trying to dicker with the URL.
abort(400);
}
elseif ($request->has('max_id'))
{
$params['max_id'] = $request->max_id;
}
elseif ($request->has('since_id'))
{
$params['since_id'] = $request->since_id;
}
return $params;
}
} }

View File

@ -71,7 +71,7 @@ div.actions span.reblogged a {
color: green; color: green;
} }
time { time, span.action-notification {
font-size: smaller; font-size: smaller;
margin-left: 1em; margin-left: 1em;
} }

View File

@ -0,0 +1,19 @@
<span title="{{ $account['acct'] }}">
<a href="{{ $account['url'] }}">
<img
src="{{ $account['avatar'] }}"
alt="{{ $account['acct'] }}"
class="avatar"
/>
{{ $account['display_name'] }}
</a>
</span>
<time datetime="{{ $created_at }}">
@php
$created_at_datetime = new Carbon\Carbon($created_at);
@endphp
@if (env('SHOW_BEATS') === true)
{{ '@' . $created_at_datetime->format('B') }} |
@endif
{{ $created_at_datetime->diffForHumans(null, false, true) }}
</time>

View File

@ -2,5 +2,6 @@
<ul> <ul>
<li><a href="/timeline/home">Timeline</a></li> <li><a href="/timeline/home">Timeline</a></li>
<li><a href="/timeline/public">Public Timeline</a></li> <li><a href="/timeline/public">Public Timeline</a></li>
<li><a href="/notifications">Notifications</a></li>
</ul> </ul>
</nav> </nav>

View File

@ -0,0 +1,24 @@
<article>
@component('account_time_info', [
'account' => $notification['account'],
'created_at' => $notification['created_at']
])
@endcomponent
<span class="action-notification">
@if ($notification['type'] === 'mention')
mentioned
@elseif ($notification['type'] === 'reblog')
reblogged
@elseif ($notification['type'] === 'favourite')
favourited
@elseif ($notification['type'] === 'follow')
followed you.
@endif
</span>
@if ($notification['status'] ?? null !== null)
@component('status', ['status' => $notification['status']])
@endcomponent
@endif
</article>

View File

@ -0,0 +1,26 @@
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $mastodon_domain }} | Notifications</title>
<link rel="stylesheet" href="/css/styles.css" />
</head>
<body>
<h1>{{ $mastodon_domain }} | Notifications</h1>
@component('navigation')
@endcomponent
@foreach ($notifications as $notification)
@component('notification', ['notification' => $notification])
@endcomponent
@endforeach
@component('pagination', ['links' => $links])
@endcomponent
</body>
</html>

View File

@ -1,24 +1,11 @@
<hr /> <hr />
<article> <article>
<span title="{{ $status['account']['acct'] }}"> @component('account_time_info', [
<a href="{{ $status['account']['url'] }}"> 'account' => $status['account'],
<img 'created_at' => $status['created_at']
src="{{ $status['account']['avatar'] }}" ])
alt="{{ $status['account']['acct'] }}" @endcomponent
class="avatar"
/>
{{ $status['account']['display_name'] }}
</a>
</span>
<time datetime="{{ $status['created_at'] }}">
@php
$created_at = new Carbon\Carbon($status['created_at']);
@endphp
@if (env('SHOW_BEATS') === true)
{{ '@' . $created_at->format('B') }} |
@endif
{{ $created_at->diffForHumans(null, false, true) }}
</time>
@if ($status['spoiler_text'] !== '') @if ($status['spoiler_text'] !== '')
<details> <details>
<summary>{{ $status['spoiler_text'] }}</summary> <summary>{{ $status['spoiler_text'] }}</summary>

View File

@ -35,6 +35,10 @@ Route::post('/timeline/home', 'TimelineController@post_status')
Route::get('/status/{status_id}', 'StatusController@show_status') Route::get('/status/{status_id}', 'StatusController@show_status')
->name('status'); ->name('status');
Route::get('/notifications', 'NotificationsController@get_notifications')
->name('notifications')
->middleware('authorize');
Route::get('/login', 'LoginController@login') Route::get('/login', 'LoginController@login')
->name('login'); ->name('login');