From 7867fcce02a8b487e0adc076442a664013b49c1c Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Fri, 16 Jan 2026 12:36:52 -0300 Subject: [PATCH] feat: Implement real-time post status updates and configurable social platform enabling/disabling. --- CLAUDE.md | 1 + GEMINI.md | 1 + app/Broadcasting/PostChannel.php | 30 +++ app/Enums/SocialPlatform.php | 15 ++ app/Events/PostPlatformStatusUpdated.php | 44 ++++ .../Controllers/Auth/FacebookController.php | 2 +- .../Controllers/Auth/InstagramController.php | 3 +- .../Controllers/Auth/LinkedInController.php | 1 + .../Auth/LinkedInPageController.php | 1 + .../Controllers/Auth/SocialController.php | 13 +- .../Controllers/Auth/ThreadsController.php | 1 + .../Controllers/Auth/TikTokController.php | 1 + app/Http/Controllers/Auth/XController.php | 1 + .../Controllers/Auth/YouTubeController.php | 1 + app/Jobs/PublishToSocialPlatform.php | 10 + app/Providers/AppServiceProvider.php | 2 + app/Services/Social/ThreadsPublisher.php | 38 +++- app/Socialite/InstagramExtendSocialite.php | 13 ++ composer.json | 3 +- composer.lock | 191 +++++++++++++++++- config/services.php | 9 +- config/trypost.php | 43 ++++ package-lock.json | 31 +++ package.json | 1 + public/apple-touch-icon.png | Bin 1662 -> 1370 bytes public/favicon.ico | Bin 4286 -> 2530 bytes public/favicon.png | Bin 1370 -> 0 bytes public/favicon.svg | 14 +- resources/js/app.ts | 1 - resources/js/echo.ts | 14 -- resources/js/pages/posts/Show.vue | 29 ++- routes/channels.php | 3 + 32 files changed, 480 insertions(+), 37 deletions(-) create mode 100644 app/Broadcasting/PostChannel.php create mode 100644 app/Events/PostPlatformStatusUpdated.php create mode 100644 app/Socialite/InstagramExtendSocialite.php create mode 100644 config/trypost.php delete mode 100644 public/favicon.png delete mode 100644 resources/js/echo.ts diff --git a/CLAUDE.md b/CLAUDE.md index f583ed4d..dc73db65 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,7 @@ ## Foundational Context - vue (VUE) - v3 - @laravel/vite-plugin-wayfinder (WAYFINDER) - v0 - eslint (ESLINT) - v9 +- laravel-echo (ECHO) - v2 - prettier (PRETTIER) - v3 ## Conventions diff --git a/GEMINI.md b/GEMINI.md index f583ed4d..dc73db65 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -28,6 +28,7 @@ ## Foundational Context - vue (VUE) - v3 - @laravel/vite-plugin-wayfinder (WAYFINDER) - v0 - eslint (ESLINT) - v9 +- laravel-echo (ECHO) - v2 - prettier (PRETTIER) - v3 ## Conventions diff --git a/app/Broadcasting/PostChannel.php b/app/Broadcasting/PostChannel.php new file mode 100644 index 00000000..de8639b6 --- /dev/null +++ b/app/Broadcasting/PostChannel.php @@ -0,0 +1,30 @@ + $user->id, + 'user_email' => $user->email, + 'post_id' => $post->id, + 'workspace_id' => $post->workspace_id, + ]); + + $result = $post->workspace->hasMember($user); + + Log::info('PostChannel::join result', [ + 'result' => $result, + 'workspace_owner_id' => $post->workspace->user_id, + 'is_owner' => $post->workspace->user_id === $user->id, + ]); + + return $result; + } +} diff --git a/app/Enums/SocialPlatform.php b/app/Enums/SocialPlatform.php index 5d7be70d..1f439853 100644 --- a/app/Enums/SocialPlatform.php +++ b/app/Enums/SocialPlatform.php @@ -91,4 +91,19 @@ public function supportsTextOnly(): bool self::Threads => true, }; } + + public function isEnabled(): bool + { + return config("trypost.platforms.{$this->value}.enabled", true); + } + + /** + * Get all enabled platforms. + * + * @return array + */ + public static function enabled(): array + { + return array_filter(self::cases(), fn (self $platform) => $platform->isEnabled()); + } } diff --git a/app/Events/PostPlatformStatusUpdated.php b/app/Events/PostPlatformStatusUpdated.php new file mode 100644 index 00000000..5cf9c9e2 --- /dev/null +++ b/app/Events/PostPlatformStatusUpdated.php @@ -0,0 +1,44 @@ +postPlatform->post_id), + ]; + } + + public function broadcastWith(): array + { + $post = $this->postPlatform->post->fresh(); + + return [ + 'post_platform' => [ + 'id' => $this->postPlatform->id, + 'status' => $this->postPlatform->status, + 'platform_url' => $this->postPlatform->platform_url, + 'error_message' => $this->postPlatform->error_message, + 'published_at' => $this->postPlatform->published_at?->toISOString(), + ], + 'post' => [ + 'id' => $post->id, + 'status' => $post->status->value, + 'published_at' => $post->published_at?->toISOString(), + ], + ]; + } +} diff --git a/app/Http/Controllers/Auth/FacebookController.php b/app/Http/Controllers/Auth/FacebookController.php index 6c9c3c51..264cc05f 100644 --- a/app/Http/Controllers/Auth/FacebookController.php +++ b/app/Http/Controllers/Auth/FacebookController.php @@ -22,11 +22,11 @@ class FacebookController extends SocialController 'pages_show_list', 'pages_read_engagement', 'pages_manage_posts', - 'pages_read_user_content', ]; public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/InstagramController.php b/app/Http/Controllers/Auth/InstagramController.php index 2c284df8..de93dd90 100644 --- a/app/Http/Controllers/Auth/InstagramController.php +++ b/app/Http/Controllers/Auth/InstagramController.php @@ -14,7 +14,7 @@ class InstagramController extends SocialController { - protected string $driver = 'facebook'; + protected string $driver = 'instagram'; protected SocialPlatform $platform = SocialPlatform::Instagram; @@ -27,6 +27,7 @@ class InstagramController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/LinkedInController.php b/app/Http/Controllers/Auth/LinkedInController.php index f8ddc2e3..c28dfde7 100644 --- a/app/Http/Controllers/Auth/LinkedInController.php +++ b/app/Http/Controllers/Auth/LinkedInController.php @@ -27,6 +27,7 @@ class LinkedInController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/LinkedInPageController.php b/app/Http/Controllers/Auth/LinkedInPageController.php index 69f054d1..b083e348 100644 --- a/app/Http/Controllers/Auth/LinkedInPageController.php +++ b/app/Http/Controllers/Auth/LinkedInPageController.php @@ -31,6 +31,7 @@ class LinkedInPageController extends SocialController public function connect(Request $request, Workspace $workspace): SymfonyResponse { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/SocialController.php b/app/Http/Controllers/Auth/SocialController.php index cfc002cd..94bf738d 100644 --- a/app/Http/Controllers/Auth/SocialController.php +++ b/app/Http/Controllers/Auth/SocialController.php @@ -16,13 +16,22 @@ class SocialController extends Controller { + protected SocialPlatform $platform; + + protected function ensurePlatformEnabled(): void + { + if (isset($this->platform) && ! $this->platform->isEnabled()) { + abort(403, 'This platform is currently unavailable.'); + } + } + public function index(Workspace $workspace): Response { $this->authorize('view', $workspace); $connectedAccounts = $workspace->socialAccounts; - $platforms = collect(SocialPlatform::cases())->map(function ($platform) use ($connectedAccounts) { + $platforms = collect(SocialPlatform::enabled())->map(function ($platform) use ($connectedAccounts) { $connected = $connectedAccounts->firstWhere('platform', $platform); return [ @@ -32,7 +41,7 @@ public function index(Workspace $workspace): Response 'connected' => $connected !== null, 'account' => $connected, ]; - }); + })->values(); return Inertia::render('accounts/Index', [ 'workspace' => $workspace, diff --git a/app/Http/Controllers/Auth/ThreadsController.php b/app/Http/Controllers/Auth/ThreadsController.php index dad7b57d..84663cf6 100644 --- a/app/Http/Controllers/Auth/ThreadsController.php +++ b/app/Http/Controllers/Auth/ThreadsController.php @@ -24,6 +24,7 @@ class ThreadsController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/TikTokController.php b/app/Http/Controllers/Auth/TikTokController.php index df3c8e56..4dcbbadf 100644 --- a/app/Http/Controllers/Auth/TikTokController.php +++ b/app/Http/Controllers/Auth/TikTokController.php @@ -24,6 +24,7 @@ class TikTokController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/XController.php b/app/Http/Controllers/Auth/XController.php index 260301d8..3bfdde15 100644 --- a/app/Http/Controllers/Auth/XController.php +++ b/app/Http/Controllers/Auth/XController.php @@ -24,6 +24,7 @@ class XController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Http/Controllers/Auth/YouTubeController.php b/app/Http/Controllers/Auth/YouTubeController.php index 2ff7e7bd..b243085b 100644 --- a/app/Http/Controllers/Auth/YouTubeController.php +++ b/app/Http/Controllers/Auth/YouTubeController.php @@ -24,6 +24,7 @@ class YouTubeController extends SocialController public function connect(Request $request, Workspace $workspace): Response { + $this->ensurePlatformEnabled(); $this->authorize('manageAccounts', $workspace); if ($workspace->hasConnectedPlatform($this->platform->value)) { diff --git a/app/Jobs/PublishToSocialPlatform.php b/app/Jobs/PublishToSocialPlatform.php index b369d66f..50e91a80 100644 --- a/app/Jobs/PublishToSocialPlatform.php +++ b/app/Jobs/PublishToSocialPlatform.php @@ -3,6 +3,7 @@ namespace App\Jobs; use App\Enums\SocialPlatform; +use App\Events\PostPlatformStatusUpdated; use App\Models\PostPlatform; use App\Services\Social\FacebookPublisher; use App\Services\Social\InstagramPublisher; @@ -29,6 +30,7 @@ public function __construct(public PostPlatform $postPlatform) {} public function handle(): void { $this->postPlatform->markAsPublishing(); + $this->broadcastStatus(); try { $publisher = $this->getPublisher(); @@ -47,6 +49,14 @@ public function handle(): void // Always check and update post status after each platform finishes $this->updatePostStatus(); + + // Broadcast final status + $this->broadcastStatus(); + } + + private function broadcastStatus(): void + { + PostPlatformStatusUpdated::dispatch($this->postPlatform->fresh()); } private function getPublisher(): LinkedInPublisher|LinkedInPagePublisher|XPublisher|TikTokPublisher|YouTubePublisher|FacebookPublisher|InstagramPublisher|ThreadsPublisher diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c022048b..d111e7d1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Socialite\InstagramExtendSocialite; use App\Socialite\LinkedInPageExtendSocialite; use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Date; @@ -34,6 +35,7 @@ public function boot(): void protected function configureSocialite(): void { + Event::listen(SocialiteWasCalled::class, InstagramExtendSocialite::class); Event::listen(SocialiteWasCalled::class, LinkedInExtendSocialite::class); Event::listen(SocialiteWasCalled::class, LinkedInPageExtendSocialite::class); Event::listen(SocialiteWasCalled::class, TikTokExtendSocialite::class); diff --git a/app/Services/Social/ThreadsPublisher.php b/app/Services/Social/ThreadsPublisher.php index 9edd7242..41fc48b2 100644 --- a/app/Services/Social/ThreadsPublisher.php +++ b/app/Services/Social/ThreadsPublisher.php @@ -73,7 +73,7 @@ private function publishTextPost(string $userId, string $accessToken, string $co private function publishImagePost(string $userId, string $accessToken, string $content, $media): array { - Log::info('Threads publishing image post', ['user_id' => $userId]); + Log::info('Threads publishing image post', ['user_id' => $userId, 'image_url' => $media->url]); // Step 1: Create container $containerResponse = Http::post("{$this->baseUrl}/{$userId}/threads", [ @@ -93,7 +93,12 @@ private function publishImagePost(string $userId, string $accessToken, string $c $containerId = $containerResponse->json()['id']; - // Step 2: Publish + Log::info('Threads image container created', ['container_id' => $containerId]); + + // Step 2: Wait for image processing + $this->waitForMediaProcessing($containerId, $accessToken); + + // Step 3: Publish return $this->publishContainer($userId, $accessToken, $containerId); } @@ -164,10 +169,8 @@ private function publishCarousel(string $userId, string $accessToken, string $co $childId = $containerResponse->json()['id']; - // Wait for video processing if needed - if ($isVideo) { - $this->waitForMediaProcessing($childId, $accessToken); - } + // Wait for media processing (both images and videos) + $this->waitForMediaProcessing($childId, $accessToken); $childContainers[] = $childId; } @@ -234,32 +237,45 @@ private function waitForMediaProcessing(string $containerId, string $accessToken { for ($i = 0; $i < $maxAttempts; $i++) { $statusResponse = Http::get("{$this->baseUrl}/{$containerId}", [ - 'fields' => 'status', + 'fields' => 'status,error_message', 'access_token' => $accessToken, ]); if ($statusResponse->failed()) { + Log::warning('Threads status check failed', [ + 'container_id' => $containerId, + 'attempt' => $i, + 'body' => $statusResponse->body(), + ]); sleep(3); continue; } - $status = $statusResponse->json()['status'] ?? 'UNKNOWN'; + $data = $statusResponse->json(); + $status = $data['status'] ?? 'UNKNOWN'; - Log::info('Threads media processing status', ['status' => $status, 'attempt' => $i]); + Log::info('Threads media processing status', [ + 'container_id' => $containerId, + 'status' => $status, + 'attempt' => $i, + 'response' => $data, + ]); if ($status === 'FINISHED') { return; } if ($status === 'ERROR') { - throw new \Exception('Threads media processing failed'); + $errorMessage = $data['error_message'] ?? 'Unknown error'; + throw new \Exception('Threads media processing failed: '.$errorMessage); } sleep(3); } - Log::warning('Threads media processing timeout, proceeding anyway'); + Log::warning('Threads media processing timeout', ['container_id' => $containerId]); + throw new \Exception('Threads media processing timeout after '.$maxAttempts.' attempts'); } private function refreshToken(SocialAccount $account): void diff --git a/app/Socialite/InstagramExtendSocialite.php b/app/Socialite/InstagramExtendSocialite.php new file mode 100644 index 00000000..004b59b1 --- /dev/null +++ b/app/Socialite/InstagramExtendSocialite.php @@ -0,0 +1,13 @@ +extendSocialite('instagram', \Laravel\Socialite\Two\FacebookProvider::class); + } +} diff --git a/composer.json b/composer.json index ae6266d7..45722e17 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", "pestphp/pest": "^4.3", - "pestphp/pest-plugin-laravel": "^4.0" + "pestphp/pest-plugin-laravel": "^4.0", + "spatie/laravel-horizon-watcher": "^1.1" }, "autoload": { "files": [ diff --git a/composer.lock b/composer.lock index 9db1311d..14001629 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c6c2e7e35c2c6d5737296f159edc9a95", + "content-hash": "6993d4c308ced358f1dca8da903c9c22", "packages": [ { "name": "aws/aws-crt-php", @@ -12063,6 +12063,195 @@ ], "time": "2025-02-07T05:00:38+00:00" }, + { + "name": "spatie/file-system-watcher", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/spatie/file-system-watcher.git", + "reference": "2fe96de88af6660b3ab7658a8f1d41b10ad0aff6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/file-system-watcher/zipball/2fe96de88af6660b3ab7658a8f1d41b10ad0aff6", + "reference": "2fe96de88af6660b3ab7658a8f1d41b10ad0aff6", + "shasum": "" + }, + "require": { + "php": "^8.3", + "symfony/process": "^7.0|^8.0" + }, + "require-dev": { + "pestphp/pest": "^4.0", + "spatie/ray": "^1.22", + "spatie/temporary-directory": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Watcher\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Watch changes in the file system using PHP", + "homepage": "https://github.com/spatie/file-system-watcher", + "keywords": [ + "file-system-watcher", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/file-system-watcher/issues", + "source": "https://github.com/spatie/file-system-watcher/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-11-26T10:41:42+00:00" + }, + { + "name": "spatie/laravel-horizon-watcher", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-horizon-watcher.git", + "reference": "141b94e8eea18f465c4419e8eea54936308a8788" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-horizon-watcher/zipball/141b94e8eea18f465c4419e8eea54936308a8788", + "reference": "141b94e8eea18f465c4419e8eea54936308a8788", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", + "php": "^8.1", + "spatie/file-system-watcher": "^1.1.1", + "spatie/laravel-package-tools": "^1.13.6" + }, + "require-dev": { + "laravel/pint": "^1.2", + "nunomaduro/collision": "^6.3.1|^8.0", + "orchestra/testbench": "^7.11|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.22.1|^2.34|^3.7", + "pestphp/pest-plugin-laravel": "^1.3|^2.3|^3.1", + "phpunit/phpunit": "^9.5.26|^10.5|^11.5.3", + "spatie/laravel-ray": "^1.31" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "HorizonWatcher": "Spatie\\HorizonWatcher\\Facades\\HorizonWatcher" + }, + "providers": [ + "Spatie\\HorizonWatcher\\HorizonWatcherServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\HorizonWatcher\\": "src", + "Spatie\\HorizonWatcher\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Automatically restart Horizon when local PHP files change", + "homepage": "https://github.com/spatie/laravel-horizon-watcher", + "keywords": [ + "laravel", + "laravel-horizon-watcher", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-horizon-watcher/issues", + "source": "https://github.com/spatie/laravel-horizon-watcher/tree/1.1.4" + }, + "time": "2025-07-17T15:43:23+00:00" + }, + { + "name": "spatie/laravel-package-tools", + "version": "1.92.7", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/f09a799850b1ed765103a4f0b4355006360c49a5", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.23|^2.1|^3.1", + "phpunit/php-code-coverage": "^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.5.24|^10.5|^11.5", + "spatie/pest-plugin-test-time": "^1.1|^2.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.92.7" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-07-17T15:46:43+00:00" + }, { "name": "staabm/side-effects-detector", "version": "1.0.5", diff --git a/config/services.php b/config/services.php index 3bae0c89..fe4534e7 100644 --- a/config/services.php +++ b/config/services.php @@ -67,13 +67,20 @@ 'redirect' => env('GOOGLE_CLIENT_REDIRECT'), ], - // Facebook (used for Facebook Pages and Instagram) + // Facebook Pages 'facebook' => [ 'client_id' => env('FACEBOOK_CLIENT_ID'), 'client_secret' => env('FACEBOOK_CLIENT_SECRET'), 'redirect' => env('FACEBOOK_CLIENT_REDIRECT'), ], + // Instagram + 'instagram' => [ + 'client_id' => env('INSTAGRAM_CLIENT_ID'), + 'client_secret' => env('INSTAGRAM_CLIENT_SECRET'), + 'redirect' => env('INSTAGRAM_CLIENT_REDIRECT'), + ], + // Threads 'threads' => [ 'client_id' => env('THREADS_CLIENT_ID'), diff --git a/config/trypost.php b/config/trypost.php new file mode 100644 index 00000000..801db826 --- /dev/null +++ b/config/trypost.php @@ -0,0 +1,43 @@ + [ + 'linkedin' => [ + 'enabled' => env('TRYPOST_LINKEDIN_ENABLED', true), + ], + 'linkedin-page' => [ + 'enabled' => env('TRYPOST_LINKEDIN_PAGE_ENABLED', true), + ], + 'x' => [ + 'enabled' => env('TRYPOST_X_ENABLED', true), + ], + 'tiktok' => [ + 'enabled' => env('TRYPOST_TIKTOK_ENABLED', true), + ], + 'youtube' => [ + 'enabled' => env('TRYPOST_YOUTUBE_ENABLED', true), + ], + 'facebook' => [ + 'enabled' => env('TRYPOST_FACEBOOK_ENABLED', true), + ], + 'instagram' => [ + 'enabled' => env('TRYPOST_INSTAGRAM_ENABLED', true), + ], + 'threads' => [ + 'enabled' => env('TRYPOST_THREADS_ENABLED', true), + ], + ], + +]; diff --git a/package-lock.json b/package-lock.json index e57b4101..5840a3bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@types/node": "^22.13.5", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-typescript": "^14.3.0", + "chokidar": "^5.0.0", "concurrently": "^9.0.1", "eslint": "^9.17.0", "eslint-config-prettier": "^10.0.1", @@ -2889,6 +2890,22 @@ "node": ">=8" } }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -6005,6 +6022,20 @@ ], "license": "MIT" }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", diff --git a/package.json b/package.json index 7e888cc5..7c79f906 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@types/node": "^22.13.5", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-typescript": "^14.3.0", + "chokidar": "^5.0.0", "concurrently": "^9.0.1", "eslint": "^9.17.0", "eslint-config-prettier": "^10.0.1", diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index c2efef6de4482ad4ec770787891fb3ab7f7b1f58..dd0665ffd4afbea3c6365e781f68f7a226d3de75 100644 GIT binary patch literal 1370 zcmV-g1*Q6lP)Zdp4)$-VZGymowurI!T9lzW;iYrX% zF)vw%j!;FH5jU=m(Q5~)(!A7qrZetEvA}wJsfPakhPn4VN>Bl~!*!rl^WB?gT5(SX zqrcGGi_7Tuwoh;qSA){mue|o_dfbyowqX6^;stZFfs*6^_ecR$v03Qqs zcFUFd1~!^&TuN!IUB3D3ReJnzhzNVPaDtmTt+0M&r`B%&cD4gS z$pKcu>ijETURA1cPAPMUP)IU>4X#?;YT3K?;I<@W{=h@Hlq6){z%jU#6lA`@6L2XB z$iaVq`~`L;z@@k&8{m6jg**c;#ThvoBG@3Oz@@k%55{2c$Qf`cj>t#eh zJ~oSRj+`Sd#R)lfn&u3-09=X-^4NKB;m8HzQXG&c?r0Q->;RYYjXZTvE_Y;yxRg)i zM7zqlA$!23d?6=37Lgg*BQE6wd8UIssgW7rQm&C_KAJW&G9z5dC3336m5GrV;!>`V zQy&kWip&_7a)F%bfcscv0k~Ar$eGV*977g_OBIQn%PFG)vOrvlkb4SKuYH@P$B)@gR5V>evY8tZt748KfyTYX=A@jI|AV*}^xYRggKG$gEfb0vGibLjg z5ljYT-?-E$GQX>gQXq4|r3m?fIbp=}GOihrx#3b4`CFV~e4ShZWUjcw@xIb?0fNN9G zH|%XNzh;IAb)fCLvfv)pwm)bV(S+y9glo3mYd^FWI`)19{N$TxYlR!|RJm}`c>-wA z<8zU47Y1=FPd?;J^IAhB`Let%s8ToSrk&TF}H2r)K=+DE#~?0*{!(}pkem* z&LgLfA}`N8H-c@(ZM7q>OO`p{U5EW{hyc$aCHBtl65js(Q^?GISzq`dG=IivV6&az c%ld5p12HEXf2#(sW&i*H07*qoM6N<$g2{fB#Q*>R literal 1662 zcmV-^27&pBP)40{{R3XI2D70000&P)t-s00030 zFd+XeApb8Q|1BW@E+GFcApbBR|1Kc^Eg=6bApb5P|1Kc^EFk|aApa~N|1BW@Eg=6e zApb5P|1BW@Eg&n7Ql0<+02OpnPE!CNzd)dXU=ZKm?|<)rkZ`ZhFmF)fpgb%900q`b zL_t(|+U%Oqj+-zLMaMYBBrGf>;s1ZNY7`jIiI_O8!Rm4!Tgf`>xtSR*$bTdkB!69t zVK_O-TcI8P=u%70x?P5+&ZXoke;#Hk@~pfohJ~`UXs9Q;_B2)gTk?{cvbc)et1K<* zr+Qaj9xAKsDzB#eYQpr7r@Sm0>dVCQfKCB0Kf{y|EP9n~<+TZwVye09V$sm9n9{PSY^C}VzXiE}GC#|%c)PWA z!&5y>`JP4VkDV6b8$Z_jJJu@A$QeFse^IT?qj=G4l>4y#(Kj|Guiq{iFk{=Fwl*76 z)xQ4|>IRkjy#1lI8JM_!yJWG>8ZJNf8dXXx zrk~GbPG4tWE+u0Q(BCgl#w@cPJ5nx)T9MuG26Ggy6OkTfT zvPjI^>P)^G7FA5^`aMfVOxu)|x2A9549ulPjgm!RI{t-}!yN(BSbnfZ0rTAmB^Yxa z0ShsC{k=0v223kLF@%7Hn5rZ4wL=GnpitveraoqSfe(_EIcO5du!CjnDf@wtxv3e z-u^bn6xYI{>k~_4b4*0{)`LMCWEV`GECi(~y~rMz#`428YDY}1JOdWj&DW^?F-Ib; zJl{lRfw{1#A0resWj2_y2v`;oie!a3x9HrIxnXL+G9OMEVvZJ7Q)Y@O(#)P#Gsb*Y zz6+8!=6Lpjd0-xx2j+o!U>=wU<`2TWpY@?U`fOKhgXs$-hQ`^*57T3Y7GFKsXEUm) zBmpLJD?F4LXCptnrod!)-s4>q&NlMH1ep8=0k2DW_S$RM1ei2s8;}?pXCpsMfQj(j zNDOf{^1}p}D=)N>81ii9hY2uO&CkS;XEQ%chDr0UB!)N}`C$S~^BV*vhRd^&A11-1 z`R$K=!+176!AgetySX#KX&8o~XtND<+Gs@?_dhEVl?dUf^KAF4`3KmZ#1o5M$8F`` z3P!Tl&&CLr3Ul6+|GA>4A2dj*!Zf~z9YX~DLkTA3XY8i74zs-cJ)DTceLfYYA-}08 zCfNm;#x)M#D5naO^M{f7;@Z>}U}BCre7ypnoy1IDM_|xHKkNy25-bsNjhURKb+p6ga^RJ>eNU6hI2E^Ow5A+W|JeZDC8Wqj7Y8Fr9@Uvc=cY`i& zDS4WCQI*lV*os&Gro@tzcjHTqy#y%$-R|Mc0;Z=P9R-{!<>DZ*bg~pkU*-o&W#<07*qo IM6N<$f;hnhc>n+a diff --git a/public/favicon.ico b/public/favicon.ico index 236fadb94188e9189807c50aa8480b3070a0cc8d..876e54b746b5e5586cca6a293dfe71cc961ff3e9 100644 GIT binary patch literal 2530 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 MXD=tVWAM5E1AP^(C;$Ke literal 4286 zcmcJSYiv|S6vqb^Sj9>!kD_?#USEjFuB*k05m`ulKhQ)0{ZR3Nj~ES!#h9qrHiD>$ zFCbA83#r6d--z!Q-dZre@B@j4kBxrw(=r7ZdN&YYP!=ggUN z&s>a|z(3zN`fK`{jA=H;%m+kf8A$GajE&{z=dAJbc7mU^S=a@A(CVLujIm?&L%;DC zLe@6JdI;b*I0WCqqi~9wGmd_s)3`n7>{QyT;Y-k-FM>{Z23kR}*bD8AB1Y}dT<%4|D*yFG{ipz1dZ-md_9asjFVAL*Xjeow=eI@kwWU_SmSU;ft*cgCUqSjPbCwmKa-bR^DI`k=CZ?Ca;^-d5Ll<&7I3$q~lW zumU~>#c()c$ zznq=I*j4Zy`~=s7e6N}x_jxK`Zh-@E10+?J+sM~|@>qVCnUcYu{D&HP^q=+0M}32-cM_!3Ciyi+W;gJQ1O$7`Uq{%qEf zQ&BAEGXHHD1l564qg*VjMHc7h8MJr9F#HPgp`07>0%S*IU-OwO9s`}T@>+3T1B!b* zKArZ1un%4W-3_mTYTL(f9k{v?=^U+ny>m|ylSw>PH%q64pG5ofa4((kv;&-?dWdAkLy`uhG@CDoiOQ8?+ z9kmLbnp3s^M6T<5NBhy**WqFab58X;{Fc}ICS9`EhD_t_k?aMYs+oF+X2x}$w)C!s zub>y+giUTd`u=o2$VPbg%w}A5K=FGP&VnVNeaN?!pjv4BMJ}c{)BXz-le3+UDBqmk zP^aQH6SlzLumSXcGEJ%aDelxC<@f^F20w!KrQDna?i}Mq^k;zX6#3f)bNvO(k)BF^ zHzB_V0(cothr0F?dSz!aw81>*Q(|8=^W<+HdO$YTVt*Rwysv=|;RCn=q%&SVZcpiq z>|ITJ5A23ruovR@SbG{bo3)A(@mAf`d#C(4&3${+iCyjbvGi?Vt?lZx!zN>grl)PO zquU0hL7OTS3ys0yLdGmDv^2C7yp|~iFVkJ{(t`!Bd#KZdp4)$-VZGymowurI!T9lzW;iYrX% zF)vw%j!;FH5jU=m(Q5~)(!A7qrZetEvA}wJsfPakhPn4VN>Bl~!*!rl^WB?gT5(SX zqrcGGi_7Tuwoh;qSA){mue|o_dfbyowqX6^;stZFfs*6^_ecR$v03Qqs zcFUFd1~!^&TuN!IUB3D3ReJnzhzNVPaDtmTt+0M&r`B%&cD4gS z$pKcu>ijETURA1cPAPMUP)IU>4X#?;YT3K?;I<@W{=h@Hlq6){z%jU#6lA`@6L2XB z$iaVq`~`L;z@@k&8{m6jg**c;#ThvoBG@3Oz@@k%55{2c$Qf`cj>t#eh zJ~oSRj+`Sd#R)lfn&u3-09=X-^4NKB;m8HzQXG&c?r0Q->;RYYjXZTvE_Y;yxRg)i zM7zqlA$!23d?6=37Lgg*BQE6wd8UIssgW7rQm&C_KAJW&G9z5dC3336m5GrV;!>`V zQy&kWip&_7a)F%bfcscv0k~Ar$eGV*977g_OBIQn%PFG)vOrvlkb4SKuYH@P$B)@gR5V>evY8tZt748KfyTYX=A@jI|AV*}^xYRggKG$gEfb0vGibLjg z5ljYT-?-E$GQX>gQXq4|r3m?fIbp=}GOihrx#3b4`CFV~e4ShZWUjcw@xIb?0fNN9G zH|%XNzh;IAb)fCLvfv)pwm)bV(S+y9glo3mYd^FWI`)19{N$TxYlR!|RJm}`c>-wA z<8zU47Y1=FPd?;J^IAhB`Let%s8ToSrk&TF}H2r)K=+DE#~?0*{!(}pkem* z&LgLfA}`N8H-c@(ZM7q>OO`p{U5EW{hyc$aCHBtl65js(Q^?GISzq`dG=IivV6&az c%ld5p12HEXf2#(sW&i*H07*qoM6N<$g2{fB#Q*>R diff --git a/public/favicon.svg b/public/favicon.svg index e4e710e0..189a0e92 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,3 +1,13 @@ - - + + + + + + + + + + + + diff --git a/resources/js/app.ts b/resources/js/app.ts index 20097f5b..fa4ef5cf 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -1,5 +1,4 @@ import '../css/app.css'; -import './echo'; import { createInertiaApp } from '@inertiajs/vue3'; import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; diff --git a/resources/js/echo.ts b/resources/js/echo.ts deleted file mode 100644 index 425da419..00000000 --- a/resources/js/echo.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Echo from 'laravel-echo'; -import Pusher from 'pusher-js'; - -window.Pusher = Pusher; - -window.Echo = new Echo({ - broadcaster: 'reverb', - key: import.meta.env.VITE_REVERB_APP_KEY, - wsHost: import.meta.env.VITE_REVERB_HOST, - wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, - wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, - forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', - enabledTransports: ['ws', 'wss'], -}); diff --git a/resources/js/pages/posts/Show.vue b/resources/js/pages/posts/Show.vue index 12deef41..ef0b296e 100644 --- a/resources/js/pages/posts/Show.vue +++ b/resources/js/pages/posts/Show.vue @@ -1,6 +1,8 @@