Update viewer page to include moderator actions, links to Twitch viewer cards
This commit is contained in:
parent
e45fb69b6a
commit
70fa331f46
@ -12,7 +12,7 @@ class ViewerController extends Controller
|
||||
{
|
||||
public function index(ViewerRequest $request, string $viewer)
|
||||
{
|
||||
$viewer = strtolower($viewer);
|
||||
$viewer = strtolower(trim($viewer));
|
||||
$message = Message::orderBy('timestamp', 'desc')
|
||||
->where('author_id', $viewer)
|
||||
->orWhere('author_login', $viewer)
|
||||
@ -45,11 +45,26 @@ public function index(ViewerRequest $request, string $viewer)
|
||||
return $b->timestamp <=> $a->timestamp;
|
||||
});
|
||||
|
||||
$user = $request->user();
|
||||
$moderatorActions = [];
|
||||
$channelPerms = $user->channelPermissions();
|
||||
$channels = $user->getTraceChannels();
|
||||
|
||||
$channelPermsIds = $channelPerms->pluck('channel_provider_id')->toArray();
|
||||
|
||||
$moderatorActions = ModeratorAction::where('target_id', $viewerId)
|
||||
->orWhere('initiator_id', $viewerId)
|
||||
->whereIn('channel_id', $channelPermsIds)
|
||||
->orderBy('timestamp', 'desc')
|
||||
->paginate(50);
|
||||
|
||||
return view('viewer.index', [
|
||||
'id' => $viewerId,
|
||||
'username' => $initialMessage->author_login,
|
||||
'usernames' => $usernames,
|
||||
'messages' => $messages,
|
||||
'actions' => $moderatorActions,
|
||||
'channels' => $channels,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public function formatted($asHtml = false) : string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target of the action, if any
|
||||
* Get the target name of the action, if any
|
||||
*
|
||||
* @param bool $linkToViewerPage Returns an HTML link to the viewer page
|
||||
* @return string
|
||||
@ -106,6 +106,30 @@ public function targetName($linkToViewerPage = false) : ?string
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target ID of the action, if any
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function targetId() : ?string
|
||||
{
|
||||
if (!empty($this->target_id)) {
|
||||
return $this->target_id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channel that the action was executed in
|
||||
*
|
||||
* @return Channel
|
||||
*/
|
||||
public function channel()
|
||||
{
|
||||
return $this->belongsTo(Channel::class, 'channel_id', 'channel_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that executed the action
|
||||
*
|
||||
|
82
resources/views/components/moderator-actions.blade.php
Normal file
82
resources/views/components/moderator-actions.blade.php
Normal file
@ -0,0 +1,82 @@
|
||||
@php
|
||||
$showChannel = $showChannel ?? false;
|
||||
@endphp
|
||||
<table class="w-full table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2">Time & date</th>
|
||||
<th class="px-4 py-2">Command</th>
|
||||
<th class="px-4 py-2">Moderator</th>
|
||||
@if ($showChannel)
|
||||
<th class="px-4 py-2">Channel</th>
|
||||
@endif
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($actions as $action)
|
||||
<tr>
|
||||
<td class="px-4 py-2 border" title="{{ $action->timestamp }} UTC" data-timestamp="{{ $action->timestamp->toIso8601String() }}"></td>
|
||||
@if (empty($action->targetName()))
|
||||
<td class="px-4 py-2 break-all border">
|
||||
<code>{{ $action->formatted() }}</code>
|
||||
</td>
|
||||
@else
|
||||
<td class="px-4 py-2 break-all border">
|
||||
<code>
|
||||
{!! $action->formatted(true) !!}
|
||||
</code>
|
||||
</td>
|
||||
@endif
|
||||
<td class="px-4 py-2 border">{{ $action->user->login }}</td>
|
||||
@if ($showChannel)
|
||||
@php
|
||||
$channel = $action->channel->username();
|
||||
@endphp
|
||||
|
||||
<td class="px-4 py-2 border">
|
||||
<a class="text-rose-400" href="{{ route('dashboard.channel', ['channel' => $channel]) }}">
|
||||
{{ $channel }}
|
||||
</a>
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@if ($actions->hasPages())
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="{{ $showChannel ? 4 : 3 }}" class="px-4 py-2 border">
|
||||
{{ $actions->links() }}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@endif
|
||||
</table>
|
||||
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const timestamps = document.querySelectorAll('[data-timestamp]');
|
||||
|
||||
// Get locale from browser for Intl.DateTimeFormat
|
||||
const locale = navigator.language;
|
||||
|
||||
const formatterOpts = {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
const formatter = new Intl.DateTimeFormat(locale, formatterOpts);
|
||||
|
||||
for (const timestamp of timestamps) {
|
||||
const date = new Date(timestamp.dataset.timestamp);
|
||||
timestamp.innerText = formatter.format(date);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
@ -1,55 +1,18 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
{{ __('Dashboard') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="overflow-hidden bg-gray-800 shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-100">
|
||||
<h2 class="font-semibold text-xl text-gray-200 leading-tight">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-200">
|
||||
{{ $channel->username() }}
|
||||
</h2>
|
||||
|
||||
<table class="table-auto w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-4 py-2">Time & date</th>
|
||||
<th class="px-4 py-2">Command</th>
|
||||
<th class="px-4 py-2">Moderator</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($actions as $action)
|
||||
<tr>
|
||||
<td class="border px-4 py-2" title="{{ $action->timestamp }}">{{ $action->timestamp }}
|
||||
</td>
|
||||
@if (empty($action->targetName()))
|
||||
<td class="border px-4 py-2 break-all">
|
||||
<code>{{ $action->formatted() }}</code>
|
||||
</td>
|
||||
@else
|
||||
<td class="border px-4 py-2 break-all">
|
||||
<code>
|
||||
{!! $action->formatted(true) !!}
|
||||
</code>
|
||||
</td>
|
||||
@endif
|
||||
<td class="border px-4 py-2">{{ $action->user->login }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@if ($actions->hasPages())
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3" class="border px-4 py-2">
|
||||
{{ $actions->links() }}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@endif
|
||||
</table>
|
||||
@include('components.moderator-actions', ['actions' => $actions])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,19 +1,19 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
{{ __('Dashboard') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="overflow-hidden bg-gray-800 shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-100">
|
||||
{{ __("Hi :user!", ['user' => Auth::user()->display_name]) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-800 p-6 -mt-6 text-gray-100">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight mb-2">
|
||||
<div class="p-6 -mt-6 text-gray-100 bg-gray-800">
|
||||
<h2 class="mb-2 text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
{{ __('Channels') }}
|
||||
</h2>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="https://decicus-cdn.b-cdn.net/fontawesome/v6.4.2/css/all.min.css" integrity="sha512-WgpJvPsU5RMfJeB5QbEbVfyuEGX+emeIHhNIFdc2SdyXVA11IyRLkdHZZHcnbxs/tCEAQFr2YEWrqqHFRL88eQ==" crossorigin="anonymous">
|
||||
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@ -17,13 +18,13 @@
|
||||
@yield('head')
|
||||
</head>
|
||||
<body class="font-sans antialiased bg-gray-900">
|
||||
<div class="flex flex-col h-screen justify-between">
|
||||
<div class="flex flex-col justify-between h-screen">
|
||||
<livewire:layout.navigation />
|
||||
|
||||
<!-- Page Heading -->
|
||||
@if (isset($header))
|
||||
<header class="bg-gray-800 shadow">
|
||||
<div class="max-w-full mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-full px-4 py-6 mx-auto sm:px-6 lg:px-8">
|
||||
{{ $header }}
|
||||
</div>
|
||||
</header>
|
||||
@ -31,24 +32,28 @@
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="max-w-full mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="overflow-hidden bg-gray-800 shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-100">
|
||||
<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">
|
||||
<p><i class="fas fa-list fa-1x"></i> This is the initial implementation of this project. A few different things that are planned:</p>
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
<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, since this API is currently only "planned" by Twitch.</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>Searching by text.</li>
|
||||
<li>Linking any affected viewer's to their Twitch viewer card.</li>
|
||||
<li>Automatic conversion to local time. Currently time & date is 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>... and probably more I'll figure out as I go along.</li>
|
||||
</ul>
|
||||
|
||||
<p class="mt-6">List of things that were planned and has been implemented in some form:</p>
|
||||
<ul class="list-disc list-inside mt-2">
|
||||
<li>Username history of a user - For any user that has been timed out, raided, etc. you can now click on their name (highlighted in orange/amber) and access their basic "viewer profile".</li>
|
||||
<p class="mt-6"><i class="fas fa-check fa-1x"></i> List of things that were planned and has been implemented in some form:</p>
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
<li><s>Username history of a user</s> - For any user that has been timed out, raided, etc. you can now click on their name (highlighted in orange/amber) and access their basic "viewer profile".</li>
|
||||
<li><s>See related moderation actions, for example if a specific viewer has been timed out or banned multiple times.</s> - Implemented on the viewer profile. Will display moderation actions for any channels you have access to.</li>
|
||||
<li><s>Automatic conversion to local time. Currently time & date is displayed in UTC.</s> - Original UTC timestamp is still available if you hover over the converted timestamp in the table.</li>
|
||||
<li><s>Linking any affected viewer's to their Twitch viewer card.</s> - Viewer card will be linked for any channel you have access to.</li>
|
||||
</ul>
|
||||
|
||||
<p class="mt-6"><i class="fas fa-bug fa-1x"></i> Known bugs:</p>
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
<li>Attempting to access a viewer page of someone who has no tracked chat messages will give you a 404. Trying to decide how I want to tackle those scenarios.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -56,25 +61,27 @@
|
||||
</div>
|
||||
|
||||
<!-- Page Content -->
|
||||
<main class="mb-auto mt-4">
|
||||
<main class="mt-4 mb-auto">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="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">
|
||||
<footer class="bottom-0 mt-4 bg-gray-800 shadow">
|
||||
<div class="max-w-full px-4 py-6 mx-auto sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
<p>TraceDash — v0.0.3 [Pre-Alpha Early Access™]</p>
|
||||
<p>TraceDash — v0.0.4 [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">
|
||||
<div class="text-sm text-right text-gray-500 dark:text-gray-400">
|
||||
<p>Please send any feedback or bug reports to @Decicus on Discord</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,15 +14,15 @@
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="font-sans text-gray-900 antialiased">
|
||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
||||
<body class="font-sans antialiased text-gray-900">
|
||||
<div class="flex flex-col items-center min-h-screen pt-6 bg-gray-100 sm:justify-center sm:pt-0 dark:bg-gray-900">
|
||||
<div>
|
||||
<a href="/" wire:navigate>
|
||||
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
|
||||
<x-application-logo class="w-20 h-20 text-gray-500 fill-current" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
|
||||
<div class="w-full px-6 py-4 mt-6 overflow-hidden bg-white shadow-md sm:max-w-md dark:bg-gray-800 sm:rounded-lg">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700">
|
||||
<nav x-data="{ open: false }" class="bg-white border-b border-gray-100 dark:bg-gray-800 dark:border-gray-700">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="max-w-full mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-full px-4 mx-auto sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<!-- Logo -->
|
||||
<div class="shrink-0 flex items-center">
|
||||
<div class="flex items-center shrink-0">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<x-application-logo class="block h-9 w-auto fill-current text-gray-800 dark:text-gray-200" />
|
||||
<x-application-logo class="block w-auto text-gray-800 fill-current h-9 dark:text-gray-200" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -23,12 +23,12 @@
|
||||
<div class="hidden sm:flex sm:items-center sm:ms-6">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
|
||||
<img src="{{ Auth::user()->avatar }}" class="h-6 w-6 rounded-full" alt="{{ Auth::user()->display_name }}" />
|
||||
<button class="inline-flex items-center px-3 py-2 text-sm font-medium leading-4 text-gray-500 transition duration-150 ease-in-out bg-white border border-transparent rounded-md dark:text-gray-400 dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none">
|
||||
<img src="{{ Auth::user()->avatar }}" class="w-6 h-6 rounded-full" alt="{{ Auth::user()->display_name }}" />
|
||||
<div class="ml-2">{{ Auth::user()->display_name }}</div>
|
||||
|
||||
<div class="ms-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<svg class="w-4 h-4 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
@ -52,9 +52,9 @@
|
||||
@endif
|
||||
|
||||
<!-- Hamburger -->
|
||||
<div class="-me-2 flex items-center sm:hidden">
|
||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<div class="flex items-center -me-2 sm:hidden">
|
||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 text-gray-400 transition duration-150 ease-in-out rounded-md dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400">
|
||||
<svg class="w-6 h-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
@ -75,7 +75,7 @@
|
||||
<!-- Responsive Settings Options -->
|
||||
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600">
|
||||
<div class="px-4">
|
||||
<div class="font-medium text-base text-gray-800 dark:text-gray-200">{{ Auth::user()->display_name }}</div>
|
||||
<div class="text-base font-medium text-gray-800 dark:text-gray-200">{{ Auth::user()->display_name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 space-y-1">
|
||||
|
@ -1,27 +1,43 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
{{ __('Viewer Details') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<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="overflow-hidden bg-white shadow-sm dark:bg-gray-800 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">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
Viewer details for: {{ $username }} [{{ $id }}]
|
||||
</h2>
|
||||
|
||||
<p class="mt-4">Seen under the following usernames:</p>
|
||||
@if (!empty($messages))
|
||||
<p class="mt-6">Seen under the following usernames:</p>
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
@foreach ($messages as $username => $message)
|
||||
<li class="mt-1">
|
||||
{{ $username }} <span class="text-gray-400">— Last seen with this username: {{ date('Y-m-d', strtotime($message->timestamp)) }}</span>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
||||
<!-- List style dots -->
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
@foreach ($messages as $username => $message)
|
||||
<li class="mt-1">
|
||||
{{ $username }} <span class="text-gray-400">— Last seen with this username: {{ date('Y-m-d', strtotime($message->timestamp)) }}</span>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@if ($actions->isNotEmpty())
|
||||
<h2 class="mt-6 text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">Moderator actions</h2>
|
||||
@include('components.moderator-actions', ['actions' => $actions, 'showChannel' => true])
|
||||
@endif
|
||||
|
||||
@if ($channels->isNotEmpty())
|
||||
<h2 class="mt-6 text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200"><i class="fab fa-twitch fa-1x"></i> Twitch viewer cards in these channels:</h2>
|
||||
<ul class="mt-2 list-disc list-inside">
|
||||
@foreach ($channels as $channel)
|
||||
<li class="mt-1">
|
||||
<a href="https://www.twitch.tv/popout/{{ $channel->username() }}/viewercard/{{ $username }}" target="_blank" class="text-amber-400">{{ $channel->username() }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user