feat(tiktok): add TikTokPhoto content type for photo carousels

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.
This commit is contained in:
Paulo Castellano 2026-05-09 11:27:24 -03:00
parent 45b5e31db0
commit facc02a30a
2 changed files with 31 additions and 1 deletions

View file

@ -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,

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use App\Enums\PostPlatform\ContentType;
use App\Enums\SocialAccount\Platform as SocialPlatform;
test('tiktok photo content type has correct platform mapping', function () {
expect(ContentType::TikTokPhoto->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');
});