feat: converts active download list to live component with polling

This commit is contained in:
2025-04-25 21:50:51 -05:00
parent 6b88483635
commit cd271b568b
12 changed files with 195 additions and 40 deletions

View File

@@ -1,4 +1,14 @@
{
"controllers": [],
"controllers": {
"@symfony/ux-live-component": {
"live": {
"enabled": true,
"fetch": "eager",
"autoimport": {
"@symfony/ux-live-component/dist/live.min.css": true
}
}
}
},
"entrypoints": []
}

View File

@@ -29,6 +29,7 @@
"symfony/stimulus-bundle": "^2.24",
"symfony/twig-bundle": "7.2.*",
"symfony/ux-icons": "^2.24",
"symfony/ux-live-component": "^2.24",
"symfony/ux-twig-component": "^2.24",
"symfony/yaml": "7.2.*",
"symfonycasts/tailwind-bundle": "^0.10.0",

96
composer.lock generated
View File

@@ -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": "0448ecb537f5d169d81a56ba2b3c2cc6",
"content-hash": "09d927397449bd08c7c2b96e35d2bd60",
"packages": [
{
"name": "1tomany/data-uri",
@@ -6976,6 +6976,100 @@
],
"time": "2025-04-04T17:32:18+00:00"
},
{
"name": "symfony/ux-live-component",
"version": "v2.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ux-live-component.git",
"reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ux-live-component/zipball/ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
"reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/property-access": "^5.4.5|^6.0|^7.0",
"symfony/property-info": "^5.4|^6.0|^7.0",
"symfony/stimulus-bundle": "^2.9",
"symfony/ux-twig-component": "^2.8",
"twig/twig": "^3.8.0"
},
"conflict": {
"symfony/config": "<5.4.0"
},
"require-dev": {
"doctrine/annotations": "^1.0",
"doctrine/collections": "^1.6.8|^2.0",
"doctrine/doctrine-bundle": "^2.4.3",
"doctrine/orm": "^2.9.4",
"doctrine/persistence": "^2.5.2|^3.0",
"phpdocumentor/reflection-docblock": "5.x-dev",
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
"symfony/expression-language": "^5.4|^6.0|^7.0",
"symfony/form": "^5.4|^6.0|^7.0",
"symfony/framework-bundle": "^5.4|^6.0|^7.0",
"symfony/options-resolver": "^5.4|^6.0|^7.0",
"symfony/phpunit-bridge": "^6.1|^7.0",
"symfony/security-bundle": "^5.4|^6.0|^7.0",
"symfony/serializer": "^5.4|^6.0|^7.0",
"symfony/twig-bundle": "^5.4|^6.0|^7.0",
"symfony/validator": "^5.4|^6.0|^7.0",
"zenstruck/browser": "^1.2.0",
"zenstruck/foundry": "^2.0"
},
"type": "symfony-bundle",
"extra": {
"thanks": {
"url": "https://github.com/symfony/ux",
"name": "symfony/ux"
}
},
"autoload": {
"psr-4": {
"Symfony\\UX\\LiveComponent\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Live components for Symfony",
"homepage": "https://symfony.com",
"keywords": [
"components",
"symfony-ux",
"twig"
],
"support": {
"source": "https://github.com/symfony/ux-live-component/tree/v2.24.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-03-12T08:41:47+00:00"
},
{
"name": "symfony/ux-twig-component",
"version": "v2.24.0",

View File

@@ -13,4 +13,5 @@ return [
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\UX\LiveComponent\LiveComponentBundle::class => ['all' => true],
];

View File

@@ -0,0 +1,5 @@
live_component:
resource: '@LiveComponentBundle/config/routes.php'
prefix: '/_components'
# adjust prefix to add localization to your components
#prefix: '/{_locale}/_components'

View File

@@ -22,4 +22,7 @@ return [
'@symfony/stimulus-bundle' => [
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
],
'@symfony/ux-live-component' => [
'path' => './vendor/symfony/ux-live-component/assets/dist/live_controller.js',
],
];

View File

@@ -19,7 +19,7 @@ final class IndexController extends AbstractController
public function index(): Response
{
return $this->render('index/index.html.twig', [
'active_downloads' => [],
'active_downloads' => $this->downloadRepository->getActivePaginated(),
'recent_downloads' => $this->downloadRepository->latest(5),
'popular_movies' => $this->tmdb->popularMovies(1, 6),
]);

View File

@@ -36,11 +36,12 @@ class DownloadRepository extends ServiceEntityRepository
return new \Doctrine\ORM\Tools\Pagination\Paginator($query);
}
public function getActivePaginated(int $pageNumber = 1, int $perPage = 10)
public function getActivePaginated(int $pageNumber = 1, int $perPage = 5)
{
$firstResult = ($pageNumber - 1) * $perPage;
$query = $this->createQueryBuilder('d')
->andWhere('d.status IN (:statuses)')
->orderBy('d.id', 'DESC')
->setParameter('statuses', ['New', 'In Progress'])
->setFirstResult($firstResult)
->setMaxResults($perPage)

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Twig\Components;
use App\Download\Framework\Repository\DownloadRepository;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
final class ActiveDownloadList
{
use DefaultActionTrait;
public function __construct(
private DownloadRepository $downloadRepository,
) {}
#[LiveAction]
public function getDownloads()
{
return $this->downloadRepository->getActivePaginated();
}
}

View File

@@ -199,6 +199,18 @@
"assets/icons/symfony.svg"
]
},
"symfony/ux-live-component": {
"version": "2.24",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.6",
"ref": "73e69baf18f47740d6f58688c5464b10cdacae06"
},
"files": [
"config/routes/ux_live_component.yaml"
]
},
"symfony/ux-twig-component": {
"version": "2.24",
"recipe": {

View File

@@ -0,0 +1,39 @@
<div{{ attributes }} class="min-w-48" data-poll="getDownloads">
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed">
<thead>
<tr class="dark:bg-gray-50">
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800 min-w-[55ch] max-w-[55ch] truncate">
Title
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
Progress
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
{% if this.getDownloads()|length > 0 %}
{% for download in this.getDownloads() %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
{{ download.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ download.progress }}</span>
</span>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
No active downloads
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>

View File

@@ -7,42 +7,7 @@
<h2 class="mb-2 text-3xl font-bold text-gray-50">Dashboard</h2>
<div class="flex flex-row gap-4">
<twig:Card title="Active Downloads" class="w-full">
<table class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed">
<thead>
<tr class="dark:bg-gray-50">
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800">
Title
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
Progress
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
{% if active_downloads|length > 0 %}
{% for download in active_downloads %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
{{ download.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ download.progress }}</span>
</span>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
No active downloads
</td>
</tr>
{% endif %}
</tbody>
</table>
<twig:ActiveDownloadList />
</twig:Card>
<twig:Card title="Recent Downloads" class="w-full">