From facc02a30a2b2664fdbb4ed29b33c885c67c5326 Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Sat, 9 May 2026 11:27:24 -0300 Subject: [PATCH] feat(tiktok): add TikTokPhoto content type for photo carousels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the enum case TikTokPhoto = 'tiktok_photo' with maxMediaCount 35, supportsImage true, supportsVideo false, 1:1 aspect ratio. The publisher service already calls /post/publish/content/init/ correctly — this enum case is the missing data layer that lets validation, UI, and defaults treat photo posts as a first-class content type. --- app/Enums/PostPlatform/ContentType.php | 9 +++++++- .../TikTokPhotoContentTypeTest.php | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php diff --git a/app/Enums/PostPlatform/ContentType.php b/app/Enums/PostPlatform/ContentType.php index 15325a99..5f15b5ec 100644 --- a/app/Enums/PostPlatform/ContentType.php +++ b/app/Enums/PostPlatform/ContentType.php @@ -29,6 +29,7 @@ enum ContentType: string // TikTok case TikTokVideo = 'tiktok_video'; + case TikTokPhoto = 'tiktok_photo'; // YouTube case YouTubeShort = 'youtube_short'; @@ -63,6 +64,7 @@ public function label(): string self::FacebookReel => 'Reel', self::FacebookStory => 'Story', self::TikTokVideo => 'Video', + self::TikTokPhoto => 'Photo carousel', self::YouTubeShort => 'Short', self::XPost => 'Post', self::ThreadsPost => 'Post', @@ -87,6 +89,7 @@ public function description(): string self::FacebookReel => 'Short video up to 90 seconds', self::FacebookStory => 'Disappears after 24 hours', self::TikTokVideo => 'Short-form video content', + self::TikTokPhoto => 'Up to 35 photos as a swipeable carousel', self::YouTubeShort => 'Vertical video up to 60 seconds', self::XPost => 'Tweet with text and media', self::ThreadsPost => 'Text post with optional media', @@ -105,7 +108,7 @@ public function platform(): SocialPlatform self::LinkedInPost, self::LinkedInCarousel => SocialPlatform::LinkedIn, self::LinkedInPagePost, self::LinkedInPageCarousel => SocialPlatform::LinkedInPage, self::FacebookPost, self::FacebookReel, self::FacebookStory => SocialPlatform::Facebook, - self::TikTokVideo => SocialPlatform::TikTok, + self::TikTokVideo, self::TikTokPhoto => SocialPlatform::TikTok, self::YouTubeShort => SocialPlatform::YouTube, self::XPost => SocialPlatform::X, self::ThreadsPost => SocialPlatform::Threads, @@ -156,6 +159,7 @@ public function aspectRatio(): ?string self::InstagramReel, self::InstagramStory => '9:16', self::FacebookReel, self::FacebookStory => '9:16', self::TikTokVideo, self::YouTubeShort => '9:16', + self::TikTokPhoto => '1:1', self::PinterestPin, self::PinterestCarousel => '2:3', self::PinterestVideoPin => '9:16', default => null, @@ -173,6 +177,7 @@ public function maxMediaCount(): int self::FacebookPost => 10, self::FacebookReel, self::FacebookStory => 1, self::TikTokVideo => 1, + self::TikTokPhoto => 35, self::YouTubeShort => 1, self::XPost => 4, self::ThreadsPost => 10, @@ -192,6 +197,7 @@ public function supportsVideo(): bool self::LinkedInCarousel, self::LinkedInPageCarousel => false, self::FacebookPost, self::FacebookReel, self::FacebookStory => true, self::TikTokVideo => true, + self::TikTokPhoto => false, self::YouTubeShort => true, self::XPost => true, self::ThreadsPost => true, @@ -207,6 +213,7 @@ public function supportsImage(): bool return match ($this) { self::InstagramReel => false, self::TikTokVideo => false, + self::TikTokPhoto => true, self::YouTubeShort => false, self::PinterestVideoPin => false, default => true, diff --git a/tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php b/tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php new file mode 100644 index 00000000..344eaf97 --- /dev/null +++ b/tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php @@ -0,0 +1,23 @@ +platform())->toBe(SocialPlatform::TikTok); +}); + +test('tiktok photo content type supports up to 35 images', function () { + expect(ContentType::TikTokPhoto->maxMediaCount())->toBe(35); +}); + +test('tiktok photo content type supports images and not video', function () { + expect(ContentType::TikTokPhoto->supportsImage())->toBeTrue(); + expect(ContentType::TikTokPhoto->supportsVideo())->toBeFalse(); +}); + +test('tiktok photo content type label is human-readable', function () { + expect(ContentType::TikTokPhoto->label())->toBe('Photo carousel'); +});