Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl/mod moderation options #65

Open
wants to merge 23 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d2de62a
implement mod delete functions
waffle-lord Oct 8, 2024
fa97936
no delete for moderators
waffle-lord Oct 8, 2024
7d3c933
add routes
waffle-lord Oct 8, 2024
39820b8
undo my dumbness :)
waffle-lord Oct 8, 2024
8e8a654
add livewire component
waffle-lord Oct 8, 2024
e2e764f
Merge remote-tracking branch 'upstream/develop' into impl/mod-card-mo…
waffle-lord Oct 28, 2024
37c7a19
Merge remote-tracking branch 'upstream/develop' into impl/mod-card-mo…
waffle-lord Nov 18, 2024
a7e545d
get component on the card
waffle-lord Nov 20, 2024
f9378b2
setup and style dropdown options
waffle-lord Nov 22, 2024
593951b
remove disabled scope
waffle-lord Nov 23, 2024
0bb71a3
hide mods for normal users
waffle-lord Nov 23, 2024
625d194
Merge remote-tracking branch 'upstream/develop' into impl/mod-card-mo…
waffle-lord Nov 23, 2024
2783c2b
use tailwinds layers for ribbon classes
waffle-lord Nov 26, 2024
34e545c
rename ribbon-disabled to ribbon-red
waffle-lord Nov 26, 2024
ea821c4
remove moderation options from mod card
waffle-lord Nov 27, 2024
ffa00e3
Merge remote-tracking branch 'upstream/develop' into impl/mod-card-mo…
waffle-lord Nov 27, 2024
d4136bf
update models and allow disabling modVersions
waffle-lord Nov 29, 2024
66fc60d
Merge remote-tracking branch 'upstream/develop' into impl/mod-card-mo…
waffle-lord Dec 2, 2024
5cd1964
replace wireConfirm with modals WIP
waffle-lord Dec 2, 2024
a4beac7
hmmm
waffle-lord Dec 3, 2024
04ea8b9
use event instead of js timeout
waffle-lord Dec 3, 2024
570742d
swap ModeratedModel for CanModerate trait
waffle-lord Dec 9, 2024
021e76c
update moderation options control
waffle-lord Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions app/Livewire/Mod/ModerationActionButton.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace App\Livewire\Mod;

use App\Models\Mod;
use App\Models\ModVersion;
use Illuminate\Support\Facades\Log;
use Livewire\Attributes\On;
use Livewire\Component;

class ModerationActionButton extends Component
{
public ?string $moderatedObjectId = null;

public string $guid = '';

public string $actionType;

public string $targetType = '';

public bool $allowActions = false;

public bool $isRunning = false;

protected $listeners = ['refreshComponent' => '$refresh'];

public function mount(): void
{
$this->guid = uniqid('', true);
}

public function render()
{
$this->allowActions = ! $this->isRunning;

return view('livewire.mod.moderation-action-button');
}

public function runActionEvent(): void
{
$this->isRunning = true;
$this->dispatch("startAction.{$this->guid}");
}

#[On('startAction.{guid}')]
public function invokeAction(): void
{
if ($this->moderatedObjectId == null || $this->moderatedObjectId == '') {
Log::info('Failed: no ID specified.');

return;
}

Log::info("Object ID: $this->moderatedObjectId");

if ($this->targetType !== 'mod' && $this->targetType !== 'modVersion') {
Log::info('Failed: invalid target type.');

return;
}

switch ($this->targetType) {
case 'mod':
$moderatedObject = Mod::where('id', '=', $this->moderatedObjectId)->first();
break;

case 'modVersion':
$moderatedObject = ModVersion::where('id', '=', $this->moderatedObjectId)->first();
break;

default:
Log::info('Failed: invalid target type.');

return;
}

if ($moderatedObject == null) {
Log::info('Failed: moderated object is null');

return;
}

switch ($this->actionType) {
case 'delete':

$moderatedObject->delete();
break;

case 'enable':
case 'disable':

$moderatedObject->toggleDisabled();
break;

default:
Log::info('Failed: invalid action type.');

return;
}

$this->js('window.location.reload()');
}
}
35 changes: 35 additions & 0 deletions app/Livewire/Mod/ModerationOptions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Livewire\Mod;

use Livewire\Component;

class ModerationOptions extends Component
{
public string $objectId;

public string $targetType;

public bool $disabled;

public string $displayName;

public bool $showDeleteDialog = false;

public bool $showDisableDialog = false;

public function render()
{
return view('livewire.mod.moderation-options');
}

public function confirmDelete(): void
{
$this->showDeleteDialog = true;
}

public function confirmDisable(): void
{
$this->showDisableDialog = true;
}
}
4 changes: 2 additions & 2 deletions app/Models/Mod.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace App\Models;

use App\Http\Filters\V1\QueryFilter;
use App\Models\Scopes\DisabledScope;
use App\Models\Scopes\PublishedScope;
use App\Traits\CanModerate;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
Expand All @@ -21,6 +21,7 @@

class Mod extends Model
{
use CanModerate;
use HasFactory;
use Searchable;
use SoftDeletes;
Expand All @@ -30,7 +31,6 @@ class Mod extends Model
*/
protected static function booted(): void
{
static::addGlobalScope(new DisabledScope);
static::addGlobalScope(new PublishedScope);
}

Expand Down
5 changes: 2 additions & 3 deletions app/Models/ModVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace App\Models;

use App\Exceptions\InvalidVersionNumberException;
use App\Models\Scopes\DisabledScope;
use App\Models\Scopes\PublishedScope;
use App\Support\Version;
use App\Traits\CanModerate;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Expand All @@ -16,6 +16,7 @@

class ModVersion extends Model
{
use CanModerate;
use HasFactory;
use SoftDeletes;

Expand All @@ -29,8 +30,6 @@ class ModVersion extends Model
*/
protected static function booted(): void
{
static::addGlobalScope(new DisabledScope);

static::addGlobalScope(new PublishedScope);

static::saving(function (ModVersion $model) {
Expand Down
18 changes: 0 additions & 18 deletions app/Models/Scopes/DisabledScope.php

This file was deleted.

8 changes: 8 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ public function isAdmin(): bool
return Str::lower($this->role?->name) === 'administrator';
}

/**
* Conveniently check is a user is a mod or an admin
*/
public function isModOrAdmin(): bool
{
return $this->isMod() || $this->isAdmin();
}

/**
* Overwritten to instead use the queued version of the VerifyEmail notification.
*/
Expand Down
13 changes: 10 additions & 3 deletions app/Policies/ModPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ public function viewAny(?User $user): bool
*/
public function view(?User $user, Mod $mod): bool
{
// Everyone can view mods.
// Everyone can view mods, unless they are disabled.
if ($mod->disabled && ! $user?->isModOrAdmin()) {
return false;
}

return true;
}

Expand All @@ -37,15 +41,18 @@ public function create(User $user): bool
*/
public function update(User $user, Mod $mod): bool
{
return false;
return $user->isMod() || $user->isAdmin() || $mod->users->contains($user);
}

/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Mod $mod): bool
{
return false;
// I'm guessing we want the mod author to also be able to do this?
// what if there are multiple authors?
// I'm leaving that out for now -waffle.lazy
return $user->isAdmin();
}

/**
Expand Down
12 changes: 12 additions & 0 deletions app/Traits/CanModerate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace App\Traits;

trait CanModerate
{
public function toggleDisabled(): void
{
$this->disabled = ! $this->disabled;
$this->save();
}
}
41 changes: 24 additions & 17 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,34 @@ main a:not(.mod-list-component):not(.tab):not([role="menuitem"]) {
}
}

.ribbon {
font-size: 18px;
font-weight: bold;
color: #fff;
@layer components {
.ribbon {
font-size: 18px;
font-weight: bold;
@apply text-white bg-cyan-500 dark:bg-cyan-700;
}

.ribbon {
--f: .5em;
position: absolute;
top: 0;
left: 0;
line-height: 1.5;
padding-inline: 1lh;
padding-bottom: var(--f);
border-image: conic-gradient(#0008 0 0) 51%/var(--f);
clip-path: polygon(100% calc(100% - var(--f)), 100% 100%, calc(100% - var(--f)) calc(100% - var(--f)), var(--f) calc(100% - var(--f)), 0 100%, 0 calc(100% - var(--f)), 999px calc(100% - var(--f) - 999px), calc(100% - 999px) calc(100% - var(--f) - 999px));
transform: translate(calc((cos(45deg) - 1) * 100%), -100%) rotate(-45deg);
transform-origin: 100% 100%;
}
}

.ribbon {
--f: .5em;
position: absolute;
top: 0;
left: 0;
line-height: 1.5;
padding-inline: 1lh;
padding-bottom: var(--f);
border-image: conic-gradient(#0008 0 0) 51%/var(--f);
clip-path: polygon(100% calc(100% - var(--f)), 100% 100%, calc(100% - var(--f)) calc(100% - var(--f)), var(--f) calc(100% - var(--f)), 0 100%, 0 calc(100% - var(--f)), 999px calc(100% - var(--f) - 999px), calc(100% - 999px) calc(100% - var(--f) - 999px));
transform: translate(calc((cos(45deg) - 1) * 100%), -100%) rotate(-45deg);
transform-origin: 100% 100%;
background-color: #0e7490;
.ribbon-red {
@apply ribbon;
@apply text-white bg-red-500 dark:bg-red-700;
}


.rainbow {
height: 100%;
width: 100%;
Expand Down
7 changes: 5 additions & 2 deletions resources/views/components/mod-card.blade.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
@props(['mod', 'version'])

<a href="{{ $mod->detailUrl() }}" class="mod-list-component relative mx-auto w-full max-w-2xl">
@if ($mod->featured && !request()->routeIs('home'))
@if ($mod->featured && !$mod->disabled && !request()->routeIs('home'))
<div class="ribbon z-10">{{ __('Featured!') }}</div>
@endif
@if ($mod->disabled)
<div class="ribbon-red z-10">{{ __('Disabled') }}</div>
@endif
<div class="flex flex-col group h-full w-full bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl overflow-hidden hover:shadow-lg hover:bg-gray-50 dark:hover:bg-black hover:shadow-gray-400 dark:hover:shadow-black transition-colors ease-out duration-700">
<div class="h-auto md:h-full md:flex">
<div class="h-auto md:h-full md:shrink-0 overflow-hidden">
<div class="relative h-auto md:h-full md:shrink-0 overflow-hidden">
@if ($mod->thumbnail)
<img src="{{ $mod->thumbnailUrl }}" alt="{{ $mod->name }}" class="h-48 w-full object-cover md:h-full md:w-48 transform group-hover:scale-110 transition-all duration-200">
@else
Expand Down
4 changes: 3 additions & 1 deletion resources/views/livewire/mod/listing.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ class="absolute top-7 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y d
@if ($mods->isNotEmpty())
<div class="my-8 grid grid-cols-1 gap-6 lg:grid-cols-2">
@foreach ($mods as $mod)
<x-mod-card :mod="$mod" :version="$mod->latestVersion" />
@if(!$mod->disabled || (auth()->check() && auth()->user()->isModOrAdmin()))
<x-mod-card :mod="$mod" :version="$mod->latestVersion" />
@endif
@endforeach
</div>
@else
Expand Down
18 changes: 18 additions & 0 deletions resources/views/livewire/mod/moderation-action-button.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<div class="flex gap-4">
@if($this->allowActions)
<x-button x-on:click="show = false">
{{ __('Cancel') }}
</x-button>
<x-danger-button wire:click="runActionEvent">
{{ __(ucfirst($this->actionType)) }}
</x-danger-button>
@endif
@if($this->isRunning)
<div class="text-blue-600">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<style>.spinner_ajPY{transform-origin:center;animation:spinner_AtaB .75s infinite linear}@keyframes spinner_AtaB{100%{transform:rotate(360deg)}}</style>
<path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z" class="spinner_ajPY"/>
</svg>
</div>
@endif
</div>
Loading
Loading