From 06c8f8b5933c057688705fdb0287e16120202f57 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 20 Aug 2026 21:28:33 +0000 Subject: [PATCH] Fix: tab switching hangs on iOS Safari MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - showView: requestAnimationFrame + мгновенный loader (UI не висит) - maybeOnboard на старте fire-and-forget (не блокирует init) - MapView: НЕ запрашивает геолокацию автоматически (только по кнопке) - MapView: правильно destroy map если container удалён (нет утечки Leaflet) - invalidateSize() перед setView чтобы тайлы 2GIS догрузились --- public/js/app.js | 30 +++++++++++++++++++----------- public/js/views/map.js | 21 ++++++++++++++++++--- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index a5b2cf8..1fff6a4 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -35,7 +35,7 @@ const App = { await API.Auth.loginWithTelegram(TG_APP.initData); await API.Auth.me(); await this.checkConsent(); - await this.maybeOnboard(); + this.maybeOnboard(); // fire-and-forget, не блокируем переход this.showView('map'); } catch (e) { this.showError('Не удалось войти через Telegram: ' + e.message); @@ -305,17 +305,25 @@ const App = { const root = document.getElementById('app'); if (window.ChatView?.cleanup) window.ChatView.cleanup(); - switch (name) { - case 'map': MapView.render(root); break; - case 'chats': ChatsView.render(root); break; - case 'chat': ChatView.render(root, params); break; - case 'profile': ProfileView.render(root); break; - case 'settings': SettingsView.render(root); break; - case 'review': ReviewView.render(root, params); break; - case 'user-reviews': UserReviewsView.render(root, params); break; - case 'user-profile': UserProfileView.render(root, params); break; - default: root.innerHTML = '
Неизвестный экран
'; + // мгновенный placeholder — чтоб UI не висел при тяжёлом render + if (root) { + root.innerHTML = '
'; } + + // defer на следующий тик — даём браузеру отрисовать таб/placeholder + requestAnimationFrame(() => { + switch (name) { + case 'map': MapView.render(root); break; + case 'chats': ChatsView.render(root); break; + case 'chat': ChatView.render(root, params); break; + case 'profile': ProfileView.render(root); break; + case 'settings': SettingsView.render(root); break; + case 'review': ReviewView.render(root, params); break; + case 'user-reviews': UserReviewsView.render(root, params); break; + case 'user-profile': UserProfileView.render(root, params); break; + default: root.innerHTML = '
Неизвестный экран
'; + } + }); }, openChat(chatId, otherId, otherName) { diff --git a/public/js/views/map.js b/public/js/views/map.js index b074c3e..238ccff 100644 --- a/public/js/views/map.js +++ b/public/js/views/map.js @@ -48,6 +48,21 @@ const MapView = { `; + // если старый map привязан к удалённому div — убьём его и создадим новый + if (this.map) { + try { + const container = this.map.getContainer(); + if (!container || !document.body.contains(container)) { + this.map.remove(); + this.map = null; + this.placeMarkersLayer = null; + this.markers = []; + } + } catch (e) { + this.map = null; + } + } + if (!this.map) { this.map = L.map('map').setView([55.7558, 37.6173], 12); // 2GIS tile layer @@ -95,12 +110,12 @@ const MapView = { if (list) list.style.display = this.showPlaces ? 'block' : 'none'; }; - // Если уже есть сохранённые координаты — покажем на карте + // если уже есть сохранённые координаты — покажем на карте (быстро) if (window._lastKnownPos) { this.applyPosition(window._lastKnownPos); - } else { - this.requestLocation(); } + // НЕ запрашиваем автоматически — это блокирует UI на iOS Safari. + // Юзер нажмёт кнопку "📍 Поделиться геолокацией". }, requestLocation() {