Add viewer display

This commit is contained in:
Alex Thomassen 2023-12-19 20:38:50 +00:00
parent 3a1fb4dfbb
commit 085f7af8f7
Signed by: Alex
GPG Key ID: 10BD786B5F6FF5DE
9 changed files with 116 additions and 12 deletions

View File

@ -47,7 +47,6 @@ class AddChannelPermissions extends Command
*/ */
public function handle() public function handle()
{ {
// Give the user a list of channels to choose from.
$channels = TraceChannel::all(); $channels = TraceChannel::all();
$channel = $this->choice('Which channel would you like to add permissions to?', $channels->pluck('channel_login')->toArray()); $channel = $this->choice('Which channel would you like to add permissions to?', $channels->pluck('channel_login')->toArray());
$channel = $channels->where('channel_login', $channel)->first(); $channel = $channels->where('channel_login', $channel)->first();
@ -64,7 +63,6 @@ public function handle()
$users = array_values($users); $users = array_values($users);
// Get the user details from the Twitch API.
$response = Http::get($this->usersApi, [ $response = Http::get($this->usersApi, [
'login' => $users, 'login' => $users,
]); ]);

View File

@ -21,10 +21,11 @@ public function index(Request $request)
public function channel(ChannelActionsRequest $request, Channel $channel) public function channel(ChannelActionsRequest $request, Channel $channel)
{ {
$limit = $request->input('limit', 50); $limit = $request->input('limit', 50);
$actions = $channel->actions()->orderBy('timestamp', 'desc')->paginate($limit);
return view('dashboard.channel', [ return view('dashboard.channel', [
'channel' => $channel, 'channel' => $channel,
'actions' => $channel->actions()->orderBy('timestamp', 'desc')->paginate($limit), 'actions' => $actions,
]); ]);
} }
} }

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Trace\Message;
class ViewerController extends Controller
{
public function index(Request $request, string $viewerId)
{
// Check if viewerId is numeric
if (!is_numeric($viewerId)) {
$message = Message::orderBy('timestamp', 'desc')
->where('author_login', $viewerId)
->firstOrFail();
$viewerId = $message->author_id;
}
// Check if viewerId exists
$initialMessage = Message::orderBy('timestamp', 'desc')
->where('author_id', $viewerId)
->firstOrFail();
// Get distinct author_login based on author_id
$usernames = Message::where('author_id', $viewerId)
->distinct('author_login')
->get()
->pluck('author_login')
->toArray();
return view('viewer.index', [
'id' => $viewerId,
'username' => $initialMessage->author_login,
'usernames' => $usernames,
]);
}
}

View File

@ -20,7 +20,7 @@ class ModeratorAction extends Model
* *
* @return string * @return string
*/ */
public function formatted() : string public function formatted($asHtml = false) : string
{ {
$cmd = $this->action; $cmd = $this->action;
$discriminator = $this->discriminator; $discriminator = $this->discriminator;
@ -38,7 +38,12 @@ public function formatted() : string
} }
if ($cmd === 'delete') { if ($cmd === 'delete') {
return sprintf('/%s %s %s %s', $cmd, $this->targetName(), $this->message, $this->message_id); $message = $this->message;
if ($asHtml) {
$message = htmlspecialchars($message);
}
return sprintf('/%s %s %s %s', $cmd, $this->targetName(), $message, $this->message_id);
} }
/** /**
@ -46,11 +51,21 @@ public function formatted() : string
*/ */
if ($cmd === 'timeout') { if ($cmd === 'timeout') {
$duration = $this->timeout_duration; $duration = $this->timeout_duration;
return sprintf('/%s %s %s %s', $cmd, $this->targetName(), $duration, $this->timeout_reason ?? ''); $reason = $this->timeout_reason;
if ($asHtml) {
$reason = htmlspecialchars($reason);
}
return sprintf('/%s %s %s %s', $cmd, $this->targetName(), $duration, $reason ?? '');
} }
if ($discriminator === 'TermAction') { if ($discriminator === 'TermAction') {
return sprintf('/%s %s %s', $cmd, $this->text, $this->term_id); $text = $this->text;
if ($asHtml) {
$text = htmlspecialchars($text);
}
return sprintf('/%s %s %s', $cmd, $text, $this->term_id);
} }
return trim(sprintf('/%s %s', $cmd, $this->targetName())); return trim(sprintf('/%s %s', $cmd, $this->targetName()));

View File

@ -32,6 +32,7 @@
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
@if ($actions->hasPages())
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="border px-4 py-2"> <td colspan="3" class="border px-4 py-2">
@ -39,6 +40,7 @@
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
@endif
</table> </table>
</div> </div>
</div> </div>

View File

@ -14,8 +14,8 @@
<!-- Scripts --> <!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js']) @vite(['resources/css/app.css', 'resources/js/app.js'])
</head> </head>
<body class="font-sans antialiased"> <body class="font-sans antialiased bg-gray-900">
<div class="min-h-screen bg-gray-100 dark:bg-gray-900"> <div class="flex flex-col h-screen justify-between">
<livewire:layout.navigation /> <livewire:layout.navigation />
<!-- Page Heading --> <!-- Page Heading -->
@ -34,13 +34,14 @@
<p>This is the initial implementation of this project. A few different things that are planned:</p> <p>This is the initial implementation of this project. A few different things that are planned:</p>
<ul class="list-disc list-inside mt-2"> <ul class="list-disc list-inside mt-2">
<li>Channels that are tracked where you have moderator access will be <a href="https://twitch.uservoice.com/forums/310213-developers/suggestions/38203849-add-endpoint-to-return-channels-where-the-user-is" class="underline text-amber-400">automatically retrieved from Twitch's API</a>. At the moment you have to be manually added to the database.</li> <li>Channels that are tracked where you have moderator access will be <a href="https://twitch.uservoice.com/forums/310213-developers/suggestions/38203849-add-endpoint-to-return-channels-where-the-user-is" class="underline text-amber-400">automatically retrieved from Twitch's API upon login</a>. At the moment I have to manually give permissions via the database.</li>
<li>Different filtering options: Type of action (timeout, raid etc.), moderator (whodunit), user/viewer (next point).</li> <li>Different filtering options: Type of action (timeout, raid etc.), moderator (whodunit), user/viewer (next point).</li>
<li>See related moderation actions, for example if a specific viewer has been timed out or banned multiple times.</li> <li>See related moderation actions, for example if a specific viewer has been timed out or banned multiple times.</li>
<li>Searching by text.</li> <li>Searching by text.</li>
<li>Linking any affected viewer's to their Twitch viewer card.</li> <li>Linking any affected viewer's to their Twitch viewer card.</li>
<li>Automatic conversion to local time. Currently it's displayed in UTC.</li> <li>Automatic conversion to local time. Currently it's displayed in UTC.</li>
<li>Ability to see a viewer's chat messages at the time of action, alongside messages before/after to see context.</li> <li>Ability to see a viewer's chat messages at the time of action, alongside messages before/after to see context.</li>
<li>Username history of a user</li>
<li>... and probably more I'll figure out as I go along.</li> <li>... and probably more I'll figure out as I go along.</li>
</ul> </ul>
</div> </div>
@ -49,9 +50,25 @@
</div> </div>
<!-- Page Content --> <!-- Page Content -->
<main> <main class="mb-auto bg-white dark:bg-gray-800 mt-4">
{{ $slot }} {{ $slot }}
</main> </main>
<!-- Footer -->
<footer class="bg-white dark:bg-gray-800 shadow bottom-0 mt-4">
<div class="max-w-full mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center">
<div class="text-sm text-gray-500 dark:text-gray-400">
<p>TraceDash &mdash; v0.0.2 [Pre-Alpha Early Access™]</p>
<p>Powered by an unhealthy amount of caffeine</p>
</div>
<div class="text-sm text-gray-500 dark:text-gray-400 text-right">
<p>Please send any feedback or bug reports to @Decicus on Discord</p>
</div>
</div>
</div>
</footer>
</div> </div>
</body> </body>
</html> </html>

View File

@ -63,7 +63,7 @@
{{-- "Three Dots" Separator --}} {{-- "Three Dots" Separator --}}
@if (is_string($element)) @if (is_string($element))
<span aria-disabled="true"> <span aria-disabled="true">
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-800 dark:text-gray-200 bg-white border border-gray-300 cursor-default leading-5">{{ $element }}</span> <span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5">{{ $element }}</span>
</span> </span>
@endif @endif

View File

@ -0,0 +1,28 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Viewer Details') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-full mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900 dark:text-gray-100">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
Viewer details for: {{ $username }} [{{ $id }}]
</h2>
<p class="mt-4">Seen under the following usernames:</p>
<!-- List style dots -->
<ul class="mt-2 list-disc list-inside">
@foreach ($usernames as $username)
<li>{{ $username }}</li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -2,6 +2,7 @@
use App\Http\Controllers\Auth\TwitchController; use App\Http\Controllers\Auth\TwitchController;
use App\Http\Controllers\DashboardController; use App\Http\Controllers\DashboardController;
use App\Http\Controllers\ViewerController;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
Route::middleware('guest')->group(function () { Route::middleware('guest')->group(function () {
@ -16,6 +17,9 @@
Route::get('/channel/{channel}', [DashboardController::class, 'channel']) Route::get('/channel/{channel}', [DashboardController::class, 'channel'])
->name('dashboard.channel'); ->name('dashboard.channel');
Route::get('/viewer/{viewerId}', [ViewerController::class, 'index'])
->name('viewer');
Route::post('logout', [TwitchController::class, 'logout']) Route::post('logout', [TwitchController::class, 'logout'])
->name('logout'); ->name('logout');
}); });