feat(#29): mashup

* update templates to be more responsive
This commit is contained in:
Benjamin Nussbaum 2025-06-06 23:06:31 +02:00
parent ed9d03d0b8
commit 56638b26e8
28 changed files with 1067 additions and 346 deletions

View file

@ -15,6 +15,7 @@ class PlaylistItem extends Model
protected $casts = [
'is_active' => 'boolean',
'last_displayed_at' => 'datetime',
'mashup' => 'json',
];
public function playlist(): BelongsTo
@ -26,4 +27,179 @@ class PlaylistItem extends Model
{
return $this->belongsTo(Plugin::class);
}
/**
* Check if this playlist item is a mashup
*/
public function isMashup(): bool
{
return ! is_null($this->mashup);
}
/**
* Get the mashup name if this is a mashup
*/
public function getMashupName(): ?string
{
return $this->mashup['mashup_name'] ?? null;
}
/**
* Get the mashup layout type if this is a mashup
*/
public function getMashupLayoutType(): ?string
{
return $this->mashup['mashup_layout'] ?? null;
}
/**
* Get all plugin IDs for this mashup
*/
public function getMashupPluginIds(): array
{
return $this->mashup['plugin_ids'] ?? [];
}
/**
* Get the number of plugins required for the current layout
*/
public function getRequiredPluginCount(): int
{
if (! $this->isMashup()) {
return 1;
}
return match ($this->getMashupLayoutType()) {
'1Lx1R', '1Tx1B' => 2, // Left-Right or Top-Bottom split
'1Lx2R', '2Lx1R', '2Tx1B', '1Tx2B' => 3, // Two on one side, one on other
'2x2' => 4, // Quadrant
default => 1,
};
}
/**
* Get the layout type (horizontal, vertical, or grid)
*/
public function getLayoutType(): string
{
if (! $this->isMashup()) {
return 'single';
}
return match ($this->getMashupLayoutType()) {
'1Lx1R', '1Lx2R', '2Lx1R' => 'vertical',
'1Tx1B', '2Tx1B', '1Tx2B' => 'horizontal',
'2x2' => 'grid',
default => 'single',
};
}
/**
* Get the layout size for a plugin based on its position
*/
public function getLayoutSize(int $position = 0): string
{
if (! $this->isMashup()) {
return 'full';
}
return match ($this->getMashupLayoutType()) {
'1Lx1R' => 'half_vertical', // Both sides are single plugins
'1Tx1B' => 'half_horizontal', // Both sides are single plugins
'2Lx1R' => match ($position) {
0, 1 => 'quadrant', // Left side has 2 plugins
2 => 'half_vertical', // Right side has 1 plugin
default => 'full'
},
'1Lx2R' => match ($position) {
0 => 'half_vertical', // Left side has 1 plugin
1, 2 => 'quadrant', // Right side has 2 plugins
default => 'full'
},
'2Tx1B' => match ($position) {
0, 1 => 'quadrant', // Top side has 2 plugins
2 => 'half_horizontal', // Bottom side has 1 plugin
default => 'full'
},
'1Tx2B' => match ($position) {
0 => 'half_horizontal', // Top side has 1 plugin
1, 2 => 'quadrant', // Bottom side has 2 plugins
default => 'full'
},
'2x2' => 'quadrant', // All positions are quadrants
default => 'full'
};
}
/**
* Render all plugins with appropriate layout
*/
public function render(): string
{
if (! $this->isMashup()) {
return view('trmnl-layouts.single', [
'slot' => $this->plugin->render('full', false),
])->render();
}
$pluginMarkups = [];
$plugins = Plugin::whereIn('id', $this->getMashupPluginIds())->get();
foreach ($plugins as $index => $plugin) {
$size = $this->getLayoutSize($index);
$pluginMarkups[] = $plugin->render($size, false);
}
return view('trmnl-layouts.mashup', [
'mashupLayout' => $this->getMashupLayoutType(),
'slot' => implode('', $pluginMarkups),
])->render();
}
/**
* Available mashup layouts with their descriptions
*/
public static function getAvailableLayouts(): array
{
return [
'1Lx1R' => '1 Left - 1 Right (2 plugins)',
'1Lx2R' => '1 Left - 2 Right (3 plugins)',
'2Lx1R' => '2 Left - 1 Right (3 plugins)',
'1Tx1B' => '1 Top - 1 Bottom (2 plugins)',
'2Tx1B' => '2 Top - 1 Bottom (3 plugins)',
'1Tx2B' => '1 Top - 2 Bottom (3 plugins)',
'2x2' => 'Quadrant (4 plugins)',
];
}
/**
* Get the required number of plugins for a given layout
*/
public static function getRequiredPluginCountForLayout(string $layout): int
{
return match ($layout) {
'1Lx1R', '1Tx1B' => 2,
'1Lx2R', '2Lx1R', '2Tx1B', '1Tx2B' => 3,
'2x2' => 4,
default => 1,
};
}
/**
* Create a new mashup with the given layout and plugins
*/
public static function createMashup(Playlist $playlist, string $layout, array $pluginIds, string $name, $order): self
{
return static::create([
'playlist_id' => $playlist->id,
'plugin_id' => $pluginIds[0], // First plugin is the main plugin
'mashup' => [
'mashup_layout' => $layout,
'mashup_name' => $name,
'plugin_ids' => $pluginIds,
],
'is_active' => true,
'order' => $order,
]);
}
}

View file

@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
@ -65,4 +66,38 @@ class Plugin extends Model
]);
}
}
/**
* Render the plugin's markup
*/
public function render(string $size = 'full', bool $standalone = true): string
{
if ($this->render_markup) {
if ($standalone) {
return view('trmnl-layouts.single', [
'slot' => Blade::render($this->render_markup, ['size' => $size, 'data' => $this->data_payload]),
])->render();
}
return Blade::render($this->render_markup, ['size' => $size, 'data' => $this->data_payload]);
}
if ($this->render_markup_view) {
if ($standalone) {
return view('trmnl-layouts.single', [
'slot' => view($this->render_markup_view, [
'size' => $size,
'data' => $this->data_payload,
])->render(),
])->render();
} else {
return view($this->render_markup_view, [
'size' => $size,
'data' => $this->data_payload,
])->render();
}
}
return '<p>No render markup yet defined for this plugin.</p>';
}
}