|
2 | 2 |
|
3 | 3 | namespace App\Livewire\Customer\Plugins; |
4 | 4 |
|
| 5 | +use App\Enums\PluginActivityType; |
5 | 6 | use App\Enums\PluginTier; |
6 | 7 | use App\Enums\PluginType; |
7 | 8 | use App\Jobs\ReviewPluginRepository; |
8 | 9 | use App\Models\Plugin; |
| 10 | +use App\Models\PluginActivity; |
9 | 11 | use App\Notifications\PluginPendingReview; |
10 | 12 | use App\Notifications\PluginSubmitted; |
11 | 13 | use App\Services\GitHubUserService; |
12 | 14 | use Flux\Flux; |
| 15 | +use Illuminate\Database\Eloquent\Collection; |
13 | 16 | use Illuminate\Support\Facades\Notification; |
| 17 | +use Illuminate\Support\Facades\RateLimiter; |
14 | 18 | use Illuminate\Support\Facades\Storage; |
15 | 19 | use Illuminate\Validation\ValidationException; |
16 | 20 | use Livewire\Attributes\Computed; |
17 | 21 | use Livewire\Attributes\Layout; |
18 | 22 | use Livewire\Attributes\Title; |
| 23 | +use Livewire\Attributes\Url; |
19 | 24 | use Livewire\Attributes\Validate; |
20 | 25 | use Livewire\Component; |
21 | 26 | use Livewire\WithFileUploads; |
@@ -47,18 +52,51 @@ class Show extends Component |
47 | 52 |
|
48 | 53 | public string $notes = ''; |
49 | 54 |
|
| 55 | + #[Url(as: 'tab')] |
50 | 56 | public string $activeTab = 'details'; |
51 | 57 |
|
52 | 58 | public string $pluginType = 'free'; |
53 | 59 |
|
54 | 60 | public ?string $tier = null; |
55 | 61 |
|
| 62 | + public string $replyMessage = ''; |
| 63 | + |
56 | 64 | #[Computed] |
57 | 65 | public function hasCompletedDeveloperOnboarding(): bool |
58 | 66 | { |
59 | 67 | return auth()->user()->developerAccount?->hasCompletedOnboarding() ?? false; |
60 | 68 | } |
61 | 69 |
|
| 70 | + /** |
| 71 | + * The full activity history, newest first. |
| 72 | + * |
| 73 | + * @return Collection<int, PluginActivity> |
| 74 | + */ |
| 75 | + #[Computed] |
| 76 | + public function activities(): Collection |
| 77 | + { |
| 78 | + return $this->plugin->activities() |
| 79 | + ->with('causer') |
| 80 | + ->orderByDesc('id') |
| 81 | + ->get(); |
| 82 | + } |
| 83 | + |
| 84 | + /** |
| 85 | + * Developers join a conversation the admins started; they can't open one. |
| 86 | + * Drafts haven't been submitted yet, so there's nothing to discuss. |
| 87 | + */ |
| 88 | + #[Computed] |
| 89 | + public function canMessageAdmins(): bool |
| 90 | + { |
| 91 | + if ($this->plugin->isDraft()) { |
| 92 | + return false; |
| 93 | + } |
| 94 | + |
| 95 | + return $this->activities->contains( |
| 96 | + fn (PluginActivity $activity): bool => $activity->type === PluginActivityType::MessageToDeveloper |
| 97 | + ); |
| 98 | + } |
| 99 | + |
62 | 100 | public function mount(string $vendor, string $package): void |
63 | 101 | { |
64 | 102 | $this->plugin = Plugin::findByVendorPackageOrFail($vendor, $package); |
@@ -234,6 +272,38 @@ public function withdrawFromReview(): void |
234 | 272 | Flux::toast(variant: 'success', text: 'Your plugin has been withdrawn from review and returned to draft.'); |
235 | 273 | } |
236 | 274 |
|
| 275 | + public function sendMessage(): void |
| 276 | + { |
| 277 | + if (! $this->canMessageAdmins) { |
| 278 | + Flux::toast(variant: 'danger', text: 'You can only reply once the Marketplace admins have messaged you about this plugin.'); |
| 279 | + |
| 280 | + return; |
| 281 | + } |
| 282 | + |
| 283 | + $key = 'plugin-message-reply:'.auth()->id(); |
| 284 | + |
| 285 | + if (RateLimiter::tooManyAttempts($key, 10)) { |
| 286 | + $seconds = RateLimiter::availableIn($key); |
| 287 | + |
| 288 | + $this->addError('replyMessage', "You're sending messages too quickly. Please wait {$seconds} seconds."); |
| 289 | + |
| 290 | + return; |
| 291 | + } |
| 292 | + |
| 293 | + $this->validate([ |
| 294 | + 'replyMessage' => ['required', 'string', 'max:5000'], |
| 295 | + ]); |
| 296 | + |
| 297 | + RateLimiter::hit($key, 60); |
| 298 | + |
| 299 | + $this->plugin->messageAdmins($this->replyMessage, auth()->id()); |
| 300 | + |
| 301 | + $this->replyMessage = ''; |
| 302 | + unset($this->activities, $this->canMessageAdmins); |
| 303 | + |
| 304 | + Flux::toast(variant: 'success', text: 'Your message has been sent to the Marketplace admins.'); |
| 305 | + } |
| 306 | + |
237 | 307 | public function returnToDraft(): void |
238 | 308 | { |
239 | 309 | if (! $this->plugin->isRejected()) { |
|
0 commit comments