From 70cd3f4e1b0e2fabdf5ff3a7bdce8f5ed8ddc368 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Mon, 13 Apr 2026 20:26:40 +0800 Subject: [PATCH] fix(download-history): use title for downloaded tooltip --- py/routes/handlers/model_handlers.py | 19 ++++-- static/css/components/shared.css | 29 ---------- static/js/managers/DownloadManager.js | 12 ++-- .../managers/downloadManager.history.test.js | 2 +- tests/routes/test_model_update_handler.py | 58 ++++++++++++++++++- 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/py/routes/handlers/model_handlers.py b/py/routes/handlers/model_handlers.py index 268657e8..8f91b5de 100644 --- a/py/routes/handlers/model_handlers.py +++ b/py/routes/handlers/model_handlers.py @@ -1532,13 +1532,20 @@ class ModelCivitaiHandler: cache = await self._service.scanner.get_cached_data() version_index = cache.version_index - history_service = await ServiceRegistry.get_downloaded_version_history_service() - downloaded_version_ids = set( - await history_service.get_downloaded_version_ids( - self._service.model_type, - model_id, + downloaded_version_ids: set[int] = set() + try: + history_service = await ServiceRegistry.get_downloaded_version_history_service() + downloaded_version_ids = set( + await history_service.get_downloaded_version_ids( + self._service.model_type, + model_id, + ) + ) + except Exception as exc: # pragma: no cover - defensive logging + self._logger.debug( + "Failed to load download history for CivitAI versions: %s", + exc, ) - ) for version in versions: version_id = None diff --git a/static/css/components/shared.css b/static/css/components/shared.css index 0f6f69fa..5baf996f 100644 --- a/static/css/components/shared.css +++ b/static/css/components/shared.css @@ -33,7 +33,6 @@ font-weight: 500; white-space: nowrap; flex-shrink: 0; - position: relative; transform: translateZ(0); will-change: transform; } @@ -43,34 +42,6 @@ font-size: 0.9em; } -.downloaded-info { - display: none; - position: absolute; - top: 100%; - right: 0; - background: var(--card-bg); - border: 1px solid color-mix(in oklch, var(--badge-update-bg, #4a90e2) 50%, transparent); - border-radius: var(--border-radius-xs); - padding: var(--space-1); - margin-top: 4px; - font-size: 0.9em; - color: var(--text-color); - white-space: normal; - word-break: break-word; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - z-index: 100; - min-width: 240px; - max-width: 320px; - transform: translateZ(0); - position: fixed; - pointer-events: none; -} - -.downloaded-badge:hover .downloaded-info { - display: block; - pointer-events: auto; -} - /* Early Access Badge */ .early-access-badge { display: inline-flex; diff --git a/static/js/managers/DownloadManager.js b/static/js/managers/DownloadManager.js index 9d442d71..2d52c95d 100644 --- a/static/js/managers/DownloadManager.js +++ b/static/js/managers/DownloadManager.js @@ -270,13 +270,13 @@ export class DownloadManager {
${localPath || ''}
`; } else if (hasBeenDownloaded) { - localStatus = `
+ const downloadedTooltip = translate( + 'modals.download.downloadedTooltip', + {}, + 'Previously downloaded, but it is not currently in your library.' + ); + localStatus = `
${translate('modals.download.downloaded', {}, 'Downloaded')} -
${translate( - 'modals.download.downloadedTooltip', - {}, - 'Previously downloaded, but it is not currently in your library.' - )}
`; } diff --git a/tests/frontend/managers/downloadManager.history.test.js b/tests/frontend/managers/downloadManager.history.test.js index a0329dab..f34e03ae 100644 --- a/tests/frontend/managers/downloadManager.history.test.js +++ b/tests/frontend/managers/downloadManager.history.test.js @@ -127,7 +127,7 @@ describe('DownloadManager version history badges', () => { expect(items).toHaveLength(2); expect(items[0].querySelector('.downloaded-badge')?.textContent).toContain('Downloaded'); - expect(items[0].querySelector('.downloaded-info')?.textContent).toContain( + expect(items[0].querySelector('.downloaded-badge')?.getAttribute('title')).toContain( 'Previously downloaded, but it is not currently in your library.' ); expect(items[0].querySelector('.local-badge')).toBeNull(); diff --git a/tests/routes/test_model_update_handler.py b/tests/routes/test_model_update_handler.py index 4dce7279..b347a14c 100644 --- a/tests/routes/test_model_update_handler.py +++ b/tests/routes/test_model_update_handler.py @@ -6,7 +6,7 @@ from types import SimpleNamespace import pytest from py.config import config -from py.routes.handlers.model_handlers import ModelUpdateHandler +from py.routes.handlers.model_handlers import ModelCivitaiHandler, ModelUpdateHandler from py.services.service_registry import ServiceRegistry from py.utils.metadata_manager import MetadataManager from py.services.model_update_service import ModelUpdateRecord, ModelVersionRecord @@ -163,6 +163,62 @@ async def test_build_version_context_includes_download_history(monkeypatch): assert overrides[124]["has_been_downloaded"] is False +@pytest.mark.asyncio +async def test_get_civitai_versions_degrades_when_download_history_unavailable(monkeypatch): + cache = SimpleNamespace(version_index={}) + service = DummyService(cache) + + class DummyProvider: + async def get_model_versions(self, model_id): + assert model_id == "42" + return { + "type": "lora", + "modelVersions": [ + { + "id": 7, + "name": "Version 7", + "files": [], + } + ], + } + + async def fake_history_service_factory(): + raise RuntimeError("download history unavailable") + + monkeypatch.setattr( + ServiceRegistry, + "get_downloaded_version_history_service", + staticmethod(fake_history_service_factory), + ) + + async def metadata_provider_factory(): + return DummyProvider() + + handler = ModelCivitaiHandler( + service=service, + settings_service=SimpleNamespace(get=lambda *_: False), + ws_manager=SimpleNamespace(), + logger=logging.getLogger(__name__), + metadata_provider_factory=metadata_provider_factory, + validate_model_type=lambda *_: True, + expected_model_types=lambda: "LoRA", + find_model_file=lambda *_: None, + metadata_sync=SimpleNamespace(), + metadata_refresh_use_case=SimpleNamespace(), + metadata_progress_callback=lambda *_args, **_kwargs: None, + ) + + response = await handler.get_civitai_versions( + SimpleNamespace(match_info={"model_id": "42"}) + ) + payload = json.loads(response.text) + + assert response.status == 200 + assert payload[0]["id"] == 7 + assert payload[0]["existsLocally"] is False + assert payload[0]["hasBeenDownloaded"] is False + + @pytest.mark.asyncio async def test_refresh_model_updates_filters_records_without_updates(): cache = SimpleNamespace(version_index={})