From 831f2118a45086d567ee9bbfb57d547ec989d9d8 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 23 Apr 2026 13:07:37 +0200 Subject: [PATCH] speed up progress bar and page loading --- assets/bootstrap.js | 6 - .../controllers/magazine_sync_controller.js | 49 ------- assets/controllers/progress_bar_controller.js | 129 +++++++++++++++--- assets/styles/layout.css | 21 +++ docker/cron/README.md | 2 +- src/Command/PrewarmCommand.php | 13 ++ src/Controller/DefaultController.php | 2 +- src/Controller/MagazineSyncController.php | 103 -------------- src/Service/CacheService.php | 5 +- src/Service/MagazineContentService.php | 126 ++++++++++------- src/Service/MagazineIndexStore.php | 2 +- src/Twig/Components/Header.php | 3 +- src/Twig/Components/Organisms/Comments.php | 28 ---- templates/base.html.twig | 7 +- templates/components/Header.html.twig | 2 +- templates/home.html.twig | 4 +- templates/pages/category.html.twig | 4 +- templates/ux/magazine/category_body.html.twig | 3 - templates/ux/magazine/header_ul.html.twig | 10 -- templates/ux/magazine/home_body.html.twig | 5 - 20 files changed, 235 insertions(+), 289 deletions(-) delete mode 100644 assets/controllers/magazine_sync_controller.js delete mode 100644 src/Controller/MagazineSyncController.php delete mode 100644 src/Twig/Components/Organisms/Comments.php delete mode 100644 templates/ux/magazine/category_body.html.twig delete mode 100644 templates/ux/magazine/header_ul.html.twig delete mode 100644 templates/ux/magazine/home_body.html.twig diff --git a/assets/bootstrap.js b/assets/bootstrap.js index b85d1d0..306468d 100644 --- a/assets/bootstrap.js +++ b/assets/bootstrap.js @@ -1,7 +1,6 @@ import { startStimulusApp } from '@symfony/stimulus-bundle'; import ArticleCommentsController from './controllers/article_comments_controller.js'; import CommentReplyController from './controllers/comment_reply_controller.js'; -import MagazineSyncController from './controllers/magazine_sync_controller.js'; const app = startStimulusApp(); @@ -11,11 +10,6 @@ try { } catch { /* already registered by the bundle */ } -try { - app.register('magazine-sync', MagazineSyncController); -} catch { - /* already registered by the bundle */ -} try { app.register('comment-reply', CommentReplyController); } catch { diff --git a/assets/controllers/magazine_sync_controller.js b/assets/controllers/magazine_sync_controller.js deleted file mode 100644 index 634a112..0000000 --- a/assets/controllers/magazine_sync_controller.js +++ /dev/null @@ -1,49 +0,0 @@ -import { Controller } from "@hotwired/stimulus"; - -/** - * After first paint, refreshes Nostr magazine indices (server-side, ≤5s) and swaps header/body HTML. - */ -export default class extends Controller { - static targets = ["headerNav", "pageBody"]; - static values = { - page: String, - slug: String, - url: String, - }; - - connect() { - this.sync(); - } - - async sync() { - const base = this.urlValue || "/ux/magazine-sync"; - const params = new URLSearchParams(); - params.set("page", this.pageValue || "article"); - const slug = this.slugValue || ""; - if (slug !== "") { - params.set("slug", slug); - } - const url = `${base}?${params.toString()}`; - try { - const res = await fetch(url, { - headers: { Accept: "application/json" }, - credentials: "same-origin", - }); - if (!res.ok) { - return; - } - const data = await res.json(); - if (!data.ok) { - return; - } - if (this.hasHeaderNavTarget && data.header) { - this.headerNavTarget.outerHTML = data.header; - } - if (this.hasPageBodyTarget && data.body) { - this.pageBodyTarget.outerHTML = data.body; - } - } catch { - /* ignore network errors */ - } - } -} diff --git a/assets/controllers/progress_bar_controller.js b/assets/controllers/progress_bar_controller.js index eb37deb..43dbcd5 100644 --- a/assets/controllers/progress_bar_controller.js +++ b/assets/controllers/progress_bar_controller.js @@ -1,20 +1,84 @@ -// assets/controllers/progress_bar_controller.js -import { Controller } from "@hotwired/stimulus"; +// Top-of-page progress: indeterminate while navigating; completes on the next page’s load. +import { Controller } from '@hotwired/stimulus'; + +const STORAGE_KEY = 'unfold_pb'; export default class extends Controller { - static targets = ["bar"]; + static targets = ['bar']; connect() { this.boundHandleInteraction = this.handleInteraction.bind(this); - document.addEventListener("click", this.boundHandleInteraction); - document.addEventListener("touchstart", this.handleTouchStart); - document.addEventListener("touchend", this.handleTouchEnd); + this.boundPageShow = this.onPageShow.bind(this); + document.addEventListener('click', this.boundHandleInteraction); + document.addEventListener('touchstart', this.handleTouchStart); + document.addEventListener('touchend', this.handleTouchEnd); + window.addEventListener('pageshow', this.boundPageShow); + this.resumeIfPending(); } disconnect() { - document.removeEventListener("click", this.boundHandleInteraction); - document.removeEventListener("touchstart", this.handleTouchStart); - document.removeEventListener("touchend", this.handleTouchEnd); + document.removeEventListener('click', this.boundHandleInteraction); + document.removeEventListener('touchstart', this.handleTouchStart); + document.removeEventListener('touchend', this.handleTouchEnd); + window.removeEventListener('pageshow', this.boundPageShow); + if (this.loadListener) { + window.removeEventListener('load', this.loadListener); + this.loadListener = null; + } + } + + onPageShow(event) { + if (event.persisted) { + sessionStorage.removeItem(STORAGE_KEY); + this.resetBar(); + } + } + + /** + * After a same-tab navigation, finish the bar as soon as the new document is fully loaded + * (or immediately if the load event already happened). + */ + resumeIfPending() { + if (sessionStorage.getItem(STORAGE_KEY) !== '1' || !this.hasBarTarget) { + return; + } + this.barTarget.classList.add('pb-indeterminate'); + const finish = () => { + this.completeToDone(); + }; + if (document.readyState === 'complete') { + requestAnimationFrame(finish); + } else { + this.loadListener = finish; + window.addEventListener('load', finish, { once: true }); + } + } + + completeToDone() { + if (sessionStorage.getItem(STORAGE_KEY) !== '1' || !this.hasBarTarget) { + return; + } + if (this.loadListener) { + window.removeEventListener('load', this.loadListener); + this.loadListener = null; + } + this.barTarget.classList.remove('pb-indeterminate'); + this.barTarget.style.transition = 'width 0.18s ease-out'; + this.barTarget.style.width = '100%'; + window.setTimeout(() => { + this.barTarget.style.transition = 'none'; + this.barTarget.style.width = '0'; + this.barTarget.style.removeProperty('transition'); + sessionStorage.removeItem(STORAGE_KEY); + }, 220); + } + + resetBar() { + if (!this.hasBarTarget) { + return; + } + this.barTarget.classList.remove('pb-indeterminate'); + this.barTarget.style.width = '0'; } handleTouchStart = (event) => { @@ -33,19 +97,46 @@ export default class extends Controller { }; handleInteraction(event) { - const link = event.target.closest("a"); - if (link && !link.hasAttribute("data-no-progress") && - !event.ctrlKey && !event.metaKey && !event.shiftKey) { - this.start(); + const link = event.target.closest('a'); + if (!link || link.hasAttribute('data-no-progress')) { + return; + } + if (event.ctrlKey || event.metaKey || event.shiftKey) { + return; + } + const t = link.getAttribute('target'); + if (t && t !== '' && t !== '_self') { + return; } + if (link.hasAttribute('download')) { + return; + } + const href = link.getAttribute('href'); + if (!href || href.startsWith('mailto:') || href.startsWith('tel:') || href.startsWith('javascript:')) { + return; + } + let url; + try { + url = new URL(href, window.location.href); + } catch { + return; + } + if (url.origin !== window.location.origin) { + return; + } + if (url.href.split('#')[0] === window.location.href.split('#')[0] && url.hash) { + return; + } + this.start(); } start() { - this.barTarget.style.width = "0"; - this.barTarget.style.transition = "none"; - setTimeout(() => { - this.barTarget.style.transition = "width 5s ease-in-out"; - this.barTarget.style.width = "100%"; - }, 10); + if (!this.hasBarTarget) { + return; + } + sessionStorage.setItem(STORAGE_KEY, '1'); + this.barTarget.style.transition = 'none'; + this.barTarget.style.width = '0'; + this.barTarget.classList.add('pb-indeterminate'); } } diff --git a/assets/styles/layout.css b/assets/styles/layout.css index d58eb24..9e12989 100644 --- a/assets/styles/layout.css +++ b/assets/styles/layout.css @@ -84,11 +84,32 @@ header { bottom: 0; height: 4px; width: 0; + transform-origin: left center; background: var(--color-primary); transition: width 0.4s ease; z-index: 1000; } +/* In-flight navigation: loops until the next page fires `load`, then the bar completes. */ +#progress-bar.pb-indeterminate { + transition: none; + animation: pb-indeterminate 0.9s ease-in-out infinite; +} + +@keyframes pb-indeterminate { + 0% { + width: 20%; + } + + 50% { + width: 55%; + } + + 100% { + width: 28%; + } +} + /* Mobile Styles */ @media (max-width: 1024px) { .header__logo { diff --git a/docker/cron/README.md b/docker/cron/README.md index ba20824..65ca204 100644 --- a/docker/cron/README.md +++ b/docker/cron/README.md @@ -1,6 +1,6 @@ # `cron` service (Docker) -The `cron` image runs a single job: **`php bin/console app:prewarm` every 10 minutes**, against the app tree bind-mounted at `/var/www/html`. +The `cron` image runs a single job: **`php bin/console app:prewarm` every 10 minutes**, against the app tree bind-mounted at `/var/www/html`. Magazine **30040** indices, **MySQL backfill** for category `a` long-form rows, profile metadata, and comment cache are updated here (or by running `app:prewarm` manually)—not from a browser request. - **Flags:** set **`PREWARM_FLAGS`** in the project `.env` (Compose injects it). Example: `PREWARM_FLAGS="--metadata-limit=30 --no-magazine"`. After editing, run `docker compose up -d --force-recreate cron` (or `docker compose up -d cron`) so the container gets the new value. If unset, `app:prewarm` uses its **built-in defaults** (same idea as running the console with no args). diff --git a/src/Command/PrewarmCommand.php b/src/Command/PrewarmCommand.php index 62f8e2b..71aa5ff 100644 --- a/src/Command/PrewarmCommand.php +++ b/src/Command/PrewarmCommand.php @@ -117,6 +117,19 @@ final class PrewarmCommand extends Command $io->note('Skipping magazine (--no-magazine).'); } + $io->section('Long-form in DB (category `a` tags missing from MySQL)'); + try { + $n = $this->magazineContent->ingestMissingLongformForAllMagazineCategories(); + if ($n === 0) { + $io->note('No missing long-form rows for category `a` coordinates (or empty magazine store).'); + } else { + $io->writeln(sprintf('Fetched or attempted ingest for %d missing coordinate(s).', $n)); + } + } catch (\Throwable $e) { + $this->logger->error('app:prewarm longform ingest failed', ['e' => $e]); + $io->warning('Long-form backfill failed: '.$e->getMessage()); + } + // MagazineRefresher sets max_execution_time (e.g. 60 for budget 30); restore before metadata. $this->disableCliExecutionTimeLimit(); diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 611ea6c..e4a2cdd 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -22,7 +22,7 @@ class DefaultController extends AbstractController public function index(): Response { return $this->render('home.html.twig', [ - 'indices' => $this->magazineContent->getHomeCategoryIndexTags(), + 'indices' => $this->magazineContent->getHomeCategoryAIndexTagsFromStoreOnly(), ]); } diff --git a/src/Controller/MagazineSyncController.php b/src/Controller/MagazineSyncController.php deleted file mode 100644 index 835492a..0000000 --- a/src/Controller/MagazineSyncController.php +++ /dev/null @@ -1,103 +0,0 @@ -query->get('page', 'article'); - if (!\in_array($page, ['home', 'category', 'article', 'articles'], true)) { - $page = 'article'; - } - $slug = (string) $request->query->get('slug', ''); - - $prefer = $slug !== '' ? [$slug] : []; - - try { - $this->refresher->refreshFromRelays(20, $prefer); - } catch (\Throwable $e) { - $this->logger->warning('MagazineSyncController: refresh failed', [ - 'message' => $e->getMessage(), - 'exception' => $e, - ]); - - return new JsonResponse( - ['ok' => false, 'error' => 'refresh_failed', 'message' => $e->getMessage()], - Response::HTTP_OK - ); - } - - $community = (bool) $this->params->get('community_articles'); - $tags = $this->magazineContent->getHomeCategoryAIndexTagsFromStoreOnly(); - $globals = [ - 'magazine_community_articles' => $community, - ]; - - $header = $this->twig->render('ux/magazine/header_ul.html.twig', array_merge($globals, [ - 'cats' => $tags, - ])); - - $body = null; - if ($page === 'home') { - $body = $this->twig->render('ux/magazine/home_body.html.twig', array_merge($globals, [ - 'indices' => $tags, - ])); - } elseif ($page === 'category' && $slug !== '') { - $data = $this->magazineContent->getCategoryPageData($slug); - $body = $this->twig->render('ux/magazine/category_body.html.twig', array_merge($globals, [ - 'list' => $data['list'], - 'category' => $data['category'], - ])); - } elseif ($page === 'articles') { - $body = null; - } - - return new JsonResponse([ - 'ok' => true, - 'header' => $header, - 'body' => $body, - ]); - } catch (\Throwable $e) { - $this->logger->error('MagazineSyncController: unexpected failure', [ - 'message' => $e->getMessage(), - 'exception' => $e, - ]); - - return new JsonResponse( - [ - 'ok' => false, - 'error' => 'server_error', - 'message' => 'Magazine UI sync could not be rendered.', - ], - Response::HTTP_OK - ); - } - } -} diff --git a/src/Service/CacheService.php b/src/Service/CacheService.php index eb3c56c..4b1d1e0 100644 --- a/src/Service/CacheService.php +++ b/src/Service/CacheService.php @@ -35,8 +35,9 @@ readonly class CacheService */ public function getMetadataBundle(string $npub): array { - $aggr = $this->nostrClient->getNostrLandAggrReaderCacheSuffix(); - $cacheKey = $aggr === '' ? '0_'.$npub : '0_'.$aggr.'_'.$npub; + // One key per author: do not split on Nostr.Land / aggr (see comment thread cache). Otherwise + // prewarm and anonymous hits do not match logged-in readers → cold Nostr on every article view. + $cacheKey = '0_'.$npub; try { $cached = $this->cache->get($cacheKey, function (ItemInterface $item) use ($npub) { $item->expiresAfter(3600); // 1 hour, adjust as needed diff --git a/src/Service/MagazineContentService.php b/src/Service/MagazineContentService.php index d7f160c..623f187 100644 --- a/src/Service/MagazineContentService.php +++ b/src/Service/MagazineContentService.php @@ -11,18 +11,13 @@ use App\Repository\ArticleRepository; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /** - * Magazine index events for templates. Reads {@see MagazineIndexStore} first; on a cold cache or when - * the last successful relay sync is older than {@see self::ROOT_REVALIDATE_SECONDS}, the service - * calls {@see MagazineRefresher} so the root index (and nav) can pick up new categories. + * Magazine index for templates. Reads {@see MagazineIndexStore} only on HTTP; relay refresh and DB + * backfill for category long-form are done by `app:prewarm` (cron) / CLI. */ final class MagazineContentService { - /** Re-fetch root from relays at most this often so new `a` tags appear in the header. */ - private const ROOT_REVALIDATE_SECONDS = 300; - public function __construct( private readonly MagazineIndexStore $store, - private readonly MagazineRefresher $refresher, private readonly ParameterBagInterface $params, private readonly ArticleRepository $articleRepository, private readonly NostrClient $nostrClient, @@ -30,26 +25,18 @@ final class MagazineContentService } /** - * "indices" for the home template: Nostr `a` tag rows for each category. + * @deprecated use {@see getHomeCategoryAIndexTagsFromStoreOnly} (identical; no blocking relay I/O) * * @return list> */ public function getHomeCategoryIndexTags(): array { - $npub = (string) $this->params->get('npub'); - $dTag = (string) $this->params->get('d_tag'); - if ($this->store->getRoot($npub, $dTag) === null) { - $this->refresher->refreshFromRelays(20, []); - } elseif ($this->shouldRevalidateRootFromRelay()) { - $this->refresher->refreshFromRelays(20, []); - } - return $this->getHomeCategoryAIndexTagsFromStoreOnly(); } /** - * Category `a` tags from the persisted root only (no relay). Used after /ux/magazine-sync - * has already called {@see MagazineRefresher::refreshFromRelays}. + * Category `a` tags from the persisted root only (no relay). The store is filled by + * `app:prewarm` / cron ({@see MagazineRefresher::refreshFromRelays}), not from HTTP. * * @return list> */ @@ -86,16 +73,6 @@ final class MagazineContentService return array_values($cats); } - private function shouldRevalidateRootFromRelay(): bool - { - $age = $this->refresher->getSecondsSinceLastRelayRun(); - if ($age === null) { - return true; - } - - return $age > self::ROOT_REVALIDATE_SECONDS; - } - /** * Category path slugs from the persisted root index (third segment of each category `a` tag). * @@ -145,15 +122,14 @@ final class MagazineContentService } /** + * Category listing from the persisted 30040 index and DB only. Does not call relays. + * Missing `Article` rows (not yet in MySQL) appear until `app:prewarm` backfills. + * * @return array{list: list
, category: array{title: string, summary: string}} */ public function getCategoryPageData(string $slug): array { $catIndex = $this->store->getCategory($slug); - if ($catIndex === null) { - $this->refresher->refreshFromRelays(20, [$slug]); - $catIndex = $this->store->getCategory($slug); - } $list = []; $coordinates = []; $category = []; @@ -188,21 +164,6 @@ final class MagazineContentService ]; } $byAddress = $this->articleRepository->findByAuthorAndSlugIndexed($pairs); - $missing = []; - foreach ($coordinates as $coordinate) { - $parts = explode(':', (string) $coordinate, 3); - if (\count($parts) < 3) { - continue; - } - $k = (string) $parts[1]."\0".trim((string) $parts[2]); - if (!isset($byAddress[$k])) { - $missing[] = (string) $coordinate; - } - } - if ($missing !== []) { - $this->nostrClient->ingestMissingLongformForCategoryCoordinates($missing); - $byAddress = $this->articleRepository->findByAuthorAndSlugIndexed($pairs); - } foreach ($coordinates as $coordinate) { $parts = explode(':', (string) $coordinate, 3); if (\count($parts) < 3) { @@ -224,6 +185,77 @@ final class MagazineContentService ]; } + /** + * For every category in the root index, fetch Nostr long-form for `a` tags missing in MySQL. + * Nostr I/O; intended for {@see PrewarmCommand} / cron only. + */ + public function ingestMissingLongformForAllMagazineCategories(): int + { + $n = 0; + foreach ($this->getCategorySlugsFromStore() as $catSlug) { + $missing = $this->findMissingLongformCoordinatesForCategory($catSlug); + if ($missing === []) { + continue; + } + $this->nostrClient->ingestMissingLongformForCategoryCoordinates($missing); + $n += \count($missing); + } + + return $n; + } + + /** + * @return list Nostr coordinates kind:pubkey:identifier + */ + private function findMissingLongformCoordinatesForCategory(string $slug): array + { + $catIndex = $this->store->getCategory($slug); + if ($catIndex === null) { + return []; + } + $coordinates = []; + foreach ($catIndex->getTags() as $tag) { + if (($tag[0] ?? null) === 'a' && isset($tag[1])) { + $coordinates[] = (string) $tag[1]; + } + } + if ($coordinates === []) { + return []; + } + $pairs = []; + foreach ($coordinates as $coordinate) { + $parts = explode(':', (string) $coordinate, 3); + if (\count($parts) < 3) { + continue; + } + $slugPart = trim((string) $parts[2]); + if ($slugPart === '') { + continue; + } + $pairs[] = [ + 'pubkey' => (string) $parts[1], + 'slug' => $slugPart, + ]; + } + if ($pairs === []) { + return []; + } + $byAddress = $this->articleRepository->findByAuthorAndSlugIndexed($pairs); + $missing = []; + foreach ($coordinates as $coordinate) { + $parts = explode(':', (string) $coordinate, 3); + if (\count($parts) < 3) { + continue; + } + $k = (string) $parts[1]."\0".trim((string) $parts[2]); + if (!isset($byAddress[$k])) { + $missing[] = (string) $coordinate; + } + } + + return $missing; + } + /** * Union of every article referenced by a category index (root 30040). Use this for magazine-wide * Atom and comment prewarm so "newest" tracks the magazine, not the generic community list. diff --git a/src/Service/MagazineIndexStore.php b/src/Service/MagazineIndexStore.php index 87f8a02..341bf47 100644 --- a/src/Service/MagazineIndexStore.php +++ b/src/Service/MagazineIndexStore.php @@ -10,7 +10,7 @@ use Psr\Cache\InvalidArgumentException; /** * Read/write persisted magazine Nostr index events (kinds 30040) without callback-based relay I/O - * on the request path. Updated by {@see MagazineRefresher} or the /ux/magazine-sync action. + * on the request path. Updated by {@see MagazineRefresher} (via `app:prewarm` / cron, or explicit CLI use). */ final class MagazineIndexStore { diff --git a/src/Twig/Components/Header.php b/src/Twig/Components/Header.php index c19cc09..82c6377 100644 --- a/src/Twig/Components/Header.php +++ b/src/Twig/Components/Header.php @@ -15,6 +15,7 @@ class Header public function __construct( private readonly MagazineContentService $magazineContent, ) { - $this->cats = $this->magazineContent->getHomeCategoryIndexTags(); + // Store only: never block the response on relay I/O (cron/pre-warm updates the store). + $this->cats = $this->magazineContent->getHomeCategoryAIndexTagsFromStoreOnly(); } } diff --git a/src/Twig/Components/Organisms/Comments.php b/src/Twig/Components/Organisms/Comments.php deleted file mode 100644 index 3fd83ba..0000000 --- a/src/Twig/Components/Organisms/Comments.php +++ /dev/null @@ -1,28 +0,0 @@ -commentThreadLoader->load((string) $current); - $this->list = $data['list']; - $this->commentLinks = $data['commentLinks']; - $this->processedContent = $data['processedContent']; - } -} diff --git a/templates/base.html.twig b/templates/base.html.twig index c687b11..7699e3b 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -29,12 +29,7 @@ {% endblock %} - + diff --git a/templates/components/Header.html.twig b/templates/components/Header.html.twig index 7306afc..402533f 100644 --- a/templates/components/Header.html.twig +++ b/templates/components/Header.html.twig @@ -11,7 +11,7 @@
-
    +
      {% for category in cats %}
    • {% endfor %} diff --git a/templates/home.html.twig b/templates/home.html.twig index 7b7f82a..7952e63 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -6,8 +6,6 @@ {% endblock %} -{% block magazine_sync_page %}home{% endblock %} - {% block ogtags %} {% set _og_image = absolute_url(asset('og-image.jpg')) %} @@ -28,7 +26,7 @@ {% endblock %} {% block body %} -
      +
      {% for item in indices %} {% endfor %} diff --git a/templates/pages/category.html.twig b/templates/pages/category.html.twig index ac80654..3c11074 100644 --- a/templates/pages/category.html.twig +++ b/templates/pages/category.html.twig @@ -1,7 +1,5 @@ {% extends 'base.html.twig' %} -{% block magazine_sync_page %}{% if app.request.attributes.get('_route') == 'articles' %}articles{% else %}category{% endif %}{% endblock %} -{% block magazine_sync_slug %}{{ (sync_slug|default(''))|e('html_attr') }}{% endblock %} {% block title %}{{ (category.title|default(''))|trim != '' ? category.title|trim ~ ' — ' ~ website_name : website_name }}{% endblock %} @@ -31,7 +29,7 @@ {% endblock %} {% block body %} -
      +
      {% endblock %} diff --git a/templates/ux/magazine/category_body.html.twig b/templates/ux/magazine/category_body.html.twig deleted file mode 100644 index 0c1cdd1..0000000 --- a/templates/ux/magazine/category_body.html.twig +++ /dev/null @@ -1,3 +0,0 @@ -
      - -
      diff --git a/templates/ux/magazine/header_ul.html.twig b/templates/ux/magazine/header_ul.html.twig deleted file mode 100644 index 86784a6..0000000 --- a/templates/ux/magazine/header_ul.html.twig +++ /dev/null @@ -1,10 +0,0 @@ -
        - {% for category in cats %} -
      • - {% endfor %} - {% if magazine_community_articles %} -
      • - Latest Articles -
      • - {% endif %} -
      diff --git a/templates/ux/magazine/home_body.html.twig b/templates/ux/magazine/home_body.html.twig deleted file mode 100644 index 549f227..0000000 --- a/templates/ux/magazine/home_body.html.twig +++ /dev/null @@ -1,5 +0,0 @@ -
      - {% for item in indices %} - - {% endfor %} -