@@ -93,6 +93,7 @@ MainWindow::MainWindow(QWidget* parent)
9393 , m_restartWorker(nullptr )
9494 , m_fetchingNames(false )
9595 , m_nameFetchSearchId(0 )
96+ , m_hasCachedData(false )
9697{
9798 setWindowTitle (" Steam Lua Patcher" );
9899 setMinimumSize (900 , 600 );
@@ -440,16 +441,23 @@ void MainWindow::displayRandomGames() {
440441 for (int i = 0 ; i < count; ++i) {
441442 const GameInfo& game = shuffled[i];
442443
444+ // Apply name cache for games with unknown names
445+ QString displayName = game.name ;
446+ if (displayName.isEmpty () || displayName.startsWith (" Unknown Game" ) || displayName == game.id ) {
447+ if (m_nameCache.contains (game.id )) {
448+ displayName = m_nameCache[game.id ];
449+ } else {
450+ displayName = " Loading..." ;
451+ m_pendingNameFetchIds.append (game.id );
452+ }
453+ }
454+
443455 QMap<QString, QString> cd;
444- cd[" name" ] = (game.name .isEmpty () || game.name == game.id || game.name == " Unknown Game" )
445- ? " Loading..." : game.name ;
456+ cd[" name" ] = displayName;
446457 cd[" appid" ] = game.id ;
447458 cd[" supported" ] = " true" ;
448459 cd[" hasFix" ] = game.hasFix ? " true" : " false" ;
449460
450- if (game.name .isEmpty () || game.name == game.id || game.name == " Unknown Game" )
451- m_pendingNameFetchIds.append (game.id );
452-
453461 GameCard* card = new GameCard (m_gridContainer);
454462 card->setGameData (cd);
455463 connect (card, &GameCard::clicked, this , &MainWindow::onCardClicked);
@@ -468,8 +476,6 @@ void MainWindow::displayRandomGames() {
468476 m_statusLabel->setText (QString (" Showing %1 random games" ).arg (m_gameCards.count ()));
469477 m_stack->setCurrentIndex (1 );
470478 m_spinner->stop ();
471- m_stack->setCurrentIndex (1 );
472- m_spinner->stop ();
473479}
474480
475481// ---- Display installed patches (Library) ----
@@ -544,37 +550,111 @@ void MainWindow::displayLibrary() {
544550
545551// ---- Sync ----
546552void MainWindow::startSync () {
547- clearGameCards ();
548-
549- for (int i = 0 ; i < 12 ; ++i) {
550- GameCard* card = new GameCard (m_gridContainer);
551- card->setSkeleton (true );
552- m_gridLayout->addWidget (card, i / 3 , i % 3 );
553- m_gameCards.append (card);
553+ // Load persistent name cache from disk
554+ loadNameCache ();
555+
556+ // Try to load cached index for instant display
557+ if (loadCachedIndex ()) {
558+ m_hasCachedData = true ;
559+ m_statusLabel->setText (" Syncing in background..." );
560+ } else {
561+ // No cache available - show skeleton placeholders
562+ m_hasCachedData = false ;
563+ clearGameCards ();
564+ for (int i = 0 ; i < 12 ; ++i) {
565+ GameCard* card = new GameCard (m_gridContainer);
566+ card->setSkeleton (true );
567+ m_gridLayout->addWidget (card, i / 3 , i % 3 );
568+ m_gameCards.append (card);
569+ }
570+ m_stack->setCurrentIndex (1 );
571+ m_spinner->stop ();
554572 }
555-
556- m_stack->setCurrentIndex (1 );
557- m_spinner->stop ();
573+
574+ // Always start background sync to get fresh data
558575 m_syncWorker = new IndexDownloadWorker (this );
559576 connect (m_syncWorker, &IndexDownloadWorker::finished, this , &MainWindow::onSyncDone);
560- connect (m_syncWorker, &IndexDownloadWorker::progress, m_statusLabel, &QLabel::setText);
577+ connect (m_syncWorker, &IndexDownloadWorker::progress, [this ](QString msg) {
578+ // Only show sync progress if we don't already have cached data displayed
579+ if (!m_hasCachedData) m_statusLabel->setText (msg);
580+ });
561581 connect (m_syncWorker, &IndexDownloadWorker::error, this , &MainWindow::onSyncError);
562582 m_syncWorker->start ();
563583}
564584
565- void MainWindow::onSyncDone (QList<GameInfo> games) {
585+ bool MainWindow::loadCachedIndex () {
586+ QString indexPath = Paths::getLocalIndexPath ();
587+ if (!QFile::exists (indexPath)) return false ;
588+
589+ QFile file (indexPath);
590+ if (!file.open (QIODevice::ReadOnly)) return false ;
591+
592+ QByteArray data = file.readAll ();
593+ file.close ();
594+
595+ QJsonDocument doc = QJsonDocument::fromJson (data);
596+ QJsonObject indexData = doc.object ();
597+ QJsonArray arr = indexData[" games" ].toArray ();
598+ if (arr.isEmpty ()) return false ;
599+
600+ // Parse games and apply name cache
601+ QList<GameInfo> games;
602+ games.reserve (arr.size ());
603+ for (const QJsonValue& val : arr) {
604+ QJsonObject obj = val.toObject ();
605+ GameInfo game;
606+ game.id = obj[" id" ].toString ();
607+ game.name = obj[" name" ].toString ();
608+ // Apply cached name if the index has an unknown name
609+ if (game.name .startsWith (" Unknown Game" ) && m_nameCache.contains (game.id )) {
610+ game.name = m_nameCache[game.id ];
611+ }
612+ game.hasFix = obj[" has_fix" ].toBool (false );
613+ games.append (game);
614+ }
615+
566616 m_supportedGames = games;
567- m_spinner->stop ();
568- m_stack->setCurrentIndex (1 );
569- m_statusLabel->setText (" Ready" );
570617 m_searchInput->setFocus ();
571- if (!m_searchInput-> text (). isEmpty ()) {
572- doSearch ();
573- } else if (m_currentMode == AppMode::LuaPatcher) {
618+
619+ // Display immediately
620+ if (m_currentMode == AppMode::LuaPatcher) {
574621 displayRandomGames ();
575622 } else if (m_currentMode == AppMode::Library) {
576623 displayLibrary ();
577624 }
625+ return true ;
626+ }
627+
628+ void MainWindow::onSyncDone (QList<GameInfo> games) {
629+ // Apply name cache to freshly downloaded data
630+ for (GameInfo& game : games) {
631+ if (game.name .startsWith (" Unknown Game" ) && m_nameCache.contains (game.id )) {
632+ game.name = m_nameCache[game.id ];
633+ }
634+ }
635+ m_supportedGames = games;
636+ m_spinner->stop ();
637+ m_stack->setCurrentIndex (1 );
638+
639+ if (m_hasCachedData) {
640+ // Background refresh done - only re-display if user isn't searching
641+ m_hasCachedData = false ;
642+ m_statusLabel->setText (QString (" Ready • %1 games" ).arg (m_supportedGames.size ()));
643+ if (m_searchInput->text ().trimmed ().isEmpty ()) {
644+ // Don't disrupt the user, just update data silently
645+ }
646+ } else {
647+ // First load (no cache was available)
648+ m_statusLabel->setText (" Ready" );
649+ m_searchInput->setFocus ();
650+ if (!m_searchInput->text ().isEmpty ()) {
651+ doSearch ();
652+ } else if (m_currentMode == AppMode::LuaPatcher) {
653+ displayRandomGames ();
654+ } else if (m_currentMode == AppMode::Library) {
655+ displayLibrary ();
656+ }
657+ }
578658}
579659
580660void MainWindow::onSyncError (QString error) {
@@ -1070,7 +1150,8 @@ void MainWindow::startBatchNameFetch() {
10701150 m_nameFetchSearchId = m_currentSearchId;
10711151 m_spinner->start ();
10721152 m_statusLabel->setText (QString (" Found %1 results %2 Fetching game names..." ).arg (m_gameCards.count ()).arg (QChar (0x2022 )));
1073- for (int i = 0 ; i < 5 && !m_pendingNameFetchIds.isEmpty (); ++i) processNextNameFetch ();
1153+ // Fetch all displayed cards concurrently for faster loading
1154+ for (int i = 0 ; i < 12 && !m_pendingNameFetchIds.isEmpty (); ++i) processNextNameFetch ();
10741155}
10751156
10761157void MainWindow::processNextNameFetch () {
@@ -1131,6 +1212,10 @@ void MainWindow::onGameNameFetched(QNetworkReply* reply) {
11311212 }
11321213
11331214 if (!gameName.isEmpty ()) {
1215+ // Persist to name cache so we never fetch this again
1216+ m_nameCache[appId] = gameName;
1217+ saveNameCache ();
1218+
11341219 for (GameCard* card : m_gameCards) {
11351220 if (card->appId () == appId) {
11361221 QMap<QString, QString> d = card->gameData ();
@@ -1180,3 +1265,29 @@ void MainWindow::onThumbnailDownloaded(QNetworkReply* reply) {
11801265 }
11811266 }
11821267}
1268+
1269+ // ---- Persistent name cache ----
1270+ void MainWindow::loadNameCache () {
1271+ QString path = QDir (Paths::getLocalCacheDir ()).filePath (" name_cache.json" );
1272+ QFile file (path);
1273+ if (!file.open (QIODevice::ReadOnly)) return ;
1274+ QJsonDocument doc = QJsonDocument::fromJson (file.readAll ());
1275+ file.close ();
1276+ QJsonObject obj = doc.object ();
1277+ for (auto it = obj.begin (); it != obj.end (); ++it) {
1278+ m_nameCache[it.key ()] = it.value ().toString ();
1279+ }
1280+ }
1281+
1282+ void MainWindow::saveNameCache () {
1283+ QString path = QDir (Paths::getLocalCacheDir ()).filePath (" name_cache.json" );
1284+ QJsonObject obj;
1285+ for (auto it = m_nameCache.begin (); it != m_nameCache.end (); ++it) {
1286+ obj[it.key ()] = it.value ();
1287+ }
1288+ QFile file (path);
1289+ if (file.open (QIODevice::WriteOnly)) {
1290+ file.write (QJsonDocument (obj).toJson (QJsonDocument::Compact));
1291+ file.close ();
1292+ }
1293+ }
0 commit comments